Layer stacking

This commit is contained in:
2026-05-02 18:18:49 +10:00
parent fb1bf01fef
commit 80399c5a15
18 changed files with 3761 additions and 799 deletions

View File

@@ -156,16 +156,15 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
mFastTransferExtensionAvailable(false),
mCaptureTexture(0),
mDecodedTexture(0),
mLayerTempTexture(0),
mFBOTexture(0),
mDecodeFrameBuf(0),
mLayerTempFrameBuf(0),
mFullscreenVAO(0),
mGlobalParamsUBO(0),
mDecodeProgram(0),
mDecodeVertexShader(0),
mDecodeFragmentShader(0),
mProgram(0),
mVertexShader(0),
mFragmentShader(0),
mGlobalParamsUBOSize(0)
{
InitializeCriticalSection(&pMutex);
@@ -228,6 +227,8 @@ OpenGLComposite::~OpenGLComposite()
glDeleteBuffers(1, &mGlobalParamsUBO);
if (mDecodeFrameBuf != 0)
glDeleteFramebuffers(1, &mDecodeFrameBuf);
if (mLayerTempFrameBuf != 0)
glDeleteFramebuffers(1, &mLayerTempFrameBuf);
if (mIdFrameBuf != 0)
glDeleteFramebuffers(1, &mIdFrameBuf);
if (mIdColorBuf != 0)
@@ -238,12 +239,14 @@ OpenGLComposite::~OpenGLComposite()
glDeleteTextures(1, &mCaptureTexture);
if (mDecodedTexture != 0)
glDeleteTextures(1, &mDecodedTexture);
if (mLayerTempTexture != 0)
glDeleteTextures(1, &mLayerTempTexture);
if (mFBOTexture != 0)
glDeleteTextures(1, &mFBOTexture);
if (mUnpinnedTextureBuffer != 0)
glDeleteBuffers(1, &mUnpinnedTextureBuffer);
destroyShaderProgram();
destroyLayerPrograms();
destroyDecodeShaderProgram();
if (mControlServer)
mControlServer->Stop();
@@ -536,13 +539,18 @@ bool OpenGLComposite::InitOpenGLState()
ControlServer::Callbacks callbacks;
callbacks.getStateJson = [this]() { return GetRuntimeStateJson(); };
callbacks.selectShader = [this](const std::string& shaderId, std::string& error) { return SelectShader(shaderId, error); };
callbacks.updateParameter = [this](const std::string& shaderId, const std::string& parameterId, const std::string& valueJson, std::string& error) {
return UpdateParameterJson(shaderId, parameterId, valueJson, error);
callbacks.addLayer = [this](const std::string& shaderId, std::string& error) { return AddLayer(shaderId, error); };
callbacks.removeLayer = [this](const std::string& layerId, std::string& error) { return RemoveLayer(layerId, error); };
callbacks.moveLayer = [this](const std::string& layerId, int direction, std::string& error) { return MoveLayer(layerId, direction, error); };
callbacks.moveLayerToIndex = [this](const std::string& layerId, std::size_t targetIndex, std::string& error) { return MoveLayerToIndex(layerId, targetIndex, error); };
callbacks.setLayerBypass = [this](const std::string& layerId, bool bypassed, std::string& error) { return SetLayerBypass(layerId, bypassed, error); };
callbacks.setLayerShader = [this](const std::string& layerId, const std::string& shaderId, std::string& error) { return SetLayerShader(layerId, shaderId, error); };
callbacks.updateLayerParameter = [this](const std::string& layerId, const std::string& parameterId, const std::string& valueJson, std::string& error) {
return UpdateLayerParameterJson(layerId, parameterId, valueJson, error);
};
callbacks.resetParameters = [this](const std::string& shaderId, std::string& error) { return ResetShaderParameters(shaderId, error); };
callbacks.setBypass = [this](bool bypassEnabled, std::string& error) { return SetBypassEnabled(bypassEnabled, error); };
callbacks.setMixAmount = [this](double mixAmount, std::string& error) { return SetMixAmount(mixAmount, error); };
callbacks.resetLayerParameters = [this](const std::string& layerId, std::string& error) { return ResetLayerParameters(layerId, error); };
callbacks.saveStackPreset = [this](const std::string& presetName, std::string& error) { return SaveStackPreset(presetName, error); };
callbacks.loadStackPreset = [this](const std::string& presetName, std::string& error) { return LoadStackPreset(presetName, error); };
callbacks.reloadShader = [this](std::string& error) {
if (!ReloadShader())
{
@@ -567,7 +575,7 @@ bool OpenGLComposite::InitOpenGLState()
return false;
}
if (! compileFragmentShader(sizeof(compilerErrorMessage), compilerErrorMessage))
if (! compileLayerPrograms(sizeof(compilerErrorMessage), compilerErrorMessage))
{
MessageBoxA(NULL, compilerErrorMessage, "OpenGL shader failed to load or compile", MB_OK);
return false;
@@ -606,10 +614,20 @@ bool OpenGLComposite::InitOpenGLState()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mFrameWidth, mFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &mLayerTempTexture);
glBindTexture(GL_TEXTURE_2D, mLayerTempTexture);
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, mFrameWidth, mFrameHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// Create Frame Buffer Object (FBO) to perform off-screen rendering of scene.
// This allows the render to be done on a framebuffer with width and height exactly matching the video format.
glGenFramebuffers(1, &mDecodeFrameBuf);
glGenFramebuffers(1, &mLayerTempFrameBuf);
glGenFramebuffers(1, &mIdFrameBuf);
glGenRenderbuffers(1, &mIdColorBuf);
glGenRenderbuffers(1, &mIdDepthBuf);
@@ -625,6 +643,15 @@ bool OpenGLComposite::InitOpenGLState()
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, mLayerTempFrameBuf);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mLayerTempTexture, 0);
glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (glStatus != GL_FRAMEBUFFER_COMPLETE)
{
MessageBox(NULL, _T("Cannot initialize layer framebuffer."), _T("OpenGL initialization error."), MB_OK);
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, mIdFrameBuf);
// Texture for FBO
@@ -872,7 +899,7 @@ bool OpenGLComposite::ReloadShader()
EnterCriticalSection(&pMutex);
wglMakeCurrent(hGLDC, hGLRC);
bool success = compileFragmentShader(sizeof(compilerErrorMessage), compilerErrorMessage);
bool success = compileLayerPrograms(sizeof(compilerErrorMessage), compilerErrorMessage);
if (mRuntimeHost)
mRuntimeHost->ClearReloadRequest();
@@ -895,6 +922,98 @@ bool OpenGLComposite::ReloadShader()
return success;
}
bool OpenGLComposite::compileSingleLayerProgram(const RuntimeRenderState& state, LayerProgram& layerProgram, int errorMessageSize, char* errorMessage)
{
GLsizei errorBufferSize = 0;
GLint compileResult = GL_FALSE;
GLint linkResult = GL_FALSE;
std::string fragmentShaderSource;
std::string loadError;
const char* vertexSource = kVertexShaderSource;
if (!mRuntimeHost->BuildLayerFragmentShaderSource(state.layerId, fragmentShaderSource, loadError))
{
CopyErrorMessage(loadError, errorMessageSize, errorMessage);
return false;
}
const char* fragmentSource = fragmentShaderSource.c_str();
GLuint newVertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(newVertexShader, 1, (const GLchar**)&vertexSource, NULL);
glCompileShader(newVertexShader);
glGetShaderiv(newVertexShader, GL_COMPILE_STATUS, &compileResult);
if (compileResult == GL_FALSE)
{
glGetShaderInfoLog(newVertexShader, errorMessageSize, &errorBufferSize, errorMessage);
glDeleteShader(newVertexShader);
return false;
}
GLuint newFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(newFragmentShader, 1, (const GLchar**)&fragmentSource, NULL);
glCompileShader(newFragmentShader);
glGetShaderiv(newFragmentShader, GL_COMPILE_STATUS, &compileResult);
if (compileResult == GL_FALSE)
{
glGetShaderInfoLog(newFragmentShader, errorMessageSize, &errorBufferSize, errorMessage);
glDeleteShader(newVertexShader);
glDeleteShader(newFragmentShader);
return false;
}
GLuint newProgram = glCreateProgram();
glAttachShader(newProgram, newVertexShader);
glAttachShader(newProgram, newFragmentShader);
glLinkProgram(newProgram);
glGetProgramiv(newProgram, GL_LINK_STATUS, &linkResult);
if (linkResult == GL_FALSE)
{
glGetProgramInfoLog(newProgram, errorMessageSize, &errorBufferSize, errorMessage);
glDeleteProgram(newProgram);
glDeleteShader(newVertexShader);
glDeleteShader(newFragmentShader);
return false;
}
layerProgram.layerId = state.layerId;
layerProgram.shaderId = state.shaderId;
layerProgram.program = newProgram;
layerProgram.vertexShader = newVertexShader;
layerProgram.fragmentShader = newFragmentShader;
return true;
}
bool OpenGLComposite::compileLayerPrograms(int errorMessageSize, char* errorMessage)
{
const std::vector<RuntimeRenderState> layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mFrameWidth, mFrameHeight) : std::vector<RuntimeRenderState>();
std::vector<LayerProgram> newPrograms;
newPrograms.reserve(layerStates.size());
for (const RuntimeRenderState& state : layerStates)
{
LayerProgram layerProgram;
if (!compileSingleLayerProgram(state, layerProgram, errorMessageSize, errorMessage))
{
for (LayerProgram& program : newPrograms)
destroySingleLayerProgram(program);
return false;
}
newPrograms.push_back(layerProgram);
}
destroyLayerPrograms();
mLayerPrograms.swap(newPrograms);
if (mRuntimeHost)
{
mRuntimeHost->SetCompileStatus(true, "Shader layers compiled successfully.");
mRuntimeHost->ClearReloadRequest();
}
return true;
}
bool OpenGLComposite::compileDecodeShader(int errorMessageSize, char* errorMessage)
{
GLsizei errorBufferSize = 0;
@@ -947,27 +1066,34 @@ bool OpenGLComposite::compileDecodeShader(int errorMessageSize, char* errorMessa
return true;
}
void OpenGLComposite::destroyShaderProgram()
void OpenGLComposite::destroySingleLayerProgram(LayerProgram& layerProgram)
{
if (mProgram != 0)
if (layerProgram.program != 0)
{
glDeleteProgram(mProgram);
mProgram = 0;
glDeleteProgram(layerProgram.program);
layerProgram.program = 0;
}
if (mFragmentShader != 0)
if (layerProgram.fragmentShader != 0)
{
glDeleteShader(mFragmentShader);
mFragmentShader = 0;
glDeleteShader(layerProgram.fragmentShader);
layerProgram.fragmentShader = 0;
}
if (mVertexShader != 0)
if (layerProgram.vertexShader != 0)
{
glDeleteShader(mVertexShader);
mVertexShader = 0;
glDeleteShader(layerProgram.vertexShader);
layerProgram.vertexShader = 0;
}
}
void OpenGLComposite::destroyLayerPrograms()
{
for (LayerProgram& layerProgram : mLayerPrograms)
destroySingleLayerProgram(layerProgram);
mLayerPrograms.clear();
}
void OpenGLComposite::destroyDecodeShaderProgram()
{
if (mDecodeProgram != 0)
@@ -1006,29 +1132,45 @@ void OpenGLComposite::renderEffect()
glDisable(GL_DEPTH_TEST);
renderDecodePass();
glBindFramebuffer(GL_FRAMEBUFFER, mIdFrameBuf);
const std::vector<RuntimeRenderState> layerStates = mRuntimeHost ? mRuntimeHost->GetLayerRenderStates(mFrameWidth, mFrameHeight) : std::vector<RuntimeRenderState>();
if (layerStates.empty() || mLayerPrograms.empty())
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, mDecodeFrameBuf);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mIdFrameBuf);
glBlitFramebuffer(0, 0, mFrameWidth, mFrameHeight, 0, 0, mFrameWidth, mFrameHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, mIdFrameBuf);
}
else
{
GLuint sourceTexture = mDecodedTexture;
for (std::size_t index = 0; index < layerStates.size() && index < mLayerPrograms.size(); ++index)
{
const std::size_t remaining = layerStates.size() - index;
const bool writeToMain = (remaining % 2) == 1;
renderShaderProgram(sourceTexture, writeToMain ? mIdFrameBuf : mLayerTempFrameBuf, mLayerPrograms[index], layerStates[index]);
sourceTexture = writeToMain ? mFBOTexture : mLayerTempTexture;
}
}
if (mFastTransferExtensionAvailable)
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
}
void OpenGLComposite::renderShaderProgram(GLuint sourceTexture, GLuint destinationFrameBuffer, const LayerProgram& layerProgram, const RuntimeRenderState& state)
{
glBindFramebuffer(GL_FRAMEBUFFER, destinationFrameBuffer);
glViewport(0, 0, mFrameWidth, mFrameHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0 + kDecodedVideoTextureUnit);
glBindTexture(GL_TEXTURE_2D, mDecodedTexture);
glBindTexture(GL_TEXTURE_2D, sourceTexture);
glBindVertexArray(mFullscreenVAO);
glUseProgram(mProgram);
if (mRuntimeHost)
{
const RuntimeRenderState state = mRuntimeHost->GetRenderState(mFrameWidth, mFrameHeight);
updateGlobalParamsBuffer(state);
}
glUseProgram(layerProgram.program);
updateGlobalParamsBuffer(state);
glDrawArrays(GL_TRIANGLES, 0, 3);
glUseProgram(0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
if (mFastTransferExtensionAvailable)
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
}
void OpenGLComposite::renderDecodePass()
@@ -1056,82 +1198,6 @@ void OpenGLComposite::renderDecodePass()
glActiveTexture(GL_TEXTURE0);
}
// Compile a fullscreen shader pass from the runtime Slang source into a core-profile
// GLSL program. The renderer owns the fullscreen pass and parameter UBO layout.
bool OpenGLComposite::compileFragmentShader(int errorMessageSize, char* errorMessage)
{
GLsizei errorBufferSize = 0;
GLint compileResult = GL_FALSE;
GLint linkResult = GL_FALSE;
std::string fragmentShaderSource;
std::string loadError;
const char* vertexSource = kVertexShaderSource;
if (!mRuntimeHost->BuildActiveFragmentShaderSource(fragmentShaderSource, loadError))
{
mRuntimeHost->SetCompileStatus(false, loadError);
CopyErrorMessage(loadError, errorMessageSize, errorMessage);
return false;
}
const char* fragmentSource = fragmentShaderSource.c_str();
GLuint newVertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(newVertexShader, 1, (const GLchar**)&vertexSource, NULL);
glCompileShader(newVertexShader);
glGetShaderiv(newVertexShader, GL_COMPILE_STATUS, &compileResult);
if (compileResult == GL_FALSE)
{
glGetShaderInfoLog(newVertexShader, errorMessageSize, &errorBufferSize, errorMessage);
glDeleteShader(newVertexShader);
return false;
}
GLuint newFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(newFragmentShader, 1, (const GLchar**)&fragmentSource, NULL);
glCompileShader(newFragmentShader);
glGetShaderiv(newFragmentShader, GL_COMPILE_STATUS, &compileResult);
if (compileResult == GL_FALSE)
{
glGetShaderInfoLog(newFragmentShader, errorMessageSize, &errorBufferSize, errorMessage);
glDeleteShader(newVertexShader);
glDeleteShader(newFragmentShader);
return false;
}
GLuint newProgram = glCreateProgram();
glAttachShader(newProgram, newVertexShader);
glAttachShader(newProgram, newFragmentShader);
glLinkProgram(newProgram);
glGetProgramiv(newProgram, GL_LINK_STATUS, &linkResult);
if (linkResult == GL_FALSE)
{
glGetProgramInfoLog(newProgram, errorMessageSize, &errorBufferSize, errorMessage);
glDeleteProgram(newProgram);
glDeleteShader(newVertexShader);
glDeleteShader(newFragmentShader);
return false;
}
destroyShaderProgram();
mProgram = newProgram;
mVertexShader = newVertexShader;
mFragmentShader = newFragmentShader;
const RuntimeRenderState state = mRuntimeHost->GetRenderState(mFrameWidth, mFrameHeight);
if (!updateGlobalParamsBuffer(state))
{
CopyErrorMessage("Failed to allocate the runtime parameter UBO.", errorMessageSize, errorMessage);
destroyShaderProgram();
return false;
}
mRuntimeHost->SetCompileStatus(true, "Shader compiled successfully.");
mRuntimeHost->ClearReloadRequest();
return true;
}
bool OpenGLComposite::PollRuntimeChanges()
{
if (!mRuntimeHost)
@@ -1154,7 +1220,7 @@ bool OpenGLComposite::PollRuntimeChanges()
return true;
char compilerErrorMessage[1024] = {};
if (!compileFragmentShader(sizeof(compilerErrorMessage), compilerErrorMessage))
if (!compileLayerPrograms(sizeof(compilerErrorMessage), compilerErrorMessage))
{
mRuntimeHost->SetCompileStatus(false, compilerErrorMessage);
mRuntimeHost->ClearReloadRequest();
@@ -1251,9 +1317,9 @@ std::string OpenGLComposite::GetRuntimeStateJson() const
return mRuntimeHost ? mRuntimeHost->BuildStateJson() : "{}";
}
bool OpenGLComposite::SelectShader(const std::string& shaderId, std::string& error)
bool OpenGLComposite::AddLayer(const std::string& shaderId, std::string& error)
{
if (!mRuntimeHost->SelectShader(shaderId, error))
if (!mRuntimeHost->AddLayer(shaderId, error))
return false;
ReloadShader();
@@ -1261,40 +1327,93 @@ bool OpenGLComposite::SelectShader(const std::string& shaderId, std::string& err
return true;
}
bool OpenGLComposite::UpdateParameterJson(const std::string& shaderId, const std::string& parameterId, const std::string& valueJson, std::string& error)
bool OpenGLComposite::RemoveLayer(const std::string& layerId, std::string& error)
{
if (!mRuntimeHost->RemoveLayer(layerId, error))
return false;
ReloadShader();
broadcastRuntimeState();
return true;
}
bool OpenGLComposite::MoveLayer(const std::string& layerId, int direction, std::string& error)
{
if (!mRuntimeHost->MoveLayer(layerId, direction, error))
return false;
ReloadShader();
broadcastRuntimeState();
return true;
}
bool OpenGLComposite::MoveLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error)
{
if (!mRuntimeHost->MoveLayerToIndex(layerId, targetIndex, error))
return false;
ReloadShader();
broadcastRuntimeState();
return true;
}
bool OpenGLComposite::SetLayerBypass(const std::string& layerId, bool bypassed, std::string& error)
{
if (!mRuntimeHost->SetLayerBypass(layerId, bypassed, error))
return false;
ReloadShader();
broadcastRuntimeState();
return true;
}
bool OpenGLComposite::SetLayerShader(const std::string& layerId, const std::string& shaderId, std::string& error)
{
if (!mRuntimeHost->SetLayerShader(layerId, shaderId, error))
return false;
ReloadShader();
broadcastRuntimeState();
return true;
}
bool OpenGLComposite::UpdateLayerParameterJson(const std::string& layerId, const std::string& parameterId, const std::string& valueJson, std::string& error)
{
JsonValue parsedValue;
if (!ParseJson(valueJson, parsedValue, error))
return false;
if (!mRuntimeHost->UpdateParameter(shaderId, parameterId, parsedValue, error))
if (!mRuntimeHost->UpdateLayerParameter(layerId, parameterId, parsedValue, error))
return false;
broadcastRuntimeState();
return true;
}
bool OpenGLComposite::ResetShaderParameters(const std::string& shaderId, std::string& error)
bool OpenGLComposite::ResetLayerParameters(const std::string& layerId, std::string& error)
{
if (!mRuntimeHost->ResetParameters(shaderId, error))
if (!mRuntimeHost->ResetLayerParameters(layerId, error))
return false;
broadcastRuntimeState();
return true;
}
bool OpenGLComposite::SetBypassEnabled(bool bypassEnabled, std::string& error)
bool OpenGLComposite::SaveStackPreset(const std::string& presetName, std::string& error)
{
if (!mRuntimeHost->SetBypass(bypassEnabled, error))
if (!mRuntimeHost->SaveStackPreset(presetName, error))
return false;
broadcastRuntimeState();
return true;
}
bool OpenGLComposite::SetMixAmount(double mixAmount, std::string& error)
bool OpenGLComposite::LoadStackPreset(const std::string& presetName, std::string& error)
{
if (!mRuntimeHost->SetMixAmount(mixAmount, error))
if (!mRuntimeHost->LoadStackPreset(presetName, error))
return false;
ReloadShader();
broadcastRuntimeState();
return true;
}