From ee43d4377cd51d112a4f0060430fe3cae8903b41 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 16 Aug 2019 15:30:19 +0100 Subject: [PATCH] Account for pipeline specialisation constants when reflecting shaders --- .../Windows/ConstantBufferPreviewer.cpp | 4 +- qrenderdoc/Windows/ConstantBufferPreviewer.h | 1 + qrenderdoc/Windows/ResourceInspector.cpp | 6 +- renderdoc/api/replay/renderdoc_replay.h | 7 +- renderdoc/core/image_viewer.cpp | 9 +- renderdoc/core/replay_proxy.cpp | 61 ++++-- renderdoc/core/replay_proxy.h | 22 +- renderdoc/driver/d3d11/d3d11_replay.cpp | 8 +- renderdoc/driver/d3d11/d3d11_replay.h | 7 +- renderdoc/driver/d3d12/d3d12_replay.cpp | 6 +- renderdoc/driver/d3d12/d3d12_replay.h | 7 +- renderdoc/driver/gl/gl_overlay.cpp | 4 +- renderdoc/driver/gl/gl_postvs.cpp | 12 +- renderdoc/driver/gl/gl_replay.cpp | 7 +- renderdoc/driver/gl/gl_replay.h | 7 +- renderdoc/driver/gl/wrappers/gl_emulated.cpp | 2 +- renderdoc/driver/vulkan/vk_info.cpp | 50 +++-- renderdoc/driver/vulkan/vk_info.h | 60 ++++-- renderdoc/driver/vulkan/vk_replay.cpp | 49 ++--- renderdoc/driver/vulkan/vk_replay.h | 7 +- .../vulkan/wrappers/vk_shader_funcs.cpp | 10 +- renderdoc/replay/replay_controller.cpp | 11 +- renderdoc/replay/replay_controller.h | 8 +- renderdoc/replay/replay_driver.h | 8 +- util/test/demos/CMakeLists.txt | 1 + util/test/demos/demos.vcxproj | 1 + util/test/demos/demos.vcxproj.filters | 3 + util/test/demos/vk/vk_spec_constants.cpp | 188 ++++++++++++++++++ util/test/tests/D3D11/D3D11_CBuffer_Zoo.py | 3 +- util/test/tests/D3D12/D3D12_CBuffer_Zoo.py | 6 +- util/test/tests/GL/GL_CBuffer_Zoo.py | 6 +- util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py | 3 +- util/test/tests/Vulkan/VK_CBuffer_Zoo.py | 9 +- util/test/tests/Vulkan/VK_Spec_Constants.py | 96 +++++++++ 34 files changed, 535 insertions(+), 154 deletions(-) create mode 100644 util/test/demos/vk/vk_spec_constants.cpp create mode 100644 util/test/tests/Vulkan/VK_Spec_Constants.py diff --git a/qrenderdoc/Windows/ConstantBufferPreviewer.cpp b/qrenderdoc/Windows/ConstantBufferPreviewer.cpp index 9441f3398..91559791d 100644 --- a/qrenderdoc/Windows/ConstantBufferPreviewer.cpp +++ b/qrenderdoc/Windows/ConstantBufferPreviewer.cpp @@ -109,6 +109,8 @@ void ConstantBufferPreviewer::OnEventChanged(uint32_t eventId) ResourceId prevShader = m_shader; + m_pipe = m_stage == ShaderStage::Compute ? m_Ctx.CurPipelineState().GetComputePipelineObject() + : m_Ctx.CurPipelineState().GetGraphicsPipelineObject(); m_shader = m_Ctx.CurPipelineState().GetShader(m_stage); QString entryPoint = m_Ctx.CurPipelineState().GetShaderEntryPoint(m_stage); const ShaderReflection *reflection = m_Ctx.CurPipelineState().GetShaderReflection(m_stage); @@ -153,7 +155,7 @@ void ConstantBufferPreviewer::OnEventChanged(uint32_t eventId) { m_Ctx.Replay().AsyncInvoke([this, prevShader, entryPoint, offs, wasEmpty](IReplayController *r) { rdcarray vars = r->GetCBufferVariableContents( - m_shader, entryPoint.toUtf8().data(), m_slot, m_cbuffer, offs); + m_pipe, m_shader, entryPoint.toUtf8().data(), m_slot, m_cbuffer, offs); GUIInvoke::call(this, [this, prevShader, vars, wasEmpty] { RDTreeViewExpansionState &prevShaderExpansionState = diff --git a/qrenderdoc/Windows/ConstantBufferPreviewer.h b/qrenderdoc/Windows/ConstantBufferPreviewer.h index 33b1995d3..ce5f7dde7 100644 --- a/qrenderdoc/Windows/ConstantBufferPreviewer.h +++ b/qrenderdoc/Windows/ConstantBufferPreviewer.h @@ -72,6 +72,7 @@ private: ICaptureContext &m_Ctx; ResourceId m_cbuffer; + ResourceId m_pipe; ResourceId m_shader; ShaderStage m_stage = ShaderStage::Vertex; uint32_t m_slot = 0; diff --git a/qrenderdoc/Windows/ResourceInspector.cpp b/qrenderdoc/Windows/ResourceInspector.cpp index 88ee5024b..cddd167be 100644 --- a/qrenderdoc/Windows/ResourceInspector.cpp +++ b/qrenderdoc/Windows/ResourceInspector.cpp @@ -478,10 +478,12 @@ void ResourceInspector::on_viewContents_clicked() // TODO need to let the user choose the entry point } + // TODO allow choosing parent pipeline? + ResourceId pipeid; ResourceId id = m_Resource; ICaptureContext *ctx = &m_Ctx; - m_Ctx.Replay().AsyncInvoke([this, ctx, id, entry](IReplayController *r) { - ShaderReflection *refl = r->GetShader(id, entry); + m_Ctx.Replay().AsyncInvoke([this, ctx, pipeid, id, entry](IReplayController *r) { + ShaderReflection *refl = r->GetShader(pipeid, id, entry); if(!refl) return; diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 3489c56b2..de11ee994 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -1178,13 +1178,15 @@ only ever have one result (only one entry point per shader). DOCUMENT(R"(Retrieve the information about the frame contained in the capture. +:param ResourceId pipeline: The pipeline state object, if applicable, that this shader is bound to. :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; + virtual ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, + ShaderEntryPoint entry) = 0; DOCUMENT(R"(Retrieve the history of modifications to the selected pixel on the selected texture. @@ -1270,6 +1272,7 @@ only ever have one result (only one entry point per shader). DOCUMENT(R"(Retrieve the contents of a constant block by reading from memory or their source otherwise. +:param ResourceId pipeline: The pipeline state object, if applicable, that this shader is bound to. :param ResourceId shader: The id of the shader to use for metadata. :param str entryPoint: The entry point of the shader being used. In some APIs, this is ignored. :param int cbufslot: The index in the :data:`ShaderReflection.constantBlocks` list to look up. @@ -1279,7 +1282,7 @@ otherwise. :return: The shader variables with their contents. :rtype: ``list`` of :class:`ShaderVariable` )"); - virtual rdcarray GetCBufferVariableContents(ResourceId shader, + virtual rdcarray GetCBufferVariableContents(ResourceId pipeline, ResourceId shader, const char *entryPoint, uint32_t cbufslot, ResourceId buffer, uint64_t offs) = 0; diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index 94d1e4b3c..e42d603d0 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -215,8 +215,8 @@ public: { return std::vector(); } - void FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, - rdcarray &outvars, const bytebuf &data) + void FillCBufferVariables(ResourceId pipeline, ResourceId shader, std::string entryPoint, + uint32_t cbufSlot, rdcarray &outvars, const bytebuf &data) { } void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData) {} @@ -235,7 +235,10 @@ public: return ResourceId(); } rdcarray GetShaderEntryPoints(ResourceId shader) { return {}; } - ShaderReflection *GetShader(ResourceId shader, ShaderEntryPoint entry) { return NULL; } + ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, ShaderEntryPoint entry) + { + return NULL; + } std::vector GetDisassemblyTargets() { return {"N/A"}; } std::string DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, const std::string &target) diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 1fb60a479..58092873e 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -734,15 +734,16 @@ CounterDescription ReplayProxy::DescribeCounter(GPUCounter counterID) template void ReplayProxy::Proxied_FillCBufferVariables(ParamSerialiser ¶mser, ReturnSerialiser &retser, - ResourceId shader, std::string entryPoint, - uint32_t cbufSlot, rdcarray &outvars, - const bytebuf &data) + ResourceId pipeline, ResourceId shader, + std::string entryPoint, uint32_t cbufSlot, + rdcarray &outvars, const bytebuf &data) { const ReplayProxyPacket expectedPacket = eReplayProxy_FillCBufferVariables; ReplayProxyPacket packet = eReplayProxy_FillCBufferVariables; { BEGIN_PARAMS(); + SERIALISE_ELEMENT(pipeline); SERIALISE_ELEMENT(shader); SERIALISE_ELEMENT(entryPoint); SERIALISE_ELEMENT(cbufSlot); @@ -753,16 +754,17 @@ void ReplayProxy::Proxied_FillCBufferVariables(ParamSerialiser ¶mser, Return { REMOTE_EXECUTION(); if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) - m_Remote->FillCBufferVariables(shader, entryPoint, cbufSlot, outvars, data); + m_Remote->FillCBufferVariables(pipeline, shader, entryPoint, cbufSlot, outvars, data); } SERIALISE_RETURN(outvars); } -void ReplayProxy::FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, +void ReplayProxy::FillCBufferVariables(ResourceId pipeline, ResourceId shader, + std::string entryPoint, uint32_t cbufSlot, rdcarray &outvars, const bytebuf &data) { - PROXY_FUNCTION(FillCBufferVariables, shader, entryPoint, cbufSlot, outvars, data); + PROXY_FUNCTION(FillCBufferVariables, pipeline, shader, entryPoint, cbufSlot, outvars, data); } template @@ -1084,22 +1086,24 @@ rdcarray ReplayProxy::GetShaderEntryPoints(ResourceId id) } template -ShaderReflection *ReplayProxy::Proxied_GetShader(ParamSerialiser ¶mser, ReturnSerialiser &retser, - ResourceId id, ShaderEntryPoint entry) +ShaderReflection *ReplayProxy::Proxied_GetShader(ParamSerialiser ¶mser, + ReturnSerialiser &retser, ResourceId pipeline, + ResourceId shader, ShaderEntryPoint entry) { const ReplayProxyPacket expectedPacket = eReplayProxy_GetShader; ReplayProxyPacket packet = eReplayProxy_GetShader; ShaderReflection *ret = NULL; // only consider eventID part of the key on APIs where shaders are mutable - ShaderReflKey key(m_APIProps.shadersMutable ? m_EventID : 0, id, entry); + ShaderReflKey key(m_APIProps.shadersMutable ? m_EventID : 0, pipeline, shader, entry); if(retser.IsReading() && m_ShaderReflectionCache.find(key) != m_ShaderReflectionCache.end()) return m_ShaderReflectionCache[key]; { BEGIN_PARAMS(); - SERIALISE_ELEMENT(id); + SERIALISE_ELEMENT(pipeline); + SERIALISE_ELEMENT(shader); SERIALISE_ELEMENT(entry); END_PARAMS(); } @@ -1107,7 +1111,7 @@ ShaderReflection *ReplayProxy::Proxied_GetShader(ParamSerialiser ¶mser, Retu { REMOTE_EXECUTION(); if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) - ret = m_Remote->GetShader(id, entry); + ret = m_Remote->GetShader(pipeline, shader, entry); } { @@ -1131,9 +1135,10 @@ ShaderReflection *ReplayProxy::Proxied_GetShader(ParamSerialiser ¶mser, Retu return m_ShaderReflectionCache[key]; } -ShaderReflection *ReplayProxy::GetShader(ResourceId id, ShaderEntryPoint entry) +ShaderReflection *ReplayProxy::GetShader(ResourceId pipeline, ResourceId shader, + ShaderEntryPoint entry) { - PROXY_FUNCTION(GetShader, id, entry); + PROXY_FUNCTION(GetShader, pipeline, shader, entry); } template @@ -1166,7 +1171,7 @@ std::string ReplayProxy::Proxied_DisassembleShader(ParamSerialiser ¶mser, if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) { - refl = m_Remote->GetShader(m_Remote->GetLiveID(Shader), EntryPoint); + refl = m_Remote->GetShader(pipeline, m_Remote->GetLiveID(Shader), EntryPoint); ret = m_Remote->DisassembleShader(pipeline, refl, target); } @@ -1591,11 +1596,13 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer for(int i = 0; i < 6; i++) if(stages[i]->resourceId != ResourceId()) - stages[i]->reflection = GetShader(GetLiveID(stages[i]->resourceId), ShaderEntryPoint()); + stages[i]->reflection = + GetShader(ResourceId(), GetLiveID(stages[i]->resourceId), ShaderEntryPoint()); if(m_D3D11PipelineState.inputAssembly.resourceId != ResourceId()) - m_D3D11PipelineState.inputAssembly.bytecode = GetShader( - GetLiveID(m_D3D11PipelineState.inputAssembly.resourceId), ShaderEntryPoint()); + m_D3D11PipelineState.inputAssembly.bytecode = + GetShader(ResourceId(), GetLiveID(m_D3D11PipelineState.inputAssembly.resourceId), + ShaderEntryPoint()); } else if(m_APIProps.pipelineType == GraphicsAPI::D3D12) { @@ -1605,9 +1612,12 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer &m_D3D12PipelineState.pixelShader, &m_D3D12PipelineState.computeShader, }; + ResourceId pipe = GetLiveID(m_D3D12PipelineState.pipelineResourceId); + for(int i = 0; i < 6; i++) if(stages[i]->resourceId != ResourceId()) - stages[i]->reflection = GetShader(GetLiveID(stages[i]->resourceId), ShaderEntryPoint()); + stages[i]->reflection = + GetShader(pipe, GetLiveID(stages[i]->resourceId), ShaderEntryPoint()); } else if(m_APIProps.pipelineType == GraphicsAPI::OpenGL) { @@ -1620,7 +1630,7 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer for(int i = 0; i < 6; i++) if(stages[i]->shaderResourceId != ResourceId()) stages[i]->reflection = - GetShader(GetLiveID(stages[i]->shaderResourceId), ShaderEntryPoint()); + GetShader(ResourceId(), GetLiveID(stages[i]->shaderResourceId), ShaderEntryPoint()); } else if(m_APIProps.pipelineType == GraphicsAPI::Vulkan) { @@ -1630,11 +1640,18 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer &m_VulkanPipelineState.fragmentShader, &m_VulkanPipelineState.computeShader, }; + ResourceId pipe = GetLiveID(m_VulkanPipelineState.graphics.pipelineResourceId); + for(int i = 0; i < 6; i++) + { + if(i == 5) + pipe = GetLiveID(m_VulkanPipelineState.compute.pipelineResourceId); + if(stages[i]->resourceId != ResourceId()) stages[i]->reflection = - GetShader(GetLiveID(stages[i]->resourceId), + GetShader(pipe, GetLiveID(stages[i]->resourceId), ShaderEntryPoint(stages[i]->entryPoint, stages[i]->stage)); + } } } } @@ -2573,7 +2590,7 @@ bool ReplayProxy::Tick(int type) case eReplayProxy_GetBuffers: GetBuffers(); break; case eReplayProxy_GetBuffer: GetBuffer(ResourceId()); break; case eReplayProxy_GetShaderEntryPoints: GetShaderEntryPoints(ResourceId()); break; - case eReplayProxy_GetShader: GetShader(ResourceId(), ShaderEntryPoint()); break; + case eReplayProxy_GetShader: GetShader(ResourceId(), ResourceId(), ShaderEntryPoint()); break; case eReplayProxy_GetDebugMessages: GetDebugMessages(); break; case eReplayProxy_GetBufferData: { @@ -2606,7 +2623,7 @@ bool ReplayProxy::Tick(int type) { rdcarray vars; bytebuf data; - FillCBufferVariables(ResourceId(), "", 0, vars, data); + FillCBufferVariables(ResourceId(), ResourceId(), "", 0, vars, data); break; } case eReplayProxy_InitPostVS: InitPostVSBuffers(0); break; diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index 4ff2988f6..1827bf711 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -490,9 +490,9 @@ public: IMPLEMENT_FUNCTION_PROXIED(std::vector, FetchCounters, const std::vector &counterID); - IMPLEMENT_FUNCTION_PROXIED(void, FillCBufferVariables, ResourceId shader, std::string entryPoint, - uint32_t cbufSlot, rdcarray &outvars, - const bytebuf &data); + IMPLEMENT_FUNCTION_PROXIED(void, FillCBufferVariables, ResourceId pipeline, ResourceId shader, + std::string entryPoint, uint32_t cbufSlot, + rdcarray &outvars, const bytebuf &data); IMPLEMENT_FUNCTION_PROXIED(void, GetBufferData, ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData); @@ -509,7 +509,7 @@ public: const std::vector &passEvents); IMPLEMENT_FUNCTION_PROXIED(rdcarray, GetShaderEntryPoints, ResourceId shader); - IMPLEMENT_FUNCTION_PROXIED(ShaderReflection *, GetShader, ResourceId shader, + IMPLEMENT_FUNCTION_PROXIED(ShaderReflection *, GetShader, ResourceId pipeline, ResourceId, ShaderEntryPoint entry); IMPLEMENT_FUNCTION_PROXIED(std::vector, GetDisassemblyTargets); @@ -638,17 +638,23 @@ private: struct ShaderReflKey { ShaderReflKey() {} - ShaderReflKey(uint32_t eid, ResourceId i, ShaderEntryPoint e) : eventId(eid), id(i), entry(e) {} + ShaderReflKey(uint32_t eid, ResourceId p, ResourceId s, ShaderEntryPoint e) + : eventId(eid), pipeline(p), shader(s), entry(e) + { + } uint32_t eventId; - ResourceId id; + ResourceId pipeline, shader; ShaderEntryPoint entry; bool operator<(const ShaderReflKey &o) const { if(eventId != o.eventId) return eventId < o.eventId; - if(id != o.id) - return id < o.id; + if(pipeline != o.pipeline) + return pipeline < o.pipeline; + + if(shader != o.shader) + return shader < o.shader; return entry < o.entry; } diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index 8d981d5e5..38215198b 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -438,7 +438,8 @@ rdcarray D3D11Replay::GetShaderEntryPoints(ResourceId shader) return {{"main", ret.stage}}; } -ShaderReflection *D3D11Replay::GetShader(ResourceId shader, ShaderEntryPoint entry) +ShaderReflection *D3D11Replay::GetShader(ResourceId pipeline, ResourceId shader, + ShaderEntryPoint entry) { auto it = WrappedShader::m_ShaderList.find(shader); @@ -659,7 +660,7 @@ void D3D11Replay::SavePipelineState(uint32_t eventId) ResourceId layoutId = GetIDForResource(rs->IA.Layout); ret.inputAssembly.resourceId = rm->GetOriginalID(layoutId); - ret.inputAssembly.bytecode = GetShader(layoutId, ShaderEntryPoint()); + ret.inputAssembly.bytecode = GetShader(ResourceId(), layoutId, ShaderEntryPoint()); ret.inputAssembly.layouts.resize(vec.size()); for(size_t i = 0; i < vec.size(); i++) @@ -2592,7 +2593,8 @@ void D3D11Replay::RenderHighlightBox(float w, float h, float scale) } } -void D3D11Replay::FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, +void D3D11Replay::FillCBufferVariables(ResourceId pipeline, ResourceId shader, + std::string entryPoint, uint32_t cbufSlot, rdcarray &outvars, const bytebuf &data) { auto it = WrappedShader::m_ShaderList.find(shader); diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index dc7794b6b..f244e3bc5 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -119,7 +119,7 @@ public: std::vector GetDebugMessages(); rdcarray GetShaderEntryPoints(ResourceId shader); - ShaderReflection *GetShader(ResourceId shader, ShaderEntryPoint entry); + ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, ShaderEntryPoint entry); std::vector GetDisassemblyTargets(); std::string DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, @@ -217,8 +217,9 @@ public: void RenderHighlightBox(float w, float h, float scale); - void FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, - rdcarray &outvars, const bytebuf &data); + void FillCBufferVariables(ResourceId pipeline, ResourceId shader, std::string entryPoint, + uint32_t cbufSlot, rdcarray &outvars, + const bytebuf &data); std::vector PixelHistory(std::vector events, ResourceId target, uint32_t x, uint32_t y, uint32_t slice, uint32_t mip, diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 48f42e335..6f9837fc4 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -409,7 +409,8 @@ rdcarray D3D12Replay::GetShaderEntryPoints(ResourceId shader) return {{"main", ret.stage}}; } -ShaderReflection *D3D12Replay::GetShader(ResourceId shader, ShaderEntryPoint entry) +ShaderReflection *D3D12Replay::GetShader(ResourceId pipeline, ResourceId shader, + ShaderEntryPoint entry) { WrappedID3D12Shader *sh = m_pDevice->GetResourceManager()->GetCurrentAs(shader); @@ -2652,7 +2653,8 @@ void D3D12Replay::GetBufferData(ResourceId buff, uint64_t offset, uint64_t lengt GetDebugManager()->GetBufferData(buffer, offset, length, retData); } -void D3D12Replay::FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, +void D3D12Replay::FillCBufferVariables(ResourceId pipeline, ResourceId shader, + std::string entryPoint, uint32_t cbufSlot, rdcarray &outvars, const bytebuf &data) { if(shader == ResourceId()) diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index 970143c08..5a107240e 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -78,7 +78,7 @@ public: std::vector GetDebugMessages(); rdcarray GetShaderEntryPoints(ResourceId shader); - ShaderReflection *GetShader(ResourceId shader, ShaderEntryPoint entry); + ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, ShaderEntryPoint entry); std::vector GetDisassemblyTargets(); std::string DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, @@ -178,8 +178,9 @@ public: void RenderHighlightBox(float w, float h, float scale); - void FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, - rdcarray &outvars, const bytebuf &data); + void FillCBufferVariables(ResourceId pipeline, ResourceId shader, std::string entryPoint, + uint32_t cbufSlot, rdcarray &outvars, + const bytebuf &data); std::vector PixelHistory(std::vector events, ResourceId target, uint32_t x, uint32_t y, uint32_t slice, uint32_t mip, diff --git a/renderdoc/driver/gl/gl_overlay.cpp b/renderdoc/driver/gl/gl_overlay.cpp index 103446b2f..6c0fd0d78 100644 --- a/renderdoc/driver/gl/gl_overlay.cpp +++ b/renderdoc/driver/gl/gl_overlay.cpp @@ -123,7 +123,7 @@ bool GLReplay::CreateOverlayProgram(GLuint Program, GLuint Pipeline, GLuint frag } if(i == 0) - vsRefl = GetShader(pipeDetails.stageShaders[i], ShaderEntryPoint()); + vsRefl = GetShader(ResourceId(), pipeDetails.stageShaders[i], ShaderEntryPoint()); } } } @@ -151,7 +151,7 @@ bool GLReplay::CreateOverlayProgram(GLuint Program, GLuint Pipeline, GLuint frag HasGLSLShaders = true; if(i == 0) - vsRefl = GetShader(progDetails.stageShaders[0], ShaderEntryPoint()); + vsRefl = GetShader(ResourceId(), progDetails.stageShaders[0], ShaderEntryPoint()); } } } diff --git a/renderdoc/driver/gl/gl_postvs.cpp b/renderdoc/driver/gl/gl_postvs.cpp index 26d308350..adb6ce02b 100644 --- a/renderdoc/driver/gl/gl_postvs.cpp +++ b/renderdoc/driver/gl/gl_postvs.cpp @@ -125,18 +125,18 @@ void GLReplay::InitPostVSBuffers(uint32_t eventId) { if(i == 0) { - vsRefl = GetShader(pipeDetails.stageShaders[i], ShaderEntryPoint()); + vsRefl = GetShader(ResourceId(), pipeDetails.stageShaders[i], ShaderEntryPoint()); glslVer = m_pDriver->m_Shaders[pipeDetails.stageShaders[0]].version; vsPatch = m_pDriver->m_Shaders[pipeDetails.stageShaders[0]].patchData; } else if(i == 2) { - tesRefl = GetShader(pipeDetails.stageShaders[2], ShaderEntryPoint()); + tesRefl = GetShader(ResourceId(), pipeDetails.stageShaders[2], ShaderEntryPoint()); tesPatch = m_pDriver->m_Shaders[pipeDetails.stageShaders[2]].patchData; } else if(i == 3) { - gsRefl = GetShader(pipeDetails.stageShaders[3], ShaderEntryPoint()); + gsRefl = GetShader(ResourceId(), pipeDetails.stageShaders[3], ShaderEntryPoint()); gsPatch = m_pDriver->m_Shaders[pipeDetails.stageShaders[3]].patchData; } @@ -202,18 +202,18 @@ void GLReplay::InitPostVSBuffers(uint32_t eventId) { if(i == 0) { - vsRefl = GetShader(progDetails.stageShaders[0], ShaderEntryPoint()); + vsRefl = GetShader(ResourceId(), progDetails.stageShaders[0], ShaderEntryPoint()); glslVer = m_pDriver->m_Shaders[progDetails.stageShaders[0]].version; vsPatch = m_pDriver->m_Shaders[progDetails.stageShaders[0]].patchData; } else if(i == 2 && progDetails.stageShaders[2] != ResourceId()) { - tesRefl = GetShader(progDetails.stageShaders[2], ShaderEntryPoint()); + tesRefl = GetShader(ResourceId(), progDetails.stageShaders[2], ShaderEntryPoint()); tesPatch = m_pDriver->m_Shaders[progDetails.stageShaders[2]].patchData; } else if(i == 3 && progDetails.stageShaders[3] != ResourceId()) { - gsRefl = GetShader(progDetails.stageShaders[3], ShaderEntryPoint()); + gsRefl = GetShader(ResourceId(), progDetails.stageShaders[3], ShaderEntryPoint()); gsPatch = m_pDriver->m_Shaders[progDetails.stageShaders[3]].patchData; } diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index d54f97178..be1e23fc9 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -726,7 +726,7 @@ rdcarray GLReplay::GetShaderEntryPoints(ResourceId shader) return {{shaderDetails.reflection.entryPoint, shaderDetails.reflection.stage}}; } -ShaderReflection *GLReplay::GetShader(ResourceId shader, ShaderEntryPoint entry) +ShaderReflection *GLReplay::GetShader(ResourceId pipeline, ResourceId shader, ShaderEntryPoint entry) { auto &shaderDetails = m_pDriver->m_Shaders[shader]; @@ -2150,8 +2150,9 @@ void GLReplay::OpenGLFillCBufferVariables(GLuint prog, bool bufferBacked, std::s } } -void GLReplay::FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, - rdcarray &outvars, const bytebuf &data) +void GLReplay::FillCBufferVariables(ResourceId pipeline, ResourceId shader, std::string entryPoint, + uint32_t cbufSlot, rdcarray &outvars, + const bytebuf &data) { WrappedOpenGL &drv = *m_pDriver; diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index bb8180460..fa7ac72d8 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -103,7 +103,7 @@ public: TextureDescription GetTexture(ResourceId id); rdcarray GetShaderEntryPoints(ResourceId shader); - ShaderReflection *GetShader(ResourceId shader, ShaderEntryPoint entry); + ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, ShaderEntryPoint entry); std::vector GetDisassemblyTargets(); std::string DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, @@ -194,8 +194,9 @@ public: void RenderHighlightBox(float w, float h, float scale); - void FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, - rdcarray &outvars, const bytebuf &data); + void FillCBufferVariables(ResourceId pipeline, ResourceId shader, std::string entryPoint, + uint32_t cbufSlot, rdcarray &outvars, + const bytebuf &data); std::vector PixelHistory(std::vector events, ResourceId target, uint32_t x, uint32_t y, uint32_t slice, uint32_t mip, diff --git a/renderdoc/driver/gl/wrappers/gl_emulated.cpp b/renderdoc/driver/gl/wrappers/gl_emulated.cpp index 8e7a0b6de..81502b748 100644 --- a/renderdoc/driver/gl/wrappers/gl_emulated.cpp +++ b/renderdoc/driver/gl/wrappers/gl_emulated.cpp @@ -3526,7 +3526,7 @@ void MakeOnlineShaderReflection(ShaderStage stage, const std::string &source, return; } - refl = *driver->GetShader(id, ShaderEntryPoint("main", ShaderStage::Fragment)); + refl = *driver->GetShader(ResourceId(), id, ShaderEntryPoint("main", ShaderStage::Fragment)); // Note that we can't fill out ShaderBindpointMapping easily on the actual driver through the // replay interface diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index a4c474c29..9307e7822 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -204,7 +204,8 @@ bool DescSetLayout::operator==(const DescSetLayout &other) const return true; } -void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, +void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, + VulkanCreationInfo &info, ResourceId id, const VkGraphicsPipelineCreateInfo *pCreateInfo) { flags = pCreateInfo->flags; @@ -223,23 +224,22 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk // VkPipelineShaderStageCreateInfo for(uint32_t i = 0; i < pCreateInfo->stageCount; i++) { - ResourceId id = GetResID(pCreateInfo->pStages[i].module); + ResourceId shadid = GetResID(pCreateInfo->pStages[i].module); // convert shader bit to shader index int stageIndex = StageIndex(pCreateInfo->pStages[i].stage); Shader &shad = shaders[stageIndex]; - shad.module = id; + shad.module = shadid; shad.entryPoint = pCreateInfo->pStages[i].pName; - ShaderModule::Reflection &reflData = info.m_ShaderModule[id].m_Reflections[shad.entryPoint]; - - reflData.Init(resourceMan, id, info.m_ShaderModule[id].spirv, shad.entryPoint, - pCreateInfo->pStages[i].stage); + ShaderModuleReflectionKey key(shad.entryPoint, ResourceId()); if(pCreateInfo->pStages[i].pSpecializationInfo) { + key = ShaderModuleReflectionKey(shad.entryPoint, id); + const byte *data = (const byte *)pCreateInfo->pStages[i].pSpecializationInfo->pData; const VkSpecializationMapEntry *maps = pCreateInfo->pStages[i].pSpecializationInfo->pMapEntries; @@ -253,6 +253,11 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk } } + ShaderModuleReflection &reflData = info.m_ShaderModule[shadid].m_Reflections[key]; + + reflData.Init(resourceMan, shadid, info.m_ShaderModule[shadid].spirv, shad.entryPoint, + pCreateInfo->pStages[i].stage, shad.specialization); + shad.refl = &reflData.refl; shad.mapping = &reflData.mapping; shad.patchData = &reflData.patchData; @@ -544,7 +549,7 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk } void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, - const VkComputePipelineCreateInfo *pCreateInfo) + ResourceId id, const VkComputePipelineCreateInfo *pCreateInfo) { flags = pCreateInfo->flags; @@ -554,19 +559,18 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk // VkPipelineShaderStageCreateInfo { - ResourceId id = GetResID(pCreateInfo->stage.module); + ResourceId shadid = GetResID(pCreateInfo->stage.module); Shader &shad = shaders[5]; // 5 is the compute shader's index (VS, TCS, TES, GS, FS, CS) - shad.module = id; + shad.module = shadid; shad.entryPoint = pCreateInfo->stage.pName; - ShaderModule::Reflection &reflData = info.m_ShaderModule[id].m_Reflections[shad.entryPoint]; - - reflData.Init(resourceMan, id, info.m_ShaderModule[id].spirv, shad.entryPoint, - pCreateInfo->stage.stage); + ShaderModuleReflectionKey key(shad.entryPoint, ResourceId()); if(pCreateInfo->stage.pSpecializationInfo) { + key = ShaderModuleReflectionKey(shad.entryPoint, id); + const byte *data = (const byte *)pCreateInfo->stage.pSpecializationInfo->pData; const VkSpecializationMapEntry *maps = pCreateInfo->stage.pSpecializationInfo->pMapEntries; @@ -580,6 +584,11 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk } } + ShaderModuleReflection &reflData = info.m_ShaderModule[shadid].m_Reflections[key]; + + reflData.Init(resourceMan, shadid, info.m_ShaderModule[shadid].spirv, shad.entryPoint, + pCreateInfo->stage.stage, shad.specialization); + shad.refl = &reflData.refl; shad.mapping = &reflData.mapping; shad.patchData = &reflData.patchData; @@ -1044,18 +1053,19 @@ void VulkanCreationInfo::ShaderModule::Init(VulkanResourceManager *resourceMan, } } -void VulkanCreationInfo::ShaderModule::Reflection::Init(VulkanResourceManager *resourceMan, - ResourceId id, const rdcspv::Reflector &spv, - const std::string &entry, - VkShaderStageFlagBits stage) +void VulkanCreationInfo::ShaderModuleReflection::Init(VulkanResourceManager *resourceMan, + ResourceId id, const rdcspv::Reflector &spv, + const std::string &entry, + VkShaderStageFlagBits stage, + const std::vector &specInfo) { if(entryPoint.empty()) { entryPoint = entry; stageIndex = StageIndex(stage); - spv.MakeReflection(GraphicsAPI::Vulkan, ShaderStage(stageIndex), entryPoint, {}, refl, mapping, - patchData); + spv.MakeReflection(GraphicsAPI::Vulkan, ShaderStage(stageIndex), entryPoint, specInfo, refl, + mapping, patchData); refl.resourceId = resourceMan->GetOriginalID(id); } diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index 827b4bfc7..3de426d32 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -135,11 +135,42 @@ struct DescUpdateTemplate struct VulkanCreationInfo { + struct ShaderModuleReflectionKey + { + ShaderModuleReflectionKey(const rdcstr &e, ResourceId p) : entryPoint(e), specialisingPipe(p) {} + bool operator<(const ShaderModuleReflectionKey &o) const + { + if(entryPoint != o.entryPoint) + return entryPoint < o.entryPoint; + + return specialisingPipe < o.specialisingPipe; + } + + // name of the entry point + rdcstr entryPoint; + // ID of the pipeline ONLY if it contains specialisation constant data + ResourceId specialisingPipe; + }; + + struct ShaderModuleReflection + { + uint32_t stageIndex; + std::string entryPoint; + std::string disassembly; + ShaderReflection refl; + ShaderBindpointMapping mapping; + SPIRVPatchData patchData; + + void Init(VulkanResourceManager *resourceMan, ResourceId id, const rdcspv::Reflector &spv, + const std::string &entry, VkShaderStageFlagBits stage, + const std::vector &specInfo); + }; + struct Pipeline { - void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, + void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, ResourceId id, const VkGraphicsPipelineCreateInfo *pCreateInfo); - void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, + void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, ResourceId id, const VkComputePipelineCreateInfo *pCreateInfo); ResourceId layout; @@ -469,23 +500,22 @@ struct VulkanCreationInfo void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, const VkShaderModuleCreateInfo *pCreateInfo); + ShaderModuleReflection &GetReflection(const rdcstr &entry, ResourceId pipe) + { + // look for one from this pipeline specifically, if it was specialised + auto it = m_Reflections.find({entry, pipe}); + if(it != m_Reflections.end()) + return it->second; + + // if not, just return the non-specialised version + return m_Reflections[{entry, ResourceId()}]; + } + rdcspv::Reflector spirv; std::string unstrippedPath; - struct Reflection - { - uint32_t stageIndex; - std::string entryPoint; - std::string disassembly; - ShaderReflection refl; - ShaderBindpointMapping mapping; - SPIRVPatchData patchData; - - void Init(VulkanResourceManager *resourceMan, ResourceId id, const rdcspv::Reflector &spv, - const std::string &entry, VkShaderStageFlagBits stage); - }; - std::map m_Reflections; + std::map m_Reflections; }; std::map m_ShaderModule; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 23c43712a..af54bfdc8 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -330,7 +330,8 @@ rdcarray VulkanReplay::GetShaderEntryPoints(ResourceId shader) return ret; } -ShaderReflection *VulkanReplay::GetShader(ResourceId shader, ShaderEntryPoint entry) +ShaderReflection *VulkanReplay::GetShader(ResourceId pipeline, ResourceId shader, + ShaderEntryPoint entry) { auto shad = m_pDriver->m_CreationInfo.m_ShaderModule.find(shader); @@ -340,11 +341,13 @@ ShaderReflection *VulkanReplay::GetShader(ResourceId shader, ShaderEntryPoint en return NULL; } - shad->second.m_Reflections[entry.name].Init(GetResourceManager(), shader, shad->second.spirv, - entry.name, - VkShaderStageFlagBits(1 << uint32_t(entry.stage))); + // if this shader was never used in a pipeline the reflection won't be prepared. Do that now - + // this will be ignored if it was already prepared. + shad->second.GetReflection(entry.name, pipeline) + .Init(GetResourceManager(), shader, shad->second.spirv, entry.name, + VkShaderStageFlagBits(1 << uint32_t(entry.stage)), {}); - return &shad->second.m_Reflections[entry.name].refl; + return &shad->second.GetReflection(entry.name, pipeline).refl; } std::vector VulkanReplay::GetDisassemblyTargets() @@ -450,7 +453,7 @@ std::string VulkanReplay::DisassembleShader(ResourceId pipeline, const ShaderRef if(target == SPIRVDisassemblyTarget || target.empty()) { - std::string &disasm = it->second.m_Reflections[refl->entryPoint.c_str()].disassembly; + std::string &disasm = it->second.GetReflection(refl->entryPoint, pipeline).disassembly; if(disasm.empty()) disasm = it->second.spirv.Disassemble(refl->entryPoint.c_str()); @@ -472,7 +475,7 @@ std::string VulkanReplay::DisassembleShader(ResourceId pipeline, const ShaderRef VkPipeline pipe = m_pDriver->GetResourceManager()->GetLiveHandle(pipeline); VkShaderStageFlagBits stageBit = - VkShaderStageFlagBits(1 << it->second.m_Reflections[refl->entryPoint.c_str()].stageIndex); + VkShaderStageFlagBits(1 << it->second.GetReflection(refl->entryPoint, pipeline).stageIndex); size_t size; vt->GetShaderInfoAMD(Unwrap(dev), Unwrap(pipe), stageBit, VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, @@ -498,7 +501,7 @@ std::string VulkanReplay::DisassembleShader(ResourceId pipeline, const ShaderRef CachePipelineExecutables(pipeline); VkShaderStageFlagBits stageBit = - VkShaderStageFlagBits(1 << it->second.m_Reflections[refl->entryPoint.c_str()].stageIndex); + VkShaderStageFlagBits(1 << it->second.GetReflection(refl->entryPoint, pipeline).stageIndex); const rdcarray &executables = m_PipelineExecutables[pipeline]; @@ -1956,13 +1959,10 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) } } -void VulkanReplay::FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, +void VulkanReplay::FillCBufferVariables(ResourceId pipeline, ResourceId shader, + std::string entryPoint, uint32_t cbufSlot, rdcarray &outvars, const bytebuf &data) { - // Correct SPIR-V will ultimately need to set explicit layout information for each type. - // For now, just assume D3D11 packing (float4 alignment on float4s, float3s, matrices, arrays and - // structures) - auto it = m_pDriver->m_CreationInfo.m_ShaderModule.find(shader); if(it == m_pDriver->m_CreationInfo.m_ShaderModule.end()) @@ -1971,8 +1971,8 @@ void VulkanReplay::FillCBufferVariables(ResourceId shader, std::string entryPoin return; } - ShaderReflection &refl = it->second.m_Reflections[entryPoint].refl; - ShaderBindpointMapping &mapping = it->second.m_Reflections[entryPoint].mapping; + ShaderReflection &refl = it->second.GetReflection(entryPoint, pipeline).refl; + ShaderBindpointMapping &mapping = it->second.GetReflection(entryPoint, pipeline).mapping; if(cbufSlot >= (uint32_t)refl.constantBlocks.count()) { @@ -1991,21 +1991,14 @@ void VulkanReplay::FillCBufferVariables(ResourceId shader, std::string entryPoin // specialised path to display specialization constants if(mapping.constantBlocks[c.bindPoint].bindset == SpecializationConstantBindSet) { - // 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); + + if(pipeIt != m_pDriver->m_CreationInfo.m_Pipeline.end()) { - auto pipeIt = m_pDriver->m_CreationInfo.m_Pipeline.find(pipeline); + auto specInfo = + pipeIt->second.shaders[it->second.GetReflection(entryPoint, pipeline).stageIndex].specialization; - if(pipeIt != m_pDriver->m_CreationInfo.m_Pipeline.end()) - { - auto specInfo = - pipeIt->second.shaders[it->second.m_Reflections[entryPoint].stageIndex].specialization; - - FillSpecConstantVariables(c.variables, outvars, specInfo); - } + FillSpecConstantVariables(c.variables, outvars, specInfo); } } else diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 2d7d49b3e..32e71d6c0 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -243,7 +243,7 @@ public: TextureDescription GetTexture(ResourceId id); rdcarray GetShaderEntryPoints(ResourceId shader); - ShaderReflection *GetShader(ResourceId shader, ShaderEntryPoint entry); + ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, ShaderEntryPoint entry); std::vector GetDisassemblyTargets(); std::string DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, @@ -334,8 +334,9 @@ public: void RenderHighlightBox(float w, float h, float scale); - void FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, - rdcarray &outvars, const bytebuf &data); + void FillCBufferVariables(ResourceId pipeline, ResourceId shader, std::string entryPoint, + uint32_t cbufSlot, rdcarray &outvars, + const bytebuf &data); std::vector PixelHistory(std::vector events, ResourceId target, uint32_t x, uint32_t y, uint32_t slice, uint32_t mip, diff --git a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp index 541c67f48..8652ef6a6 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_shader_funcs.cpp @@ -459,7 +459,7 @@ bool WrappedVulkan::Serialise_vkCreateGraphicsPipelines( VulkanCreationInfo::Pipeline &pipeInfo = m_CreationInfo.m_Pipeline[live]; - pipeInfo.Init(GetResourceManager(), m_CreationInfo, &CreateInfo); + pipeInfo.Init(GetResourceManager(), m_CreationInfo, live, &CreateInfo); ResourceId renderPassID = GetResID(CreateInfo.renderPass); @@ -588,7 +588,8 @@ VkResult WrappedVulkan::vkCreateGraphicsPipelines(VkDevice device, VkPipelineCac { GetResourceManager()->AddLiveResource(id, pPipelines[i]); - m_CreationInfo.m_Pipeline[id].Init(GetResourceManager(), m_CreationInfo, &pCreateInfos[i]); + m_CreationInfo.m_Pipeline[id].Init(GetResourceManager(), m_CreationInfo, id, + &pCreateInfos[i]); } } } @@ -657,7 +658,7 @@ bool WrappedVulkan::Serialise_vkCreateComputePipelines(SerialiserType &ser, VkDe live = GetResourceManager()->WrapResource(Unwrap(device), pipe); GetResourceManager()->AddLiveResource(Pipeline, pipe); - m_CreationInfo.m_Pipeline[live].Init(GetResourceManager(), m_CreationInfo, &CreateInfo); + m_CreationInfo.m_Pipeline[live].Init(GetResourceManager(), m_CreationInfo, live, &CreateInfo); } } @@ -758,7 +759,8 @@ VkResult WrappedVulkan::vkCreateComputePipelines(VkDevice device, VkPipelineCach { GetResourceManager()->AddLiveResource(id, pPipelines[i]); - m_CreationInfo.m_Pipeline[id].Init(GetResourceManager(), m_CreationInfo, &pCreateInfos[i]); + m_CreationInfo.m_Pipeline[id].Init(GetResourceManager(), m_CreationInfo, id, + &pCreateInfos[i]); } } } diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 63f4a65d6..f41a3e161 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -593,11 +593,12 @@ rdcarray ReplayController::GetShaderEntryPoints(ResourceId sha return m_pDevice->GetShaderEntryPoints(m_pDevice->GetLiveID(shader)); } -ShaderReflection *ReplayController::GetShader(ResourceId shader, ShaderEntryPoint entry) +ShaderReflection *ReplayController::GetShader(ResourceId pipeline, ResourceId shader, + ShaderEntryPoint entry) { CHECK_REPLAY_THREAD(); - return m_pDevice->GetShader(m_pDevice->GetLiveID(shader), entry); + return m_pDevice->GetShader(m_pDevice->GetLiveID(pipeline), m_pDevice->GetLiveID(shader), entry); } rdcarray ReplayController::GetUsage(ResourceId id) @@ -1704,7 +1705,8 @@ void ReplayController::FreeTrace(ShaderDebugTrace *trace) } rdcarray ReplayController::GetCBufferVariableContents( - ResourceId shader, const char *entryPoint, uint32_t cbufslot, ResourceId buffer, uint64_t offs) + ResourceId pipeline, ResourceId shader, const char *entryPoint, uint32_t cbufslot, + ResourceId buffer, uint64_t offs) { CHECK_REPLAY_THREAD(); @@ -1718,10 +1720,11 @@ rdcarray ReplayController::GetCBufferVariableContents( rdcarray v; + pipeline = m_pDevice->GetLiveID(pipeline); shader = m_pDevice->GetLiveID(shader); if(shader != ResourceId()) - m_pDevice->FillCBufferVariables(shader, entryPoint, cbufslot, v, data); + m_pDevice->FillCBufferVariables(pipeline, shader, entryPoint, cbufslot, v, data); return v; } diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 9e0acdcf5..865ef2332 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -182,7 +182,7 @@ public: rdcarray GetDebugMessages(); rdcarray GetShaderEntryPoints(ResourceId shader); - ShaderReflection *GetShader(ResourceId shader, ShaderEntryPoint entry); + ShaderReflection *GetShader(ResourceId pipeline, 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); @@ -201,9 +201,9 @@ public: bool SaveTexture(const TextureSave &saveData, const char *path); - rdcarray GetCBufferVariableContents(ResourceId shader, const char *entryPoint, - uint32_t cbufslot, ResourceId buffer, - uint64_t offs); + rdcarray GetCBufferVariableContents(ResourceId pipeline, ResourceId shader, + const char *entryPoint, uint32_t cbufslot, + ResourceId buffer, uint64_t offs); rdcarray GetSupportedWindowSystems(); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 9a0a88233..b80267a57 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -103,7 +103,8 @@ public: virtual std::vector GetDebugMessages() = 0; virtual rdcarray GetShaderEntryPoints(ResourceId shader) = 0; - virtual ShaderReflection *GetShader(ResourceId shader, ShaderEntryPoint entry) = 0; + virtual ShaderReflection *GetShader(ResourceId pipeline, ResourceId shader, + ShaderEntryPoint entry) = 0; virtual std::vector GetDisassemblyTargets() = 0; virtual std::string DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, @@ -149,8 +150,9 @@ public: virtual CounterDescription DescribeCounter(GPUCounter counterID) = 0; virtual std::vector FetchCounters(const std::vector &counterID) = 0; - virtual void FillCBufferVariables(ResourceId shader, std::string entryPoint, uint32_t cbufSlot, - rdcarray &outvars, const bytebuf &data) = 0; + virtual void FillCBufferVariables(ResourceId pipeline, ResourceId shader, std::string entryPoint, + uint32_t cbufSlot, rdcarray &outvars, + const bytebuf &data) = 0; virtual std::vector PixelHistory(std::vector events, ResourceId target, uint32_t x, uint32_t y, diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt index 5ba8aae9e..8716f5173 100644 --- a/util/test/demos/CMakeLists.txt +++ b/util/test/demos/CMakeLists.txt @@ -23,6 +23,7 @@ set(VULKAN_SRC vk/vk_sample_locations.cpp vk/vk_secondary_cmdbuf.cpp vk/vk_simple_triangle.cpp + vk/vk_spec_constants.cpp vk/vk_structured_buffer_nested.cpp vk/vk_vertex_attr_zoo.cpp vk/vk_video_textures.cpp diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index d796d41ce..269efc5bb 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -213,6 +213,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 2d71df1ba..9fca1c2be 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -318,6 +318,9 @@ Vulkan\demos + + Vulkan\demos + diff --git a/util/test/demos/vk/vk_spec_constants.cpp b/util/test/demos/vk/vk_spec_constants.cpp new file mode 100644 index 000000000..015da2610 --- /dev/null +++ b/util/test/demos/vk/vk_spec_constants.cpp @@ -0,0 +1,188 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "vk_test.h" + +TEST(VK_Spec_Constants, VulkanGraphicsTest) +{ + static constexpr const char *Description = + "Tests using the same shader multiple times with specialisation constants"; + + const std::string vertex = R"EOSHADER( +#version 420 core + +layout(location = 0) in vec3 Position; + +void main() +{ + gl_Position = vec4(Position.xyz*vec3(1,-1,1), 1); +} + +)EOSHADER"; + + const std::string pixel = R"EOSHADER( +#version 420 core + +layout(location = 0, index = 0) out vec4 Color; + +layout(constant_id = 0) const int numcols = 0; + +layout(set = 0, binding = 0, std140) uniform constsbuf +{ + vec4 col[numcols+1]; +}; + +void main() +{ + Color = vec4(0,0,0,1); + for(int i=0; i < numcols; i++) + Color += col[i]; +} + +)EOSHADER"; + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + VkDescriptorSetLayout setlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, + })); + + VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({setlayout})); + + vkh::GraphicsPipelineCreateInfo pipeCreateInfo; + + pipeCreateInfo.layout = layout; + pipeCreateInfo.renderPass = mainWindow->rp; + + pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)}; + pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = { + vkh::vertexAttr(0, 0, DefaultA2V, pos), + }; + + pipeCreateInfo.stages = { + CompileShaderModule(vertex, ShaderLang::glsl, ShaderStage::vert, "main"), + CompileShaderModule(pixel, ShaderLang::glsl, ShaderStage::frag, "main"), + }; + + VkSpecializationMapEntry specmap[1] = { + {0, 0, sizeof(uint32_t)}, + }; + + uint32_t specval = 0; + + VkSpecializationInfo spec = {}; + spec.mapEntryCount = 1; + spec.pMapEntries = specmap; + spec.dataSize = sizeof(specval); + spec.pData = &specval; + + pipeCreateInfo.stages[1].pSpecializationInfo = &spec; + + VkPipeline pipe[4] = {}; + for(size_t i = 0; i < ARRAY_COUNT(pipe); i++) + { + specval = (uint32_t)i; + pipe[i] = createGraphicsPipeline(pipeCreateInfo); + } + + Vec4f cbufferdata[4] = { + Vec4f(1.0f, 0.0f, 0.0f, 0.0f), Vec4f(0.0, 1.0f, 0.0f, 0.0f), Vec4f(0.0, 0.0f, 1.0f, 0.0f), + }; + + AllocatedBuffer cb( + allocator, vkh::BufferCreateInfo(sizeof(cbufferdata), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + cb.upload(cbufferdata); + + VkDescriptorSet descset = allocateDescriptorSet(setlayout); + + vkh::updateDescriptorSets( + device, { + vkh::WriteDescriptorSet(descset, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer)}), + }); + + AllocatedBuffer vb( + allocator, vkh::BufferCreateInfo(sizeof(DefaultTri), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + vb.upload(DefaultTri); + + while(Running()) + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + VkImage swapimg = + StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkCmdClearColorImage(cmd, swapimg, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0.4f, 0.5f, 0.6f, 1.0f), 1, + vkh::ImageSubresourceRange()); + + vkCmdBeginRenderPass( + cmd, vkh::RenderPassBeginInfo(mainWindow->rp, mainWindow->GetFB(), mainWindow->scissor), + VK_SUBPASS_CONTENTS_INLINE); + + VkViewport v = mainWindow->viewport; + v.width /= ARRAY_COUNT(pipe); + + vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor); + vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0}); + + for(size_t i = 0; i < ARRAY_COUNT(pipe); i++) + { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe[i]); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, 1, &descset, 0, + NULL); + vkCmdSetViewport(cmd, 0, 1, &v); + vkCmdDraw(cmd, 3, 1, 0, 0); + + v.x += v.width; + } + + vkCmdEndRenderPass(cmd); + + FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkEndCommandBuffer(cmd); + + Submit(0, 1, {cmd}); + + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/tests/D3D11/D3D11_CBuffer_Zoo.py b/util/test/tests/D3D11/D3D11_CBuffer_Zoo.py index 95e3f545d..d0a2f0491 100644 --- a/util/test/tests/D3D11/D3D11_CBuffer_Zoo.py +++ b/util/test/tests/D3D11/D3D11_CBuffer_Zoo.py @@ -23,7 +23,8 @@ class D3D11_CBuffer_Zoo(rdtest.TestCase): cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0) var_check = rdtest.ConstantBufferChecker( - self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0, cbuf.resourceId, cbuf.byteOffset)) diff --git a/util/test/tests/D3D12/D3D12_CBuffer_Zoo.py b/util/test/tests/D3D12/D3D12_CBuffer_Zoo.py index b391cfff1..17b6a7c07 100644 --- a/util/test/tests/D3D12/D3D12_CBuffer_Zoo.py +++ b/util/test/tests/D3D12/D3D12_CBuffer_Zoo.py @@ -23,7 +23,8 @@ class D3D12_CBuffer_Zoo(rdtest.TestCase): cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0) var_check = rdtest.ConstantBufferChecker( - self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0, cbuf.resourceId, cbuf.byteOffset)) @@ -328,7 +329,8 @@ class D3D12_CBuffer_Zoo(rdtest.TestCase): cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 1, 0) var_check = rdtest.ConstantBufferChecker( - self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 1, cbuf.resourceId, cbuf.byteOffset)) diff --git a/util/test/tests/GL/GL_CBuffer_Zoo.py b/util/test/tests/GL/GL_CBuffer_Zoo.py index e83707479..b0ed803f5 100644 --- a/util/test/tests/GL/GL_CBuffer_Zoo.py +++ b/util/test/tests/GL/GL_CBuffer_Zoo.py @@ -23,7 +23,8 @@ class GL_CBuffer_Zoo(rdtest.TestCase): cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0) var_check = rdtest.ConstantBufferChecker( - self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0, cbuf.resourceId, cbuf.byteOffset)) @@ -378,7 +379,8 @@ class GL_CBuffer_Zoo(rdtest.TestCase): cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 1, 0) var_check = rdtest.ConstantBufferChecker( - self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 1, cbuf.resourceId, cbuf.byteOffset)) diff --git a/util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py b/util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py index d9857ff2e..d971627cf 100644 --- a/util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py +++ b/util/test/tests/Vulkan/VK_Adv_CBuffer_Zoo.py @@ -24,7 +24,8 @@ class VK_Adv_CBuffer_Zoo(rdtest.TestCase): cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0) var_check = rdtest.ConstantBufferChecker( - self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0, cbuf.resourceId, cbuf.byteOffset)) diff --git a/util/test/tests/Vulkan/VK_CBuffer_Zoo.py b/util/test/tests/Vulkan/VK_CBuffer_Zoo.py index b94589943..d56bda7a1 100644 --- a/util/test/tests/Vulkan/VK_CBuffer_Zoo.py +++ b/util/test/tests/Vulkan/VK_CBuffer_Zoo.py @@ -30,7 +30,8 @@ class VK_CBuffer_Zoo(rdtest.TestCase): cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0) var_check = rdtest.ConstantBufferChecker( - self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0, cbuf.resourceId, cbuf.byteOffset)) @@ -321,7 +322,8 @@ class VK_CBuffer_Zoo(rdtest.TestCase): cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 1, 0) var_check = rdtest.ConstantBufferChecker( - self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 1, cbuf.resourceId, cbuf.byteOffset)) @@ -359,7 +361,8 @@ class VK_CBuffer_Zoo(rdtest.TestCase): cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0) var_check = rdtest.ConstantBufferChecker( - self.controller.GetCBufferVariableContents(pipe.GetShader(stage), + self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0, cbuf.resourceId, cbuf.byteOffset)) diff --git a/util/test/tests/Vulkan/VK_Spec_Constants.py b/util/test/tests/Vulkan/VK_Spec_Constants.py new file mode 100644 index 000000000..ed3d8ad92 --- /dev/null +++ b/util/test/tests/Vulkan/VK_Spec_Constants.py @@ -0,0 +1,96 @@ +import renderdoc as rd +import rdtest + + +class VK_Spec_Constants(rdtest.TestCase): + demos_test_name = 'VK_Spec_Constants' + + def check_capture(self): + # find the first draw + draw = self.find_draw("Draw") + + # Make an output so we can pick pixels + out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture) + + # We should have 4 draws, with spec constant values 0, 1, 2, 3 + for num_colors in range(4): + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, False) + + pipe: rd.PipeState = self.controller.GetPipelineState() + + shader: rd.ShaderReflection = pipe.GetShaderReflection(rd.ShaderStage.Pixel) + + # uniform buffer and spec constants + self.check(len(shader.constantBlocks) == 2) + self.check(shader.constantBlocks[0].bufferBacked) + self.check(not shader.constantBlocks[1].bufferBacked) + self.check(len(shader.constantBlocks[1].variables) == 1) + + # should be an array of num_colors+1 elements + array_len = shader.constantBlocks[0].variables[0].type.descriptor.elements + if not rdtest.value_compare(array_len, num_colors+1): + raise rdtest.TestFailureException("CBuffer variable is array of {}, not {}".format(array_len, num_colors+1)) + + if num_colors > 0: + cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(rd.ShaderStage.Pixel, 0, 0) + + cb_vars = self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(rd.ShaderStage.Pixel), + pipe.GetShaderEntryPoint(rd.ShaderStage.Pixel), 0, + cbuf.resourceId, cbuf.byteOffset) + + self.check(len(cb_vars) == 1) + + if not rdtest.value_compare(len(cb_vars[0].members), num_colors+1): + raise rdtest.TestFailureException("CBuffer variable is array of {}, not {}".format(len(cb_vars[0].members), num_colors+1)) + + for col in range(num_colors): + expected = [0.0, 0.0, 0.0, 0.0] + expected[col] = 1.0 + + val = [i for i in cb_vars[0].members[col].value.fv[0:4]] + + if not rdtest.value_compare(val, expected): + raise rdtest.TestFailureException("Cbuffer[{}] value {} doesn't match expectation {}".format(col, val, expected)) + + rdtest.log.success("Draw with {} colors uniform buffer is as expected".format(num_colors)) + + cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(rd.ShaderStage.Pixel, 1, 0) + + cb_vars = self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(), + pipe.GetShader(rd.ShaderStage.Pixel), + pipe.GetShaderEntryPoint(rd.ShaderStage.Pixel), 1, + cbuf.resourceId, cbuf.byteOffset) + + self.check(len(cb_vars) == 1) + + if not rdtest.value_compare(cb_vars[0].value.i.x, num_colors): + raise rdtest.TestFailureException("Spec constant is {}, not {}".format(cb_vars[0].value.i.x, num_colors)) + + rdtest.log.success("Draw with {} colors specialisation constant is as expected".format(num_colors)) + + tex = rd.TextureDisplay() + tex.resourceId = pipe.GetOutputTargets()[0].resourceId + out.SetTextureDisplay(tex) + + view = pipe.GetViewport(0) + + # Sample the centre of the viewport + picked: rd.PixelValue = out.PickPixel(tex.resourceId, False, + int(view.x) + int(view.width / 2), int(view.height / 2), 0, 0, 0) + + # the first num_colors components should be 0.6, the rest should be 0.1 (alpha is always 1.0) + expected = [0.0, 0.0, 0.0, 1.0] + for col in range(num_colors): + expected[col] += 1.0 + + if not rdtest.value_compare(picked.floatValue, expected): + raise rdtest.TestFailureException("Picked value {} doesn't match expectation {}".format(picked.floatValue, expected)) + + rdtest.log.success("Draw with {} colors picked value is as expected".format(num_colors)) + + draw = draw.next + + out.Shutdown()