Update
This commit is contained in:
@@ -41,9 +41,11 @@
|
||||
#include "OpenGLComposite.h"
|
||||
#include "GLExtensions.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <initguid.h>
|
||||
DEFINE_GUID(IID_PinnedMemoryAllocator,
|
||||
@@ -51,7 +53,20 @@ DEFINE_GUID(IID_PinnedMemoryAllocator,
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* kFragmentShaderFilename = "video_effect.frag";
|
||||
const char* kSlangShaderRelativePath = "apps/LoopThroughWithOpenGLCompositing/video_effect.slang";
|
||||
const char* kRuntimeShaderCacheDirectory = "shader_cache";
|
||||
const char* kRuntimeRawShaderFilename = "video_effect.raw.frag";
|
||||
const char* kRuntimePatchedShaderFilename = "video_effect.frag";
|
||||
const char* kVertexShaderSource =
|
||||
"#version 130\n"
|
||||
"out vec2 vTexCoord;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec2 positions[3] = vec2[3](vec2(-1.0, -1.0), vec2(3.0, -1.0), vec2(-1.0, 3.0));\n"
|
||||
" vec2 texCoords[3] = vec2[3](vec2(0.0, 0.0), vec2(2.0, 0.0), vec2(0.0, 2.0));\n"
|
||||
" gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);\n"
|
||||
" vTexCoord = texCoords[gl_VertexID];\n"
|
||||
"}\n";
|
||||
|
||||
std::string GetExecutableDirectory()
|
||||
{
|
||||
@@ -68,6 +83,19 @@ std::string GetExecutableDirectory()
|
||||
return path.substr(0, slashIndex);
|
||||
}
|
||||
|
||||
bool ReplaceAll(std::string& text, const std::string& from, const std::string& to)
|
||||
{
|
||||
bool replaced = false;
|
||||
std::string::size_type startPos = 0;
|
||||
while ((startPos = text.find(from, startPos)) != std::string::npos)
|
||||
{
|
||||
text.replace(startPos, from.length(), to);
|
||||
startPos += to.length();
|
||||
replaced = true;
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
void CopyErrorMessage(const std::string& message, int errorMessageSize, char* errorMessage)
|
||||
{
|
||||
if (!errorMessage || errorMessageSize <= 0)
|
||||
@@ -97,6 +125,158 @@ bool LoadTextFile(const std::string& path, std::string& contents, std::string& e
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::filesystem::path FindRepoRoot()
|
||||
{
|
||||
std::vector<std::filesystem::path> rootsToTry;
|
||||
|
||||
char currentDirBuffer[MAX_PATH] = {};
|
||||
if (GetCurrentDirectoryA(MAX_PATH, currentDirBuffer) > 0)
|
||||
rootsToTry.push_back(std::filesystem::path(currentDirBuffer));
|
||||
|
||||
std::string executableDirectory = GetExecutableDirectory();
|
||||
if (!executableDirectory.empty())
|
||||
rootsToTry.push_back(std::filesystem::path(executableDirectory));
|
||||
|
||||
for (const std::filesystem::path& startPath : rootsToTry)
|
||||
{
|
||||
std::filesystem::path candidate = startPath;
|
||||
for (int depth = 0; depth < 8 && !candidate.empty(); ++depth)
|
||||
{
|
||||
if (std::filesystem::exists(candidate / kSlangShaderRelativePath))
|
||||
return candidate;
|
||||
|
||||
candidate = candidate.parent_path();
|
||||
}
|
||||
}
|
||||
|
||||
return std::filesystem::path();
|
||||
}
|
||||
|
||||
bool FindSlangCompiler(const std::filesystem::path& repoRoot, std::filesystem::path& slangCompilerPath, std::string& error)
|
||||
{
|
||||
std::filesystem::path thirdPartyPath = repoRoot / "3rdParty";
|
||||
if (!std::filesystem::exists(thirdPartyPath))
|
||||
{
|
||||
error = "Could not locate the 3rdParty directory from the application runtime path.";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(thirdPartyPath))
|
||||
{
|
||||
if (!entry.is_directory())
|
||||
continue;
|
||||
|
||||
std::filesystem::path candidate = entry.path() / "bin" / "slangc.exe";
|
||||
if (std::filesystem::exists(candidate))
|
||||
{
|
||||
slangCompilerPath = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
error = "Could not find slangc.exe under 3rdParty.";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RunProcessAndWait(const std::string& commandLine, std::string& error)
|
||||
{
|
||||
STARTUPINFOA startupInfo = {};
|
||||
PROCESS_INFORMATION processInfo = {};
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
|
||||
std::vector<char> mutableCommandLine(commandLine.begin(), commandLine.end());
|
||||
mutableCommandLine.push_back('\0');
|
||||
|
||||
if (!CreateProcessA(NULL, mutableCommandLine.data(), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &processInfo))
|
||||
{
|
||||
error = "Failed to start slangc.exe.";
|
||||
return false;
|
||||
}
|
||||
|
||||
WaitForSingleObject(processInfo.hProcess, INFINITE);
|
||||
|
||||
DWORD exitCode = 0;
|
||||
GetExitCodeProcess(processInfo.hProcess, &exitCode);
|
||||
|
||||
CloseHandle(processInfo.hThread);
|
||||
CloseHandle(processInfo.hProcess);
|
||||
|
||||
if (exitCode != 0)
|
||||
{
|
||||
error = "slangc.exe returned a non-zero exit code while compiling the runtime shader.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PatchGeneratedSlangGLSL(std::string& shaderText, std::string& error)
|
||||
{
|
||||
bool replacedVersion = ReplaceAll(shaderText, "#version 450", "#version 130");
|
||||
ReplaceAll(shaderText, "#extension GL_EXT_samplerless_texture_functions : require\n", "");
|
||||
ReplaceAll(shaderText, "layout(row_major) uniform;\n", "");
|
||||
ReplaceAll(shaderText, "layout(row_major) buffer;\n", "");
|
||||
ReplaceAll(shaderText, "layout(binding = 0)\nuniform texture2D UYVYtex_0;", "uniform sampler2D UYVYtex;");
|
||||
ReplaceAll(shaderText, "layout(location = 0)\nout vec4 entryPointParam_fragmentMain_0;\n", "");
|
||||
ReplaceAll(shaderText, "layout(location = 0)\nin vec2 input_texCoord_0;\n", "in vec2 vTexCoord;\n");
|
||||
ReplaceAll(shaderText, "UYVYtex_0", "UYVYtex");
|
||||
ReplaceAll(shaderText, "input_texCoord_0", "vTexCoord");
|
||||
ReplaceAll(shaderText, "entryPointParam_fragmentMain_0 =", "gl_FragColor =");
|
||||
|
||||
if (!replacedVersion)
|
||||
{
|
||||
error = "Generated Slang GLSL did not contain the expected version header.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuildFragmentShaderSourceFromSlang(std::string& shaderSource, std::string& error)
|
||||
{
|
||||
std::filesystem::path repoRoot = FindRepoRoot();
|
||||
if (repoRoot.empty())
|
||||
{
|
||||
error = "Could not locate the repository root to load video_effect.slang.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::filesystem::path slangSourcePath = repoRoot / kSlangShaderRelativePath;
|
||||
if (!std::filesystem::exists(slangSourcePath))
|
||||
{
|
||||
error = "Could not find video_effect.slang.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::filesystem::path slangCompilerPath;
|
||||
if (!FindSlangCompiler(repoRoot, slangCompilerPath, error))
|
||||
return false;
|
||||
|
||||
std::filesystem::path shaderCachePath = std::filesystem::path(GetExecutableDirectory()) / kRuntimeShaderCacheDirectory;
|
||||
std::filesystem::create_directories(shaderCachePath);
|
||||
|
||||
std::filesystem::path rawShaderPath = shaderCachePath / kRuntimeRawShaderFilename;
|
||||
std::filesystem::path patchedShaderPath = shaderCachePath / kRuntimePatchedShaderFilename;
|
||||
|
||||
std::string commandLine = "\"" + slangCompilerPath.string() + "\" \"" + slangSourcePath.string()
|
||||
+ "\" -target glsl -profile glsl_430 -entry fragmentMain -stage fragment -o \"" + rawShaderPath.string() + "\"";
|
||||
|
||||
if (!RunProcessAndWait(commandLine, error))
|
||||
return false;
|
||||
|
||||
if (!LoadTextFile(rawShaderPath.string(), shaderSource, error))
|
||||
return false;
|
||||
|
||||
if (!PatchGeneratedSlangGLSL(shaderSource, error))
|
||||
return false;
|
||||
|
||||
std::ofstream patchedShaderOutput(patchedShaderPath.string().c_str(), std::ios::binary);
|
||||
if (patchedShaderOutput)
|
||||
patchedShaderOutput << shaderSource;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
||||
@@ -109,6 +289,10 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
||||
mFastTransferExtensionAvailable(false),
|
||||
mCaptureTexture(0),
|
||||
mFBOTexture(0),
|
||||
mProgram(0),
|
||||
mVertexShader(0),
|
||||
mFragmentShader(0),
|
||||
mUYVYtexUniform(-1),
|
||||
mRotateAngle(0.0f),
|
||||
mRotateAngleRate(0.0f)
|
||||
{
|
||||
@@ -164,6 +348,8 @@ OpenGLComposite::~OpenGLComposite()
|
||||
mPlayoutAllocator = NULL;
|
||||
}
|
||||
|
||||
destroyShaderProgram();
|
||||
|
||||
DeleteCriticalSection(&pMutex);
|
||||
}
|
||||
|
||||
@@ -177,7 +363,6 @@ bool OpenGLComposite::InitDeckLink()
|
||||
IDeckLinkDisplayMode* pDLDisplayMode = NULL;
|
||||
BMDDisplayMode displayMode = bmdModeHD1080p5994; // mode to use for capture and playout
|
||||
int outputFrameRowBytes;
|
||||
float fps;
|
||||
HRESULT result;
|
||||
|
||||
result = CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**)&pDLIterator);
|
||||
@@ -267,10 +452,7 @@ bool OpenGLComposite::InitDeckLink()
|
||||
if (! InitOpenGLState())
|
||||
goto error;
|
||||
|
||||
// Compute a rotate angle rate so box will spin at a rate independent of video mode frame rate
|
||||
pDLDisplayMode->GetFrameRate(&mFrameDuration, &mFrameTimescale);
|
||||
fps = (float)mFrameTimescale / (float)mFrameDuration;
|
||||
mRotateAngleRate = 35.0f / fps; // rotate box through 35 degrees every second
|
||||
|
||||
// Resize window to match video frame, but scale large formats down by half for viewing
|
||||
if (mFrameWidth < 1920)
|
||||
@@ -413,7 +595,7 @@ bool OpenGLComposite::InitOpenGLState()
|
||||
if (! ResolveGLExtensions())
|
||||
return false;
|
||||
|
||||
// Prepare the shader used to perform colour space conversion on the video texture
|
||||
// Prepare the runtime shader program generated from the Slang source file.
|
||||
char compilerErrorMessage[1024];
|
||||
if (! compileFragmentShader(sizeof(compilerErrorMessage), compilerErrorMessage))
|
||||
{
|
||||
@@ -421,13 +603,8 @@ bool OpenGLComposite::InitOpenGLState()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup the scene
|
||||
glShadeModel( GL_SMOOTH ); // Enable smooth shading
|
||||
glClearColor( 0.0f, 0.0f, 0.0f, 0.5f ); // Black background
|
||||
glClearDepth( 1.0f ); // Depth buffer setup
|
||||
glEnable( GL_DEPTH_TEST ); // Enable depth testing
|
||||
glDepthFunc( GL_LEQUAL ); // Type of depth test to do
|
||||
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
if (! mFastTransferExtensionAvailable)
|
||||
{
|
||||
@@ -546,8 +723,8 @@ void OpenGLComposite::VideoFrameArrived(IDeckLinkVideoInputFrame* inputFrame, bo
|
||||
inputFrameBuffer->Release();
|
||||
}
|
||||
|
||||
// Draw the captured video frame texture onto a box, rendering to the off-screen frame buffer.
|
||||
// Read the rendered scene back from the frame buffer and schedule it for playout.
|
||||
// Render the live video texture through the runtime shader into the off-screen framebuffer.
|
||||
// Read the result back from the frame buffer and schedule it for playout.
|
||||
void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completionResult)
|
||||
{
|
||||
EnterCriticalSection(&pMutex);
|
||||
@@ -560,111 +737,9 @@ void OpenGLComposite::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame,
|
||||
// make GL context current in this thread
|
||||
wglMakeCurrent( hGLDC, hGLRC );
|
||||
|
||||
// Draw OpenGL scene to the off-screen frame buffer
|
||||
// Draw the effect output to the off-screen framebuffer.
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mIdFrameBuf);
|
||||
|
||||
// Setup view and projection
|
||||
GLfloat aspectRatio = (GLfloat)mFrameWidth / (GLfloat)mFrameHeight;
|
||||
glViewport (0, 0, mFrameWidth, mFrameHeight);
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
glLoadIdentity();
|
||||
gluPerspective( 45.0f, aspectRatio, 0.1f, 100.0f );
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glLoadIdentity();
|
||||
|
||||
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
||||
glScalef( aspectRatio, 1.0f, 1.0f ); // Scale x for correct aspect ratio
|
||||
glTranslatef( 0.0f, 0.0f, -4.0f ); // Move into screen
|
||||
glRotatef( mRotateAngle, 1.0f, 1.0f, 1.0f ); // Rotate model around a vector
|
||||
mRotateAngle -= mRotateAngleRate; // update the rotation angle for next iteration
|
||||
glFinish(); // Ensure changes to GL state are complete
|
||||
|
||||
// Draw a colourful frame around the front face of the box
|
||||
// (provides a pleasing nesting effect when you connect the playout output to the capture input)
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
glColor3f( 1.0f, 0.0f, 0.0f );
|
||||
glVertex3f( 1.2f, 1.2f, 1.0f);
|
||||
glVertex3f( 1.0f, 1.0f, 1.0f);
|
||||
glColor3f( 0.0f, 0.0f, 1.0f );
|
||||
glVertex3f( 1.2f, -1.2f, 1.0f);
|
||||
glVertex3f( 1.0f, -1.0f, 1.0f);
|
||||
glColor3f( 0.0f, 1.0f, 0.0f );
|
||||
glVertex3f(-1.2f, -1.2f, 1.0f);
|
||||
glVertex3f(-1.0f, -1.0f, 1.0f);
|
||||
glColor3f( 1.0f, 1.0f, 0.0f );
|
||||
glVertex3f(-1.2f, 1.2f, 1.0f);
|
||||
glVertex3f(-1.0f, 1.0f, 1.0f);
|
||||
glColor3f( 1.0f, 0.0f, 0.0f );
|
||||
glVertex3f( 1.2f, 1.2f, 1.0f);
|
||||
glVertex3f( 1.0f, 1.0f, 1.0f);
|
||||
glEnd();
|
||||
|
||||
if (mHasNoInputSource)
|
||||
{
|
||||
// Draw a big X when no input is available on capture
|
||||
glBegin( GL_QUADS );
|
||||
glColor3f( 1.0f, 0.0f, 1.0f );
|
||||
glVertex3f( 0.8f, 0.9f, 1.0f );
|
||||
glVertex3f( 0.9f, 0.8f, 1.0f );
|
||||
glColor3f( 1.0f, 1.0f, 0.0f );
|
||||
glVertex3f( -0.8f, -0.9f, 1.0f );
|
||||
glVertex3f( -0.9f, -0.8f, 1.0f );
|
||||
glColor3f( 1.0f, 0.0f, 1.0f );
|
||||
glVertex3f( -0.8f, 0.9f, 1.0f );
|
||||
glVertex3f( -0.9f, 0.8f, 1.0f );
|
||||
glColor3f( 1.0f, 1.0f, 0.0f );
|
||||
glVertex3f( 0.8f, -0.9f, 1.0f );
|
||||
glVertex3f( 0.9f, -0.8f, 1.0f );
|
||||
glEnd();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mFastTransferExtensionAvailable)
|
||||
{
|
||||
// Signal that we're about to draw using mCaptureTexture onto mFBOTexture
|
||||
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::CPUtoGPU);
|
||||
}
|
||||
|
||||
// Pass texture unit 0 to the fragment shader as a uniform variable
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, mCaptureTexture);
|
||||
glUseProgram(mProgram);
|
||||
GLint locUYVYtex = glGetUniformLocation(mProgram, "UYVYtex");
|
||||
glUniform1i(locUYVYtex, 0); // Bind texture unit 0
|
||||
|
||||
// Draw front and back faces of box applying video texture to each face
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f ); // Top right of front side
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex3f( -1.0f, 1.0f, 1.0f ); // Top left of front side
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex3f( -1.0f, -1.0f, 1.0f ); // Bottom left of front side
|
||||
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f ); // Bottom right of front side
|
||||
|
||||
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f ); // Top right of back side
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex3f( -1.0f, -1.0f, -1.0f ); // Top left of back side
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex3f( -1.0f, 1.0f, -1.0f ); // Bottom left of back side
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, -1.0f ); // Bottom right of back side
|
||||
glEnd();
|
||||
|
||||
// Draw left and right sides of box with partially transparent video texture
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0.1f, 0.0f); glVertex3f( -1.0f, 1.0f, 1.0f ); // Top right of left side
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex3f( -1.0f, 1.0f, -1.0f ); // Top left of left side
|
||||
glTexCoord2f(1.0f, 1.0f); glVertex3f( -1.0f, -1.0f, -1.0f ); // Bottom left of left side
|
||||
glTexCoord2f(0.1f, 1.0f); glVertex3f( -1.0f, -1.0f, 1.0f ); // Bottom right of left side
|
||||
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, -1.0f ); // Top right of right side
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f ); // Top left of right side
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f ); // Bottom left of right side
|
||||
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f ); // Bottom right of right side
|
||||
glEnd();
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glUseProgram(0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
renderEffect();
|
||||
|
||||
IDeckLinkVideoBuffer* outputVideoFrameBuffer;
|
||||
if (outputVideoFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&outputVideoFrameBuffer) != S_OK)
|
||||
@@ -772,26 +847,93 @@ bool OpenGLComposite::Stop()
|
||||
return true;
|
||||
}
|
||||
|
||||
// Setup fragment shader to take YCbCr 4:2:2 video texture in UYVY macropixel format
|
||||
// and perform colour space conversion to RGBA in the GPU.
|
||||
bool OpenGLComposite::compileFragmentShader(int errorMessageSize, char* errorMessage)
|
||||
bool OpenGLComposite::ReloadShader()
|
||||
{
|
||||
GLsizei errorBufferSize;
|
||||
GLint compileResult, linkResult;
|
||||
std::string shaderPath = GetExecutableDirectory();
|
||||
std::string fragmentShaderSource;
|
||||
std::string loadError;
|
||||
char compilerErrorMessage[1024];
|
||||
|
||||
if (shaderPath.empty())
|
||||
EnterCriticalSection(&pMutex);
|
||||
wglMakeCurrent(hGLDC, hGLRC);
|
||||
|
||||
bool success = compileFragmentShader(sizeof(compilerErrorMessage), compilerErrorMessage);
|
||||
|
||||
wglMakeCurrent(NULL, NULL);
|
||||
LeaveCriticalSection(&pMutex);
|
||||
|
||||
if (!success)
|
||||
MessageBoxA(NULL, compilerErrorMessage, "Slang shader reload failed", MB_OK);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void OpenGLComposite::destroyShaderProgram()
|
||||
{
|
||||
if (mProgram != 0)
|
||||
{
|
||||
CopyErrorMessage("Could not determine executable directory for fragment shader loading.", errorMessageSize, errorMessage);
|
||||
return false;
|
||||
glDeleteProgram(mProgram);
|
||||
mProgram = 0;
|
||||
}
|
||||
|
||||
shaderPath += "\\";
|
||||
shaderPath += kFragmentShaderFilename;
|
||||
if (mFragmentShader != 0)
|
||||
{
|
||||
glDeleteShader(mFragmentShader);
|
||||
mFragmentShader = 0;
|
||||
}
|
||||
|
||||
if (!LoadTextFile(shaderPath, fragmentShaderSource, loadError))
|
||||
if (mVertexShader != 0)
|
||||
{
|
||||
glDeleteShader(mVertexShader);
|
||||
mVertexShader = 0;
|
||||
}
|
||||
|
||||
mUYVYtexUniform = -1;
|
||||
}
|
||||
|
||||
void OpenGLComposite::renderEffect()
|
||||
{
|
||||
glViewport(0, 0, mFrameWidth, mFrameHeight);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
if (mHasNoInputSource)
|
||||
return;
|
||||
|
||||
if (mFastTransferExtensionAvailable)
|
||||
{
|
||||
// Signal that we're about to draw using mCaptureTexture onto mFBOTexture.
|
||||
VideoFrameTransfer::beginTextureInUse(VideoFrameTransfer::CPUtoGPU);
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, mCaptureTexture);
|
||||
glUseProgram(mProgram);
|
||||
|
||||
if (mUYVYtexUniform >= 0)
|
||||
glUniform1i(mUYVYtexUniform, 0);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
glUseProgram(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
if (mFastTransferExtensionAvailable)
|
||||
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
|
||||
}
|
||||
|
||||
// Compile a fullscreen shader pass from the runtime Slang source. The Slang compiler
|
||||
// emits modern GLSL which we patch into a compatibility-profile shader that can run
|
||||
// inside the sample's WGL context.
|
||||
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 (!BuildFragmentShaderSourceFromSlang(fragmentShaderSource, loadError))
|
||||
{
|
||||
CopyErrorMessage(loadError, errorMessageSize, errorMessage);
|
||||
return false;
|
||||
@@ -799,30 +941,50 @@ bool OpenGLComposite::compileFragmentShader(int errorMessageSize, char* errorMes
|
||||
|
||||
const char* fragmentSource = fragmentShaderSource.c_str();
|
||||
|
||||
mFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
glShaderSource(mFragmentShader, 1, (const GLchar**)&fragmentSource, NULL);
|
||||
glCompileShader(mFragmentShader);
|
||||
|
||||
glGetShaderiv(mFragmentShader, GL_COMPILE_STATUS, &compileResult);
|
||||
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(mFragmentShader, errorMessageSize, &errorBufferSize, errorMessage);
|
||||
glGetShaderInfoLog(newVertexShader, errorMessageSize, &errorBufferSize, errorMessage);
|
||||
glDeleteShader(newVertexShader);
|
||||
return false;
|
||||
}
|
||||
|
||||
mProgram = glCreateProgram();
|
||||
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;
|
||||
}
|
||||
|
||||
glAttachShader(mProgram, mFragmentShader);
|
||||
glLinkProgram(mProgram);
|
||||
|
||||
glGetProgramiv(mProgram, GL_LINK_STATUS, &linkResult);
|
||||
GLuint newProgram = glCreateProgram();
|
||||
glAttachShader(newProgram, newVertexShader);
|
||||
glAttachShader(newProgram, newFragmentShader);
|
||||
glLinkProgram(newProgram);
|
||||
glGetProgramiv(newProgram, GL_LINK_STATUS, &linkResult);
|
||||
if (linkResult == GL_FALSE)
|
||||
{
|
||||
glGetProgramInfoLog(mProgram, errorMessageSize, &errorBufferSize, errorMessage);
|
||||
glGetProgramInfoLog(newProgram, errorMessageSize, &errorBufferSize, errorMessage);
|
||||
glDeleteProgram(newProgram);
|
||||
glDeleteShader(newVertexShader);
|
||||
glDeleteShader(newFragmentShader);
|
||||
return false;
|
||||
}
|
||||
|
||||
destroyShaderProgram();
|
||||
|
||||
mProgram = newProgram;
|
||||
mVertexShader = newVertexShader;
|
||||
mFragmentShader = newFragmentShader;
|
||||
mUYVYtexUniform = glGetUniformLocation(mProgram, "UYVYtex");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user