279 lines
7.6 KiB
C++
279 lines
7.6 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)
|
|
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;
|
|
}
|
|
|
|
if (layerIt->renderer)
|
|
layerIt->renderer->ShutdownGl();
|
|
layerIt = mLayers.erase(layerIt);
|
|
}
|
|
|
|
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
|
|
{
|
|
if (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;
|
|
next.renderer = std::make_unique<RuntimeShaderRenderer>();
|
|
mLayers.push_back(std::move(next));
|
|
program = &mLayers.back();
|
|
}
|
|
|
|
if (program->shaderId == layer.shaderId && program->sourceFingerprint == fingerprint && program->renderer && program->renderer->HasProgram())
|
|
continue;
|
|
if (program->pendingFingerprint == fingerprint)
|
|
continue;
|
|
|
|
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;
|
|
}
|
|
|
|
bool RuntimeRenderScene::HasLayers()
|
|
{
|
|
ConsumePreparedPrograms();
|
|
|
|
for (const std::string& layerId : mLayerOrder)
|
|
{
|
|
const LayerProgram* layer = FindLayer(layerId);
|
|
if (layer && layer->renderer && layer->renderer->HasProgram())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void RuntimeRenderScene::RenderFrame(uint64_t frameIndex, unsigned width, unsigned height)
|
|
{
|
|
ConsumePreparedPrograms();
|
|
|
|
std::vector<LayerProgram*> readyLayers;
|
|
for (const std::string& layerId : mLayerOrder)
|
|
{
|
|
LayerProgram* layer = FindLayer(layerId);
|
|
if (!layer || !layer->renderer || !layer->renderer->HasProgram())
|
|
continue;
|
|
readyLayers.push_back(layer);
|
|
}
|
|
|
|
if (readyLayers.empty())
|
|
return;
|
|
|
|
GLint outputFramebuffer = 0;
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &outputFramebuffer);
|
|
|
|
if (readyLayers.size() == 1)
|
|
{
|
|
readyLayers.front()->renderer->RenderFrame(frameIndex, width, height);
|
|
return;
|
|
}
|
|
|
|
if (!EnsureLayerTargets(width, height))
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(outputFramebuffer));
|
|
readyLayers.back()->renderer->RenderFrame(frameIndex, width, height);
|
|
return;
|
|
}
|
|
|
|
GLuint layerInputTexture = 0;
|
|
std::size_t nextTargetIndex = 0;
|
|
for (std::size_t layerIndex = 0; layerIndex < readyLayers.size(); ++layerIndex)
|
|
{
|
|
const bool isFinalLayer = layerIndex == readyLayers.size() - 1;
|
|
if (isFinalLayer)
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(outputFramebuffer));
|
|
readyLayers[layerIndex]->renderer->RenderFrame(frameIndex, width, height, layerInputTexture, layerInputTexture);
|
|
continue;
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mLayerFramebuffers[nextTargetIndex]);
|
|
readyLayers[layerIndex]->renderer->RenderFrame(frameIndex, width, height, layerInputTexture, layerInputTexture);
|
|
layerInputTexture = mLayerTextures[nextTargetIndex];
|
|
nextTargetIndex = 1 - nextTargetIndex;
|
|
}
|
|
}
|
|
|
|
void RuntimeRenderScene::ShutdownGl()
|
|
{
|
|
mPrepareWorker.Stop();
|
|
for (LayerProgram& layer : mLayers)
|
|
{
|
|
if (layer.renderer)
|
|
layer.renderer->ShutdownGl();
|
|
}
|
|
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;
|
|
}
|
|
|
|
std::unique_ptr<RuntimeShaderRenderer> nextRenderer = std::make_unique<RuntimeShaderRenderer>();
|
|
std::string error;
|
|
if (!nextRenderer->CommitPreparedProgram(preparedProgram, error))
|
|
{
|
|
preparedProgram.ReleaseGl();
|
|
continue;
|
|
}
|
|
|
|
if (layer->renderer)
|
|
layer->renderer->ShutdownGl();
|
|
layer->renderer = std::move(nextRenderer);
|
|
layer->shaderId = preparedProgram.shaderId;
|
|
layer->sourceFingerprint = preparedProgram.sourceFingerprint;
|
|
layer->pendingFingerprint.clear();
|
|
}
|
|
}
|
|
|
|
bool RuntimeRenderScene::EnsureLayerTargets(unsigned width, unsigned height)
|
|
{
|
|
if (width == 0 || height == 0)
|
|
return false;
|
|
if (mLayerFramebuffers[0] != 0 && mLayerFramebuffers[1] != 0 && mLayerTextures[0] != 0 && mLayerTextures[1] != 0
|
|
&& mLayerTargetWidth == width && mLayerTargetHeight == height)
|
|
return true;
|
|
|
|
DestroyLayerTargets();
|
|
mLayerTargetWidth = width;
|
|
mLayerTargetHeight = height;
|
|
|
|
glGenFramebuffers(2, mLayerFramebuffers);
|
|
glGenTextures(2, mLayerTextures);
|
|
for (int index = 0; index < 2; ++index)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, mLayerTextures[index]);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexImage2D(
|
|
GL_TEXTURE_2D,
|
|
0,
|
|
GL_RGBA8,
|
|
static_cast<GLsizei>(width),
|
|
static_cast<GLsizei>(height),
|
|
0,
|
|
GL_BGRA,
|
|
GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
nullptr);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, mLayerFramebuffers[index]);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mLayerTextures[index], 0);
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
DestroyLayerTargets();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
return true;
|
|
}
|
|
|
|
void RuntimeRenderScene::DestroyLayerTargets()
|
|
{
|
|
if (mLayerFramebuffers[0] != 0 || mLayerFramebuffers[1] != 0)
|
|
glDeleteFramebuffers(2, mLayerFramebuffers);
|
|
if (mLayerTextures[0] != 0 || mLayerTextures[1] != 0)
|
|
glDeleteTextures(2, mLayerTextures);
|
|
mLayerFramebuffers[0] = 0;
|
|
mLayerFramebuffers[1] = 0;
|
|
mLayerTextures[0] = 0;
|
|
mLayerTextures[1] = 0;
|
|
mLayerTargetWidth = 0;
|
|
mLayerTargetHeight = 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
std::string RuntimeRenderScene::Fingerprint(const RuntimeShaderArtifact& artifact)
|
|
{
|
|
const std::hash<std::string> hasher;
|
|
return artifact.shaderId + ":" + std::to_string(artifact.fragmentShaderSource.size()) + ":" + std::to_string(hasher(artifact.fragmentShaderSource));
|
|
}
|