Clean up
This commit is contained in:
@@ -1,271 +0,0 @@
|
||||
#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));
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../runtime/RuntimeLayerModel.h"
|
||||
#include "RuntimeShaderPrepareWorker.h"
|
||||
#include "RuntimeShaderRenderer.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class RuntimeRenderScene
|
||||
{
|
||||
public:
|
||||
RuntimeRenderScene() = default;
|
||||
RuntimeRenderScene(const RuntimeRenderScene&) = delete;
|
||||
RuntimeRenderScene& operator=(const RuntimeRenderScene&) = delete;
|
||||
~RuntimeRenderScene();
|
||||
|
||||
bool StartPrepareWorker(std::unique_ptr<HiddenGlWindow> sharedWindow, std::string& error);
|
||||
bool CommitRenderLayers(const std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers, std::string& error);
|
||||
bool HasLayers();
|
||||
void RenderFrame(uint64_t frameIndex, unsigned width, unsigned height, GLuint videoInputTexture = 0);
|
||||
void ShutdownGl();
|
||||
|
||||
private:
|
||||
struct LayerProgram
|
||||
{
|
||||
std::string layerId;
|
||||
std::string shaderId;
|
||||
std::string sourceFingerprint;
|
||||
std::string pendingFingerprint;
|
||||
std::vector<RuntimePreparedShaderProgram> pendingPreparedPrograms;
|
||||
struct PassProgram
|
||||
{
|
||||
std::string passId;
|
||||
std::vector<std::string> inputNames;
|
||||
std::string outputName;
|
||||
std::unique_ptr<RuntimeShaderRenderer> renderer;
|
||||
};
|
||||
std::vector<PassProgram> passes;
|
||||
};
|
||||
|
||||
void ConsumePreparedPrograms();
|
||||
void ReleasePendingPrograms(LayerProgram& layer);
|
||||
void TryCommitPendingPrograms(LayerProgram& layer);
|
||||
bool EnsureLayerTargets(unsigned width, unsigned height);
|
||||
void DestroyLayerTargets();
|
||||
GLuint RenderLayer(LayerProgram& layer, uint64_t frameIndex, unsigned width, unsigned height, GLuint videoInputTexture, GLuint layerInputTexture, GLuint outputFramebuffer, bool renderToOutput);
|
||||
LayerProgram* FindLayer(const std::string& layerId);
|
||||
const LayerProgram* FindLayer(const std::string& layerId) const;
|
||||
LayerProgram::PassProgram* FindPass(LayerProgram& layer, const std::string& passId);
|
||||
static std::string Fingerprint(const RuntimeShaderArtifact& artifact);
|
||||
|
||||
RuntimeShaderPrepareWorker mPrepareWorker;
|
||||
std::vector<LayerProgram> mLayers;
|
||||
std::vector<std::string> mLayerOrder;
|
||||
GLuint mLayerFramebuffers[4] = {};
|
||||
GLuint mLayerTextures[4] = {};
|
||||
unsigned mLayerTargetWidth = 0;
|
||||
unsigned mLayerTargetHeight = 0;
|
||||
};
|
||||
@@ -1,219 +0,0 @@
|
||||
#include "RuntimeRenderScene.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifndef GL_FRAMEBUFFER_BINDING
|
||||
#define GL_FRAMEBUFFER_BINDING 0x8CA6
|
||||
#endif
|
||||
|
||||
bool RuntimeRenderScene::HasLayers()
|
||||
{
|
||||
ConsumePreparedPrograms();
|
||||
|
||||
for (const std::string& layerId : mLayerOrder)
|
||||
{
|
||||
const LayerProgram* layer = FindLayer(layerId);
|
||||
if (!layer)
|
||||
continue;
|
||||
for (const LayerProgram::PassProgram& pass : layer->passes)
|
||||
{
|
||||
if (pass.renderer && pass.renderer->HasProgram())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RuntimeRenderScene::RenderFrame(uint64_t frameIndex, unsigned width, unsigned height, GLuint videoInputTexture)
|
||||
{
|
||||
ConsumePreparedPrograms();
|
||||
|
||||
std::vector<LayerProgram*> readyLayers;
|
||||
for (const std::string& layerId : mLayerOrder)
|
||||
{
|
||||
LayerProgram* layer = FindLayer(layerId);
|
||||
if (!layer)
|
||||
continue;
|
||||
for (const LayerProgram::PassProgram& pass : layer->passes)
|
||||
{
|
||||
if (pass.renderer && pass.renderer->HasProgram())
|
||||
{
|
||||
readyLayers.push_back(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (readyLayers.empty())
|
||||
return;
|
||||
|
||||
GLint outputFramebuffer = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &outputFramebuffer);
|
||||
|
||||
if (readyLayers.size() == 1)
|
||||
{
|
||||
RenderLayer(*readyLayers.front(), frameIndex, width, height, videoInputTexture, videoInputTexture, static_cast<GLuint>(outputFramebuffer), true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EnsureLayerTargets(width, height))
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(outputFramebuffer));
|
||||
RenderLayer(*readyLayers.back(), frameIndex, width, height, videoInputTexture, videoInputTexture, static_cast<GLuint>(outputFramebuffer), true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Shader source contract:
|
||||
// - gVideoInput is the decoded current input texture for every layer in the stack.
|
||||
// - gLayerInput starts as gVideoInput for the first layer, then becomes the previous layer output.
|
||||
GLuint layerInputTexture = videoInputTexture;
|
||||
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));
|
||||
RenderLayer(*readyLayers[layerIndex], frameIndex, width, height, videoInputTexture, layerInputTexture, static_cast<GLuint>(outputFramebuffer), true);
|
||||
continue;
|
||||
}
|
||||
|
||||
RenderLayer(*readyLayers[layerIndex], frameIndex, width, height, videoInputTexture, layerInputTexture, mLayerFramebuffers[nextTargetIndex], true);
|
||||
layerInputTexture = mLayerTextures[nextTargetIndex];
|
||||
nextTargetIndex = 1 - nextTargetIndex;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint RuntimeRenderScene::RenderLayer(
|
||||
LayerProgram& layer,
|
||||
uint64_t frameIndex,
|
||||
unsigned width,
|
||||
unsigned height,
|
||||
GLuint videoInputTexture,
|
||||
GLuint layerInputTexture,
|
||||
GLuint outputFramebuffer,
|
||||
bool renderToOutput)
|
||||
{
|
||||
GLuint namedOutputs[2] = {};
|
||||
std::string namedOutputNames[2];
|
||||
std::size_t nextTargetIndex = 2;
|
||||
GLuint lastOutputTexture = layerInputTexture;
|
||||
|
||||
for (LayerProgram::PassProgram& pass : layer.passes)
|
||||
{
|
||||
if (!pass.renderer || !pass.renderer->HasProgram())
|
||||
continue;
|
||||
|
||||
GLuint sourceTexture = videoInputTexture;
|
||||
if (!pass.inputNames.empty())
|
||||
{
|
||||
const std::string& inputName = pass.inputNames.front();
|
||||
if (inputName == "videoInput")
|
||||
{
|
||||
sourceTexture = videoInputTexture;
|
||||
}
|
||||
else if (inputName != "layerInput")
|
||||
{
|
||||
// Named intermediate pass inputs currently use the gVideoInput binding slot as the
|
||||
// selected pass source. Layer stack shaders should use gLayerInput for previous-layer
|
||||
// sampling and gVideoInput for the original input frame.
|
||||
for (std::size_t index = 0; index < 2; ++index)
|
||||
{
|
||||
if (namedOutputNames[index] == inputName)
|
||||
{
|
||||
sourceTexture = namedOutputs[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool writesLayerOutput = pass.outputName == "layerOutput";
|
||||
if (writesLayerOutput && renderToOutput)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, outputFramebuffer);
|
||||
pass.renderer->RenderFrame(frameIndex, width, height, sourceTexture, layerInputTexture);
|
||||
lastOutputTexture = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!EnsureLayerTargets(width, height))
|
||||
continue;
|
||||
|
||||
const std::size_t targetIndex = nextTargetIndex;
|
||||
nextTargetIndex = nextTargetIndex == 2 ? 3 : 2;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mLayerFramebuffers[targetIndex]);
|
||||
pass.renderer->RenderFrame(frameIndex, width, height, sourceTexture, layerInputTexture);
|
||||
const std::size_t namedIndex = targetIndex - 2;
|
||||
namedOutputs[namedIndex] = mLayerTextures[targetIndex];
|
||||
namedOutputNames[namedIndex] = pass.outputName;
|
||||
lastOutputTexture = mLayerTextures[targetIndex];
|
||||
}
|
||||
|
||||
return lastOutputTexture;
|
||||
}
|
||||
|
||||
bool RuntimeRenderScene::EnsureLayerTargets(unsigned width, unsigned height)
|
||||
{
|
||||
if (width == 0 || height == 0)
|
||||
return false;
|
||||
if (mLayerFramebuffers[0] != 0 && mLayerFramebuffers[1] != 0 && mLayerFramebuffers[2] != 0 && mLayerFramebuffers[3] != 0
|
||||
&& mLayerTextures[0] != 0 && mLayerTextures[1] != 0 && mLayerTextures[2] != 0 && mLayerTextures[3] != 0
|
||||
&& mLayerTargetWidth == width && mLayerTargetHeight == height)
|
||||
return true;
|
||||
|
||||
DestroyLayerTargets();
|
||||
mLayerTargetWidth = width;
|
||||
mLayerTargetHeight = height;
|
||||
|
||||
glGenFramebuffers(4, mLayerFramebuffers);
|
||||
glGenTextures(4, mLayerTextures);
|
||||
for (int index = 0; index < 4; ++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 || mLayerFramebuffers[2] != 0 || mLayerFramebuffers[3] != 0)
|
||||
glDeleteFramebuffers(4, mLayerFramebuffers);
|
||||
if (mLayerTextures[0] != 0 || mLayerTextures[1] != 0 || mLayerTextures[2] != 0 || mLayerTextures[3] != 0)
|
||||
glDeleteTextures(4, mLayerTextures);
|
||||
for (int index = 0; index < 4; ++index)
|
||||
{
|
||||
mLayerFramebuffers[index] = 0;
|
||||
mLayerTextures[index] = 0;
|
||||
}
|
||||
mLayerTargetWidth = 0;
|
||||
mLayerTargetHeight = 0;
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
#include "RuntimeShaderParams.h"
|
||||
|
||||
#include "Std140Buffer.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace
|
||||
{
|
||||
ShaderParameterValue DefaultValueForDefinition(const ShaderParameterDefinition& definition)
|
||||
{
|
||||
ShaderParameterValue value;
|
||||
switch (definition.type)
|
||||
{
|
||||
case ShaderParameterType::Float:
|
||||
value.numberValues = definition.defaultNumbers.empty() ? std::vector<double>{ 0.0 } : definition.defaultNumbers;
|
||||
break;
|
||||
case ShaderParameterType::Vec2:
|
||||
value.numberValues = definition.defaultNumbers.size() == 2 ? definition.defaultNumbers : std::vector<double>{ 0.0, 0.0 };
|
||||
break;
|
||||
case ShaderParameterType::Color:
|
||||
value.numberValues = definition.defaultNumbers.size() == 4 ? definition.defaultNumbers : std::vector<double>{ 1.0, 1.0, 1.0, 1.0 };
|
||||
break;
|
||||
case ShaderParameterType::Boolean:
|
||||
value.booleanValue = definition.defaultBoolean;
|
||||
break;
|
||||
case ShaderParameterType::Enum:
|
||||
value.enumValue = definition.defaultEnumValue;
|
||||
break;
|
||||
case ShaderParameterType::Text:
|
||||
value.textValue = definition.defaultTextValue;
|
||||
break;
|
||||
case ShaderParameterType::Trigger:
|
||||
value.numberValues = { 0.0, -1000000.0 };
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
int EnumIndexForDefault(const ShaderParameterDefinition& definition, const ShaderParameterValue& value)
|
||||
{
|
||||
for (std::size_t optionIndex = 0; optionIndex < definition.enumOptions.size(); ++optionIndex)
|
||||
{
|
||||
if (definition.enumOptions[optionIndex].value == value.enumValue)
|
||||
return static_cast<int>(optionIndex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double UtcSecondsOfDay()
|
||||
{
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto seconds = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
|
||||
const long long secondsPerDay = 24 * 60 * 60;
|
||||
long long daySeconds = seconds % secondsPerDay;
|
||||
if (daySeconds < 0)
|
||||
daySeconds += secondsPerDay;
|
||||
return static_cast<double>(daySeconds);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<unsigned char> BuildRuntimeShaderGlobalParamsStd140(
|
||||
const RuntimeShaderArtifact& artifact,
|
||||
uint64_t frameIndex,
|
||||
unsigned width,
|
||||
unsigned height)
|
||||
{
|
||||
std::vector<unsigned char> buffer;
|
||||
buffer.reserve(512);
|
||||
|
||||
AppendStd140Float(buffer, static_cast<float>(frameIndex) / 60.0f);
|
||||
AppendStd140Vec2(buffer, static_cast<float>(width), static_cast<float>(height));
|
||||
AppendStd140Vec2(buffer, static_cast<float>(width), static_cast<float>(height));
|
||||
AppendStd140Float(buffer, static_cast<float>(UtcSecondsOfDay()));
|
||||
AppendStd140Float(buffer, 0.0f);
|
||||
AppendStd140Float(buffer, 0.37f);
|
||||
AppendStd140Float(buffer, static_cast<float>(frameIndex));
|
||||
AppendStd140Float(buffer, 1.0f);
|
||||
AppendStd140Float(buffer, 0.0f);
|
||||
AppendStd140Int(buffer, 0);
|
||||
AppendStd140Int(buffer, 0);
|
||||
AppendStd140Int(buffer, 0);
|
||||
|
||||
for (const ShaderParameterDefinition& definition : artifact.parameterDefinitions)
|
||||
{
|
||||
const auto valueIt = artifact.parameterValues.find(definition.id);
|
||||
const ShaderParameterValue value = valueIt == artifact.parameterValues.end()
|
||||
? DefaultValueForDefinition(definition)
|
||||
: valueIt->second;
|
||||
switch (definition.type)
|
||||
{
|
||||
case ShaderParameterType::Float:
|
||||
AppendStd140Float(buffer, value.numberValues.empty() ? 0.0f : static_cast<float>(value.numberValues[0]));
|
||||
break;
|
||||
case ShaderParameterType::Vec2:
|
||||
AppendStd140Vec2(buffer,
|
||||
value.numberValues.size() > 0 ? static_cast<float>(value.numberValues[0]) : 0.0f,
|
||||
value.numberValues.size() > 1 ? static_cast<float>(value.numberValues[1]) : 0.0f);
|
||||
break;
|
||||
case ShaderParameterType::Color:
|
||||
AppendStd140Vec4(buffer,
|
||||
value.numberValues.size() > 0 ? static_cast<float>(value.numberValues[0]) : 1.0f,
|
||||
value.numberValues.size() > 1 ? static_cast<float>(value.numberValues[1]) : 1.0f,
|
||||
value.numberValues.size() > 2 ? static_cast<float>(value.numberValues[2]) : 1.0f,
|
||||
value.numberValues.size() > 3 ? static_cast<float>(value.numberValues[3]) : 1.0f);
|
||||
break;
|
||||
case ShaderParameterType::Boolean:
|
||||
AppendStd140Int(buffer, value.booleanValue ? 1 : 0);
|
||||
break;
|
||||
case ShaderParameterType::Enum:
|
||||
AppendStd140Int(buffer, EnumIndexForDefault(definition, value));
|
||||
break;
|
||||
case ShaderParameterType::Text:
|
||||
break;
|
||||
case ShaderParameterType::Trigger:
|
||||
AppendStd140Int(buffer, value.numberValues.empty() ? 0 : static_cast<int>(value.numberValues[0]));
|
||||
AppendStd140Float(buffer, value.numberValues.size() > 1 ? static_cast<float>(value.numberValues[1]) : -1000000.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.resize(AlignStd140(buffer.size(), 16), 0);
|
||||
return buffer;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../runtime/RuntimeShaderArtifact.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
std::vector<unsigned char> BuildRuntimeShaderGlobalParamsStd140(
|
||||
const RuntimeShaderArtifact& artifact,
|
||||
uint64_t frameIndex,
|
||||
unsigned width,
|
||||
unsigned height);
|
||||
@@ -1,179 +0,0 @@
|
||||
#include "RuntimeShaderPrepareWorker.h"
|
||||
|
||||
#include "../../platform/HiddenGlWindow.h"
|
||||
#include "RuntimeShaderRenderer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
RuntimeShaderPrepareWorker::~RuntimeShaderPrepareWorker()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool RuntimeShaderPrepareWorker::Start(std::unique_ptr<HiddenGlWindow> sharedWindow, std::string& error)
|
||||
{
|
||||
if (mThread.joinable())
|
||||
return true;
|
||||
if (!sharedWindow || sharedWindow->DeviceContext() == nullptr || sharedWindow->Context() == nullptr)
|
||||
{
|
||||
error = "Runtime shader prepare worker needs an existing shared GL context.";
|
||||
return false;
|
||||
}
|
||||
|
||||
mWindow = std::move(sharedWindow);
|
||||
mStopping.store(false, std::memory_order_release);
|
||||
mStarted.store(false, std::memory_order_release);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mStartupReady = false;
|
||||
mStartupError.clear();
|
||||
}
|
||||
mThread = std::thread([this]() { ThreadMain(); });
|
||||
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
if (!mStartupCondition.wait_for(lock, std::chrono::seconds(3), [this]() {
|
||||
return mStartupReady || !mStartupError.empty();
|
||||
}))
|
||||
{
|
||||
error = "Timed out starting runtime shader prepare worker.";
|
||||
lock.unlock();
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
if (!mStartupError.empty())
|
||||
{
|
||||
error = mStartupError;
|
||||
lock.unlock();
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuntimeShaderPrepareWorker::Stop()
|
||||
{
|
||||
mStopping.store(true, std::memory_order_release);
|
||||
mCondition.notify_all();
|
||||
if (mThread.joinable())
|
||||
mThread.join();
|
||||
|
||||
std::deque<RuntimePreparedShaderProgram> completed;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mRequests.clear();
|
||||
completed.swap(mCompleted);
|
||||
}
|
||||
for (RuntimePreparedShaderProgram& program : completed)
|
||||
program.ReleaseGl();
|
||||
mWindow.reset();
|
||||
mStarted.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
void RuntimeShaderPrepareWorker::Submit(const std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
for (const RenderCadenceCompositor::RuntimeRenderLayerModel& layer : layers)
|
||||
{
|
||||
if (layer.artifact.passes.empty() && layer.artifact.fragmentShaderSource.empty())
|
||||
continue;
|
||||
|
||||
std::vector<RuntimeShaderPassArtifact> passes = layer.artifact.passes;
|
||||
if (passes.empty())
|
||||
{
|
||||
RuntimeShaderPassArtifact pass;
|
||||
pass.passId = "main";
|
||||
pass.fragmentShaderSource = layer.artifact.fragmentShaderSource;
|
||||
pass.outputName = "layerOutput";
|
||||
passes.push_back(std::move(pass));
|
||||
}
|
||||
|
||||
auto sameLayer = [&layer](const PrepareRequest& existing) {
|
||||
return existing.layerId == layer.id;
|
||||
};
|
||||
mRequests.erase(std::remove_if(mRequests.begin(), mRequests.end(), sameLayer), mRequests.end());
|
||||
|
||||
for (const RuntimeShaderPassArtifact& pass : passes)
|
||||
{
|
||||
PrepareRequest request;
|
||||
request.layerId = layer.id;
|
||||
request.shaderId = layer.shaderId;
|
||||
request.passId = pass.passId;
|
||||
request.sourceFingerprint = Fingerprint(layer.artifact);
|
||||
request.artifact = layer.artifact;
|
||||
request.passArtifact = pass;
|
||||
mRequests.push_back(std::move(request));
|
||||
}
|
||||
}
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
bool RuntimeShaderPrepareWorker::TryConsume(RuntimePreparedShaderProgram& preparedProgram)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if (mCompleted.empty())
|
||||
return false;
|
||||
|
||||
preparedProgram = std::move(mCompleted.front());
|
||||
mCompleted.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuntimeShaderPrepareWorker::ThreadMain()
|
||||
{
|
||||
if (!mWindow || !mWindow->MakeCurrent())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mStartupError = "Runtime shader prepare worker could not make shared GL context current.";
|
||||
mStartupCondition.notify_all();
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mStartupReady = true;
|
||||
}
|
||||
mStarted.store(true, std::memory_order_release);
|
||||
mStartupCondition.notify_all();
|
||||
|
||||
while (!mStopping.load(std::memory_order_acquire))
|
||||
{
|
||||
PrepareRequest request;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
mCondition.wait(lock, [this]() {
|
||||
return mStopping.load(std::memory_order_acquire) || !mRequests.empty();
|
||||
});
|
||||
if (mStopping.load(std::memory_order_acquire))
|
||||
break;
|
||||
request = std::move(mRequests.front());
|
||||
mRequests.pop_front();
|
||||
}
|
||||
|
||||
RuntimePreparedShaderProgram preparedProgram;
|
||||
RuntimeShaderRenderer::BuildPreparedPassProgram(
|
||||
request.layerId,
|
||||
request.sourceFingerprint,
|
||||
request.artifact,
|
||||
request.passArtifact,
|
||||
preparedProgram);
|
||||
glFlush();
|
||||
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mCompleted.push_back(std::move(preparedProgram));
|
||||
}
|
||||
|
||||
mWindow->ClearCurrent();
|
||||
}
|
||||
|
||||
std::string RuntimeShaderPrepareWorker::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));
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "RuntimeShaderProgram.h"
|
||||
#include "../../runtime/RuntimeLayerModel.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
class HiddenGlWindow;
|
||||
|
||||
class RuntimeShaderPrepareWorker
|
||||
{
|
||||
public:
|
||||
RuntimeShaderPrepareWorker() = default;
|
||||
RuntimeShaderPrepareWorker(const RuntimeShaderPrepareWorker&) = delete;
|
||||
RuntimeShaderPrepareWorker& operator=(const RuntimeShaderPrepareWorker&) = delete;
|
||||
~RuntimeShaderPrepareWorker();
|
||||
|
||||
bool Start(std::unique_ptr<HiddenGlWindow> sharedWindow, std::string& error);
|
||||
void Stop();
|
||||
|
||||
void Submit(const std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers);
|
||||
bool TryConsume(RuntimePreparedShaderProgram& preparedProgram);
|
||||
|
||||
private:
|
||||
struct PrepareRequest
|
||||
{
|
||||
std::string layerId;
|
||||
std::string shaderId;
|
||||
std::string passId;
|
||||
std::string sourceFingerprint;
|
||||
RuntimeShaderArtifact artifact;
|
||||
RuntimeShaderPassArtifact passArtifact;
|
||||
};
|
||||
|
||||
void ThreadMain();
|
||||
static std::string Fingerprint(const RuntimeShaderArtifact& artifact);
|
||||
|
||||
std::unique_ptr<HiddenGlWindow> mWindow;
|
||||
std::mutex mMutex;
|
||||
std::condition_variable mCondition;
|
||||
std::deque<PrepareRequest> mRequests;
|
||||
std::deque<RuntimePreparedShaderProgram> mCompleted;
|
||||
std::condition_variable mStartupCondition;
|
||||
std::thread mThread;
|
||||
std::atomic<bool> mStopping{ false };
|
||||
std::atomic<bool> mStarted{ false };
|
||||
bool mStartupReady = false;
|
||||
std::string mStartupError;
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "GLExtensions.h"
|
||||
#include "../../runtime/RuntimeShaderArtifact.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct RuntimePreparedShaderProgram
|
||||
{
|
||||
std::string layerId;
|
||||
std::string shaderId;
|
||||
std::string passId;
|
||||
std::string sourceFingerprint;
|
||||
RuntimeShaderArtifact artifact;
|
||||
RuntimeShaderPassArtifact passArtifact;
|
||||
std::vector<std::string> inputNames;
|
||||
std::string outputName;
|
||||
GLuint program = 0;
|
||||
GLuint vertexShader = 0;
|
||||
GLuint fragmentShader = 0;
|
||||
bool succeeded = false;
|
||||
std::string error;
|
||||
|
||||
void ReleaseGl()
|
||||
{
|
||||
if (program != 0)
|
||||
glDeleteProgram(program);
|
||||
if (vertexShader != 0)
|
||||
glDeleteShader(vertexShader);
|
||||
if (fragmentShader != 0)
|
||||
glDeleteShader(fragmentShader);
|
||||
program = 0;
|
||||
vertexShader = 0;
|
||||
fragmentShader = 0;
|
||||
}
|
||||
};
|
||||
@@ -1,320 +0,0 @@
|
||||
#include "RuntimeShaderRenderer.h"
|
||||
|
||||
#include "RuntimeShaderParams.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr GLuint kGlobalParamsBindingPoint = 0;
|
||||
constexpr GLuint kVideoInputTextureUnit = 0;
|
||||
constexpr GLuint kLayerInputTextureUnit = 1;
|
||||
|
||||
const char* kVertexShaderSource = R"GLSL(
|
||||
#version 430 core
|
||||
out vec2 vTexCoord;
|
||||
void main()
|
||||
{
|
||||
vec2 positions[3] = vec2[3](
|
||||
vec2(-1.0, -1.0),
|
||||
vec2( 3.0, -1.0),
|
||||
vec2(-1.0, 3.0));
|
||||
vec2 texCoords[3] = vec2[3](
|
||||
vec2(0.0, 0.0),
|
||||
vec2(2.0, 0.0),
|
||||
vec2(0.0, 2.0));
|
||||
gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);
|
||||
vTexCoord = texCoords[gl_VertexID];
|
||||
}
|
||||
)GLSL";
|
||||
|
||||
}
|
||||
|
||||
RuntimeShaderRenderer::~RuntimeShaderRenderer()
|
||||
{
|
||||
ShutdownGl();
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::CommitPreparedProgram(RuntimePreparedShaderProgram& preparedProgram, std::string& error)
|
||||
{
|
||||
if (!preparedProgram.succeeded || preparedProgram.program == 0)
|
||||
{
|
||||
error = preparedProgram.error.empty() ? "Prepared runtime shader program is not valid." : preparedProgram.error;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EnsureStaticGlResources(error))
|
||||
return false;
|
||||
|
||||
DestroyProgram();
|
||||
mProgram = preparedProgram.program;
|
||||
mVertexShader = preparedProgram.vertexShader;
|
||||
mFragmentShader = preparedProgram.fragmentShader;
|
||||
mArtifact = preparedProgram.artifact;
|
||||
preparedProgram.program = 0;
|
||||
preparedProgram.vertexShader = 0;
|
||||
preparedProgram.fragmentShader = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::UpdateArtifactState(const RuntimeShaderArtifact& artifact)
|
||||
{
|
||||
mArtifact.parameterDefinitions = artifact.parameterDefinitions;
|
||||
mArtifact.parameterValues = artifact.parameterValues;
|
||||
mArtifact.message = artifact.message;
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::BuildPreparedProgram(
|
||||
const std::string& layerId,
|
||||
const std::string& sourceFingerprint,
|
||||
const RuntimeShaderArtifact& artifact,
|
||||
RuntimePreparedShaderProgram& preparedProgram)
|
||||
{
|
||||
RuntimeShaderPassArtifact passArtifact;
|
||||
passArtifact.passId = "main";
|
||||
passArtifact.fragmentShaderSource = artifact.fragmentShaderSource;
|
||||
passArtifact.outputName = "layerOutput";
|
||||
if (!artifact.passes.empty())
|
||||
passArtifact = artifact.passes.front();
|
||||
return BuildPreparedPassProgram(layerId, sourceFingerprint, artifact, passArtifact, preparedProgram);
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::BuildPreparedPassProgram(
|
||||
const std::string& layerId,
|
||||
const std::string& sourceFingerprint,
|
||||
const RuntimeShaderArtifact& artifact,
|
||||
const RuntimeShaderPassArtifact& passArtifact,
|
||||
RuntimePreparedShaderProgram& preparedProgram)
|
||||
{
|
||||
preparedProgram = RuntimePreparedShaderProgram();
|
||||
preparedProgram.layerId = layerId;
|
||||
preparedProgram.shaderId = artifact.shaderId;
|
||||
preparedProgram.passId = passArtifact.passId;
|
||||
preparedProgram.sourceFingerprint = sourceFingerprint;
|
||||
preparedProgram.artifact = artifact;
|
||||
preparedProgram.passArtifact = passArtifact;
|
||||
preparedProgram.inputNames = passArtifact.inputNames;
|
||||
preparedProgram.outputName = passArtifact.outputName.empty() ? passArtifact.passId : passArtifact.outputName;
|
||||
|
||||
if (passArtifact.fragmentShaderSource.empty())
|
||||
{
|
||||
preparedProgram.error = "Cannot prepare an empty fragment shader.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!BuildProgram(
|
||||
passArtifact.fragmentShaderSource,
|
||||
preparedProgram.program,
|
||||
preparedProgram.vertexShader,
|
||||
preparedProgram.fragmentShader,
|
||||
preparedProgram.error))
|
||||
{
|
||||
preparedProgram.ReleaseGl();
|
||||
return false;
|
||||
}
|
||||
|
||||
preparedProgram.succeeded = true;
|
||||
AssignSamplerUniforms(preparedProgram.program);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::RenderFrame(uint64_t frameIndex, unsigned width, unsigned height, GLuint sourceTexture, GLuint layerInputTexture)
|
||||
{
|
||||
if (mProgram == 0)
|
||||
return;
|
||||
|
||||
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
UpdateGlobalParams(frameIndex, width, height);
|
||||
BindRuntimeTextures(sourceTexture, layerInputTexture);
|
||||
glBindVertexArray(mVertexArray);
|
||||
glUseProgram(mProgram);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glUseProgram(0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::ShutdownGl()
|
||||
{
|
||||
DestroyProgram();
|
||||
DestroyStaticGlResources();
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::EnsureStaticGlResources(std::string& error)
|
||||
{
|
||||
if (mVertexArray == 0)
|
||||
glGenVertexArrays(1, &mVertexArray);
|
||||
if (mGlobalParamsBuffer == 0)
|
||||
{
|
||||
glGenBuffers(1, &mGlobalParamsBuffer);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, mGlobalParamsBuffer);
|
||||
glBufferData(GL_UNIFORM_BUFFER, 1024, nullptr, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
if (mFallbackSourceTexture == 0)
|
||||
{
|
||||
const unsigned char pixels[] = {
|
||||
0, 0, 0, 255,
|
||||
96, 64, 32, 255,
|
||||
64, 96, 160, 255,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
glGenTextures(1, &mFallbackSourceTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, mFallbackSourceTexture);
|
||||
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, 2, 2, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
if (mVertexArray == 0 || mGlobalParamsBuffer == 0 || mFallbackSourceTexture == 0)
|
||||
{
|
||||
error = "Failed to create runtime shader GL resources.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::BuildProgram(const std::string& fragmentShaderSource, GLuint& program, GLuint& vertexShader, GLuint& fragmentShader, std::string& error)
|
||||
{
|
||||
program = 0;
|
||||
vertexShader = 0;
|
||||
fragmentShader = 0;
|
||||
|
||||
if (!CompileShader(GL_VERTEX_SHADER, kVertexShaderSource, vertexShader, error))
|
||||
return false;
|
||||
if (!CompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource.c_str(), fragmentShader, error))
|
||||
{
|
||||
glDeleteShader(vertexShader);
|
||||
vertexShader = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
program = glCreateProgram();
|
||||
glAttachShader(program, vertexShader);
|
||||
glAttachShader(program, fragmentShader);
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint linkResult = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linkResult);
|
||||
if (linkResult == GL_FALSE)
|
||||
{
|
||||
std::array<char, 4096> log = {};
|
||||
GLsizei length = 0;
|
||||
glGetProgramInfoLog(program, static_cast<GLsizei>(log.size()), &length, log.data());
|
||||
error = std::string(log.data(), static_cast<std::size_t>(length));
|
||||
glDeleteProgram(program);
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
program = 0;
|
||||
vertexShader = 0;
|
||||
fragmentShader = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
const GLuint globalParamsIndex = glGetUniformBlockIndex(program, "GlobalParams");
|
||||
if (globalParamsIndex != GL_INVALID_INDEX)
|
||||
glUniformBlockBinding(program, globalParamsIndex, kGlobalParamsBindingPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::AssignSamplerUniforms(GLuint program)
|
||||
{
|
||||
glUseProgram(program);
|
||||
const GLint videoInputLocation = glGetUniformLocation(program, "gVideoInput");
|
||||
if (videoInputLocation >= 0)
|
||||
glUniform1i(videoInputLocation, static_cast<GLint>(kVideoInputTextureUnit));
|
||||
const GLint videoInputArrayLocation = glGetUniformLocation(program, "gVideoInput_0");
|
||||
if (videoInputArrayLocation >= 0)
|
||||
glUniform1i(videoInputArrayLocation, static_cast<GLint>(kVideoInputTextureUnit));
|
||||
const GLint layerInputLocation = glGetUniformLocation(program, "gLayerInput");
|
||||
if (layerInputLocation >= 0)
|
||||
glUniform1i(layerInputLocation, static_cast<GLint>(kLayerInputTextureUnit));
|
||||
const GLint layerInputArrayLocation = glGetUniformLocation(program, "gLayerInput_0");
|
||||
if (layerInputArrayLocation >= 0)
|
||||
glUniform1i(layerInputArrayLocation, static_cast<GLint>(kLayerInputTextureUnit));
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::UpdateGlobalParams(uint64_t frameIndex, unsigned width, unsigned height)
|
||||
{
|
||||
std::vector<unsigned char>& buffer = mGlobalParamsScratch;
|
||||
buffer = BuildRuntimeShaderGlobalParamsStd140(mArtifact, frameIndex, width, height);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, mGlobalParamsBuffer);
|
||||
const GLsizeiptr bufferSize = static_cast<GLsizeiptr>(buffer.size());
|
||||
if (mGlobalParamsBufferSize != bufferSize)
|
||||
{
|
||||
glBufferData(GL_UNIFORM_BUFFER, bufferSize, buffer.data(), GL_DYNAMIC_DRAW);
|
||||
mGlobalParamsBufferSize = bufferSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, bufferSize, buffer.data());
|
||||
}
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, kGlobalParamsBindingPoint, mGlobalParamsBuffer);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::BindRuntimeTextures(GLuint sourceTexture, GLuint layerInputTexture)
|
||||
{
|
||||
const GLuint resolvedSourceTexture = sourceTexture != 0 ? sourceTexture : mFallbackSourceTexture;
|
||||
const GLuint resolvedLayerInputTexture = layerInputTexture != 0 ? layerInputTexture : resolvedSourceTexture;
|
||||
glActiveTexture(GL_TEXTURE0 + kVideoInputTextureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, resolvedSourceTexture);
|
||||
glActiveTexture(GL_TEXTURE0 + kLayerInputTextureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, resolvedLayerInputTexture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
bool RuntimeShaderRenderer::CompileShader(GLenum shaderType, const char* source, GLuint& shader, std::string& error)
|
||||
{
|
||||
shader = glCreateShader(shaderType);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint compileResult = GL_FALSE;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
|
||||
if (compileResult != GL_FALSE)
|
||||
return true;
|
||||
|
||||
std::array<char, 4096> log = {};
|
||||
GLsizei length = 0;
|
||||
glGetShaderInfoLog(shader, static_cast<GLsizei>(log.size()), &length, log.data());
|
||||
error = std::string(log.data(), static_cast<std::size_t>(length));
|
||||
glDeleteShader(shader);
|
||||
shader = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::DestroyProgram()
|
||||
{
|
||||
if (mProgram != 0)
|
||||
glDeleteProgram(mProgram);
|
||||
if (mVertexShader != 0)
|
||||
glDeleteShader(mVertexShader);
|
||||
if (mFragmentShader != 0)
|
||||
glDeleteShader(mFragmentShader);
|
||||
mProgram = 0;
|
||||
mVertexShader = 0;
|
||||
mFragmentShader = 0;
|
||||
}
|
||||
|
||||
void RuntimeShaderRenderer::DestroyStaticGlResources()
|
||||
{
|
||||
if (mGlobalParamsBuffer != 0)
|
||||
glDeleteBuffers(1, &mGlobalParamsBuffer);
|
||||
if (mVertexArray != 0)
|
||||
glDeleteVertexArrays(1, &mVertexArray);
|
||||
if (mFallbackSourceTexture != 0)
|
||||
glDeleteTextures(1, &mFallbackSourceTexture);
|
||||
mGlobalParamsBuffer = 0;
|
||||
mGlobalParamsBufferSize = 0;
|
||||
mVertexArray = 0;
|
||||
mFallbackSourceTexture = 0;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "GLExtensions.h"
|
||||
#include "RuntimeShaderProgram.h"
|
||||
#include "../../runtime/RuntimeShaderArtifact.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class RuntimeShaderRenderer
|
||||
{
|
||||
public:
|
||||
RuntimeShaderRenderer() = default;
|
||||
RuntimeShaderRenderer(const RuntimeShaderRenderer&) = delete;
|
||||
RuntimeShaderRenderer& operator=(const RuntimeShaderRenderer&) = delete;
|
||||
~RuntimeShaderRenderer();
|
||||
|
||||
bool CommitPreparedProgram(RuntimePreparedShaderProgram& preparedProgram, std::string& error);
|
||||
bool HasProgram() const { return mProgram != 0; }
|
||||
void UpdateArtifactState(const RuntimeShaderArtifact& artifact);
|
||||
void RenderFrame(uint64_t frameIndex, unsigned width, unsigned height, GLuint sourceTexture = 0, GLuint layerInputTexture = 0);
|
||||
void ShutdownGl();
|
||||
|
||||
static bool BuildPreparedProgram(
|
||||
const std::string& layerId,
|
||||
const std::string& sourceFingerprint,
|
||||
const RuntimeShaderArtifact& artifact,
|
||||
RuntimePreparedShaderProgram& preparedProgram);
|
||||
static bool BuildPreparedPassProgram(
|
||||
const std::string& layerId,
|
||||
const std::string& sourceFingerprint,
|
||||
const RuntimeShaderArtifact& artifact,
|
||||
const RuntimeShaderPassArtifact& passArtifact,
|
||||
RuntimePreparedShaderProgram& preparedProgram);
|
||||
|
||||
private:
|
||||
bool EnsureStaticGlResources(std::string& error);
|
||||
static bool CompileShader(GLenum shaderType, const char* source, GLuint& shader, std::string& error);
|
||||
static bool BuildProgram(const std::string& fragmentShaderSource, GLuint& program, GLuint& vertexShader, GLuint& fragmentShader, std::string& error);
|
||||
static void AssignSamplerUniforms(GLuint program);
|
||||
void UpdateGlobalParams(uint64_t frameIndex, unsigned width, unsigned height);
|
||||
void BindRuntimeTextures(GLuint sourceTexture, GLuint layerInputTexture);
|
||||
void DestroyProgram();
|
||||
void DestroyStaticGlResources();
|
||||
|
||||
RuntimeShaderArtifact mArtifact;
|
||||
GLuint mProgram = 0;
|
||||
GLuint mVertexShader = 0;
|
||||
GLuint mFragmentShader = 0;
|
||||
GLuint mVertexArray = 0;
|
||||
GLuint mGlobalParamsBuffer = 0;
|
||||
GLsizeiptr mGlobalParamsBufferSize = 0;
|
||||
GLuint mFallbackSourceTexture = 0;
|
||||
std::vector<unsigned char> mGlobalParamsScratch;
|
||||
};
|
||||
Reference in New Issue
Block a user