From dc8347262e67f958ba442956139469fbb915495b Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 19 Jun 2018 20:45:02 +0100 Subject: [PATCH] Add support for ARB_gl_spirv. Closes #767 --- renderdoc/driver/gl/gl_common.h | 3 + renderdoc/driver/gl/gl_driver.cpp | 9 +- renderdoc/driver/gl/gl_driver.h | 20 +- renderdoc/driver/gl/gl_hookset.h | 2 + renderdoc/driver/gl/gl_hookset_defs.h | 8 +- renderdoc/driver/gl/gl_initstate.cpp | 35 ++- renderdoc/driver/gl/gl_replay.cpp | 79 +++++- .../driver/gl/wrappers/gl_shader_funcs.cpp | 193 +++++++++++++- .../driver/shaders/spirv/spirv_common.cpp | 228 ++++++++++++++++ renderdoc/driver/shaders/spirv/spirv_common.h | 16 ++ .../shaders/spirv/spirv_disassemble.cpp | 2 +- renderdoc/driver/vulkan/vk_info.cpp | 18 +- renderdoc/driver/vulkan/vk_info.h | 9 +- renderdoc/driver/vulkan/vk_replay.cpp | 243 +----------------- renderdoc/driver/vulkan/vk_replay.h | 3 - renderdoc/driver/vulkan/vk_shader_cache.cpp | 69 +++-- 16 files changed, 615 insertions(+), 322 deletions(-) diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index 59dd33d4b..8045ece0e 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -1772,6 +1772,9 @@ enum class GLChunk : uint32_t glMaxShaderCompilerThreadsARB, glMaxShaderCompilerThreadsKHR, + glSpecializeShader, + glSpecializeShaderARB, + Max, }; diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index da9ea992a..46dd49fce 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -77,6 +77,7 @@ void WrappedOpenGL::BuildGLExtensions() m_GLExtensions.push_back("GL_ARB_geometry_shader4"); m_GLExtensions.push_back("GL_ARB_get_program_binary"); m_GLExtensions.push_back("GL_ARB_get_texture_sub_image"); + m_GLExtensions.push_back("GL_ARB_gl_spirv"); m_GLExtensions.push_back("GL_ARB_gpu_shader_fp64"); m_GLExtensions.push_back("GL_ARB_gpu_shader5"); m_GLExtensions.push_back("GL_ARB_half_float_pixel"); @@ -135,6 +136,7 @@ void WrappedOpenGL::BuildGLExtensions() m_GLExtensions.push_back("GL_ARB_shading_language_packing"); m_GLExtensions.push_back("GL_ARB_shadow"); m_GLExtensions.push_back("GL_ARB_shadow_ambient"); + m_GLExtensions.push_back("GL_ARB_spirv_extensions"); m_GLExtensions.push_back("GL_ARB_stencil_texturing"); m_GLExtensions.push_back("GL_ARB_sync"); m_GLExtensions.push_back("GL_ARB_tessellation_shader"); @@ -3917,6 +3919,12 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) // Just in case it gets exported and imported, completely ignore it. return true; + case GLChunk::glShaderBinary: return Serialise_glShaderBinary(ser, 0, NULL, eGL_NONE, NULL, 0); + + case GLChunk::glSpecializeShaderARB: + case GLChunk::glSpecializeShader: + return Serialise_glSpecializeShader(ser, 0, NULL, 0, NULL, NULL); + // these functions are not currently serialised - they do nothing on replay and are not // serialised for information (it would be harmless and perhaps useful for the user to see // where and how they're called). @@ -4198,7 +4206,6 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) case GLChunk::glActiveShaderProgram: case GLChunk::glActiveShaderProgramEXT: case GLChunk::glProgramBinary: - case GLChunk::glShaderBinary: case GLChunk::glReleaseShaderCompiler: case GLChunk::glFrameTerminatorGREMEDY: case GLChunk::glDiscardFramebufferEXT: diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 0819ebd4b..5da2aee30 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -308,7 +308,21 @@ private: GLuint prog; int version; - void Compile(WrappedOpenGL &gl, ResourceId id, GLuint realShader); + // 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 &gl, ResourceId id, GLuint realShader); + void ProcessSPIRVCompilation(WrappedOpenGL &gl, ResourceId id, GLuint realShader, + const GLchar *pEntryPoint, GLuint numSpecializationConstants, + const GLuint *pConstantIndex, const GLuint *pConstantValue); }; struct ProgramData @@ -2241,6 +2255,10 @@ public: GLsizei samples); void glFramebufferTexture2DMultisampleEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples); + + IMPLEMENT_FUNCTION_SERIALISED(void, glSpecializeShader, GLuint shader, const GLchar *pEntryPoint, + GLuint numSpecializationConstants, const GLuint *pConstantIndex, + const GLuint *pConstantValue); }; class ScopedDebugContext diff --git a/renderdoc/driver/gl/gl_hookset.h b/renderdoc/driver/gl/gl_hookset.h index 2c738df10..e1a68b894 100644 --- a/renderdoc/driver/gl/gl_hookset.h +++ b/renderdoc/driver/gl/gl_hookset.h @@ -662,6 +662,8 @@ struct GLHookSet // ARB_parallel_shader_compile PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR; // aliases glMaxShaderCompilerThreadsARB + // ARB_gl_spirv + PFNGLSPECIALIZESHADERPROC glSpecializeShader; // aliases glSpecializeShaderARB // EXT_direct_state_access below here. We only include the functions relevant for core 3.2+ GL, // not any diff --git a/renderdoc/driver/gl/gl_hookset_defs.h b/renderdoc/driver/gl/gl_hookset_defs.h index c166f3d61..c633a1158 100644 --- a/renderdoc/driver/gl/gl_hookset_defs.h +++ b/renderdoc/driver/gl/gl_hookset_defs.h @@ -1082,6 +1082,8 @@ HookExtensionAlias(PFNGLREADNPIXELSPROC, glReadnPixels, glReadnPixelsARB); \ HookExtensionAlias(PFNGLREADNPIXELSPROC, glReadnPixels, glReadnPixelsEXT); \ HookExtension(PFNGLTEXTUREBARRIERPROC, glTextureBarrier); \ + HookExtension(PFNGLSPECIALIZESHADERPROC, glSpecializeShader); \ + HookExtensionAlias(PFNGLSPECIALIZESHADERPROC, glSpecializeShader, glSpecializeShaderARB); \ HookExtension(PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC, glMultiDrawArraysIndirectCount); \ HookExtensionAlias(PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC, glMultiDrawArraysIndirectCount, glMultiDrawArraysIndirectCountARB); \ HookExtension(PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC, glMultiDrawElementsIndirectCount); \ @@ -2342,6 +2344,8 @@ HookAliasWrapper8(void, glReadnPixelsEXT, glReadnPixels, GLint, x, GLint, y, GLsizei, width, GLsizei, height, GLenum, format, GLenum, type, GLsizei, bufSize, void *, data); \ HookWrapper8(void, glReadnPixels, GLint, x, GLint, y, GLsizei, width, GLsizei, height, GLenum, format, GLenum, type, GLsizei, bufSize, void *, data); \ HookWrapper0(void, glTextureBarrier); \ + HookAliasWrapper5(void, glSpecializeShaderARB, glSpecializeShader, GLuint, shader, const GLchar *, pEntryPoint, GLuint, numSpecializationConstants, const GLuint *, pConstantIndex, const GLuint *, pConstantValue); \ + HookWrapper5(void, glSpecializeShader, GLuint, shader, const GLchar *, pEntryPoint, GLuint, numSpecializationConstants, const GLuint *, pConstantIndex, const GLuint *, pConstantValue); \ HookAliasWrapper5(void, glMultiDrawArraysIndirectCountARB, glMultiDrawArraysIndirectCount, GLenum, mode, const void *, indirect, GLintptr, drawcount, GLsizei, maxdrawcount, GLsizei, stride); \ HookWrapper5(void, glMultiDrawArraysIndirectCount, GLenum, mode, const void *, indirect, GLintptr, drawcount, GLsizei, maxdrawcount, GLsizei, stride); \ HookAliasWrapper6(void, glMultiDrawElementsIndirectCountARB, glMultiDrawElementsIndirectCount, GLenum, mode, GLenum, type, const void *, indirect, GLintptr, drawcount, GLsizei, maxdrawcount, GLsizei, stride); \ @@ -2491,7 +2495,6 @@ // unsupported entry points - used for dummy functions #define DefineUnsupportedDummies() \ - HookWrapper5(void, glSpecializeShader, GLuint, shader, const GLchar *, pEntryPoint, GLuint, numSpecializationConstants, const GLuint *, pConstantIndex, const GLuint *, pConstantValue); \ HookWrapper8(void, glPrimitiveBoundingBoxARB, GLfloat, minX, GLfloat, minY, GLfloat, minZ, GLfloat, minW, GLfloat, maxX, GLfloat, maxY, GLfloat, maxZ, GLfloat, maxW); \ HookWrapper1(GLuint64, glGetTextureHandleARB, GLuint, texture); \ HookWrapper2(GLuint64, glGetTextureSamplerHandleARB, GLuint, texture, GLuint, sampler); \ @@ -2511,7 +2514,6 @@ HookWrapper3(void, glGetVertexAttribLui64vARB, GLuint, index, GLenum, pname, GLuint64EXT *, params); \ HookWrapper3(GLsync, glCreateSyncFromCLeventARB, struct _cl_context *, context, struct _cl_event *, event, GLbitfield, flags); \ HookWrapper5(void, glFramebufferTextureFaceARB, GLenum, target, GLenum, attachment, GLuint, texture, GLint, level, GLenum, face); \ - HookWrapper5(void, glSpecializeShaderARB, GLuint, shader, const GLchar *, pEntryPoint, GLuint, numSpecializationConstants, const GLuint *, pConstantIndex, const GLuint *, pConstantValue); \ HookWrapper2(void, glUniform1i64ARB, GLint, location, GLint64, x); \ HookWrapper3(void, glUniform2i64ARB, GLint, location, GLint64, x, GLint64, y); \ HookWrapper4(void, glUniform3i64ARB, GLint, location, GLint64, x, GLint64, y, GLint64, z); \ @@ -4470,7 +4472,6 @@ #define CheckUnsupported() \ - HandleUnsupported(PFNGLSPECIALIZESHADERPROC, glSpecializeShader); \ HandleUnsupported(PFNGLPRIMITIVEBOUNDINGBOXARBPROC, glPrimitiveBoundingBoxARB); \ HandleUnsupported(PFNGLGETTEXTUREHANDLEARBPROC, glGetTextureHandleARB); \ HandleUnsupported(PFNGLGETTEXTURESAMPLERHANDLEARBPROC, glGetTextureSamplerHandleARB); \ @@ -4490,7 +4491,6 @@ HandleUnsupported(PFNGLGETVERTEXATTRIBLUI64VARBPROC, glGetVertexAttribLui64vARB); \ HandleUnsupported(PFNGLCREATESYNCFROMCLEVENTARBPROC, glCreateSyncFromCLeventARB); \ HandleUnsupported(PFNGLFRAMEBUFFERTEXTUREFACEARBPROC, glFramebufferTextureFaceARB); \ - HandleUnsupported(PFNGLSPECIALIZESHADERARBPROC, glSpecializeShaderARB); \ HandleUnsupported(PFNGLUNIFORM1I64ARBPROC, glUniform1i64ARB); \ HandleUnsupported(PFNGLUNIFORM2I64ARBPROC, glUniform2i64ARB); \ HandleUnsupported(PFNGLUNIFORM3I64ARBPROC, glUniform3i64ARB); \ diff --git a/renderdoc/driver/gl/gl_initstate.cpp b/renderdoc/driver/gl/gl_initstate.cpp index 18d6c68e7..0fd55403d 100644 --- a/renderdoc/driver/gl/gl_initstate.cpp +++ b/renderdoc/driver/gl/gl_initstate.cpp @@ -1104,15 +1104,34 @@ bool GLResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceId r } } - char **srcs = new char *[shadDetails.sources.size()]; - for(size_t s = 0; s < shadDetails.sources.size(); s++) - srcs[s] = (char *)shadDetails.sources[s].c_str(); - gl.glShaderSource(shad, (GLsizei)shadDetails.sources.size(), srcs, NULL); + if(!shadDetails.sources.empty()) + { + char **srcs = new char *[shadDetails.sources.size()]; + for(size_t s = 0; s < shadDetails.sources.size(); s++) + srcs[s] = (char *)shadDetails.sources[s].c_str(); + gl.glShaderSource(shad, (GLsizei)shadDetails.sources.size(), srcs, NULL); - SAFE_DELETE_ARRAY(srcs); - gl.glCompileShader(shad); - gl.glAttachShader(initProg, shad); - gl.glDeleteShader(shad); + SAFE_DELETE_ARRAY(srcs); + gl.glCompileShader(shad); + gl.glAttachShader(initProg, shad); + gl.glDeleteShader(shad); + } + else if(!shadDetails.spirvWords.empty()) + { + gl.glShaderBinary(1, &shad, eGL_SHADER_BINARY_FORMAT_SPIR_V, shadDetails.spirvWords.data(), + (GLsizei)shadDetails.spirvWords.size() * sizeof(uint32_t)); + + gl.glSpecializeShader(shad, shadDetails.entryPoint.c_str(), + (GLuint)shadDetails.specIDs.size(), shadDetails.specIDs.data(), + shadDetails.specValues.data()); + + gl.glAttachShader(initProg, shad); + gl.glDeleteShader(shad); + } + else + { + RDCERR("Unexpectedly empty shader in program initial state!"); + } } // Some drivers optimize out uniforms if they dont change any active vertex shader outputs. diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index e9b1eb565..79124daa1 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -847,6 +847,7 @@ void GLReplay::SavePipelineState() }; ShaderReflection *refls[6] = {NULL}; ShaderBindpointMapping *mappings[6] = {NULL}; + bool spirv[6] = {false}; for(int i = 0; i < 6; i++) { @@ -879,10 +880,25 @@ void GLReplay::SavePipelineState() if(pipeDetails.stageShaders[i] != ResourceId()) { curProg = rm->GetCurrentResource(pipeDetails.stagePrograms[i]).name; - stages[i]->reflection = refls[i] = - GetShader(pipeDetails.stageShaders[i], ShaderEntryPoint()); - GetBindpointMapping(gl.GetHookset(), curProg, (int)i, refls[i], - stages[i]->bindpointMapping); + + auto &shaderDetails = m_pDriver->m_Shaders[pipeDetails.stageShaders[i]]; + + if(shaderDetails.prog == 0) + stages[i]->reflection = refls[i] = NULL; + else + stages[i]->reflection = refls[i] = &shaderDetails.reflection; + + if(!shaderDetails.spirvWords.empty()) + { + stages[i]->bindpointMapping = shaderDetails.mapping; + spirv[i] = true; + } + else + { + GetBindpointMapping(gl.GetHookset(), curProg, (int)i, refls[i], + stages[i]->bindpointMapping); + } + mappings[i] = &stages[i]->bindpointMapping; stages[i]->programResourceId = rm->GetOriginalID(pipeDetails.stagePrograms[i]); @@ -906,8 +922,24 @@ void GLReplay::SavePipelineState() { if(progDetails.stageShaders[i] != ResourceId()) { - stages[i]->reflection = refls[i] = GetShader(progDetails.stageShaders[i], ShaderEntryPoint()); - GetBindpointMapping(gl.GetHookset(), curProg, (int)i, refls[i], stages[i]->bindpointMapping); + auto &shaderDetails = m_pDriver->m_Shaders[progDetails.stageShaders[i]]; + + if(shaderDetails.prog == 0) + stages[i]->reflection = refls[i] = NULL; + else + stages[i]->reflection = refls[i] = &shaderDetails.reflection; + + if(!shaderDetails.spirvWords.empty()) + { + stages[i]->bindpointMapping = shaderDetails.mapping; + spirv[i] = true; + } + else + { + GetBindpointMapping(gl.GetHookset(), curProg, (int)i, refls[i], + stages[i]->bindpointMapping); + } + mappings[i] = &stages[i]->bindpointMapping; stages[i]->programResourceId = rm->GetOriginalID(id); @@ -923,7 +955,13 @@ void GLReplay::SavePipelineState() // !!!NOTE!!! This function will MODIFY the refls[] binding arrays. // See inside this function for what it does and why. for(size_t i = 0; i < ARRAY_COUNT(refls); i++) + { + // don't resort if it's SPIR-V + if(spirv[i]) + continue; + ResortBindings(refls[i], mappings[i]); + } RDCEraseEl(pipe.transformFeedback); @@ -1987,8 +2025,33 @@ void GLReplay::FillCBufferVariables(ResourceId shader, string entryPoint, uint32 const ConstantBlock &cblock = shaderDetails.reflection.constantBlocks[cbufSlot]; - FillCBufferVariables(gl, curProg, cblock.bufferBacked ? true : false, "", cblock.variables, - outvars, data); + if(shaderDetails.spirvWords.empty()) + { + FillCBufferVariables(gl, curProg, cblock.bufferBacked ? true : false, "", cblock.variables, + outvars, data); + } + else + { + if(shaderDetails.mapping.constantBlocks[cbufSlot].bindset == SpecializationConstantBindSet) + { + std::vector specconsts; + + for(size_t i = 0; i < shaderDetails.specIDs.size(); i++) + { + SpecConstant spec; + spec.specID = shaderDetails.specIDs[i]; + spec.data.resize(sizeof(shaderDetails.specValues[i])); + memcpy(&spec.data[0], &shaderDetails.specValues[i], spec.data.size()); + specconsts.push_back(spec); + } + + FillSpecConstantVariables(cblock.variables, outvars, specconsts); + } + else + { + SPIRVFillCBufferVariables(cblock.variables, outvars, data, 0); + } + } } void GLReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index a044bc86a..8ed029ad3 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -54,12 +54,78 @@ std::string DoStringise(const GLshaderbitfield &el) END_BITFIELD_STRINGISE(); } -void WrappedOpenGL::ShaderData::Compile(WrappedOpenGL &gl, ResourceId id, GLuint realShader) +void WrappedOpenGL::ShaderData::ProcessSPIRVCompilation(WrappedOpenGL &gl, ResourceId id, + GLuint realShader, const GLchar *pEntryPoint, + GLuint numSpecializationConstants, + const GLuint *pConstantIndex, + const GLuint *pConstantValue) +{ + reflection.resourceId = id; + reflection.entryPoint = pEntryPoint; + reflection.stage = MakeShaderStage(type); + reflection.encoding = ShaderEncoding::SPIRV; + reflection.rawBytes.assign((byte *)spirv.spirv.data(), spirv.spirv.size() * sizeof(uint32_t)); + + // we discard this too, because we don't need it - we don't do any SPIR-V patching in GL + SPIRVPatchData patchData; + + spirv.MakeReflection(ShaderStage(ShaderIdx(type)), pEntryPoint, reflection, mapping, patchData); + + version = 460; + + entryPoint = pEntryPoint; + if(numSpecializationConstants > 0) + { + specIDs.assign(pConstantIndex, pConstantIndex + numSpecializationConstants); + specValues.assign(pConstantValue, pConstantValue + numSpecializationConstants); + } + + const GLHookSet &real = gl.GetHookset(); + + GLuint sepshader = real.glCreateShader(type); + if(sepshader) + { + real.glShaderBinary(1, &sepshader, eGL_SHADER_BINARY_FORMAT_SPIR_V, reflection.rawBytes.data(), + (GLsizei)reflection.rawBytes.size()); + + real.glSpecializeShader(sepshader, pEntryPoint, numSpecializationConstants, pConstantIndex, + pConstantValue); + + GLint compiled = 0; + + real.glGetShaderiv(sepshader, eGL_COMPILE_STATUS, &compiled); + + if(compiled) + { + prog = real.glCreateProgram(); + + real.glAttachShader(prog, sepshader); + real.glProgramParameteri(prog, eGL_PROGRAM_SEPARABLE, GL_TRUE); + real.glLinkProgram(prog); + + gl.glGetProgramiv(prog, eGL_LINK_STATUS, &compiled); + + if(!compiled) + { + RDCERR("Re-compiled but couldn't link SPIR-V program"); + } + } + else + { + RDCERR("Couldn't re-compile SPIR-V shader"); + } + real.glDeleteShader(sepshader); + } +} + +void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &gl, ResourceId id, GLuint realShader) { bool pointSizeUsed = false, clipDistanceUsed = false; if(type == eGL_VERTEX_SHADER) CheckVertexOutputUses(sources, pointSizeUsed, clipDistanceUsed); + entryPoint = "main"; + string concatenated; for(size_t i = 0; i < sources.size(); i++) @@ -374,7 +440,8 @@ bool WrappedOpenGL::Serialise_glCompileShader(SerialiserType &ser, GLuint shader m_Real.glCompileShader(shader.name); - m_Shaders[liveId].Compile(*this, GetResourceManager()->GetOriginalID(liveId), shader.name); + m_Shaders[liveId].ProcessCompilation(*this, GetResourceManager()->GetOriginalID(liveId), + shader.name); AddResourceInitChunk(shader); } @@ -403,7 +470,7 @@ void WrappedOpenGL::glCompileShader(GLuint shader) else { ResourceId id = GetResourceManager()->GetID(ShaderRes(GetCtx(), shader)); - m_Shaders[id].Compile(*this, id, shader); + m_Shaders[id].ProcessCompilation(*this, id, shader); } } @@ -606,7 +673,7 @@ bool WrappedOpenGL::Serialise_glCreateShaderProgramv(SerialiserType &ser, GLenum shadDetails.sources.swap(src); shadDetails.prog = sepprog; - shadDetails.Compile(*this, Program, 0); + shadDetails.ProcessCompilation(*this, Program, 0); GetResourceManager()->AddLiveResource(Program, res); @@ -670,7 +737,7 @@ GLuint WrappedOpenGL::glCreateShaderProgramv(GLenum type, GLsizei count, const G shadDetails.sources.swap(src); shadDetails.prog = sepprog; - shadDetails.Compile(*this, id, 0); + shadDetails.ProcessCompilation(*this, id, 0); } return real; @@ -1178,15 +1245,62 @@ void WrappedOpenGL::glValidateProgramPipeline(GLuint pipeline) m_Real.glValidateProgramPipeline(pipeline); } +template +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); + + m_Real.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) { - // deliberately don't forward on this call when writing, since we want to coax the app into - // providing non-binary shaders. + // 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)) { m_Real.glShaderBinary(count, shaders, binaryformat, binary, length); } + else if(IsCaptureMode(m_State) && binaryformat == eGL_SHADER_BINARY_FORMAT_SPIR_V) + { + SERIALISE_TIME_CALL(m_Real.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()); + } + } + } } void WrappedOpenGL::glProgramBinary(GLuint program, GLenum binaryFormat, const void *binary, @@ -1588,7 +1702,7 @@ bool WrappedOpenGL::Serialise_glCompileShaderIncludeARB(SerialiserType &ser, GLu m_Real.glCompileShaderIncludeARB(shader.name, count, path, NULL); - shadDetails.Compile(*this, GetResourceManager()->GetOriginalID(liveId), shader.name); + shadDetails.ProcessCompilation(*this, GetResourceManager()->GetOriginalID(liveId), shader.name); AddResourceInitChunk(shader); } @@ -1627,7 +1741,7 @@ void WrappedOpenGL::glCompileShaderIncludeARB(GLuint shader, GLsizei count, for(int32_t i = 0; i < count; i++) shadDetails.includepaths.push_back(path[i]); - shadDetails.Compile(*this, id, shader); + shadDetails.ProcessCompilation(*this, id, shader); } } @@ -1717,7 +1831,63 @@ void WrappedOpenGL::glMaxShaderCompilerThreadsKHR(GLuint count) m_Real.glMaxShaderCompilerThreadsKHR(count); } +template +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()) + { + ResourceId liveId = GetResourceManager()->GetID(shader); + + m_Real.glSpecializeShader(shader.name, pEntryPoint, numSpecializationConstants, pConstantIndex, + pConstantValue); + + ParseSPIRV(m_Shaders[liveId].spirvWords.data(), m_Shaders[liveId].spirvWords.size(), + m_Shaders[liveId].spirv); + + 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(m_Real.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()); + } + } } INSTANTIATE_FUNCTION_SERIALISED(void, glCreateShader, GLenum type, GLuint shader); @@ -1757,3 +1927,8 @@ INSTANTIATE_FUNCTION_SERIALISED(void, glCompileShaderIncludeARB, GLuint shaderHa 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); \ No newline at end of file diff --git a/renderdoc/driver/shaders/spirv/spirv_common.cpp b/renderdoc/driver/shaders/spirv/spirv_common.cpp index 4dbcad585..6c0022556 100644 --- a/renderdoc/driver/shaders/spirv/spirv_common.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_common.cpp @@ -48,3 +48,231 @@ void ShutdownSPIRVCompiler() glslang::FinalizeProcess(); } } + +void SPIRVFillCBufferVariables(const rdcarray &invars, + vector &outvars, const bytebuf &data, + size_t baseOffset) +{ + for(size_t v = 0; v < invars.size(); v++) + { + std::string basename = invars[v].name; + + uint32_t rows = invars[v].type.descriptor.rows; + uint32_t cols = invars[v].type.descriptor.columns; + uint32_t elems = RDCMAX(1U, invars[v].type.descriptor.elements); + bool rowMajor = invars[v].type.descriptor.rowMajorStorage != 0; + bool isArray = elems > 1; + + size_t dataOffset = + baseOffset + invars[v].reg.vec * sizeof(float) * 4 + invars[v].reg.comp * sizeof(float); + + if(!invars[v].type.members.empty() || (rows == 0 && cols == 0)) + { + ShaderVariable var; + var.name = basename; + var.rows = var.columns = 0; + var.type = VarType::Float; + var.rowMajor = rowMajor; + + vector varmembers; + + if(isArray) + { + for(uint32_t i = 0; i < elems; i++) + { + ShaderVariable vr; + vr.name = StringFormat::Fmt("%s[%u]", basename.c_str(), i); + vr.rows = vr.columns = 0; + vr.type = VarType::Float; + vr.rowMajor = rowMajor; + + vector mems; + + SPIRVFillCBufferVariables(invars[v].type.members, mems, data, dataOffset); + + dataOffset += invars[v].type.descriptor.arrayByteStride; + + vr.isStruct = true; + + vr.members = mems; + + varmembers.push_back(vr); + } + + var.isStruct = false; + } + else + { + var.isStruct = true; + + SPIRVFillCBufferVariables(invars[v].type.members, varmembers, data, dataOffset); + } + + { + var.members = varmembers; + outvars.push_back(var); + } + + continue; + } + + size_t outIdx = outvars.size(); + outvars.resize(outvars.size() + 1); + + { + outvars[outIdx].name = basename; + outvars[outIdx].rows = 1; + outvars[outIdx].type = invars[v].type.descriptor.type; + outvars[outIdx].isStruct = false; + outvars[outIdx].columns = cols; + outvars[outIdx].rowMajor = rowMajor; + + size_t elemByteSize = 4; + if(outvars[outIdx].type == VarType::Double) + elemByteSize = 8; + + ShaderVariable &var = outvars[outIdx]; + + if(!isArray) + { + outvars[outIdx].rows = rows; + + if(dataOffset < data.size()) + { + const byte *d = &data[dataOffset]; + + RDCASSERT(rows <= 4 && rows * cols <= 16, rows, cols); + + if(!rowMajor) + { + uint32_t tmp[16] = {0}; + + for(uint32_t c = 0; c < cols; c++) + { + size_t srcoffs = 4 * elemByteSize * c; + size_t dstoffs = rows * elemByteSize * c; + memcpy((byte *)(tmp) + dstoffs, d + srcoffs, + RDCMIN(data.size() - dataOffset + srcoffs, elemByteSize * rows)); + } + + // transpose + for(size_t r = 0; r < rows; r++) + for(size_t c = 0; c < cols; c++) + outvars[outIdx].value.uv[r * cols + c] = tmp[c * rows + r]; + } + else + { + for(uint32_t r = 0; r < rows; r++) + { + size_t srcoffs = 4 * elemByteSize * r; + size_t dstoffs = cols * elemByteSize * r; + memcpy((byte *)(&outvars[outIdx].value.uv[0]) + dstoffs, d + srcoffs, + RDCMIN(data.size() - dataOffset + srcoffs, elemByteSize * cols)); + } + } + } + } + else + { + var.name = outvars[outIdx].name; + var.rows = 0; + var.columns = 0; + + bool isMatrix = rows > 1 && cols > 1; + + vector varmembers; + varmembers.resize(elems); + + std::string base = outvars[outIdx].name; + + // primary is the 'major' direction + // so we copy secondaryDim number of primaryDim-sized elements + uint32_t primaryDim = cols; + uint32_t secondaryDim = rows; + if(isMatrix && rowMajor) + { + primaryDim = rows; + secondaryDim = cols; + } + + for(uint32_t e = 0; e < elems; e++) + { + varmembers[e].name = StringFormat::Fmt("%s[%u]", base.c_str(), e); + varmembers[e].rows = rows; + varmembers[e].type = invars[v].type.descriptor.type; + varmembers[e].isStruct = false; + varmembers[e].columns = cols; + varmembers[e].rowMajor = rowMajor; + + size_t rowDataOffset = dataOffset; + + dataOffset += invars[v].type.descriptor.arrayByteStride; + + if(rowDataOffset < data.size()) + { + const byte *d = &data[rowDataOffset]; + + // each primary element (row or column) is stored in a float4. + // we copy some padding here, but that will come out in the wash + // when we transpose + for(uint32_t s = 0; s < secondaryDim; s++) + { + uint32_t matStride = primaryDim; + if(matStride == 3) + matStride = 4; + memcpy(&(varmembers[e].value.uv[primaryDim * s]), d + matStride * elemByteSize * s, + RDCMIN(data.size() - rowDataOffset, elemByteSize * primaryDim)); + } + + if(!rowMajor) + { + ShaderVariable tmp = varmembers[e]; + // transpose + for(size_t ri = 0; ri < rows; ri++) + for(size_t ci = 0; ci < cols; ci++) + varmembers[e].value.uv[ri * cols + ci] = tmp.value.uv[ci * rows + ri]; + } + } + } + + { + var.isStruct = false; + var.members = varmembers; + } + } + } + } +} + +void FillSpecConstantVariables(const rdcarray &invars, + std::vector &outvars, + const std::vector &specInfo) +{ + outvars.resize(invars.size()); + for(size_t v = 0; v < invars.size(); v++) + { + outvars[v].rows = invars[v].type.descriptor.rows; + outvars[v].columns = invars[v].type.descriptor.columns; + outvars[v].isStruct = !invars[v].type.members.empty(); + RDCASSERT(!outvars[v].isStruct); + outvars[v].name = invars[v].name; + outvars[v].type = invars[v].type.descriptor.type; + + outvars[v].value.uv[0] = (invars[v].defaultValue & 0xFFFFFFFF); + outvars[v].value.uv[1] = ((invars[v].defaultValue >> 32) & 0xFFFFFFFF); + } + + // find any actual values specified + for(size_t i = 0; i < specInfo.size(); i++) + { + for(size_t v = 0; v < invars.size(); v++) + { + if(specInfo[i].specID == invars[v].reg.vec) + { + memcpy(outvars[v].value.uv, specInfo[i].data.data(), + RDCMIN(specInfo[i].data.size(), sizeof(outvars[v].value.uv))); + break; + } + } + } +} diff --git a/renderdoc/driver/shaders/spirv/spirv_common.h b/renderdoc/driver/shaders/spirv/spirv_common.h index 426a9d520..a2e8aa8e1 100644 --- a/renderdoc/driver/shaders/spirv/spirv_common.h +++ b/renderdoc/driver/shaders/spirv/spirv_common.h @@ -143,3 +143,19 @@ struct SPVModule string CompileSPIRV(const SPIRVCompilationSettings &settings, const vector &sources, vector &spirv); void ParseSPIRV(uint32_t *spirv, size_t spirvLength, SPVModule &module); + +void SPIRVFillCBufferVariables(const rdcarray &invars, + vector &outvars, const bytebuf &data, + size_t baseOffset); + +static const uint32_t SpecializationConstantBindSet = 1234567; + +struct SpecConstant +{ + uint32_t specID; + std::vector data; +}; + +void FillSpecConstantVariables(const rdcarray &invars, + std::vector &outvars, + const std::vector &specInfo); \ No newline at end of file diff --git a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp index 79903746a..2632d4f00 100644 --- a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp @@ -4394,7 +4394,7 @@ void SPVModule::MakeReflection(ShaderStage stage, const string &entryPoint, // set something crazy so this doesn't overlap with a real buffer binding // also identify this as specialization constant data - bindmap.bindset = 123456; // magic constants :( + bindmap.bindset = SpecializationConstantBindSet; bindmap.bind = -1; bindmap.arraySize = 1; bindmap.used = true; diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index 0551b85d2..71be261f0 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -172,17 +172,14 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk if(pCreateInfo->pStages[i].pSpecializationInfo) { - shad.specdata.resize(pCreateInfo->pStages[i].pSpecializationInfo->dataSize); - memcpy(&shad.specdata[0], pCreateInfo->pStages[i].pSpecializationInfo->pData, - shad.specdata.size()); + const byte *data = (const byte *)pCreateInfo->pStages[i].pSpecializationInfo->pData; const VkSpecializationMapEntry *maps = pCreateInfo->pStages[i].pSpecializationInfo->pMapEntries; for(uint32_t s = 0; s < pCreateInfo->pStages[i].pSpecializationInfo->mapEntryCount; s++) { - Shader::SpecInfo spec; + SpecConstant spec; spec.specID = maps[s].constantID; - spec.data = &shad.specdata[maps[s].offset]; - spec.size = maps[s].size; + spec.data.assign(data + maps[s].offset, data + maps[s].offset + maps[s].size); // ignore maps[s].size, assume it's enough for the type shad.specialization.push_back(spec); } @@ -423,16 +420,15 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk if(pCreateInfo->stage.pSpecializationInfo) { - shad.specdata.resize(pCreateInfo->stage.pSpecializationInfo->dataSize); - memcpy(&shad.specdata[0], pCreateInfo->stage.pSpecializationInfo->pData, shad.specdata.size()); + const byte *data = (const byte *)pCreateInfo->stage.pSpecializationInfo->pData; const VkSpecializationMapEntry *maps = pCreateInfo->stage.pSpecializationInfo->pMapEntries; for(uint32_t s = 0; s < pCreateInfo->stage.pSpecializationInfo->mapEntryCount; s++) { - Shader::SpecInfo spec; + SpecConstant spec; spec.specID = maps[s].constantID; - spec.data = &shad.specdata[maps[s].offset]; - spec.size = maps[s].size; + spec.data.assign(data + maps[s].offset, data + maps[s].offset + maps[s].size); + // ignore maps[s].size, assume it's enough for the type shad.specialization.push_back(spec); } } diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index 827e94e29..9d22bcca5 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -138,14 +138,7 @@ struct VulkanCreationInfo ShaderBindpointMapping *mapping; SPIRVPatchData *patchData; - vector specdata; - struct SpecInfo - { - uint32_t specID; - byte *data; - size_t size; - }; - vector specialization; + vector specialization; }; Shader shaders[6]; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 46e5aaff3..71b7f178b 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -834,8 +834,7 @@ void VulkanReplay::SavePipelineState() for(size_t s = 0; s < p.shaders[i].specialization.size(); s++) { stage.specialization[s].specializationId = p.shaders[i].specialization[s].specID; - stage.specialization[s].data.assign(p.shaders[i].specialization[s].data, - p.shaders[i].specialization[s].size); + stage.specialization[s].data = p.shaders[i].specialization[s].data; } } } @@ -911,8 +910,7 @@ void VulkanReplay::SavePipelineState() for(size_t s = 0; s < p.shaders[i].specialization.size(); s++) { stages[i]->specialization[s].specializationId = p.shaders[i].specialization[s].specID; - stages[i]->specialization[s].data.assign(p.shaders[i].specialization[s].data, - p.shaders[i].specialization[s].size); + stages[i]->specialization[s].data = p.shaders[i].specialization[s].data; } } @@ -1472,201 +1470,6 @@ void VulkanReplay::SavePipelineState() } } -void VulkanReplay::FillCBufferVariables(rdcarray invars, - vector &outvars, const bytebuf &data, - size_t baseOffset) -{ - for(size_t v = 0; v < invars.size(); v++) - { - std::string basename = invars[v].name; - - uint32_t rows = invars[v].type.descriptor.rows; - uint32_t cols = invars[v].type.descriptor.columns; - uint32_t elems = RDCMAX(1U, invars[v].type.descriptor.elements); - bool rowMajor = invars[v].type.descriptor.rowMajorStorage != 0; - bool isArray = elems > 1; - - size_t dataOffset = - baseOffset + invars[v].reg.vec * sizeof(Vec4f) + invars[v].reg.comp * sizeof(float); - - if(!invars[v].type.members.empty() || (rows == 0 && cols == 0)) - { - ShaderVariable var; - var.name = basename; - var.rows = var.columns = 0; - var.type = VarType::Float; - var.rowMajor = rowMajor; - - vector varmembers; - - if(isArray) - { - for(uint32_t i = 0; i < elems; i++) - { - ShaderVariable vr; - vr.name = StringFormat::Fmt("%s[%u]", basename.c_str(), i); - vr.rows = vr.columns = 0; - vr.type = VarType::Float; - vr.rowMajor = rowMajor; - - vector mems; - - FillCBufferVariables(invars[v].type.members, mems, data, dataOffset); - - dataOffset += invars[v].type.descriptor.arrayByteStride; - - vr.isStruct = true; - - vr.members = mems; - - varmembers.push_back(vr); - } - - var.isStruct = false; - } - else - { - var.isStruct = true; - - FillCBufferVariables(invars[v].type.members, varmembers, data, dataOffset); - } - - { - var.members = varmembers; - outvars.push_back(var); - } - - continue; - } - - size_t outIdx = outvars.size(); - outvars.resize(outvars.size() + 1); - - { - outvars[outIdx].name = basename; - outvars[outIdx].rows = 1; - outvars[outIdx].type = invars[v].type.descriptor.type; - outvars[outIdx].isStruct = false; - outvars[outIdx].columns = cols; - outvars[outIdx].rowMajor = rowMajor; - - size_t elemByteSize = 4; - if(outvars[outIdx].type == VarType::Double) - elemByteSize = 8; - - ShaderVariable &var = outvars[outIdx]; - - if(!isArray) - { - outvars[outIdx].rows = rows; - - if(dataOffset < data.size()) - { - const byte *d = &data[dataOffset]; - - RDCASSERT(rows <= 4 && rows * cols <= 16, rows, cols); - - if(!rowMajor) - { - uint32_t tmp[16] = {0}; - - for(uint32_t c = 0; c < cols; c++) - { - size_t srcoffs = 4 * elemByteSize * c; - size_t dstoffs = rows * elemByteSize * c; - memcpy((byte *)(tmp) + dstoffs, d + srcoffs, - RDCMIN(data.size() - dataOffset + srcoffs, elemByteSize * rows)); - } - - // transpose - for(size_t r = 0; r < rows; r++) - for(size_t c = 0; c < cols; c++) - outvars[outIdx].value.uv[r * cols + c] = tmp[c * rows + r]; - } - else - { - for(uint32_t r = 0; r < rows; r++) - { - size_t srcoffs = 4 * elemByteSize * r; - size_t dstoffs = cols * elemByteSize * r; - memcpy((byte *)(&outvars[outIdx].value.uv[0]) + dstoffs, d + srcoffs, - RDCMIN(data.size() - dataOffset + srcoffs, elemByteSize * cols)); - } - } - } - } - else - { - var.name = outvars[outIdx].name; - var.rows = 0; - var.columns = 0; - - bool isMatrix = rows > 1 && cols > 1; - - vector varmembers; - varmembers.resize(elems); - - std::string base = outvars[outIdx].name; - - // primary is the 'major' direction - // so we copy secondaryDim number of primaryDim-sized elements - uint32_t primaryDim = cols; - uint32_t secondaryDim = rows; - if(isMatrix && rowMajor) - { - primaryDim = rows; - secondaryDim = cols; - } - - for(uint32_t e = 0; e < elems; e++) - { - varmembers[e].name = StringFormat::Fmt("%s[%u]", base.c_str(), e); - varmembers[e].rows = rows; - varmembers[e].type = invars[v].type.descriptor.type; - varmembers[e].isStruct = false; - varmembers[e].columns = cols; - varmembers[e].rowMajor = rowMajor; - - size_t rowDataOffset = dataOffset; - - dataOffset += invars[v].type.descriptor.arrayByteStride; - - if(rowDataOffset < data.size()) - { - const byte *d = &data[rowDataOffset]; - - // each primary element (row or column) is stored in a float4. - // we copy some padding here, but that will come out in the wash - // when we transpose - for(uint32_t s = 0; s < secondaryDim; s++) - { - uint32_t matStride = primaryDim; - if(matStride == 3) - matStride = 4; - memcpy(&(varmembers[e].value.uv[primaryDim * s]), d + matStride * elemByteSize * s, - RDCMIN(data.size() - rowDataOffset, elemByteSize * primaryDim)); - } - - if(!rowMajor) - { - ShaderVariable tmp = varmembers[e]; - // transpose - for(size_t ri = 0; ri < rows; ri++) - for(size_t ci = 0; ci < cols; ci++) - varmembers[e].value.uv[ri * cols + ci] = tmp.value.uv[ci * rows + ri]; - } - } - } - - { - var.isStruct = false; - var.members = varmembers; - } - } - } - } -} - void VulkanReplay::FillCBufferVariables(ResourceId shader, string entryPoint, uint32_t cbufSlot, vector &outvars, const bytebuf &data) { @@ -1695,29 +1498,17 @@ void VulkanReplay::FillCBufferVariables(ResourceId shader, string entryPoint, ui if(c.bufferBacked) { - FillCBufferVariables(c.variables, outvars, data, 0); + SPIRVFillCBufferVariables(c.variables, outvars, data, 0); } else { - // very specialised (and rather ugly) path to display specialization constants - // magic constant here matches the one generated in SPVModule::MakeReflection( - if(mapping.constantBlocks[c.bindPoint].bindset == 123456) + // specialised path to display specialization constants + if(mapping.constantBlocks[c.bindPoint].bindset == SpecializationConstantBindSet) { - outvars.resize(c.variables.size()); - for(size_t v = 0; v < c.variables.size(); v++) - { - outvars[v].rows = c.variables[v].type.descriptor.rows; - outvars[v].columns = c.variables[v].type.descriptor.columns; - outvars[v].isStruct = !c.variables[v].type.members.empty(); - RDCASSERT(!outvars[v].isStruct); - outvars[v].name = c.variables[v].name; - outvars[v].type = c.variables[v].type.descriptor.type; - - outvars[v].value.uv[0] = (c.variables[v].defaultValue & 0xFFFFFFFF); - outvars[v].value.uv[1] = ((c.variables[v].defaultValue >> 32) & 0xFFFFFFFF); - } - - ResourceId pipeline = m_pDriver->m_RenderState.graphics.pipeline; + // TODO we shouldn't be looking up the pipeline here, this query should work regardless. + ResourceId pipeline = refl.stage == ShaderStage::Compute + ? m_pDriver->m_RenderState.compute.pipeline + : m_pDriver->m_RenderState.graphics.pipeline; if(pipeline != ResourceId()) { auto pipeIt = m_pDriver->m_CreationInfo.m_Pipeline.find(pipeline); @@ -1727,19 +1518,7 @@ void VulkanReplay::FillCBufferVariables(ResourceId shader, string entryPoint, ui auto specInfo = pipeIt->second.shaders[it->second.m_Reflections[entryPoint].stageIndex].specialization; - // find any actual values specified - for(size_t i = 0; i < specInfo.size(); i++) - { - for(size_t v = 0; v < c.variables.size(); v++) - { - if(specInfo[i].specID == c.variables[v].reg.vec) - { - memcpy(outvars[v].value.uv, specInfo[i].data, - RDCMIN(specInfo[i].size, sizeof(outvars[v].value.uv))); - break; - } - } - } + FillSpecConstantVariables(c.variables, outvars, specInfo); } } } @@ -1748,7 +1527,7 @@ void VulkanReplay::FillCBufferVariables(ResourceId shader, string entryPoint, ui bytebuf pushdata; pushdata.resize(sizeof(m_pDriver->m_RenderState.pushconsts)); memcpy(&pushdata[0], m_pDriver->m_RenderState.pushconsts, pushdata.size()); - FillCBufferVariables(c.variables, outvars, pushdata, 0); + SPIRVFillCBufferVariables(c.variables, outvars, pushdata, 0); } } } diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index ba1839c48..447477c9e 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -331,9 +331,6 @@ private: void CreateTexImageView(VkImageAspectFlags aspectFlags, VkImage liveIm, VulkanCreationInfo::Image &iminfo); - void FillCBufferVariables(rdcarray, vector &outvars, - const bytebuf &data, size_t baseOffset); - VulkanDebugManager *GetDebugManager(); VulkanResourceManager *GetResourceManager(); diff --git a/renderdoc/driver/vulkan/vk_shader_cache.cpp b/renderdoc/driver/vulkan/vk_shader_cache.cpp index 97f7f1749..4e410769a 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.cpp +++ b/renderdoc/driver/vulkan/vk_shader_cache.cpp @@ -268,20 +268,27 @@ void VulkanShaderCache::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &p static VkPipelineShaderStageCreateInfo stages[6]; static VkSpecializationInfo specInfo[6]; static vector specMapEntries; + static std::vector specdata; size_t specEntries = 0; + size_t specSize = 0; for(uint32_t i = 0; i < 6; i++) - if(pipeInfo.shaders[i].module != ResourceId()) - if(!pipeInfo.shaders[i].specialization.empty()) - specEntries += pipeInfo.shaders[i].specialization.size(); + { + specEntries += pipeInfo.shaders[i].specialization.size(); + for(size_t s = 0; s < pipeInfo.shaders[i].specialization.size(); s++) + specSize += pipeInfo.shaders[i].specialization[s].data.size(); + } specMapEntries.resize(specEntries); + specdata.resize(specSize); VkSpecializationMapEntry *entry = &specMapEntries[0]; uint32_t stageCount = 0; + specSize = 0; + // reserve space for spec constants for(uint32_t i = 0; i < 6; i++) { if(pipeInfo.shaders[i].module != ResourceId()) @@ -299,27 +306,20 @@ void VulkanShaderCache::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &p specInfo[i].pMapEntries = entry; specInfo[i].mapEntryCount = (uint32_t)pipeInfo.shaders[i].specialization.size(); - byte *minDataPtr = NULL; - byte *maxDataPtr = NULL; - for(size_t s = 0; s < pipeInfo.shaders[i].specialization.size(); s++) { entry[s].constantID = pipeInfo.shaders[i].specialization[s].specID; - entry[s].size = pipeInfo.shaders[i].specialization[s].size; + entry[s].size = pipeInfo.shaders[i].specialization[s].data.size(); + entry[s].offset = (uint32_t)specSize; - if(minDataPtr == NULL) - minDataPtr = pipeInfo.shaders[i].specialization[s].data; - else - minDataPtr = RDCMIN(minDataPtr, pipeInfo.shaders[i].specialization[s].data); + specSize += entry[s].size; - maxDataPtr = RDCMAX(minDataPtr, pipeInfo.shaders[i].specialization[s].data + entry[s].size); + memcpy(&specdata[0] + entry[s].offset, pipeInfo.shaders[i].specialization[s].data.data(), + entry[s].size); } - for(size_t s = 0; s < pipeInfo.shaders[i].specialization.size(); s++) - entry[s].offset = (uint32_t)(pipeInfo.shaders[i].specialization[s].data - minDataPtr); - - specInfo[i].dataSize = (maxDataPtr - minDataPtr); - specInfo[i].pData = (const void *)minDataPtr; + specInfo[i].dataSize = specdata.size(); + specInfo[i].pData = specdata.data(); entry += specInfo[i].mapEntryCount; } @@ -531,13 +531,17 @@ void VulkanShaderCache::MakeComputePipelineInfo(VkComputePipelineCreateInfo &pip VkPipelineShaderStageCreateInfo stage; // Returned by value static VkSpecializationInfo specInfo; static vector specMapEntries; + static std::vector specdata; const uint32_t i = 5; // Compute stage RDCASSERT(pipeInfo.shaders[i].module != ResourceId()); - size_t specEntries = 0; - if(!pipeInfo.shaders[i].specialization.empty()) - specEntries += pipeInfo.shaders[i].specialization.size(); + size_t specEntries = pipeInfo.shaders[i].specialization.size(); + size_t specSize = 0; + for(size_t s = 0; s < pipeInfo.shaders[i].specialization.size(); s++) + specSize += pipeInfo.shaders[i].specialization[s].data.size(); + + specdata.resize(specSize); specMapEntries.resize(specEntries); VkSpecializationMapEntry *entry = &specMapEntries[0]; @@ -550,35 +554,28 @@ void VulkanShaderCache::MakeComputePipelineInfo(VkComputePipelineCreateInfo &pip stage.pSpecializationInfo = NULL; stage.flags = VK_SHADER_STAGE_COMPUTE_BIT; + specSize = 0; + if(!pipeInfo.shaders[i].specialization.empty()) { stage.pSpecializationInfo = &specInfo; specInfo.pMapEntries = entry; specInfo.mapEntryCount = (uint32_t)pipeInfo.shaders[i].specialization.size(); - byte *minDataPtr = NULL; - byte *maxDataPtr = NULL; - for(size_t s = 0; s < pipeInfo.shaders[i].specialization.size(); s++) { entry[s].constantID = pipeInfo.shaders[i].specialization[s].specID; - entry[s].size = pipeInfo.shaders[i].specialization[s].size; + entry[s].size = pipeInfo.shaders[i].specialization[s].data.size(); + entry[s].offset = (uint32_t)specSize; - if(minDataPtr == NULL) - minDataPtr = pipeInfo.shaders[i].specialization[s].data; - else - minDataPtr = RDCMIN(minDataPtr, pipeInfo.shaders[i].specialization[s].data); + specSize += entry[s].size; - maxDataPtr = RDCMAX(minDataPtr, pipeInfo.shaders[i].specialization[s].data + entry[s].size); + memcpy(&specdata[0] + entry[s].offset, pipeInfo.shaders[i].specialization[s].data.data(), + entry[s].size); } - for(size_t s = 0; s < pipeInfo.shaders[i].specialization.size(); s++) - entry[s].offset = (uint32_t)(pipeInfo.shaders[i].specialization[s].data - minDataPtr); - - specInfo.dataSize = (maxDataPtr - minDataPtr); - specInfo.pData = (const void *)minDataPtr; - - entry += specInfo.mapEntryCount; + specInfo.dataSize = specdata.size(); + specInfo.pData = specdata.data(); } VkComputePipelineCreateInfo ret = {