Files
renderdoc/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp
T

2041 lines
63 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2015-2019 Baldur Karlsson
* Copyright (c) 2014 Crytek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "../gl_driver.h"
#include "../gl_shader_refl.h"
#include "common/common.h"
#include "driver/shaders/spirv/glslang_compile.h"
#include "driver/shaders/spirv/spirv_compile.h"
#include "strings/string_utils.h"
enum GLshaderbitfield
{
};
DECLARE_REFLECTION_ENUM(GLshaderbitfield);
template <>
rdcstr DoStringise(const GLshaderbitfield &el)
{
RDCCOMPILE_ASSERT(sizeof(GLshaderbitfield) == sizeof(GLbitfield) &&
sizeof(GLshaderbitfield) == sizeof(uint32_t),
"Fake bitfield enum must be uint32_t sized");
BEGIN_BITFIELD_STRINGISE(GLshaderbitfield);
{
STRINGISE_BITFIELD_BIT(GL_VERTEX_SHADER_BIT);
STRINGISE_BITFIELD_BIT(GL_TESS_CONTROL_SHADER_BIT);
STRINGISE_BITFIELD_BIT(GL_TESS_EVALUATION_SHADER_BIT);
STRINGISE_BITFIELD_BIT(GL_GEOMETRY_SHADER_BIT);
STRINGISE_BITFIELD_BIT(GL_FRAGMENT_SHADER_BIT);
STRINGISE_BITFIELD_BIT(GL_COMPUTE_SHADER_BIT);
}
END_BITFIELD_STRINGISE();
}
void WrappedOpenGL::ShaderData::ProcessSPIRVCompilation(WrappedOpenGL &drv, ResourceId id,
GLuint realShader, const GLchar *pEntryPoint,
GLuint numSpecializationConstants,
const GLuint *pConstantIndex,
const GLuint *pConstantValue)
{
reflection.resourceId = id;
std::vector<SpecConstant> specInfo;
for(size_t i = 0; i < specInfo.size(); i++)
{
specInfo.push_back(SpecConstant(pConstantIndex[i], pConstantValue[i], 4));
}
spirv.MakeReflection(GraphicsAPI::OpenGL, ShaderStage(ShaderIdx(type)), pEntryPoint, specInfo,
reflection, mapping, patchData);
version = 460;
entryPoint = pEntryPoint;
if(numSpecializationConstants > 0)
{
specIDs.assign(pConstantIndex, pConstantIndex + numSpecializationConstants);
specValues.assign(pConstantValue, pConstantValue + numSpecializationConstants);
}
}
void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceId id,
GLuint realShader)
{
FixedFunctionVertexOutputs outputUsage = {};
if(type == eGL_VERTEX_SHADER)
CheckVertexOutputUses(sources, outputUsage);
entryPoint = "main";
std::string concatenated;
for(size_t i = 0; i < sources.size(); i++)
{
if(sources.size() > 1)
{
if(i > 0)
concatenated += "\n";
concatenated += "/////////////////////////////";
concatenated += StringFormat::Fmt("// Source file %u", (uint32_t)i);
concatenated += "/////////////////////////////";
concatenated += "\n";
}
concatenated += sources[i];
}
size_t offs = concatenated.find("#version");
if(offs == std::string::npos)
{
// if there's no #version it's assumed to be 100 which we set below
version = 0;
}
else
{
// see if we find a second result after the first
size_t offs2 = concatenated.find("#version", offs + 1);
if(offs2 == std::string::npos)
{
version = ParseVersionStatement(concatenated.c_str() + offs);
}
else
{
// slow path, multiple #version matches so the first one might be in a comment. We need to
// search from the start, past comments and whitespace, to find the first real #version.
const char *search = concatenated.c_str();
const char *end = search + concatenated.size();
while(search < end)
{
// skip whitespace
if(isspace(*search))
{
search++;
continue;
}
// skip single-line C++ style comments
if(search + 1 < end && search[0] == '/' && search[1] == '/')
{
// continue until the next newline
while(search < end && search[0] != '\r' && search[0] != '\n')
search++;
// continue, the whitespace skip above will skip the newline
continue;
}
// skip multi-line C style comments
if(search + 1 < end && search[0] == '/' && search[1] == '*')
{
// continue until the ending marker
while(search + 1 < end && (search[0] != '*' || search[1] != '/'))
search++;
// skip the end marker
search += 2;
// continue, the whitespace skip above will skip the newline
continue;
}
// missing #version is valid, so just exit
if(search + sizeof("#version") > end)
{
RDCERR("Bad shader - reached end of text after skipping all comments and whitespace");
break;
}
std::string versionText(search, search + sizeof("#version") - 1);
// if we found the version, parse it
if(versionText == "#version")
version = ParseVersionStatement(search);
// otherwise break - a missing #version is valid, and a legal #version cannot occur anywhere
// after this point.
break;
}
}
}
// default to version 100
if(version == 0)
version = 100;
GLint status = 0;
if(realShader == 0)
status = 1;
else
drv.glGetShaderiv(realShader, eGL_COMPILE_STATUS, &status);
// if we don't have program_interface_query, need to compile the shader with glslang to be able
// to reflect with. This is needed on capture or replay
if(!HasExt[ARB_program_interface_query] && status == 1)
glslangShader = CompileShaderForReflection(rdcspv::ShaderStage(ShaderIdx(type)), sources);
if(IsReplayMode(drv.GetState()) && !drv.IsInternalShader())
{
// no shaders made under this point should be reflected themselves, they're only used for
// reflection
drv.PushInternalShader();
if(status == 0)
{
RDCDEBUG("Real shader failed to compile, so skipping separable program and reflection.");
}
else
{
bool reflected = false;
// if we have separate shader object support, we can create a separable program and reflect it
// - this may or may not be emulated depending on if ARB_program_interface_query is supported.
if(HasExt[ARB_separate_shader_objects])
{
GLuint sepProg = MakeSeparableShaderProgram(drv, type, sources, NULL);
if(sepProg == 0)
{
RDCERR(
"Couldn't make separable program for shader via patching - functionality will be "
"broken.");
}
else
{
MakeShaderReflection(type, sepProg, reflection, outputUsage);
reflected = true;
drv.glDeleteProgram(sepProg);
}
}
else
{
// if we don't have separate shader objects, we manually reflect directly with glslang to
// avoid having to litter MakeSeparableShaderProgram() and child functions with checks about
// whether separable programs are actually supported or if we're just faking it to reflect.
// In this case we forcibly emulate ARB_program_interface_query.
RDCASSERT(!HasExt[ARB_program_interface_query]);
// to do this, we need to create an empty program object and manually configure its glslang
// program.
GLuint fakeProgram = drv.glCreateProgram();
ResourceId progid = drv.GetResourceManager()->GetID(ProgramRes(drv.GetCtx(), fakeProgram));
ProgramData &progDetails = drv.m_Programs[progid];
progDetails.linked = true;
progDetails.glslangProgram = LinkProgramForReflection({glslangShader});
MakeShaderReflection(type, fakeProgram, reflection, outputUsage);
reflected = true;
drv.glDeleteProgram(fakeProgram);
}
if(reflected)
{
std::vector<uint32_t> spirvwords;
rdcspv::CompilationSettings settings(rdcspv::InputLanguage::OpenGLGLSL,
rdcspv::ShaderStage(ShaderIdx(type)));
std::string s = rdcspv::Compile(settings, sources, spirvwords);
if(!spirvwords.empty())
spirv.Parse(spirvwords);
else
disassembly = s;
reflection.resourceId = id;
reflection.rawBytes.assign((byte *)concatenated.c_str(), concatenated.size());
reflection.debugInfo.files.resize(1);
reflection.debugInfo.files[0].filename = "main.glsl";
reflection.debugInfo.files[0].contents = concatenated;
}
}
drv.PopInternalShader();
}
}
#pragma region Shaders
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glCreateShader(SerialiserType &ser, GLenum type, GLuint shader)
{
SERIALISE_ELEMENT(type);
SERIALISE_ELEMENT_LOCAL(Shader, GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)))
.TypedAs("GLResource"_lit);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GLuint real = GL.glCreateShader(type);
GLResource res = ShaderRes(GetCtx(), real);
ResourceId liveId = GetResourceManager()->RegisterResource(res);
m_Shaders[liveId].type = type;
GetResourceManager()->AddLiveResource(Shader, res);
AddResource(Shader, ResourceType::Shader, "Shader");
}
return true;
}
GLuint WrappedOpenGL::glCreateShader(GLenum type)
{
GLuint real;
SERIALISE_TIME_CALL(real = GL.glCreateShader(type));
GLResource res = ShaderRes(GetCtx(), real);
ResourceId id = GetResourceManager()->RegisterResource(res);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glCreateShader(ser, type, real);
chunk = scope.Get();
}
GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id);
RDCASSERT(record);
record->AddChunk(chunk);
}
else
{
GetResourceManager()->AddLiveResource(id, res);
}
m_Shaders[id].type = type;
return real;
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glShaderSource(SerialiserType &ser, GLuint shaderHandle, GLsizei count,
const GLchar *const *source, const GLint *length)
{
SERIALISE_ELEMENT_LOCAL(shader, ShaderRes(GetCtx(), shaderHandle));
// serialisation can't handle the length parameter neatly, so we compromise by serialising via a
// vector
std::vector<std::string> sources;
if(ser.IsWriting())
{
sources.reserve(count);
for(GLsizei c = 0; c < count; c++)
{
sources.push_back((length && length[c] > 0) ? std::string(source[c], source[c] + length[c])
: std::string(source[c]));
}
}
SERIALISE_ELEMENT(count);
SERIALISE_ELEMENT(sources);
SERIALISE_ELEMENT_ARRAY(length, count);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
std::vector<const char *> strs;
for(size_t i = 0; i < sources.size(); i++)
strs.push_back(sources[i].c_str());
ResourceId liveId = GetResourceManager()->GetID(shader);
m_Shaders[liveId].sources = sources;
GL.glShaderSource(shader.name, (GLsizei)sources.size(), strs.data(), NULL);
// if we've already disassembled this shader, undo all that.
// Note this means we don't support compiling the same shader multiple times
// attached to different programs, but that is *utterly crazy* and anyone
// who tries to actually do that should be ashamed.
// Doing this means we support the case of recompiling a shader different ways
// and relinking a program before use, which is still moderately crazy and
// so people who do that should be moderately ashamed.
if(m_Shaders[liveId].reflection.resourceId != ResourceId())
{
m_Shaders[liveId].spirv = rdcspv::Reflector();
m_Shaders[liveId].reflection = ShaderReflection();
}
AddResourceInitChunk(shader);
}
return true;
}
void WrappedOpenGL::glShaderSource(GLuint shader, GLsizei count, const GLchar *const *string,
const GLint *length)
{
SERIALISE_TIME_CALL(GL.glShaderSource(shader, count, string, length));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
shader);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glShaderSource(ser, shader, count, string, length);
record->AddChunk(scope.Get());
}
}
// if we're capturing and don't have ARB_program_interface_query we're going to have to emulate
// it using glslang for compilation and reflection
if(IsReplayMode(m_State) || !HasExt[ARB_program_interface_query])
{
ResourceId id = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));
m_Shaders[id].sources.clear();
m_Shaders[id].sources.reserve(count);
for(GLsizei i = 0; i < count; i++)
m_Shaders[id].sources.push_back((length && length[i] > 0)
? std::string(string[i], string[i] + length[i])
: std::string(string[i]));
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glCompileShader(SerialiserType &ser, GLuint shaderHandle)
{
SERIALISE_ELEMENT_LOCAL(shader, ShaderRes(GetCtx(), shaderHandle));
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
ResourceId liveId = GetResourceManager()->GetID(shader);
GL.glCompileShader(shader.name);
m_Shaders[liveId].ProcessCompilation(*this, GetResourceManager()->GetOriginalID(liveId),
shader.name);
AddResourceInitChunk(shader);
}
return true;
}
void WrappedOpenGL::glCompileShader(GLuint shader)
{
GL.glCompileShader(shader);
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
shader);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glCompileShader(ser, shader);
record->AddChunk(scope.Get());
}
}
{
ResourceId id = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));
// if we're capturing and don't have ARB_program_interface_query we're going to have to emulate
// it using glslang for compilation and reflection
if(IsReplayMode(m_State) || !HasExt[ARB_program_interface_query])
m_Shaders[id].ProcessCompilation(*this, id, shader);
}
}
void WrappedOpenGL::glReleaseShaderCompiler()
{
GL.glReleaseShaderCompiler();
}
void WrappedOpenGL::glDeleteShader(GLuint shader)
{
GL.glDeleteShader(shader);
GLResource res = ShaderRes(GetCtx(), shader);
if(GetResourceManager()->HasCurrentResource(res))
{
if(GetResourceManager()->HasResourceRecord(res))
GetResourceManager()->GetResourceRecord(res)->Delete(GetResourceManager());
GetResourceManager()->UnregisterResource(res);
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glAttachShader(SerialiserType &ser, GLuint programHandle,
GLuint shaderHandle)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_ELEMENT_LOCAL(shader, ShaderRes(GetCtx(), shaderHandle));
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
ResourceId liveProgId = GetResourceManager()->GetID(program);
ResourceId liveShadId = GetResourceManager()->GetID(shader);
m_Programs[liveProgId].shaders.push_back(liveShadId);
GL.glAttachShader(program.name, shader.name);
AddResourceInitChunk(program);
DerivedResource(program, GetResourceManager()->GetOriginalID(liveShadId));
}
return true;
}
void WrappedOpenGL::glAttachShader(GLuint program, GLuint shader)
{
SERIALISE_TIME_CALL(GL.glAttachShader(program, shader));
if(program && shader)
{
if(IsCaptureMode(m_State))
{
GLResourceRecord *progRecord =
GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
GLResourceRecord *shadRecord =
GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
RDCASSERT(progRecord && shadRecord);
if(progRecord && shadRecord)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glAttachShader(ser, program, shader);
progRecord->AddParent(shadRecord);
progRecord->AddChunk(scope.Get());
}
}
{
ResourceId progid = GetResourceManager()->GetID(ProgramRes(GetCtx(), program));
ResourceId shadid = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));
m_Programs[progid].shaders.push_back(shadid);
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glDetachShader(SerialiserType &ser, GLuint programHandle,
GLuint shaderHandle)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_ELEMENT_LOCAL(shader, ShaderRes(GetCtx(), shaderHandle));
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
ResourceId liveProgId = GetResourceManager()->GetID(program);
ResourceId liveShadId = GetResourceManager()->GetID(shader);
// in order to be able to relink programs, we don't replay detaches. This should be valid as
// it's legal to have a shader attached to multiple programs, so even if it's attached again
// that doesn't affect the attach here.
/*
if(!m_Programs[liveProgId].linked)
{
for(auto it = m_Programs[liveProgId].shaders.begin();
it != m_Programs[liveProgId].shaders.end(); ++it)
{
if(*it == liveShadId)
{
m_Programs[liveProgId].shaders.erase(it);
break;
}
}
}
GL.glDetachShader(GetResourceManager()->GetLiveResource(progid).name,
GetResourceManager()->GetLiveResource(shadid).name);
*/
}
return true;
}
void WrappedOpenGL::glDetachShader(GLuint program, GLuint shader)
{
SERIALISE_TIME_CALL(GL.glDetachShader(program, shader));
if(program && shader)
{
// check that shader still exists, it might have been deleted. If it has, it's not too important
// that we detach the shader (only important if the program will attach it elsewhere).
if(IsCaptureMode(m_State) && GetResourceManager()->HasCurrentResource(ShaderRes(GetCtx(), shader)))
{
GLResourceRecord *progRecord =
GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
RDCASSERT(progRecord);
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glDetachShader(ser, program, shader);
progRecord->AddChunk(scope.Get());
}
}
{
ResourceId progid = GetResourceManager()->GetID(ProgramRes(GetCtx(), program));
ResourceId shadid = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));
if(!m_Programs[progid].linked)
{
for(auto it = m_Programs[progid].shaders.begin(); it != m_Programs[progid].shaders.end(); ++it)
{
if(*it == shadid)
{
m_Programs[progid].shaders.erase(it);
break;
}
}
}
}
}
}
#pragma endregion
#pragma region Programs
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glCreateShaderProgramv(SerialiserType &ser, GLenum type, GLsizei count,
const GLchar *const *strings, GLuint program)
{
SERIALISE_ELEMENT(type);
SERIALISE_ELEMENT(count);
SERIALISE_ELEMENT_ARRAY(strings, count);
SERIALISE_ELEMENT_LOCAL(Program, GetResourceManager()->GetID(ProgramRes(GetCtx(), program)))
.TypedAs("GLResource"_lit);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
std::vector<std::string> src;
for(GLsizei i = 0; i < count; i++)
src.push_back(strings[i]);
GLuint real = GL.glCreateShaderProgramv(type, count, strings);
GLResource res = ProgramRes(GetCtx(), real);
ResourceId liveId = m_ResourceManager->RegisterResource(res);
auto &progDetails = m_Programs[liveId];
progDetails.linked = true;
progDetails.shaders.push_back(liveId);
progDetails.stageShaders[ShaderIdx(type)] = liveId;
progDetails.shaderProgramUnlinkable = true;
auto &shadDetails = m_Shaders[liveId];
shadDetails.type = type;
shadDetails.sources.swap(src);
shadDetails.ProcessCompilation(*this, Program, 0);
GetResourceManager()->AddLiveResource(Program, res);
AddResource(Program, ResourceType::StateObject, "Program");
}
return true;
}
GLuint WrappedOpenGL::glCreateShaderProgramv(GLenum type, GLsizei count, const GLchar *const *strings)
{
GLuint real;
SERIALISE_TIME_CALL(real = GL.glCreateShaderProgramv(type, count, strings));
if(real == 0)
return real;
GLResource res = ProgramRes(GetCtx(), real);
ResourceId id = GetResourceManager()->RegisterResource(res);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glCreateShaderProgramv(ser, type, count, strings, real);
chunk = scope.Get();
}
GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id);
RDCASSERT(record);
// we always want to mark programs as dirty so we can serialise their
// locations as initial state (and form a remapping table)
GetResourceManager()->MarkDirtyResource(id);
record->AddChunk(chunk);
}
else
{
RDCERR("Should not use glCreateShaderProgramv internally on replay");
}
return real;
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glCreateProgram(SerialiserType &ser, GLuint program)
{
SERIALISE_ELEMENT_LOCAL(Program, GetResourceManager()->GetID(ProgramRes(GetCtx(), program)))
.TypedAs("GLResource"_lit);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GLuint real = GL.glCreateProgram();
GLResource res = ProgramRes(GetCtx(), real);
ResourceId liveId = m_ResourceManager->RegisterResource(res);
m_Programs[liveId].linked = false;
GetResourceManager()->AddLiveResource(Program, res);
AddResource(Program, ResourceType::StateObject, "Program");
}
return true;
}
GLuint WrappedOpenGL::glCreateProgram()
{
GLuint real;
SERIALISE_TIME_CALL(real = GL.glCreateProgram());
GLResource res = ProgramRes(GetCtx(), real);
ResourceId id = GetResourceManager()->RegisterResource(res);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glCreateProgram(ser, real);
chunk = scope.Get();
}
GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id);
RDCASSERT(record);
// we always want to mark programs as dirty so we can serialise their
// locations as initial state (and form a remapping table)
GetResourceManager()->MarkDirtyResource(id);
record->AddChunk(chunk);
}
else
{
GetResourceManager()->AddLiveResource(id, res);
}
m_Programs[id].linked = false;
return real;
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glLinkProgram(SerialiserType &ser, GLuint programHandle)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
ResourceId progid = GetResourceManager()->GetID(program);
ProgramData &progDetails = m_Programs[progid];
progDetails.linked = true;
for(size_t s = 0; s < 6; s++)
{
for(size_t sh = 0; sh < progDetails.shaders.size(); sh++)
{
if(m_Shaders[progDetails.shaders[sh]].type == ShaderEnum(s))
progDetails.stageShaders[s] = progDetails.shaders[sh];
}
}
if(!HasExt[ARB_program_interface_query])
{
std::vector<glslang::TShader *> glslangShaders;
for(ResourceId id : progDetails.stageShaders)
{
if(id == ResourceId())
continue;
glslang::TShader *s = m_Shaders[id].glslangShader;
if(s == NULL)
{
RDCERR("Shader attached with no compiled glslang reflection shader!");
continue;
}
glslangShaders.push_back(m_Shaders[id].glslangShader);
}
progDetails.glslangProgram = LinkProgramForReflection(glslangShaders);
}
GL.glLinkProgram(program.name);
AddResourceInitChunk(program);
}
return true;
}
void WrappedOpenGL::glLinkProgram(GLuint program)
{
SERIALISE_TIME_CALL(GL.glLinkProgram(program));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
program);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glLinkProgram(ser, program);
record->AddChunk(scope.Get());
}
// we need initial contents for programs to know any initial bindings potentially if they change
// over the frame, and for uniform location remapping.
// We just inject a call to prepare the initial contents now, any other post-link data setting
// will be replayed as expected.
if(IsActiveCapturing(m_State))
{
GetResourceManager()->ContextPrepare_InitialState(ProgramRes(GetCtx(), program));
}
}
{
ResourceId progid = GetResourceManager()->GetID(ProgramRes(GetCtx(), program));
ProgramData &progDetails = m_Programs[progid];
progDetails.linked = true;
for(size_t s = 0; s < 6; s++)
{
for(size_t sh = 0; sh < progDetails.shaders.size(); sh++)
{
if(m_Shaders[progDetails.shaders[sh]].type == ShaderEnum(s))
progDetails.stageShaders[s] = progDetails.shaders[sh];
}
}
if(!HasExt[ARB_program_interface_query])
{
std::vector<glslang::TShader *> glslangShaders;
for(ResourceId id : progDetails.stageShaders)
{
if(id == ResourceId())
continue;
glslang::TShader *s = m_Shaders[id].glslangShader;
if(s == NULL)
{
RDCERR("Shader attached with no compiled glslang reflection shader!");
continue;
}
glslangShaders.push_back(m_Shaders[id].glslangShader);
}
progDetails.glslangProgram = LinkProgramForReflection(glslangShaders);
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glUniformBlockBinding(SerialiserType &ser, GLuint programHandle,
GLuint uniformBlockIndex,
GLuint uniformBlockBinding)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_ELEMENT(uniformBlockIndex);
SERIALISE_ELEMENT(uniformBlockBinding);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glUniformBlockBinding(program.name, uniformBlockIndex, uniformBlockBinding);
AddResourceInitChunk(program);
}
return true;
}
void WrappedOpenGL::glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex,
GLuint uniformBlockBinding)
{
SERIALISE_TIME_CALL(GL.glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding));
// we should only capture this while active, since the initial states will grab everything at the
// start of the frame and we only want to pick up dynamic changes after that.
if(IsActiveCapturing(m_State))
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glUniformBlockBinding(ser, program, uniformBlockIndex, uniformBlockBinding);
GetContextRecord()->AddChunk(scope.Get());
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glShaderStorageBlockBinding(SerialiserType &ser, GLuint programHandle,
GLuint storageBlockIndex,
GLuint storageBlockBinding)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_ELEMENT(storageBlockIndex);
SERIALISE_ELEMENT(storageBlockBinding);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glShaderStorageBlockBinding(program.name, storageBlockIndex, storageBlockBinding);
AddResourceInitChunk(program);
}
return true;
}
void WrappedOpenGL::glShaderStorageBlockBinding(GLuint program, GLuint storageBlockIndex,
GLuint storageBlockBinding)
{
SERIALISE_TIME_CALL(GL.glShaderStorageBlockBinding(program, storageBlockIndex, storageBlockBinding));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
program);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glShaderStorageBlockBinding(ser, program, storageBlockIndex, storageBlockBinding);
record->AddChunk(scope.Get());
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glBindAttribLocation(SerialiserType &ser, GLuint programHandle,
GLuint index, const GLchar *name)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_ELEMENT(index);
SERIALISE_ELEMENT(name);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glBindAttribLocation(program.name, index, name);
AddResourceInitChunk(program);
}
return true;
}
void WrappedOpenGL::glBindAttribLocation(GLuint program, GLuint index, const GLchar *name)
{
SERIALISE_TIME_CALL(GL.glBindAttribLocation(program, index, name));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
program);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glBindAttribLocation(ser, program, index, name);
record->AddChunk(scope.Get());
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glBindFragDataLocation(SerialiserType &ser, GLuint programHandle,
GLuint color, const GLchar *name)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_ELEMENT(color);
SERIALISE_ELEMENT(name);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glBindFragDataLocation(program.name, color, name);
AddResourceInitChunk(program);
}
return true;
}
void WrappedOpenGL::glBindFragDataLocation(GLuint program, GLuint color, const GLchar *name)
{
SERIALISE_TIME_CALL(GL.glBindFragDataLocation(program, color, name));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
program);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glBindFragDataLocation(ser, program, color, name);
record->AddChunk(scope.Get());
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glUniformSubroutinesuiv(SerialiserType &ser, GLenum shadertype,
GLsizei count, const GLuint *indices)
{
SERIALISE_ELEMENT(shadertype);
SERIALISE_ELEMENT(count);
SERIALISE_ELEMENT_ARRAY(indices, count);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glUniformSubroutinesuiv(shadertype, count, indices);
APIProps.ShaderLinkage = true;
}
return true;
}
void WrappedOpenGL::glUniformSubroutinesuiv(GLenum shadertype, GLsizei count, const GLuint *indices)
{
SERIALISE_TIME_CALL(GL.glUniformSubroutinesuiv(shadertype, count, indices));
if(IsActiveCapturing(m_State))
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glUniformSubroutinesuiv(ser, shadertype, count, indices);
GetContextRecord()->AddChunk(scope.Get());
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glBindFragDataLocationIndexed(SerialiserType &ser,
GLuint programHandle, GLuint colorNumber,
GLuint index, const GLchar *name)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_ELEMENT(colorNumber);
SERIALISE_ELEMENT(index);
SERIALISE_ELEMENT(name);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glBindFragDataLocationIndexed(program.name, colorNumber, index, name);
AddResourceInitChunk(program);
}
return true;
}
void WrappedOpenGL::glBindFragDataLocationIndexed(GLuint program, GLuint colorNumber, GLuint index,
const GLchar *name)
{
SERIALISE_TIME_CALL(GL.glBindFragDataLocationIndexed(program, colorNumber, index, name));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
program);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glBindFragDataLocationIndexed(ser, program, colorNumber, index, name);
record->AddChunk(scope.Get());
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glTransformFeedbackVaryings(SerialiserType &ser, GLuint programHandle,
GLsizei count,
const GLchar *const *varyings,
GLenum bufferMode)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_ELEMENT(count);
SERIALISE_ELEMENT_ARRAY(varyings, count);
SERIALISE_ELEMENT(bufferMode);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glTransformFeedbackVaryings(program.name, count, varyings, bufferMode);
AddResourceInitChunk(program);
}
return true;
}
void WrappedOpenGL::glTransformFeedbackVaryings(GLuint program, GLsizei count,
const GLchar *const *varyings, GLenum bufferMode)
{
SERIALISE_TIME_CALL(GL.glTransformFeedbackVaryings(program, count, varyings, bufferMode));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
program);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glTransformFeedbackVaryings(ser, program, count, varyings, bufferMode);
record->AddChunk(scope.Get());
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glProgramParameteri(SerialiserType &ser, GLuint programHandle,
GLenum pname, GLint value)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_ELEMENT(pname);
SERIALISE_ELEMENT(value);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glProgramParameteri(program.name, pname, value);
AddResourceInitChunk(program);
}
return true;
}
void WrappedOpenGL::glProgramParameteri(GLuint program, GLenum pname, GLint value)
{
SERIALISE_TIME_CALL(GL.glProgramParameteri(program, pname, value));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ProgramRes(GetCtx(), program));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
program);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glProgramParameteri(ser, program, pname, value);
record->AddChunk(scope.Get());
}
}
}
void WrappedOpenGL::glDeleteProgram(GLuint program)
{
GL.glDeleteProgram(program);
GLResource res = ProgramRes(GetCtx(), program);
if(GetResourceManager()->HasCurrentResource(res))
{
if(GetResourceManager()->HasResourceRecord(res))
GetResourceManager()->GetResourceRecord(res)->Delete(GetResourceManager());
GetResourceManager()->UnregisterResource(res);
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glUseProgram(SerialiserType &ser, GLuint programHandle)
{
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glUseProgram(program.name);
}
return true;
}
void WrappedOpenGL::glUseProgram(GLuint program)
{
SERIALISE_TIME_CALL(GL.glUseProgram(program));
GetCtxData().m_Program = program;
if(IsActiveCapturing(m_State))
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glUseProgram(ser, program);
GetContextRecord()->AddChunk(scope.Get());
GetResourceManager()->MarkResourceFrameReferenced(ProgramRes(GetCtx(), program), eFrameRef_Read);
}
}
void WrappedOpenGL::glValidateProgram(GLuint program)
{
GL.glValidateProgram(program);
}
void WrappedOpenGL::glValidateProgramPipeline(GLuint pipeline)
{
GL.glValidateProgramPipeline(pipeline);
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glShaderBinary(SerialiserType &ser, GLsizei count,
const GLuint *shaders, GLenum binaryformat,
const void *binary, GLsizei length)
{
SERIALISE_ELEMENT(count);
SERIALISE_ELEMENT_LOCAL(shader, ShaderRes(GetCtx(), shaders[0]));
SERIALISE_ELEMENT(binaryformat);
SERIALISE_ELEMENT_ARRAY(binary, length);
SERIALISE_ELEMENT(length);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
ResourceId liveId = GetResourceManager()->GetID(shader);
GL.glShaderBinary(1, &shader.name, binaryformat, binary, length);
m_Shaders[liveId].spirvWords.assign((uint32_t *)binary, (uint32_t *)((byte *)binary + length));
AddResourceInitChunk(shader);
}
return true;
}
void WrappedOpenGL::glShaderBinary(GLsizei count, const GLuint *shaders, GLenum binaryformat,
const void *binary, GLsizei length)
{
// conditionally forward on this call when capturing, since we want to coax the app into
// providing non-binary shaders unless it's a format we understand: SPIR-V.
if(IsReplayMode(m_State))
{
GL.glShaderBinary(count, shaders, binaryformat, binary, length);
if(binaryformat == eGL_SHADER_BINARY_FORMAT_SPIR_V)
{
for(GLsizei i = 0; i < count; i++)
{
ResourceId liveId = GetResourceManager()->GetID(ShaderRes(GetCtx(), shaders[i]));
m_Shaders[liveId].spirvWords.assign((uint32_t *)binary,
(uint32_t *)((byte *)binary + length));
}
}
}
else if(IsCaptureMode(m_State) && binaryformat == eGL_SHADER_BINARY_FORMAT_SPIR_V)
{
SERIALISE_TIME_CALL(GL.glShaderBinary(count, shaders, binaryformat, binary, length));
for(GLsizei i = 0; i < count; i++)
{
GLResourceRecord *record =
GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shaders[i]));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
shaders[i]);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glShaderBinary(ser, 1, shaders + i, binaryformat, binary, length);
record->AddChunk(scope.Get());
m_Shaders[record->GetResourceID()].spirvWords.assign((uint32_t *)binary,
(uint32_t *)((byte *)binary + length));
}
}
}
}
void WrappedOpenGL::glProgramBinary(GLuint program, GLenum binaryFormat, const void *binary,
GLsizei length)
{
// deliberately don't forward on this call when writing, since we want to coax the app into
// providing non-binary shaders.
if(IsReplayMode(m_State))
{
GL.glProgramBinary(program, binaryFormat, binary, length);
}
}
#pragma endregion
#pragma region Program Pipelines
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glUseProgramStages(SerialiserType &ser, GLuint pipelineHandle,
GLbitfield stages, GLuint programHandle)
{
SERIALISE_ELEMENT_LOCAL(pipeline, ProgramPipeRes(GetCtx(), pipelineHandle));
SERIALISE_ELEMENT_TYPED(GLshaderbitfield, stages);
SERIALISE_ELEMENT_LOCAL(program, ProgramRes(GetCtx(), programHandle));
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
if(program.name)
{
ResourceId livePipeId = GetResourceManager()->GetID(pipeline);
ResourceId liveProgId = GetResourceManager()->GetID(program);
PipelineData &pipeDetails = m_Pipelines[livePipeId];
ProgramData &progDetails = m_Programs[liveProgId];
for(size_t s = 0; s < 6; s++)
{
if(stages & ShaderBit(s))
{
for(size_t sh = 0; sh < progDetails.shaders.size(); sh++)
{
if(m_Shaders[progDetails.shaders[sh]].type == ShaderEnum(s))
{
pipeDetails.stagePrograms[s] = liveProgId;
pipeDetails.stageShaders[s] = progDetails.shaders[sh];
break;
}
}
}
}
GL.glUseProgramStages(pipeline.name, stages, program.name);
}
else
{
ResourceId livePipeId = GetResourceManager()->GetID(pipeline);
PipelineData &pipeDetails = m_Pipelines[livePipeId];
for(size_t s = 0; s < 6; s++)
{
if(stages & ShaderBit(s))
{
pipeDetails.stagePrograms[s] = ResourceId();
pipeDetails.stageShaders[s] = ResourceId();
}
}
GL.glUseProgramStages(pipeline.name, stages, 0);
}
AddResourceInitChunk(pipeline);
}
return true;
}
void WrappedOpenGL::glUseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program)
{
SERIALISE_TIME_CALL(GL.glUseProgramStages(pipeline, stages, program));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record =
GetResourceManager()->GetResourceRecord(ProgramPipeRes(GetCtx(), pipeline));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
pipeline);
if(record == NULL)
return;
if(IsActiveCapturing(m_State))
{
GetResourceManager()->MarkResourceFrameReferenced(record->Resource, eFrameRef_ReadBeforeWrite);
GetResourceManager()->MarkResourceFrameReferenced(ProgramRes(GetCtx(), program),
eFrameRef_Read);
}
if(m_HighTrafficResources.find(record->GetResourceID()) != m_HighTrafficResources.end() &&
IsBackgroundCapturing(m_State))
return;
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glUseProgramStages(ser, pipeline, stages, program);
Chunk *chunk = scope.Get();
if(IsActiveCapturing(m_State))
{
GetContextRecord()->AddChunk(chunk);
}
else
{
record->AddChunk(chunk);
record->UpdateCount++;
if(record->UpdateCount > 10)
{
m_HighTrafficResources.insert(record->GetResourceID());
GetResourceManager()->MarkDirtyResource(record->GetResourceID());
}
}
}
else
{
if(program)
{
ResourceId pipeID = GetResourceManager()->GetID(ProgramPipeRes(GetCtx(), pipeline));
ResourceId progID = GetResourceManager()->GetID(ProgramRes(GetCtx(), program));
PipelineData &pipeDetails = m_Pipelines[pipeID];
ProgramData &progDetails = m_Programs[progID];
for(size_t s = 0; s < 6; s++)
{
if(stages & ShaderBit(s))
{
for(size_t sh = 0; sh < progDetails.shaders.size(); sh++)
{
if(m_Shaders[progDetails.shaders[sh]].type == ShaderEnum(s))
{
pipeDetails.stagePrograms[s] = progID;
pipeDetails.stageShaders[s] = progDetails.shaders[sh];
break;
}
}
}
}
}
else
{
ResourceId pipeID = GetResourceManager()->GetID(ProgramPipeRes(GetCtx(), pipeline));
PipelineData &pipeDetails = m_Pipelines[pipeID];
for(size_t s = 0; s < 6; s++)
{
if(stages & ShaderBit(s))
{
pipeDetails.stagePrograms[s] = ResourceId();
pipeDetails.stageShaders[s] = ResourceId();
}
}
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glGenProgramPipelines(SerialiserType &ser, GLsizei n, GLuint *pipelines)
{
SERIALISE_ELEMENT(n);
SERIALISE_ELEMENT_LOCAL(pipeline, GetResourceManager()->GetID(ProgramPipeRes(GetCtx(), *pipelines)))
.TypedAs("GLResource"_lit);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GLuint real = 0;
GL.glGenProgramPipelines(1, &real);
GL.glBindProgramPipeline(real);
GL.glBindProgramPipeline(0);
GLResource res = ProgramPipeRes(GetCtx(), real);
ResourceId live = m_ResourceManager->RegisterResource(res);
GetResourceManager()->AddLiveResource(pipeline, res);
AddResource(pipeline, ResourceType::StateObject, "Pipeline");
}
return true;
}
void WrappedOpenGL::glGenProgramPipelines(GLsizei n, GLuint *pipelines)
{
SERIALISE_TIME_CALL(GL.glGenProgramPipelines(n, pipelines));
for(GLsizei i = 0; i < n; i++)
{
GLResource res = ProgramPipeRes(GetCtx(), pipelines[i]);
ResourceId id = GetResourceManager()->RegisterResource(res);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glGenProgramPipelines(ser, 1, pipelines + i);
chunk = scope.Get();
}
GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id);
RDCASSERT(record);
record->AddChunk(chunk);
}
else
{
GetResourceManager()->AddLiveResource(id, res);
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glCreateProgramPipelines(SerialiserType &ser, GLsizei n,
GLuint *pipelines)
{
SERIALISE_ELEMENT(n);
SERIALISE_ELEMENT_LOCAL(pipeline, GetResourceManager()->GetID(ProgramPipeRes(GetCtx(), *pipelines)))
.TypedAs("GLResource"_lit);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GLuint real = 0;
GL.glCreateProgramPipelines(1, &real);
GLResource res = ProgramPipeRes(GetCtx(), real);
ResourceId live = m_ResourceManager->RegisterResource(res);
GetResourceManager()->AddLiveResource(pipeline, res);
AddResource(pipeline, ResourceType::StateObject, "Pipeline");
}
return true;
}
void WrappedOpenGL::glCreateProgramPipelines(GLsizei n, GLuint *pipelines)
{
SERIALISE_TIME_CALL(GL.glCreateProgramPipelines(n, pipelines));
for(GLsizei i = 0; i < n; i++)
{
GLResource res = ProgramPipeRes(GetCtx(), pipelines[i]);
ResourceId id = GetResourceManager()->RegisterResource(res);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glCreateProgramPipelines(ser, 1, pipelines + i);
chunk = scope.Get();
}
GLResourceRecord *record = GetResourceManager()->AddResourceRecord(id);
RDCASSERT(record);
record->AddChunk(chunk);
}
else
{
GetResourceManager()->AddLiveResource(id, res);
}
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glBindProgramPipeline(SerialiserType &ser, GLuint pipelineHandle)
{
SERIALISE_ELEMENT_LOCAL(pipeline, ProgramPipeRes(GetCtx(), pipelineHandle));
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
GL.glBindProgramPipeline(pipeline.name);
}
return true;
}
void WrappedOpenGL::glBindProgramPipeline(GLuint pipeline)
{
SERIALISE_TIME_CALL(GL.glBindProgramPipeline(pipeline));
GetCtxData().m_ProgramPipeline = pipeline;
if(IsActiveCapturing(m_State))
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glBindProgramPipeline(ser, pipeline);
GetContextRecord()->AddChunk(scope.Get());
GetResourceManager()->MarkResourceFrameReferenced(ProgramPipeRes(GetCtx(), pipeline),
eFrameRef_Read);
// mark all the sub programs referenced
GLenum programBinds[] = {
eGL_VERTEX_SHADER, eGL_FRAGMENT_SHADER, eGL_GEOMETRY_SHADER,
eGL_TESS_CONTROL_SHADER, eGL_TESS_EVALUATION_SHADER, eGL_COMPUTE_SHADER,
};
for(GLenum progbind : programBinds)
{
GLuint prog = 0;
GL.glGetProgramPipelineiv(pipeline, progbind, (GLint *)&prog);
if(prog)
GetResourceManager()->MarkResourceFrameReferenced(ProgramRes(GetCtx(), prog), eFrameRef_Read);
}
}
}
void WrappedOpenGL::glActiveShaderProgram(GLuint pipeline, GLuint program)
{
GL.glActiveShaderProgram(pipeline, program);
}
GLuint WrappedOpenGL::GetUniformProgram()
{
ContextData &cd = GetCtxData();
// program gets first dibs, if one is bound then that's where glUniform* calls go.
if(cd.m_Program != 0)
{
return cd.m_Program;
}
else if(cd.m_ProgramPipeline != 0)
{
GLuint ret = 0;
// otherwise, query the active program for the pipeline (could cache this above in
// glActiveShaderProgram)
// we do this query every time instead of caching the result, since I think it's unlikely that
// we'll ever hit this path (most people using separable programs will use the glProgramUniform*
// interface).
// That way we don't pay the cost of a potentially expensive query unless we really need it.
GL.glGetProgramPipelineiv(cd.m_ProgramPipeline, eGL_ACTIVE_PROGRAM, (GLint *)&ret);
return ret;
}
return 0;
}
void WrappedOpenGL::glDeleteProgramPipelines(GLsizei n, const GLuint *pipelines)
{
for(GLsizei i = 0; i < n; i++)
{
GLResource res = ProgramPipeRes(GetCtx(), pipelines[i]);
if(GetResourceManager()->HasCurrentResource(res))
{
if(GetResourceManager()->HasResourceRecord(res))
GetResourceManager()->GetResourceRecord(res)->Delete(GetResourceManager());
GetResourceManager()->UnregisterResource(res);
}
}
GL.glDeleteProgramPipelines(n, pipelines);
}
#pragma endregion
#pragma region ARB_shading_language_include
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glCompileShaderIncludeARB(SerialiserType &ser, GLuint shaderHandle,
GLsizei count, const GLchar *const *path,
const GLint *length)
{
SERIALISE_ELEMENT_LOCAL(shader, ShaderRes(GetCtx(), shaderHandle));
SERIALISE_ELEMENT(count);
SERIALISE_ELEMENT_ARRAY(path, count);
SERIALISE_ELEMENT_ARRAY(length, count);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
CheckReplayFunctionPresent(GL.glCompileShaderIncludeARB);
ResourceId liveId = GetResourceManager()->GetID(shader);
auto &shadDetails = m_Shaders[liveId];
shadDetails.includepaths.clear();
shadDetails.includepaths.reserve(count);
for(int32_t i = 0; i < count; i++)
shadDetails.includepaths.push_back(path[i]);
GL.glCompileShaderIncludeARB(shader.name, count, path, NULL);
shadDetails.ProcessCompilation(*this, GetResourceManager()->GetOriginalID(liveId), shader.name);
AddResourceInitChunk(shader);
}
return true;
}
void WrappedOpenGL::glCompileShaderIncludeARB(GLuint shader, GLsizei count,
const GLchar *const *path, const GLint *length)
{
SERIALISE_TIME_CALL(GL.glCompileShaderIncludeARB(shader, count, path, length));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
shader);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glCompileShaderIncludeARB(ser, shader, count, path, length);
record->AddChunk(scope.Get());
}
}
else
{
ResourceId id = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));
auto &shadDetails = m_Shaders[id];
shadDetails.includepaths.clear();
shadDetails.includepaths.reserve(count);
for(int32_t i = 0; i < count; i++)
shadDetails.includepaths.push_back(path[i]);
shadDetails.ProcessCompilation(*this, id, shader);
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glNamedStringARB(SerialiserType &ser, GLenum type, GLint namelen,
const GLchar *nameStr, GLint stringlen,
const GLchar *valStr)
{
SERIALISE_ELEMENT(type);
SERIALISE_ELEMENT(namelen);
SERIALISE_ELEMENT_LOCAL(
name, nameStr ? std::string(nameStr, nameStr + (namelen > 0 ? namelen : strlen(nameStr))) : "");
SERIALISE_ELEMENT(stringlen);
SERIALISE_ELEMENT_LOCAL(
value,
valStr ? std::string(valStr, valStr + (stringlen > 0 ? stringlen : strlen(valStr))) : "");
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
CheckReplayFunctionPresent(GL.glNamedStringARB);
GL.glNamedStringARB(type, (GLint)name.length(), name.c_str(), (GLint)value.length(),
value.c_str());
}
return true;
}
void WrappedOpenGL::glNamedStringARB(GLenum type, GLint namelen, const GLchar *name,
GLint stringlen, const GLchar *str)
{
SERIALISE_TIME_CALL(GL.glNamedStringARB(type, namelen, name, stringlen, str));
if(IsCaptureMode(m_State))
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glNamedStringARB(ser, type, namelen, name, stringlen, str);
// if a program repeatedly created/destroyed named strings this will fill up with useless
// strings,
// but chances are that won't be the case - a few will be created at init time and that's it
m_DeviceRecord->AddChunk(scope.Get());
}
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glDeleteNamedStringARB(SerialiserType &ser, GLint namelen,
const GLchar *nameStr)
{
SERIALISE_ELEMENT(namelen);
SERIALISE_ELEMENT_LOCAL(
name, nameStr ? std::string(nameStr, nameStr + (namelen > 0 ? namelen : strlen(nameStr))) : "");
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
CheckReplayFunctionPresent(GL.glDeleteNamedStringARB);
GL.glDeleteNamedStringARB((GLint)name.length(), name.c_str());
}
return true;
}
void WrappedOpenGL::glDeleteNamedStringARB(GLint namelen, const GLchar *name)
{
SERIALISE_TIME_CALL(GL.glDeleteNamedStringARB(namelen, name));
if(IsCaptureMode(m_State))
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glDeleteNamedStringARB(ser, namelen, name);
// if a program repeatedly created/destroyed named strings this will fill up with useless
// strings,
// but chances are that won't be the case - a few will be created at init time and that's it
m_DeviceRecord->AddChunk(scope.Get());
}
}
#pragma endregion
void WrappedOpenGL::glMaxShaderCompilerThreadsKHR(GLuint count)
{
// pass through, don't record
GL.glMaxShaderCompilerThreadsKHR(count);
}
template <typename SerialiserType>
bool WrappedOpenGL::Serialise_glSpecializeShader(SerialiserType &ser, GLuint shaderHandle,
const GLchar *pEntryPoint,
GLuint numSpecializationConstants,
const GLuint *pConstantIndex,
const GLuint *pConstantValue)
{
SERIALISE_ELEMENT_LOCAL(shader, ShaderRes(GetCtx(), shaderHandle));
SERIALISE_ELEMENT(pEntryPoint);
SERIALISE_ELEMENT(numSpecializationConstants);
SERIALISE_ELEMENT_ARRAY(pConstantIndex, numSpecializationConstants);
SERIALISE_ELEMENT_ARRAY(pConstantValue, numSpecializationConstants);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
CheckReplayFunctionPresent(GL.glSpecializeShader);
ResourceId liveId = GetResourceManager()->GetID(shader);
GL.glSpecializeShader(shader.name, pEntryPoint, numSpecializationConstants, pConstantIndex,
pConstantValue);
m_Shaders[liveId].spirv.Parse(m_Shaders[liveId].spirvWords);
m_Shaders[liveId].ProcessSPIRVCompilation(*this, GetResourceManager()->GetOriginalID(liveId),
shader.name, pEntryPoint, numSpecializationConstants,
pConstantIndex, pConstantValue);
AddResourceInitChunk(shader);
}
return true;
}
void WrappedOpenGL::glSpecializeShader(GLuint shader, const GLchar *pEntryPoint,
GLuint numSpecializationConstants,
const GLuint *pConstantIndex, const GLuint *pConstantValue)
{
SERIALISE_TIME_CALL(GL.glSpecializeShader(shader, pEntryPoint, numSpecializationConstants,
pConstantIndex, pConstantValue));
if(IsCaptureMode(m_State))
{
GLResourceRecord *record = GetResourceManager()->GetResourceRecord(ShaderRes(GetCtx(), shader));
RDCASSERTMSG("Couldn't identify object passed to function. Mismatched or bad GLuint?", record,
shader);
if(record)
{
USE_SCRATCH_SERIALISER();
SCOPED_SERIALISE_CHUNK(gl_CurChunk);
Serialise_glSpecializeShader(ser, shader, pEntryPoint, numSpecializationConstants,
pConstantIndex, pConstantValue);
record->AddChunk(scope.Get());
ResourceId id = record->GetResourceID();
m_Shaders[id].spirv.Parse(m_Shaders[id].spirvWords);
m_Shaders[id].ProcessSPIRVCompilation(
*this, id, shader, pEntryPoint, numSpecializationConstants, pConstantIndex, pConstantValue);
}
}
else
{
ResourceId liveId = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader));
m_Shaders[liveId].spirv.Parse(m_Shaders[liveId].spirvWords);
m_Shaders[liveId].ProcessSPIRVCompilation(*this, liveId, shader, pEntryPoint,
numSpecializationConstants, pConstantIndex,
pConstantValue);
}
}
INSTANTIATE_FUNCTION_SERIALISED(void, glCreateShader, GLenum type, GLuint shader);
INSTANTIATE_FUNCTION_SERIALISED(void, glShaderSource, GLuint shaderHandle, GLsizei count,
const GLchar *const *source, const GLint *length);
INSTANTIATE_FUNCTION_SERIALISED(void, glCompileShader, GLuint shaderHandle);
INSTANTIATE_FUNCTION_SERIALISED(void, glAttachShader, GLuint programHandle, GLuint shaderHandle);
INSTANTIATE_FUNCTION_SERIALISED(void, glDetachShader, GLuint programHandle, GLuint shaderHandle);
INSTANTIATE_FUNCTION_SERIALISED(void, glCreateShaderProgramv, GLenum type, GLsizei count,
const GLchar *const *strings, GLuint program);
INSTANTIATE_FUNCTION_SERIALISED(void, glCreateProgram, GLuint program);
INSTANTIATE_FUNCTION_SERIALISED(void, glLinkProgram, GLuint programHandle);
INSTANTIATE_FUNCTION_SERIALISED(void, glUniformBlockBinding, GLuint programHandle,
GLuint uniformBlockIndex, GLuint uniformBlockBinding);
INSTANTIATE_FUNCTION_SERIALISED(void, glShaderStorageBlockBinding, GLuint programHandle,
GLuint storageBlockIndex, GLuint storageBlockBinding);
INSTANTIATE_FUNCTION_SERIALISED(void, glBindAttribLocation, GLuint programHandle, GLuint index,
const GLchar *name);
INSTANTIATE_FUNCTION_SERIALISED(void, glBindFragDataLocation, GLuint programHandle, GLuint color,
const GLchar *name);
INSTANTIATE_FUNCTION_SERIALISED(void, glUniformSubroutinesuiv, GLenum shadertype, GLsizei count,
const GLuint *indices);
INSTANTIATE_FUNCTION_SERIALISED(void, glBindFragDataLocationIndexed, GLuint programHandle,
GLuint colorNumber, GLuint index, const GLchar *name);
INSTANTIATE_FUNCTION_SERIALISED(void, glTransformFeedbackVaryings, GLuint programHandle,
GLsizei count, const GLchar *const *varyings, GLenum bufferMode);
INSTANTIATE_FUNCTION_SERIALISED(void, glProgramParameteri, GLuint programHandle, GLenum pname,
GLint value);
INSTANTIATE_FUNCTION_SERIALISED(void, glUseProgram, GLuint programHandle);
INSTANTIATE_FUNCTION_SERIALISED(void, glUseProgramStages, GLuint pipelineHandle, GLbitfield stages,
GLuint programHandle);
INSTANTIATE_FUNCTION_SERIALISED(void, glGenProgramPipelines, GLsizei n, GLuint *pipelines);
INSTANTIATE_FUNCTION_SERIALISED(void, glCreateProgramPipelines, GLsizei n, GLuint *pipelines);
INSTANTIATE_FUNCTION_SERIALISED(void, glBindProgramPipeline, GLuint pipelineHandle);
INSTANTIATE_FUNCTION_SERIALISED(void, glCompileShaderIncludeARB, GLuint shaderHandle, GLsizei count,
const GLchar *const *path, const GLint *length);
INSTANTIATE_FUNCTION_SERIALISED(void, glNamedStringARB, GLenum type, GLint namelen,
const GLchar *nameStr, GLint stringlen, const GLchar *valStr);
INSTANTIATE_FUNCTION_SERIALISED(void, glDeleteNamedStringARB, GLint namelen, const GLchar *nameStr);
INSTANTIATE_FUNCTION_SERIALISED(void, glShaderBinary, GLsizei count, const GLuint *shaders,
GLenum binaryformat, const void *binary, GLsizei length);
INSTANTIATE_FUNCTION_SERIALISED(void, glSpecializeShader, GLuint shader, const GLchar *pEntryPoint,
GLuint numSpecializationConstants, const GLuint *pConstantIndex,
const GLuint *pConstantValue);