diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index 28e00c650..f7def69d7 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -207,6 +207,7 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderSourceFile) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderVariable) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, SigParameter) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, TextureDescription) +TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderEntryPoint) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, Attachment) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, BindingElement) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, Blend) diff --git a/qrenderdoc/Windows/ResourceInspector.cpp b/qrenderdoc/Windows/ResourceInspector.cpp index ba0e4488c..ecc7aa2a4 100644 --- a/qrenderdoc/Windows/ResourceInspector.cpp +++ b/qrenderdoc/Windows/ResourceInspector.cpp @@ -163,6 +163,8 @@ void ResourceInspector::Inspect(ResourceId id) ui->viewContents->setVisible(m_Ctx.GetTexture(id) || m_Ctx.GetBuffer(id)); + m_Entries.clear(); + m_ResourceModel->reset(); m_FilterModel->sort(0); @@ -181,7 +183,16 @@ void ResourceInspector::Inspect(ResourceId id) m_Ctx.Replay().AsyncInvoke([this, id](IReplayController *r) { rdcarray usage = r->GetUsage(id); - GUIInvoke::call([this, id, usage] { + rdcarray entries = r->GetShaderEntryPoints(id); + + GUIInvoke::call([this, id, entries, usage] { + + if(!entries.isEmpty()) + { + m_Entries = entries; + ui->viewContents->setVisible(true); + } + CombineUsageEvents( m_Ctx, usage, [this, id](uint32_t startEID, uint32_t endEID, ResourceUsage use) { QString text; @@ -392,6 +403,30 @@ void ResourceInspector::on_viewContents_clicked() m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } + else if(!m_Entries.isEmpty()) + { + ShaderEntryPoint entry = m_Entries[0]; + + if(m_Entries.count() > 1) + { + // TODO need to let the user choose the entry point + } + + ResourceId id = m_Resource; + ICaptureContext *ctx = &m_Ctx; + m_Ctx.Replay().AsyncInvoke([ctx, id, entry](IReplayController *r) { + ShaderReflection *refl = r->GetShader(id, entry); + + if(!refl) + return; + + GUIInvoke::call([ctx, refl] { + IShaderViewer *viewer = ctx->ViewShader(refl, ResourceId()); + + ctx->AddDockWindow(viewer->Widget(), DockReference::MainToolArea, NULL); + }); + }); + } } void ResourceInspector::on_resourceUsage_doubleClicked(const QModelIndex &index) diff --git a/qrenderdoc/Windows/ResourceInspector.h b/qrenderdoc/Windows/ResourceInspector.h index 5a14e407c..9a71d55b3 100644 --- a/qrenderdoc/Windows/ResourceInspector.h +++ b/qrenderdoc/Windows/ResourceInspector.h @@ -74,6 +74,8 @@ private: Ui::ResourceInspector *ui; ICaptureContext &m_Ctx; + rdcarray m_Entries; + ResourceId m_Resource; ResourceListItemModel *m_ResourceModel; QSortFilterProxyModel *m_FilterModel; diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 809068224..ae96461fa 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -844,6 +844,27 @@ newly generated messages will be returned after that. )"); virtual rdcarray GetDebugMessages() = 0; + DOCUMENT(R"(Retrieve a list of entry points for a shader. + +If the given ID doesn't specify a shader, an empty list will be return. On some APIs, the list will +only ever have one result (only one entry point per shader). + +:param ResourceId shader: The shader to look up entry points for. +:return: The list of the :class:`ShaderEntryPoint` messages. +:rtype: ``list`` of :class:`ShaderEntryPoint` +)"); + virtual rdcarray GetShaderEntryPoints(ResourceId shader) = 0; + + DOCUMENT(R"(Retrieve the information about the frame contained in the capture. + +:param ResourceId shader: The shader to get reflection data for. +:param ShaderEntryPoint entry: The entry point within the shader to reflect. May be ignored on some + APIs +:return: The frame information. +:rtype: ShaderReflection +)"); + virtual ShaderReflection *GetShader(ResourceId shader, ShaderEntryPoint entry) = 0; + DOCUMENT(R"(Retrieve the history of modifications to the selected pixel on the selected texture. :param ResourceId texture: The texture to search for modifications. diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 5ba86f601..7accee5e9 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -451,6 +451,18 @@ able to be read from and written to arbitrarily. DECLARE_REFLECTION_STRUCT(ShaderResource); +DOCUMENT("Describes an entry point in a shader."); +struct ShaderEntryPoint +{ + DOCUMENT("The name of the entry point."); + rdcstr name; + + DOCUMENT("The :class:`ShaderStage` for this entry point ."); + ShaderStage stage; +}; + +DECLARE_REFLECTION_STRUCT(ShaderEntryPoint); + DOCUMENT("Contains a single flag used at compile-time on a shader."); struct ShaderCompileFlag { diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index 8c93cb3cf..822035d1d 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -202,6 +202,7 @@ public: { return ResourceId(); } + rdcarray GetShaderEntryPoints(ResourceId shader) { return {}; } ShaderReflection *GetShader(ResourceId shader, string entryPoint) { return NULL; } vector GetDisassemblyTargets() { return {"N/A"}; } string DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, const string &target) diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 18ff3d904..7af88521d 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -783,6 +783,38 @@ ResourceId ReplayProxy::RenderOverlay(ResourceId texid, CompType typeHint, Debug PROXY_FUNCTION(RenderOverlay, texid, typeHint, overlay, eventID, passEvents); } +template +rdcarray ReplayProxy::Proxied_GetShaderEntryPoints(ParamSerialiser ¶mser, + ReturnSerialiser &retser, + ResourceId id) +{ + const ReplayProxyPacket packet = eReplayProxy_GetShaderEntryPoints; + rdcarray ret; + + { + BEGIN_PARAMS(); + SERIALISE_ELEMENT(id); + END_PARAMS(); + } + + if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) + ret = m_Remote->GetShaderEntryPoints(id); + + { + ReturnSerialiser &ser = retser; + PACKET_HEADER(packet); + SERIALISE_ELEMENT(ret); + ser.EndChunk(); + } + + return ret; +} + +rdcarray ReplayProxy::GetShaderEntryPoints(ResourceId id) +{ + PROXY_FUNCTION(GetShaderEntryPoints, id); +} + template ShaderReflection *ReplayProxy::Proxied_GetShader(ParamSerialiser ¶mser, ReturnSerialiser &retser, ResourceId id, std::string entryPoint) diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index bf1588fa5..35c053182 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -47,6 +47,7 @@ enum ReplayProxyPacket eReplayProxy_GetTexture, eReplayProxy_GetBuffers, eReplayProxy_GetBuffer, + eReplayProxy_GetShaderEntryPoints, eReplayProxy_GetShader, eReplayProxy_GetDebugMessages, @@ -439,6 +440,7 @@ public: DebugOverlay overlay, uint32_t eventID, const std::vector &passEvents); + IMPLEMENT_FUNCTION_PROXIED(rdcarray, GetShaderEntryPoints, ResourceId shader); IMPLEMENT_FUNCTION_PROXIED(ShaderReflection *, GetShader, ResourceId shader, std::string entryPoint); diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index b32375e45..f0559eaa8 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -228,6 +228,18 @@ TextureDescription D3D11Replay::GetTexture(ResourceId id) return tex; } +rdcarray D3D11Replay::GetShaderEntryPoints(ResourceId shader) +{ + auto it = WrappedShader::m_ShaderList.find(shader); + + if(it == WrappedShader::m_ShaderList.end()) + return {}; + + ShaderReflection &ret = it->second->GetDetails(); + + return {{"main", ret.Stage}}; +} + ShaderReflection *D3D11Replay::GetShader(ResourceId shader, string entryPoint) { auto it = WrappedShader::m_ShaderList.find(shader); diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index 67ff80c2c..c70fb7684 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -59,6 +59,7 @@ public: vector GetDebugMessages(); + rdcarray GetShaderEntryPoints(ResourceId shader); ShaderReflection *GetShader(ResourceId shader, string entryPoint); vector GetDisassemblyTargets(); diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 88c54fac6..21ae33122 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -234,6 +234,19 @@ TextureDescription D3D12Replay::GetTexture(ResourceId id) return ret; } +rdcarray D3D12Replay::GetShaderEntryPoints(ResourceId shader) +{ + WrappedID3D12Shader *sh = + m_pDevice->GetResourceManager()->GetCurrentAs(shader); + + if(!sh) + return {}; + + ShaderReflection &ret = sh->GetDetails(); + + return {{"main", ret.Stage}}; +} + ShaderReflection *D3D12Replay::GetShader(ResourceId shader, string entryPoint) { WrappedID3D12Shader *sh = diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index dc5b51b6e..bc7afd342 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -57,6 +57,7 @@ public: vector GetDebugMessages(); + rdcarray GetShaderEntryPoints(ResourceId shader); ShaderReflection *GetShader(ResourceId shader, string entryPoint); vector GetDisassemblyTargets(); diff --git a/renderdoc/driver/gl/gl_common.cpp b/renderdoc/driver/gl/gl_common.cpp index e415128dd..2efadb16d 100644 --- a/renderdoc/driver/gl/gl_common.cpp +++ b/renderdoc/driver/gl/gl_common.cpp @@ -1071,6 +1071,22 @@ TextureFilter MakeFilter(GLenum minf, GLenum magf, bool shadowSampler, float max return ret; } +ShaderStage MakeShaderStage(GLenum type) +{ + switch(type) + { + case eGL_VERTEX_SHADER: return ShaderStage::Vertex; + case eGL_TESS_CONTROL_SHADER: return ShaderStage::Tess_Control; + case eGL_TESS_EVALUATION_SHADER: return ShaderStage::Tess_Eval; + case eGL_GEOMETRY_SHADER: return ShaderStage::Geometry; + case eGL_FRAGMENT_SHADER: return ShaderStage::Fragment; + case eGL_COMPUTE_SHADER: return ShaderStage::Compute; + default: RDCERR("Unexpected shader stage %s", ToStr(type).c_str()); + } + + return ShaderStage::Count; +} + CompareFunc MakeCompareFunc(GLenum func) { switch(func) diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index 511e1591b..83b8cde80 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -296,6 +296,7 @@ GLenum MakeGLPrimitiveTopology(Topology Topo); BufferCategory MakeBufferCategory(GLenum bufferTarget); AddressMode MakeAddressMode(GLenum addr); TextureFilter MakeFilter(GLenum minf, GLenum magf, bool shadowSampler, float maxAniso); +ShaderStage MakeShaderStage(GLenum type); CompareFunc MakeCompareFunc(GLenum func); StencilOp MakeStencilOp(GLenum op); LogicOp MakeLogicOp(GLenum op); diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index aaf175d8f..f7f11b7e4 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -759,6 +759,22 @@ vector GLReplay::GetDebugMessages() return m_pDriver->GetDebugMessages(); } +rdcarray GLReplay::GetShaderEntryPoints(ResourceId shader) +{ + if(m_pDriver->m_Shaders.find(shader) == m_pDriver->m_Shaders.end()) + return {}; + + WrappedOpenGL::ShaderData &shaderDetails = m_pDriver->m_Shaders[shader]; + + if(shaderDetails.prog == 0) + { + RDCERR("Can't get shader details without separable program"); + return {}; + } + + return {{"main", MakeShaderStage(shaderDetails.type)}}; +} + ShaderReflection *GLReplay::GetShader(ResourceId shader, string entryPoint) { auto &shaderDetails = m_pDriver->m_Shaders[shader]; diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 79436613c..bbf2d1962 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -101,6 +101,8 @@ public: std::vector GetTextures(); TextureDescription GetTexture(ResourceId id); + + rdcarray GetShaderEntryPoints(ResourceId shader); ShaderReflection *GetShader(ResourceId shader, string entryPoint); vector GetDisassemblyTargets(); diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index 5cfcfa9cb..920c8ad67 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -117,20 +117,7 @@ void WrappedOpenGL::ShaderData::Compile(WrappedOpenGL &gl, ResourceId id, GLuint reflection.ID = id; reflection.EntryPoint = "main"; - switch(settings.stage) - { - case SPIRVShaderStage::Vertex: reflection.Stage = ShaderStage::Vertex; break; - case SPIRVShaderStage::TessControl: reflection.Stage = ShaderStage::Tess_Control; break; - case SPIRVShaderStage::TessEvaluation: reflection.Stage = ShaderStage::Tess_Eval; break; - case SPIRVShaderStage::Geometry: reflection.Stage = ShaderStage::Geometry; break; - case SPIRVShaderStage::Fragment: reflection.Stage = ShaderStage::Fragment; break; - case SPIRVShaderStage::Compute: reflection.Stage = ShaderStage::Compute; break; - case SPIRVShaderStage::Invalid: - default: - RDCERR("Unexpected shader stage %u", settings.stage); - reflection.Stage = ShaderStage::Vertex; - break; - } + reflection.Stage = MakeShaderStage(type); // TODO sort these so that the first file contains the entry point reflection.DebugInfo.files.resize(sources.size()); diff --git a/renderdoc/driver/shaders/spirv/spirv_common.h b/renderdoc/driver/shaders/spirv/spirv_common.h index f7183fee1..95925008f 100644 --- a/renderdoc/driver/shaders/spirv/spirv_common.h +++ b/renderdoc/driver/shaders/spirv/spirv_common.h @@ -129,6 +129,7 @@ struct SPVModule SPVInstruction *GetByID(uint32_t id); string Disassemble(const string &entryPoint); + std::vector EntryPoints() const; ShaderStage StageForEntry(const string &entryPoint) const; void MakeReflection(ShaderStage stage, const string &entryPoint, ShaderReflection &reflection, diff --git a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp index f2bcac7da..66b05d9f9 100644 --- a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp @@ -3877,6 +3877,17 @@ void AddSignatureParameter(bool isInput, ShaderStage stage, uint32_t id, } } +std::vector SPVModule::EntryPoints() const +{ + std::vector ret; + + for(SPVInstruction *inst : entries) + if(inst->entry) + ret.push_back(inst->entry->name); + + return ret; +} + ShaderStage SPVModule::StageForEntry(const string &entryPoint) const { for(SPVInstruction *inst : entries) diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 27ce7bdc0..b8e42ec02 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -879,6 +879,23 @@ BufferDescription VulkanReplay::GetBuffer(ResourceId id) return ret; } +rdcarray VulkanReplay::GetShaderEntryPoints(ResourceId shader) +{ + auto shad = m_pDriver->m_CreationInfo.m_ShaderModule.find(shader); + + if(shad == m_pDriver->m_CreationInfo.m_ShaderModule.end()) + return {}; + + std::vector entries = shad->second.spirv.EntryPoints(); + + rdcarray ret; + + for(const std::string &e : entries) + ret.push_back({e, shad->second.spirv.StageForEntry(e)}); + + return ret; +} + ShaderReflection *VulkanReplay::GetShader(ResourceId shader, string entryPoint) { auto shad = m_pDriver->m_CreationInfo.m_ShaderModule.find(shader); diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 3269dbf5e..e40ca341a 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -145,6 +145,7 @@ public: std::vector GetTextures(); TextureDescription GetTexture(ResourceId id); + rdcarray GetShaderEntryPoints(ResourceId shader); ShaderReflection *GetShader(ResourceId shader, string entryPoint); vector GetDisassemblyTargets(); diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index d10bc5b15..6574ca149 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -224,6 +224,15 @@ void DoSerialise(SerialiserType &ser, ShaderResource &el) SIZE_CHECK(80); } +template +void DoSerialise(SerialiserType &ser, ShaderEntryPoint &el) +{ + SERIALISE_MEMBER(name); + SERIALISE_MEMBER(stage); + + SIZE_CHECK(24); +} + template void DoSerialise(SerialiserType &ser, ShaderCompileFlag &el) { @@ -2199,6 +2208,7 @@ INSTANTIATE_SERIALISE_TYPE(ShaderConstant) INSTANTIATE_SERIALISE_TYPE(ConstantBlock) INSTANTIATE_SERIALISE_TYPE(ShaderSampler) INSTANTIATE_SERIALISE_TYPE(ShaderResource) +INSTANTIATE_SERIALISE_TYPE(ShaderEntryPoint) INSTANTIATE_SERIALISE_TYPE(ShaderCompileFlags) INSTANTIATE_SERIALISE_TYPE(ShaderDebugChunk) INSTANTIATE_SERIALISE_TYPE(ShaderReflection) diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 0ce7ff1ef..6c94dfeee 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -318,6 +318,16 @@ rdcarray ReplayController::GetDebugMessages() return m_pDevice->GetDebugMessages(); } +rdcarray ReplayController::GetShaderEntryPoints(ResourceId shader) +{ + return m_pDevice->GetShaderEntryPoints(m_pDevice->GetLiveID(shader)); +} + +ShaderReflection *ReplayController::GetShader(ResourceId shader, ShaderEntryPoint entry) +{ + return m_pDevice->GetShader(m_pDevice->GetLiveID(shader), entry.name); +} + rdcarray ReplayController::GetUsage(ResourceId id) { id = m_pDevice->GetLiveID(id); diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 5b07e5be2..11a888143 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -167,6 +167,9 @@ public: const rdcarray &GetResources(); rdcarray GetDebugMessages(); + rdcarray GetShaderEntryPoints(ResourceId shader); + ShaderReflection *GetShader(ResourceId shader, ShaderEntryPoint entry); + rdcarray PixelHistory(ResourceId target, uint32_t x, uint32_t y, uint32_t slice, uint32_t mip, uint32_t sampleIdx, CompType typeHint); ShaderDebugTrace *DebugVertex(uint32_t vertid, uint32_t instid, uint32_t idx, uint32_t instOffset, diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 133bbab3a..8300a6b73 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -100,6 +100,7 @@ public: virtual vector GetDebugMessages() = 0; + virtual rdcarray GetShaderEntryPoints(ResourceId shader) = 0; virtual ShaderReflection *GetShader(ResourceId shader, string entryPoint) = 0; virtual vector GetDisassemblyTargets() = 0;