mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-01 12:20:56 +00:00
2041 lines
63 KiB
C++
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);
|