diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index c759869b7..c114d4bfd 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -580,11 +580,11 @@ WrappedOpenGL::WrappedOpenGL(GLPlatform &platform) m_DeviceRecord = m_ContextRecord = NULL; ResourceIDGen::SetReplayResourceIDs(); - - InitSPIRVCompiler(); - RenderDoc::Inst().RegisterShutdownFunction(&ShutdownSPIRVCompiler); } + InitSPIRVCompiler(); + RenderDoc::Inst().RegisterShutdownFunction(&ShutdownSPIRVCompiler); + m_FakeBB_FBO = 0; m_FakeBB_Color = 0; m_FakeBB_DepthStencil = 0; diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index a845f9e1a..9e4c2740f 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -252,78 +252,6 @@ private: map m_Buffers; - // map with key being mip level, value being stored data - typedef std::map> CompressedDataStore; - - struct ShaderData - { - ShaderData() : type(eGL_NONE), prog(0), version(0) {} - GLenum type; - vector sources; - vector includepaths; - SPVModule spirv; - std::string disassembly; - ShaderReflection reflection; - GLuint prog; - int version; - - // used for if the application actually uploaded SPIR-V - std::vector spirvWords; - - // the parameters passed to glSpecializeShader - std::string entryPoint; - std::vector specIDs; - std::vector specValues; - - // pre-calculated bindpoint mapping for SPIR-V shaders. NOT valid for normal GLSL shaders - ShaderBindpointMapping mapping; - - void ProcessCompilation(WrappedOpenGL &drv, ResourceId id, GLuint realShader); - void ProcessSPIRVCompilation(WrappedOpenGL &drv, ResourceId id, GLuint realShader, - const GLchar *pEntryPoint, GLuint numSpecializationConstants, - const GLuint *pConstantIndex, const GLuint *pConstantValue); - }; - - struct ProgramData - { - ProgramData() : linked(false) { RDCEraseEl(stageShaders); } - vector shaders; - - map locationTranslate; - - // this flag indicates the program was created with glCreateShaderProgram and cannot be relinked - // again (because that function implicitly detaches and destroys the shader). However we only - // need to relink when restoring things like frag data or attrib bindings which must be relinked - // to apply - and since the application *also* could not have relinked them, they must be - // unchanged since creation. So in this case, we can skip the relink since it was impossible for - // the application to modify anything. - bool shaderProgramUnlinkable = false; - bool linked; - ResourceId stageShaders[6]; - }; - - struct PipelineData - { - PipelineData() - { - RDCEraseEl(stagePrograms); - RDCEraseEl(stageShaders); - } - - struct ProgramUse - { - ProgramUse(ResourceId id_, GLbitfield use_) : id(id_), use(use_) {} - ResourceId id; - GLbitfield use; - }; - - ResourceId stagePrograms[6]; - ResourceId stageShaders[6]; - }; - - map m_Shaders; - map m_Programs; - map m_Pipelines; vector> m_DependentReplacements; GLuint m_FakeBB_FBO; @@ -612,6 +540,85 @@ public: void StartFrameCapture(void *dev, void *wnd); bool EndFrameCapture(void *dev, void *wnd); + // map with key being mip level, value being stored data + typedef std::map> CompressedDataStore; + + struct ShaderData + { + ShaderData() : type(eGL_NONE), prog(0), version(0) {} + GLenum type; + vector sources; + vector includepaths; + SPVModule spirv; + std::string disassembly; + ShaderReflection reflection; + GLuint prog; + int version; + + // used only when we're capturing and don't have driver-side reflection so we need to emulate + glslang::TShader *glslangShader = NULL; + + // used for if the application actually uploaded SPIR-V + std::vector spirvWords; + + // the parameters passed to glSpecializeShader + std::string entryPoint; + std::vector specIDs; + std::vector specValues; + + // pre-calculated bindpoint mapping for SPIR-V shaders. NOT valid for normal GLSL shaders + ShaderBindpointMapping mapping; + + void ProcessCompilation(WrappedOpenGL &drv, ResourceId id, GLuint realShader); + void ProcessSPIRVCompilation(WrappedOpenGL &drv, ResourceId id, GLuint realShader, + const GLchar *pEntryPoint, GLuint numSpecializationConstants, + const GLuint *pConstantIndex, const GLuint *pConstantValue); + }; + + struct ProgramData + { + ProgramData() : linked(false) { RDCEraseEl(stageShaders); } + vector shaders; + + map locationTranslate; + + // this flag indicates the program was created with glCreateShaderProgram and cannot be relinked + // again (because that function implicitly detaches and destroys the shader). However we only + // need to relink when restoring things like frag data or attrib bindings which must be relinked + // to apply - and since the application *also* could not have relinked them, they must be + // unchanged since creation. So in this case, we can skip the relink since it was impossible for + // the application to modify anything. + bool shaderProgramUnlinkable = false; + bool linked; + ResourceId stageShaders[6]; + + // used only when we're capturing and don't have driver-side reflection so we need to emulate + glslang::TProgram *glslangProgram = NULL; + }; + + struct PipelineData + { + PipelineData() + { + RDCEraseEl(stagePrograms); + RDCEraseEl(stageShaders); + } + + struct ProgramUse + { + ProgramUse(ResourceId id_, GLbitfield use_) : id(id_), use(use_) {} + ResourceId id; + GLbitfield use; + }; + + ResourceId stagePrograms[6]; + ResourceId stageShaders[6]; + }; + + std::map m_Shaders; + std::map m_Programs; + std::map m_Pipelines; + struct TextureData { TextureData() diff --git a/renderdoc/driver/gl/gl_program_iterate.cpp b/renderdoc/driver/gl/gl_program_iterate.cpp index 2ad84da38..a6814c36b 100644 --- a/renderdoc/driver/gl/gl_program_iterate.cpp +++ b/renderdoc/driver/gl/gl_program_iterate.cpp @@ -764,19 +764,24 @@ static void ForAllProgramUniforms(SerialiserType *ser, CaptureState state, GLuin } // apply SSBO bindings - for(const ProgramBinding &bind : serialisedUniforms.SSBOBindings) + // GLES does not allow modification of SSBO bindings - which is good as we don't need to restore + // them, since they're immutable. + if(!IsGLES) { - GLuint idx = GL.glGetProgramResourceIndex(progDst, eGL_SHADER_STORAGE_BLOCK, bind.Name.c_str()); - if(idx != GL_INVALID_INDEX) + for(const ProgramBinding &bind : serialisedUniforms.SSBOBindings) { - if(GL.glShaderStorageBlockBinding) + GLuint idx = + GL.glGetProgramResourceIndex(progDst, eGL_SHADER_STORAGE_BLOCK, bind.Name.c_str()); + if(idx != GL_INVALID_INDEX) { - GL.glShaderStorageBlockBinding(progDst, idx, bind.Binding); - } - else - { - // TODO glShaderStorageBlockBinding is not core GLES - RDCERR("glShaderStorageBlockBinding is not supported!"); + if(GL.glShaderStorageBlockBinding) + { + GL.glShaderStorageBlockBinding(progDst, idx, bind.Binding); + } + else + { + RDCERR("glShaderStorageBlockBinding is not supported!"); + } } } } diff --git a/renderdoc/driver/gl/wrappers/gl_emulated.cpp b/renderdoc/driver/gl/wrappers/gl_emulated.cpp index 9cb2f8ff6..3ce6d5bd2 100644 --- a/renderdoc/driver/gl/wrappers/gl_emulated.cpp +++ b/renderdoc/driver/gl/wrappers/gl_emulated.cpp @@ -30,6 +30,7 @@ #include "driver/gl/gl_dispatch_table.h" #include "driver/gl/gl_driver.h" #include "driver/gl/gl_resources.h" +#include "driver/shaders/spirv/spirv_common.h" namespace glEmulate { @@ -1386,6 +1387,199 @@ void APIENTRY _glClearBufferData(GLenum target, GLenum internalformat, GLenum fo #pragma region GLES Compatibility +static ReflectionInterface ConvertInterface(GLenum programInterface) +{ + ReflectionInterface ret = ReflectionInterface::Uniform; + + switch(programInterface) + { + case eGL_PROGRAM_INPUT: ret = ReflectionInterface::Input; break; + case eGL_PROGRAM_OUTPUT: ret = ReflectionInterface::Output; break; + case eGL_UNIFORM: ret = ReflectionInterface::Uniform; break; + case eGL_UNIFORM_BLOCK: ret = ReflectionInterface::UniformBlock; break; + case eGL_SHADER_STORAGE_BLOCK: ret = ReflectionInterface::ShaderStorageBlock; break; + case eGL_ATOMIC_COUNTER_BUFFER: ret = ReflectionInterface::AtomicCounterBuffer; break; + default: + RDCERR("Unexpected program interface being queried: %s", ToStr(programInterface).c_str()); + break; + } + + return ret; +} + +static ReflectionProperty ConvertProperty(GLenum prop) +{ + ReflectionProperty ret = ReflectionProperty::ActiveResources; + + switch(prop) + { + case eGL_ACTIVE_RESOURCES: ret = ReflectionProperty::ActiveResources; break; + case eGL_BUFFER_BINDING: ret = ReflectionProperty::BufferBinding; break; + case eGL_TOP_LEVEL_ARRAY_STRIDE: ret = ReflectionProperty::TopLevelArrayStride; break; + case eGL_BLOCK_INDEX: ret = ReflectionProperty::BlockIndex; break; + case eGL_ARRAY_SIZE: ret = ReflectionProperty::ArraySize; break; + case eGL_IS_ROW_MAJOR: ret = ReflectionProperty::IsRowMajor; break; + case eGL_NUM_ACTIVE_VARIABLES: ret = ReflectionProperty::NumActiveVariables; break; + case eGL_BUFFER_DATA_SIZE: ret = ReflectionProperty::BufferDataSize; break; + case eGL_NAME_LENGTH: ret = ReflectionProperty::NameLength; break; + case eGL_TYPE: ret = ReflectionProperty::Type; break; + case eGL_LOCATION_COMPONENT: ret = ReflectionProperty::LocationComponent; break; + case eGL_REFERENCED_BY_VERTEX_SHADER: ret = ReflectionProperty::ReferencedByVertexShader; break; + case eGL_REFERENCED_BY_TESS_CONTROL_SHADER: + ret = ReflectionProperty::ReferencedByTessControlShader; + break; + case eGL_REFERENCED_BY_TESS_EVALUATION_SHADER: + ret = ReflectionProperty::ReferencedByTessEvaluationShader; + break; + case eGL_REFERENCED_BY_GEOMETRY_SHADER: + ret = ReflectionProperty::ReferencedByGeometryShader; + break; + case eGL_REFERENCED_BY_FRAGMENT_SHADER: + ret = ReflectionProperty::ReferencedByFragmentShader; + break; + case eGL_REFERENCED_BY_COMPUTE_SHADER: + ret = ReflectionProperty::ReferencedByComputeShader; + break; + case eGL_ATOMIC_COUNTER_BUFFER_INDEX: ret = ReflectionProperty::AtomicCounterBufferIndex; break; + case eGL_OFFSET: ret = ReflectionProperty::Offset; break; + case eGL_MATRIX_STRIDE: ret = ReflectionProperty::MatrixStride; break; + case eGL_ARRAY_STRIDE: ret = ReflectionProperty::ArrayStride; break; + case eGL_LOCATION: ret = ReflectionProperty::Location; break; + default: RDCERR("Unexpected program property being queried: %s", ToStr(prop).c_str()); break; + } + + return ret; +} + +void APIENTRY _glGetProgramInterfaceiv(GLuint program, GLenum programInterface, GLenum pname, + GLint *params) +{ + if(driver == NULL) + { + RDCERR("No driver available, can't emulate glGetProgramInterfaceiv"); + *params = 0; + return; + } + + ResourceId id = driver->GetResourceManager()->GetID(ProgramRes(driver->GetCtx(), program)); + + WrappedOpenGL::ProgramData &details = driver->m_Programs[id]; + + if(!details.glslangProgram) + { + *params = 0; + return; + } + + glslangGetProgramInterfaceiv(details.glslangProgram, ConvertInterface(programInterface), + ConvertProperty(pname), params); +} + +void APIENTRY _glGetProgramResourceiv(GLuint program, GLenum programInterface, GLuint index, + GLsizei propCount, const GLenum *props, GLsizei bufSize, + GLsizei *length, GLint *params) +{ + if(driver == NULL) + { + RDCERR("No driver available, can't emulate glGetProgramResourceiv"); + if(length) + *length = 0; + if(params) + memset(params, 0, sizeof(GLint) * bufSize); + return; + } + + ResourceId id = driver->GetResourceManager()->GetID(ProgramRes(driver->GetCtx(), program)); + + WrappedOpenGL::ProgramData &details = driver->m_Programs[id]; + + if(!details.glslangProgram) + { + if(length) + *length = 0; + if(params) + memset(params, 0, sizeof(GLint) * bufSize); + return; + } + + std::vector properties(propCount); + + for(GLsizei i = 0; i < propCount; i++) + properties[i] = ConvertProperty(props[i]); + + glslangGetProgramResourceiv(details.glslangProgram, ConvertInterface(programInterface), index, + properties, bufSize, length, params); + + // fetch locations by hand from the driver + for(GLsizei i = 0; i < propCount; i++) + { + if(props[i] == eGL_LOCATION) + { + if(params[i] >= 0) + { + const char *name = glslangGetProgramResourceName(details.glslangProgram, + ConvertInterface(programInterface), index); + + if(programInterface == eGL_UNIFORM) + params[i] = GL.glGetUniformLocation(program, name); + else if(programInterface == eGL_PROGRAM_INPUT) + params[i] = GL.glGetAttribLocation(program, name); + else + params[i] = index; + } + } + } +} + +void APIENTRY _glGetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, + GLsizei bufSize, GLsizei *length, GLchar *name) +{ + if(driver == NULL) + { + RDCERR("No driver available, can't emulate glGetProgramResourceName"); + if(length) + *length = 0; + if(name && bufSize) + memset(name, 0, bufSize); + return; + } + + ResourceId id = driver->GetResourceManager()->GetID(ProgramRes(driver->GetCtx(), program)); + + WrappedOpenGL::ProgramData &details = driver->m_Programs[id]; + + if(!details.glslangProgram) + { + if(length) + *length = 0; + if(name && bufSize) + memset(name, 0, bufSize); + return; + } + + const char *fetchedName = glslangGetProgramResourceName( + details.glslangProgram, ConvertInterface(programInterface), index); + + if(fetchedName) + { + size_t nameLen = strlen(fetchedName); + if(length) + *length = (int32_t)nameLen; + + memcpy(name, fetchedName, RDCMIN((size_t)bufSize, nameLen)); + name[bufSize - 1] = 0; + if(nameLen < (size_t)bufSize) + name[nameLen] = 0; + } + else + { + if(length) + *length = 0; + if(name && bufSize) + memset(name, 0, bufSize); + } +} + void APIENTRY _glGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params) { if(driver == NULL) @@ -1812,6 +2006,10 @@ void GLDispatchTable::EmulateRequiredExtensions() { EMULATE_FUNC(glGetTexLevelParameteriv); EMULATE_FUNC(glGetTexLevelParameterfv); + + EMULATE_FUNC(glGetProgramInterfaceiv); + EMULATE_FUNC(glGetProgramResourceiv); + EMULATE_FUNC(glGetProgramResourceName); } if(GLCoreVersion < 32) diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index c648273b4..71e9775b5 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -234,42 +234,50 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI else drv.glGetShaderiv(realShader, eGL_COMPILE_STATUS, &status); - if(sepProg == 0 && status == 1) - sepProg = MakeSeparableShaderProgram(drv, type, sources, NULL); - - if(status == 0) + if(IsCaptureMode(drv.GetState())) { - RDCDEBUG("Real shader failed to compile, so skipping separable program and reflection."); - } - else if(sepProg == 0) - { - RDCERR( - "Couldn't make separable program for shader via patching - functionality will be broken."); + glslangShader = CompileShaderForReflection(SPIRVShaderStage(ShaderIdx(type)), sources); } else { - prog = sepProg; - MakeShaderReflection(type, sepProg, reflection, pointSizeUsed, clipDistanceUsed); + if(sepProg == 0 && status == 1) + sepProg = MakeSeparableShaderProgram(drv, type, sources, NULL); - vector spirvwords; - - SPIRVCompilationSettings settings(SPIRVSourceLanguage::OpenGLGLSL, - SPIRVShaderStage(ShaderIdx(type))); - - string s = CompileSPIRV(settings, sources, spirvwords); - if(!spirvwords.empty()) - ParseSPIRV(&spirvwords.front(), spirvwords.size(), spirv); + if(status == 0) + { + RDCDEBUG("Real shader failed to compile, so skipping separable program and reflection."); + } + else if(sepProg == 0) + { + RDCERR( + "Couldn't make separable program for shader via patching - functionality will be " + "broken."); + } else - disassembly = s; + { + prog = sepProg; + MakeShaderReflection(type, sepProg, reflection, pointSizeUsed, clipDistanceUsed); - reflection.resourceId = id; - reflection.entryPoint = "main"; + vector spirvwords; - reflection.stage = MakeShaderStage(type); + SPIRVCompilationSettings settings(SPIRVSourceLanguage::OpenGLGLSL, + SPIRVShaderStage(ShaderIdx(type))); - reflection.debugInfo.files.resize(1); - reflection.debugInfo.files[0].filename = "main.glsl"; - reflection.debugInfo.files[0].contents = concatenated; + string s = CompileSPIRV(settings, sources, spirvwords); + if(!spirvwords.empty()) + ParseSPIRV(&spirvwords.front(), spirvwords.size(), spirv); + else + disassembly = s; + + reflection.resourceId = id; + reflection.entryPoint = "main"; + + reflection.stage = MakeShaderStage(type); + + reflection.debugInfo.files.resize(1); + reflection.debugInfo.files[0].filename = "main.glsl"; + reflection.debugInfo.files[0].contents = concatenated; + } } } @@ -330,10 +338,10 @@ GLuint WrappedOpenGL::glCreateShader(GLenum type) else { GetResourceManager()->AddLiveResource(id, res); - - m_Shaders[id].type = type; } + m_Shaders[id].type = type; + return real; } @@ -415,7 +423,10 @@ void WrappedOpenGL::glShaderSource(GLuint shader, GLsizei count, const GLchar *c record->AddChunk(scope.Get()); } } - else + + // 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(); @@ -466,10 +477,14 @@ void WrappedOpenGL::glCompileShader(GLuint shader) record->AddChunk(scope.Get()); } } - else + { ResourceId id = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)); - m_Shaders[id].ProcessCompilation(*this, id, 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); } } @@ -539,7 +554,10 @@ void WrappedOpenGL::glAttachShader(GLuint program, GLuint shader) progRecord->AddChunk(scope.Get()); } } - else + + // 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 progid = GetResourceManager()->GetID(ProgramRes(GetCtx(), program)); ResourceId shadid = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)); @@ -608,7 +626,10 @@ void WrappedOpenGL::glDetachShader(GLuint program, GLuint shader) progRecord->AddChunk(scope.Get()); } } - else + + // 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 progid = GetResourceManager()->GetID(ProgramRes(GetCtx(), program)); ResourceId shadid = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)); @@ -797,7 +818,7 @@ GLuint WrappedOpenGL::glCreateProgram() record->AddChunk(chunk); } - else + { GetResourceManager()->AddLiveResource(id, res); @@ -857,7 +878,7 @@ void WrappedOpenGL::glLinkProgram(GLuint program) record->AddChunk(scope.Get()); } } - else + { ResourceId progid = GetResourceManager()->GetID(ProgramRes(GetCtx(), program)); @@ -873,6 +894,25 @@ void WrappedOpenGL::glLinkProgram(GLuint program) progDetails.stageShaders[s] = progDetails.shaders[sh]; } } + + if(IsCaptureMode(m_State) && !HasExt[ARB_program_interface_query]) + { + std::vector glslangShaders; + + for(ResourceId id : progDetails.shaders) + { + 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); + } } } diff --git a/renderdoc/driver/shaders/spirv/spirv_common.cpp b/renderdoc/driver/shaders/spirv/spirv_common.cpp index 6c0022556..5e12cdc8e 100644 --- a/renderdoc/driver/shaders/spirv/spirv_common.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_common.cpp @@ -31,6 +31,8 @@ #include "3rdparty/glslang/glslang/Public/ShaderLang.h" static bool inited = false; +std::vector allocatedShaders; +std::vector allocatedPrograms; void InitSPIRVCompiler() { @@ -45,6 +47,16 @@ void ShutdownSPIRVCompiler() { if(inited) { + // programs must be deleted before shaders + for(glslang::TProgram *program : allocatedPrograms) + delete program; + + for(glslang::TShader *shader : allocatedShaders) + delete shader; + + allocatedPrograms.clear(); + allocatedShaders.clear(); + glslang::FinalizeProcess(); } } @@ -276,3 +288,174 @@ void FillSpecConstantVariables(const rdcarray &invars, } } } + +void glslangGetProgramInterfaceiv(glslang::TProgram *program, ReflectionInterface programInterface, + ReflectionProperty pname, int32_t *params) +{ + *params = 0; + + if(pname == ReflectionProperty::ActiveResources) + { + switch(programInterface) + { + case ReflectionInterface::Input: *params = program->getNumLiveAttributes(); break; + case ReflectionInterface::Output: + // unsupported + *params = 0; + break; + case ReflectionInterface::Uniform: *params = program->getNumLiveUniformVariables(); break; + case ReflectionInterface::UniformBlock: *params = program->getNumLiveUniformBlocks(); break; + case ReflectionInterface::ShaderStorageBlock: + // unsupported + *params = 0; + break; + case ReflectionInterface::AtomicCounterBuffer: + // unsupported + *params = 0; + break; + } + } + else + { + RDCERR("Unsupported reflection property %d", pname); + } +} + +void glslangGetProgramResourceiv(glslang::TProgram *program, ReflectionInterface programInterface, + uint32_t index, const std::vector &props, + int32_t bufSize, int32_t *length, int32_t *params) +{ + if(programInterface == ReflectionInterface::Output || + programInterface == ReflectionInterface::ShaderStorageBlock || + programInterface == ReflectionInterface::AtomicCounterBuffer) + { + RDCWARN("unsupported program interface"); + } + + // all of our properties are single-element values, so we just loop up to buffer size or number of + // properties, whichever comes first. + for(size_t i = 0; i < RDCMIN((size_t)bufSize, props.size()); i++) + { + switch(props[i]) + { + case ReflectionProperty::ActiveResources: + RDCERR("Unhandled reflection property ActiveResources"); + params[i] = 0; + break; + case ReflectionProperty::BufferBinding: + RDCASSERT(programInterface == ReflectionInterface::UniformBlock); + params[i] = program->getUniformBlockBinding(index); + break; + case ReflectionProperty::TopLevelArrayStride: + // TODO glslang doesn't give us this + params[i] = 16; + break; + case ReflectionProperty::BlockIndex: + RDCASSERT(programInterface == ReflectionInterface::Uniform); + params[i] = program->getUniformBlockIndex(index); + break; + case ReflectionProperty::ArraySize: + if(programInterface == ReflectionInterface::Uniform) + params[i] = program->getUniformArraySize(index); + else if(programInterface == ReflectionInterface::Input) + // TODO assuming all inputs are non-arrayed + params[i] = 1; + else + RDCERR("Unsupported interface for ArraySize query"); + break; + case ReflectionProperty::IsRowMajor: + // TODO glslang doesn't expose this, assume column major. + params[i] = 0; + break; + case ReflectionProperty::NumActiveVariables: + // TODO glslang doesn't give us this + params[i] = 1; + break; + case ReflectionProperty::BufferDataSize: + RDCASSERT(programInterface == ReflectionInterface::UniformBlock); + params[i] = program->getUniformBlockSize(index); + break; + case ReflectionProperty::NameLength: + // The name length includes a terminating null character. + if(programInterface == ReflectionInterface::Uniform) + params[i] = (int32_t)strlen(program->getUniformName(index)) + 1; + else if(programInterface == ReflectionInterface::UniformBlock) + params[i] = (int32_t)strlen(program->getUniformBlockName(index)) + 1; + else if(programInterface == ReflectionInterface::Input) + params[i] = (int32_t)strlen(program->getAttributeName(index)) + 1; + else + RDCERR("Unsupported interface for NameLEngth query"); + break; + case ReflectionProperty::Type: + if(programInterface == ReflectionInterface::Uniform) + params[i] = program->getUniformType(index); + else if(programInterface == ReflectionInterface::Input) + params[i] = program->getAttributeType(index); + else + RDCERR("Unsupported interface for Type query"); + break; + case ReflectionProperty::LocationComponent: + // TODO glslang doesn't give us this information + params[i] = 0; + break; + case ReflectionProperty::ReferencedByVertexShader: + case ReflectionProperty::ReferencedByTessControlShader: + case ReflectionProperty::ReferencedByTessEvaluationShader: + case ReflectionProperty::ReferencedByGeometryShader: + case ReflectionProperty::ReferencedByFragmentShader: + case ReflectionProperty::ReferencedByComputeShader: + // TODO glslang doesn't give us this information + params[i] = 1; + break; + case ReflectionProperty::AtomicCounterBufferIndex: + RDCERR("Atomic counters not supported"); + break; + case ReflectionProperty::Offset: + RDCASSERT(programInterface == ReflectionInterface::Uniform); + params[i] = program->getUniformBufferOffset(index); + break; + case ReflectionProperty::MatrixStride: + RDCASSERT(programInterface == ReflectionInterface::Uniform); + // TODO glslang doesn't give us this information + params[i] = 64; + break; + case ReflectionProperty::ArrayStride: + RDCASSERT(programInterface == ReflectionInterface::Uniform); + // TODO glslang doesn't give us this information + params[i] = 64; + break; + case ReflectionProperty::Location: + // have to query the actual implementation, which is handled elsewhere. We return either -1 + // for uniforms that don't have a location (i.e. are in a block) or 0 for bare uniforms + if(programInterface == ReflectionInterface::Uniform) + params[i] = program->getUniformBlockIndex(index) >= 0 ? -1 : 0; + else if(programInterface == ReflectionInterface::Input) + params[i] = index; + break; + } + } +} + +const char *glslangGetProgramResourceName(glslang::TProgram *program, + ReflectionInterface programInterface, uint32_t index) +{ + const char *fetchedName = ""; + + switch(programInterface) + { + case ReflectionInterface::Input: fetchedName = program->getAttributeName(index); break; + case ReflectionInterface::Output: RDCWARN("Output attributes unsupported"); break; + case ReflectionInterface::Uniform: fetchedName = program->getUniformName(index); break; + case ReflectionInterface::UniformBlock: + fetchedName = program->getUniformBlockName(index); + break; + case ReflectionInterface::ShaderStorageBlock: + RDCWARN("shader storage blocks unsupported"); + break; + case ReflectionInterface::AtomicCounterBuffer: + RDCWARN("atomic counter buffers unsupported"); + break; + } + + return fetchedName; +} diff --git a/renderdoc/driver/shaders/spirv/spirv_common.h b/renderdoc/driver/shaders/spirv/spirv_common.h index 943a76db7..f6479b474 100644 --- a/renderdoc/driver/shaders/spirv/spirv_common.h +++ b/renderdoc/driver/shaders/spirv/spirv_common.h @@ -159,4 +159,60 @@ struct SpecConstant void FillSpecConstantVariables(const rdcarray &invars, std::vector &outvars, - const std::vector &specInfo); \ No newline at end of file + const std::vector &specInfo); + +namespace glslang +{ +class TShader; +class TProgram; +}; + +glslang::TShader *CompileShaderForReflection(SPIRVShaderStage stage, + const std::vector &sources); +glslang::TProgram *LinkProgramForReflection(const std::vector &shaders); + +enum class ReflectionInterface +{ + Input, + Output, + Uniform, + UniformBlock, + ShaderStorageBlock, + AtomicCounterBuffer, +}; + +enum class ReflectionProperty +{ + ActiveResources, + BufferBinding, + TopLevelArrayStride, + BlockIndex, + ArraySize, + IsRowMajor, + NumActiveVariables, + BufferDataSize, + NameLength, + Type, + LocationComponent, + ReferencedByVertexShader, + ReferencedByTessControlShader, + ReferencedByTessEvaluationShader, + ReferencedByGeometryShader, + ReferencedByFragmentShader, + ReferencedByComputeShader, + AtomicCounterBufferIndex, + Offset, + MatrixStride, + ArrayStride, + Location, +}; + +void glslangGetProgramInterfaceiv(glslang::TProgram *program, ReflectionInterface programInterface, + ReflectionProperty pname, int32_t *params); + +void glslangGetProgramResourceiv(glslang::TProgram *program, ReflectionInterface programInterface, + uint32_t index, const std::vector &props, + int32_t bufSize, int32_t *length, int32_t *params); + +const char *glslangGetProgramResourceName(glslang::TProgram *program, + ReflectionInterface programInterface, uint32_t index); \ No newline at end of file diff --git a/renderdoc/driver/shaders/spirv/spirv_compile.cpp b/renderdoc/driver/shaders/spirv/spirv_compile.cpp index 18f416352..f0ca67397 100644 --- a/renderdoc/driver/shaders/spirv/spirv_compile.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_compile.cpp @@ -212,3 +212,60 @@ string CompileSPIRV(const SPIRVCompilationSettings &settings, return errors; } + +extern std::vector allocatedShaders; +extern std::vector allocatedPrograms; + +glslang::TShader *CompileShaderForReflection(SPIRVShaderStage stage, + const std::vector &sources) +{ + EShLanguage lang = EShLanguage(stage); + + glslang::TShader *shader = new glslang::TShader(lang); + + const char **strs = new const char *[sources.size()]; + + for(size_t i = 0; i < sources.size(); i++) + strs[i] = sources[i].c_str(); + + shader->setStrings(strs, (int)sources.size()); + + if(shader->parse(&DefaultResources, 100, false, EShMsgRelaxedErrors)) + { + allocatedShaders.push_back(shader); + return shader; + } + else + { + RDCERR("glslang failed to compile shader:\n\n%s\n\n%s", shader->getInfoLog(), + shader->getInfoDebugLog()); + + delete shader; + + return NULL; + } +} + +glslang::TProgram *LinkProgramForReflection(const std::vector &shaders) +{ + glslang::TProgram *program = new glslang::TProgram(); + + for(glslang::TShader *shader : shaders) + program->addShader(shader); + + if(program->link(EShMsgDefault)) + { + program->buildReflection(); + allocatedPrograms.push_back(program); + return program; + } + else + { + RDCERR("glslang failed to link program:\n\n%s\n\n%s", program->getInfoLog(), + program->getInfoDebugLog()); + + delete program; + + return NULL; + } +} \ No newline at end of file