272 lines
7.5 KiB
C++
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));
|
|
}
|