Files
video-shader-toys/apps/RenderCadenceCompositor/render/runtime/RuntimeRenderScene.cpp
Aiden c25ae7b25b
Some checks failed
CI / React UI Build (push) Successful in 11s
CI / Windows Release Package (push) Has been cancelled
CI / Native Windows Build And Tests (push) Has been cancelled
input buffer
2026-05-12 21:05:42 +10:00

272 lines
7.5 KiB
C++

#include "RuntimeRenderScene.h"
#include "../../platform/HiddenGlWindow.h"
#include <algorithm>
#include <functional>
#include <utility>
#ifndef GL_FRAMEBUFFER_BINDING
#define GL_FRAMEBUFFER_BINDING 0x8CA6
#endif
RuntimeRenderScene::~RuntimeRenderScene()
{
ShutdownGl();
}
bool RuntimeRenderScene::StartPrepareWorker(std::unique_ptr<HiddenGlWindow> sharedWindow, std::string& error)
{
return mPrepareWorker.Start(std::move(sharedWindow), error);
}
bool RuntimeRenderScene::CommitRenderLayers(const std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers, std::string& error)
{
ConsumePreparedPrograms();
std::vector<std::string> nextOrder;
std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel> layersToPrepare;
nextOrder.reserve(layers.size());
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
{
if (!layer.bypass)
nextOrder.push_back(layer.id);
}
for (auto layerIt = mLayers.begin(); layerIt != mLayers.end();)
{
const bool stillPresent = std::find(nextOrder.begin(), nextOrder.end(), layerIt->layerId) != nextOrder.end();
if (stillPresent)
{
++layerIt;
continue;
}
for (LayerProgram::PassProgram& pass : layerIt->passes)
{
if (pass.renderer)
pass.renderer->ShutdownGl();
}
ReleasePendingPrograms(*layerIt);
layerIt = mLayers.erase(layerIt);
}
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
{
if (layer.bypass)
continue;
if (layer.artifact.passes.empty() && layer.artifact.fragmentShaderSource.empty())
continue;
const std::string fingerprint = Fingerprint(layer.artifact);
LayerProgram* program = FindLayer(layer.id);
if (!program)
{
LayerProgram next;
next.layerId = layer.id;
mLayers.push_back(std::move(next));
program = &mLayers.back();
}
bool hasReadyPass = false;
for (const LayerProgram::PassProgram& pass : program->passes)
{
if (pass.renderer && pass.renderer->HasProgram())
{
hasReadyPass = true;
break;
}
}
if (program->shaderId == layer.shaderId && program->sourceFingerprint == fingerprint && hasReadyPass)
{
for (LayerProgram::PassProgram& pass : program->passes)
{
if (pass.renderer)
pass.renderer->UpdateArtifactState(layer.artifact);
}
continue;
}
if (program->pendingFingerprint == fingerprint)
continue;
ReleasePendingPrograms(*program);
program->shaderId = layer.shaderId;
program->pendingFingerprint = fingerprint;
layersToPrepare.push_back(layer);
}
mLayerOrder = std::move(nextOrder);
if (!layersToPrepare.empty())
mPrepareWorker.Submit(layersToPrepare);
error.clear();
return true;
}
void RuntimeRenderScene::ShutdownGl()
{
mPrepareWorker.Stop();
for (LayerProgram& layer : mLayers)
{
for (LayerProgram::PassProgram& pass : layer.passes)
{
if (pass.renderer)
pass.renderer->ShutdownGl();
}
ReleasePendingPrograms(layer);
}
mLayers.clear();
mLayerOrder.clear();
DestroyLayerTargets();
}
void RuntimeRenderScene::ConsumePreparedPrograms()
{
RuntimePreparedShaderProgram preparedProgram;
while (mPrepareWorker.TryConsume(preparedProgram))
{
if (!preparedProgram.succeeded)
{
preparedProgram.ReleaseGl();
continue;
}
LayerProgram* layer = FindLayer(preparedProgram.layerId);
if (!layer || layer->pendingFingerprint != preparedProgram.sourceFingerprint)
{
preparedProgram.ReleaseGl();
continue;
}
bool replacesExistingPendingPass = false;
for (RuntimePreparedShaderProgram& existing : layer->pendingPreparedPrograms)
{
if (existing.passId != preparedProgram.passId)
continue;
existing.ReleaseGl();
existing = std::move(preparedProgram);
replacesExistingPendingPass = true;
break;
}
if (!replacesExistingPendingPass)
layer->pendingPreparedPrograms.push_back(std::move(preparedProgram));
TryCommitPendingPrograms(*layer);
}
}
void RuntimeRenderScene::ReleasePendingPrograms(LayerProgram& layer)
{
for (RuntimePreparedShaderProgram& program : layer.pendingPreparedPrograms)
program.ReleaseGl();
layer.pendingPreparedPrograms.clear();
}
void RuntimeRenderScene::TryCommitPendingPrograms(LayerProgram& layer)
{
if (layer.pendingPreparedPrograms.empty())
return;
const RuntimeShaderArtifact& artifact = layer.pendingPreparedPrograms.front().artifact;
const std::size_t expectedPassCount = artifact.passes.empty() ? 1 : artifact.passes.size();
if (layer.pendingPreparedPrograms.size() < expectedPassCount)
return;
std::vector<LayerProgram::PassProgram> nextPasses;
nextPasses.reserve(expectedPassCount);
for (const RuntimeShaderPassArtifact& passArtifact : artifact.passes)
{
auto preparedIt = std::find_if(
layer.pendingPreparedPrograms.begin(),
layer.pendingPreparedPrograms.end(),
[&passArtifact](const RuntimePreparedShaderProgram& prepared) {
return prepared.passId == passArtifact.passId;
});
if (preparedIt == layer.pendingPreparedPrograms.end())
return;
std::unique_ptr<RuntimeShaderRenderer> nextRenderer = std::make_unique<RuntimeShaderRenderer>();
std::string error;
if (!nextRenderer->CommitPreparedProgram(*preparedIt, error))
{
ReleasePendingPrograms(layer);
return;
}
LayerProgram::PassProgram nextPass;
nextPass.passId = preparedIt->passId;
nextPass.inputNames = preparedIt->inputNames;
nextPass.outputName = preparedIt->outputName.empty() ? preparedIt->passId : preparedIt->outputName;
nextPass.renderer = std::move(nextRenderer);
nextPasses.push_back(std::move(nextPass));
}
if (artifact.passes.empty())
{
RuntimePreparedShaderProgram& prepared = layer.pendingPreparedPrograms.front();
std::unique_ptr<RuntimeShaderRenderer> nextRenderer = std::make_unique<RuntimeShaderRenderer>();
std::string error;
if (!nextRenderer->CommitPreparedProgram(prepared, error))
{
ReleasePendingPrograms(layer);
return;
}
LayerProgram::PassProgram nextPass;
nextPass.passId = prepared.passId;
nextPass.inputNames = prepared.inputNames;
nextPass.outputName = prepared.outputName.empty() ? prepared.passId : prepared.outputName;
nextPass.renderer = std::move(nextRenderer);
nextPasses.push_back(std::move(nextPass));
}
for (LayerProgram::PassProgram& pass : layer.passes)
{
if (pass.renderer)
pass.renderer->ShutdownGl();
}
layer.passes = std::move(nextPasses);
layer.shaderId = artifact.shaderId;
layer.sourceFingerprint = layer.pendingPreparedPrograms.front().sourceFingerprint;
layer.pendingFingerprint.clear();
layer.pendingPreparedPrograms.clear();
}
RuntimeRenderScene::LayerProgram* RuntimeRenderScene::FindLayer(const std::string& layerId)
{
for (LayerProgram& layer : mLayers)
{
if (layer.layerId == layerId)
return &layer;
}
return nullptr;
}
const RuntimeRenderScene::LayerProgram* RuntimeRenderScene::FindLayer(const std::string& layerId) const
{
for (const LayerProgram& layer : mLayers)
{
if (layer.layerId == layerId)
return &layer;
}
return nullptr;
}
RuntimeRenderScene::LayerProgram::PassProgram* RuntimeRenderScene::FindPass(LayerProgram& layer, const std::string& passId)
{
for (LayerProgram::PassProgram& pass : layer.passes)
{
if (pass.passId == passId)
return &pass;
}
return nullptr;
}
std::string RuntimeRenderScene::Fingerprint(const RuntimeShaderArtifact& artifact)
{
const std::hash<std::string> hasher;
std::string source;
for (const RuntimeShaderPassArtifact& pass : artifact.passes)
source += pass.passId + ":" + pass.outputName + ":" + pass.fragmentShaderSource + "\n";
if (source.empty())
source = artifact.fragmentShaderSource;
return artifact.shaderId + ":" + std::to_string(source.size()) + ":" + std::to_string(hasher(source));
}