From db22eb64fbf212ab2eecf44aec5a05ab6a7c62d0 Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 21 Dec 2015 23:34:24 +0100 Subject: [PATCH] Implement quad overdraw display modes (not quite 100% yet) --- ReleaseNotes.md | 1 - renderdoc/data/embedded_files.h | 2 + renderdoc/data/renderdoc.rc | 2 + renderdoc/data/resource.h | 2 + renderdoc/data/spv/quadresolve.frag | 64 +++ renderdoc/data/spv/quadwrite.frag | 64 +++ renderdoc/driver/vulkan/vk_core.h | 46 +- renderdoc/driver/vulkan/vk_debug.cpp | 527 +++++++++++++++++- renderdoc/driver/vulkan/vk_debug.h | 12 +- renderdoc/driver/vulkan/vk_replay.cpp | 9 +- renderdoc/driver/vulkan/vk_state.cpp | 61 +- renderdoc/driver/vulkan/vk_state.h | 1 + .../driver/vulkan/wrappers/vk_draw_funcs.cpp | 28 +- renderdoc/renderdoc.vcxproj | 2 + renderdoc/renderdoc.vcxproj.filters | 6 + 15 files changed, 769 insertions(+), 58 deletions(-) create mode 100644 renderdoc/data/spv/quadresolve.frag create mode 100644 renderdoc/data/spv/quadwrite.frag diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 42a1a6b39..ceeadf9bd 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -31,7 +31,6 @@ Known Issues * Memory/image barriers are as yet unverified, potentially could lead to bad capture or replay. * Sparse images with mips or array slices will not properly replay -* 'Quad overdraw' debug overlays aren't implemented. Future work, post 1.0 ======== diff --git a/renderdoc/data/embedded_files.h b/renderdoc/data/embedded_files.h index 79140a5ae..51a17d367 100644 --- a/renderdoc/data/embedded_files.h +++ b/renderdoc/data/embedded_files.h @@ -62,5 +62,7 @@ DECLARE_EMBED(spv_histogram_comp); DECLARE_EMBED(spv_outline_frag); DECLARE_EMBED(spv_debuguniforms_h); DECLARE_EMBED(spv_texsample_h); +DECLARE_EMBED(spv_quadresolve_frag); +DECLARE_EMBED(spv_quadwrite_frag); #undef DECLARE_EMBED diff --git a/renderdoc/data/renderdoc.rc b/renderdoc/data/renderdoc.rc index 7c3e7d04d..45ae930d0 100644 --- a/renderdoc/data/renderdoc.rc +++ b/renderdoc/data/renderdoc.rc @@ -146,6 +146,8 @@ RESOURCE_spv_histogram_comp TYPE_EMBED "spv/histogram.comp" RESOURCE_spv_outline_frag TYPE_EMBED "spv/outline.frag" RESOURCE_spv_debuguniforms_h TYPE_EMBED "spv/debuguniforms.h" RESOURCE_spv_texsample_h TYPE_EMBED "spv/texsample.h" +RESOURCE_spv_quadresolve_frag TYPE_EMBED "spv/quadresolve.frag" +RESOURCE_spv_quadwrite_frag TYPE_EMBED "spv/quadwrite.frag" #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// diff --git a/renderdoc/data/resource.h b/renderdoc/data/resource.h index d291ff158..ca9110fde 100644 --- a/renderdoc/data/resource.h +++ b/renderdoc/data/resource.h @@ -48,6 +48,8 @@ #define RESOURCE_spv_outline_frag 415 #define RESOURCE_spv_debuguniforms_h 416 #define RESOURCE_spv_texsample_h 417 +#define RESOURCE_spv_quadresolve_frag 418 +#define RESOURCE_spv_quadwrite_frag 419 #if !defined(STRINGIZE) #define STRINGIZE2(a) #a diff --git a/renderdoc/data/spv/quadresolve.frag b/renderdoc/data/spv/quadresolve.frag new file mode 100644 index 000000000..91c9e1575 --- /dev/null +++ b/renderdoc/data/spv/quadresolve.frag @@ -0,0 +1,64 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2015 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. + ******************************************************************************/ + +//////////////////////////////////////////////////////////////////////////////////////////// +// Below shaders courtesy of Stephen Hill (@self_shadow), converted to glsl trivially +// +// http://blog.selfshadow.com/2012/11/12/counting-quads/ +// https://github.com/selfshadow/demos/blob/master/QuadShading/QuadShading.fx +//////////////////////////////////////////////////////////////////////////////////////////// + +layout(binding = 0, r32ui) uniform coherent uimage2DArray overdrawImage; + +#define NUM_RAMP_COLOURS 128 + +layout(binding = 1) uniform OverdrawRampColors +{ + vec4 colors[NUM_RAMP_COLOURS]; +} ramp; + +vec4 ToColour(uint v) +{ + return ramp.colors[min(v, uint(NUM_RAMP_COLOURS-1))]; +} + +layout (location = 0) out vec4 color_out; + +void main() +{ + ivec2 quad = ivec2(gl_FragCoord.xy*0.5f); + + uint overdraw = 0; + for(uint i = 0; i < 4; i++) + overdraw += imageLoad(overdrawImage, ivec3(quad, i)).x/(i + 1); + + color_out = ToColour(overdraw); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Above shaders courtesy of Stephen Hill (@self_shadow), converted to glsl trivially +// +// http://blog.selfshadow.com/2012/11/12/counting-quads/ +// https://github.com/selfshadow/demos/blob/master/QuadShading/QuadShading.fx +//////////////////////////////////////////////////////////////////////////////////////////// diff --git a/renderdoc/data/spv/quadwrite.frag b/renderdoc/data/spv/quadwrite.frag new file mode 100644 index 000000000..0f1a825f4 --- /dev/null +++ b/renderdoc/data/spv/quadwrite.frag @@ -0,0 +1,64 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2015 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. + ******************************************************************************/ + +// outer code will hoist this up to just after the #version +//#extension GL_ARB_derivative_control : require + +//////////////////////////////////////////////////////////////////////////////////////////// +// Below shaders courtesy of Stephen Hill (@self_shadow), converted to glsl trivially +// +// http://blog.selfshadow.com/2012/11/12/counting-quads/ +// https://github.com/selfshadow/demos/blob/master/QuadShading/QuadShading.fx +//////////////////////////////////////////////////////////////////////////////////////////// + +// descriptor set will be patched from 0 to whichever descriptor set we're using in code +layout(set = 0, binding = 0, r32ui) uniform coherent uimage2DArray overdrawImage; +layout(early_fragment_tests) in; + +void main() +{ + uint c0 = uint(gl_SampleMaskIn[0]); + + // Obtain coverage for all pixels in the quad, via 'message passing'*. + // (* For more details, see: + // "Shader Amortization using Pixel Quad Message Passing", Eric Penner, GPU Pro 2.) + uvec2 p = uvec2(uint(gl_FragCoord.x) & 1u, uint(gl_FragCoord.y) & 1u); + ivec2 sign = ivec2(p.x > 0u ? -1 : 1, p.y > 0u ? -1 : 1); + uint c1 = c0 + sign.x*int(dFdxFine(c0)); + uint c2 = c0 + sign.y*int(dFdyFine(c0)); + uint c3 = c2 + sign.x*int(dFdxFine(c2)); + + // Count the live pixels, minus 1 (zero indexing) + uint pixelCount = c0 + c1 + c2 + c3 - 1; + + ivec3 quad = ivec3(gl_FragCoord.xy*0.5, pixelCount); + imageAtomicAdd(overdrawImage, quad, 1); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Above shaders courtesy of Stephen Hill (@self_shadow), converted to glsl trivially +// +// http://blog.selfshadow.com/2012/11/12/counting-quads/ +// https://github.com/selfshadow/demos/blob/master/QuadShading/QuadShading.fx +//////////////////////////////////////////////////////////////////////////////////////////// diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 48e0b1cd8..c24568588 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -98,8 +98,24 @@ struct DrawcallTreeNode struct DrawcallCallback { - virtual void PreDraw(uint32_t eid) = 0; - virtual void PostDraw(uint32_t eid) = 0; + // the three callbacks are used to allow the callback implementor to either + // do a modified draw before or after the real thing. + // + // PreDraw() + // do draw call as specified by the log + // PostDraw() + // if PostDraw() returns true: + // do draw call again + // PostRedraw() + // + // So either the modification happens in PreDraw, the modified draw happens, + // then in PostDraw() the implementation can elect to undo the modifications + // and do the real draw by returning true. OR they can do nothing in PreDraw, + // do the real draw, then in PostDraw return true to apply the modifications + // which are then undone in PostRedraw. + virtual void PreDraw(uint32_t eid, VkCommandBuffer cmd) = 0; + virtual bool PostDraw(uint32_t eid, VkCommandBuffer cmd) = 0; + virtual void PostRedraw(uint32_t eid, VkCommandBuffer cmd) = 0; }; class WrappedVulkan : public IFrameCapturer @@ -241,17 +257,6 @@ private: vector m_CleanupMems; vector m_CleanupEvents; - // return the pre-selected device and queue - VkDevice GetDev() { RDCASSERT(m_Device != VK_NULL_HANDLE); return m_Device; } - VkQueue GetQ() { RDCASSERT(m_Device != VK_NULL_HANDLE); return m_Queue; } - VkInstance GetInstance() { RDCASSERT(m_Instance != VK_NULL_HANDLE); return m_Instance; } - VkPhysicalDevice GetPhysDev() { RDCASSERT(m_PhysicalDevice != VK_NULL_HANDLE); return m_PhysicalDevice; } - VkCommandBuffer GetNextCmd(); - void SubmitCmds(); - VkSemaphore GetNextSemaphore(); - void SubmitSemaphores(); - void FlushQ(); - const VkPhysicalDeviceFeatures &GetDeviceFeatures() { return m_PhysicalDeviceData.features; } const VkPhysicalDeviceProperties &GetDeviceProps() @@ -483,6 +488,8 @@ public: VulkanResourceManager *GetResourceManager() { return m_ResourceManager; } VulkanDebugManager *GetDebugManager() { return m_DebugManager; } + + LogState GetState() { return m_State; } VulkanReplay *GetReplay() { return &m_Replay; } @@ -503,6 +510,19 @@ public: FetchAPIEvent GetEvent(uint32_t eventID); const FetchDrawcall *GetDrawcall(uint32_t frameID, uint32_t eventID); + // return the pre-selected device and queue + VkDevice GetDev() { RDCASSERT(m_Device != VK_NULL_HANDLE); return m_Device; } + VkQueue GetQ() { RDCASSERT(m_Device != VK_NULL_HANDLE); return m_Queue; } + VkInstance GetInstance() { RDCASSERT(m_Instance != VK_NULL_HANDLE); return m_Instance; } + VkPhysicalDevice GetPhysDev() { RDCASSERT(m_PhysicalDevice != VK_NULL_HANDLE); return m_PhysicalDevice; } + VkCommandBuffer GetNextCmd(); + void SubmitCmds(); + VkSemaphore GetNextSemaphore(); + void SubmitSemaphores(); + void FlushQ(); + + VulkanRenderState &GetRenderState() { return m_RenderState; } + void SetDrawcallCB(DrawcallCallback *cb) { m_DrawcallCallback = cb; } // Device initialization diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index bba4c631f..fb5565b2f 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -240,6 +240,7 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) { // VKTODOLOW needs tidy up - isn't scalable. Needs more classes like UBO above. m_pDriver = driver; + m_State = m_pDriver->GetState(); m_ResourceManager = m_pDriver->GetResourceManager(); @@ -281,6 +282,11 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) m_OverlayNoDepthRP = VK_NULL_HANDLE; RDCEraseEl(m_OverlayDim); m_OverlayMemSize = 0; + + m_QuadDescSetLayout = VK_NULL_HANDLE; + m_QuadResolvePipeLayout = VK_NULL_HANDLE; + m_QuadDescSet = VK_NULL_HANDLE; + m_QuadResolvePipeline = VK_NULL_HANDLE; m_MeshDescSetLayout = VK_NULL_HANDLE; m_MeshPipeLayout = VK_NULL_HANDLE; @@ -299,6 +305,9 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) m_OutlineDescSet = VK_NULL_HANDLE; m_OutlinePipeline = VK_NULL_HANDLE; + m_MeshFetchDescSetLayout = VK_NULL_HANDLE; + m_MeshFetchDescSet = VK_NULL_HANDLE; + m_Device = dev; bool success = LoadShaderCache("vkshaders.cache", m_ShaderCacheMagic, m_ShaderCacheVersion, m_ShaderCache, ShaderCacheCallbacks); @@ -367,6 +376,7 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetResourceManager()->WrapResource(Unwrap(dev), m_OutlineDescSetLayout); } + if(m_State < WRITING) { VkDescriptorSetLayoutBinding layoutBinding[] = { { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL, } @@ -432,6 +442,24 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetResourceManager()->WrapResource(Unwrap(dev), m_TextDescSetLayout); } + + if(m_State < WRITING) + { + VkDescriptorSetLayoutBinding layoutBinding[] = { + { 0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_ALL, NULL, }, + { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL, }, + }; + + VkDescriptorSetLayoutCreateInfo descsetLayoutInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, NULL, 0, + ARRAY_COUNT(layoutBinding), &layoutBinding[0], + }; + + // because this will go through partial replay, needs proper creation info etc so we go through our wrapped + // function + vkr = m_pDriver->vkCreateDescriptorSetLayout(dev, &descsetLayoutInfo, NULL, &m_QuadDescSetLayout); + RDCASSERT(vkr == VK_SUCCESS); + } { VkDescriptorSetLayoutBinding layoutBinding[] = { @@ -477,6 +505,14 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetResourceManager()->WrapResource(Unwrap(dev), m_TextPipeLayout); + pipeLayoutInfo.pSetLayouts = &m_QuadDescSetLayout; + + if(m_State < WRITING) + { + vkr = m_pDriver->vkCreatePipelineLayout(dev, &pipeLayoutInfo, NULL, &m_QuadResolvePipeLayout); + RDCASSERT(vkr == VK_SUCCESS); + } + pipeLayoutInfo.pSetLayouts = UnwrapPtr(m_OutlineDescSetLayout); vkr = vt->CreatePipelineLayout(Unwrap(dev), &pipeLayoutInfo, NULL, &m_OutlinePipeLayout); @@ -507,7 +543,7 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) VkDescriptorPoolCreateInfo descpoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, NULL, - 0, 8+ARRAY_COUNT(m_TexDisplayDescSet), + 0, 9+ARRAY_COUNT(m_TexDisplayDescSet), ARRAY_COUNT(descPoolTypes), &descPoolTypes[0], }; @@ -542,6 +578,15 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetResourceManager()->WrapResource(Unwrap(dev), m_TextDescSet); + if(m_State < WRITING) + { + descAllocInfo.pSetLayouts = UnwrapPtr(m_QuadDescSetLayout); + vkr = vt->AllocateDescriptorSets(Unwrap(dev), &descAllocInfo, &m_QuadDescSet); + RDCASSERT(vkr == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(dev), m_QuadDescSet); + } + descAllocInfo.pSetLayouts = UnwrapPtr(m_OutlineDescSetLayout); vkr = vt->AllocateDescriptorSets(Unwrap(dev), &descAllocInfo, &m_OutlineDescSet); RDCASSERT(vkr == VK_SUCCESS); @@ -565,11 +610,14 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetResourceManager()->WrapResource(Unwrap(dev), m_HistogramDescSet[1]); - descAllocInfo.pSetLayouts = UnwrapPtr(m_MeshFetchDescSetLayout); - vkr = vt->AllocateDescriptorSets(Unwrap(dev), &descAllocInfo, &m_MeshFetchDescSet); - RDCASSERT(vkr == VK_SUCCESS); - - GetResourceManager()->WrapResource(Unwrap(dev), m_MeshFetchDescSet); + if(m_State < WRITING) + { + descAllocInfo.pSetLayouts = UnwrapPtr(m_MeshFetchDescSetLayout); + vkr = vt->AllocateDescriptorSets(Unwrap(dev), &descAllocInfo, &m_MeshFetchDescSet); + RDCASSERT(vkr == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(dev), m_MeshFetchDescSet); + } m_ReadbackWindow.Create(driver, dev, STAGE_BUFFER_BYTE_SIZE, 1, GPUBuffer::eGPUBufferReadback); @@ -600,6 +648,8 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetEmbeddedResource(spv_minmaxresult_comp), GetEmbeddedResource(spv_histogram_comp), GetEmbeddedResource(spv_outline_frag), + GetEmbeddedResource(spv_quadresolve_frag), + GetEmbeddedResource(spv_quadwrite_frag), }; SPIRVShaderStage shaderStages[] = { @@ -615,6 +665,8 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) eSPIRVCompute, eSPIRVCompute, eSPIRVFragment, + eSPIRVFragment, + eSPIRVFragment, }; enum shaderIdx @@ -631,6 +683,8 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) MINMAXRESULTCS, HISTOGRAMCS, OUTLINEFS, + QUADRESOLVEFS, + QUADWRITEFS, NUM_SHADERS, }; @@ -656,12 +710,28 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) for(size_t i=0; i < ARRAY_COUNT(module); i++) { + sources[0] = "#version 430 core\n"; sources[2] = ""; sources[3] = shaderSources[i]; if(sources[3].find("#include \"texsample.h\"") != string::npos) sources[2] = GetEmbeddedResource(spv_texsample_h); + // hoist up any #extension directives + size_t extsearch = 0; + do + { + extsearch = sources[3].find("#extension", extsearch); + + if(extsearch == string::npos) + break; + + size_t begin = extsearch; + extsearch = sources[3].find('\n', extsearch); + + sources[0] += sources[3].substr(begin, extsearch-begin+1); + } while(extsearch != string::npos); + string err = GetSPIRVBlob(shaderStages[i], sources, &shaderSPIRV[i]); RDCASSERT(err.empty() && shaderSPIRV[i]); @@ -669,6 +739,9 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, NULL, 0, shaderSPIRV[i]->size()*sizeof(uint32_t), &(*shaderSPIRV[i])[0], }; + + if(i == QUADWRITEFS) + m_QuadSPIRV = shaderSPIRV[i]; vkr = vt->CreateShaderModule(Unwrap(dev), &modinfo, NULL, &module[i]); RDCASSERT(vkr == VK_SUCCESS); @@ -679,6 +752,7 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) m_CacheShaders = false; VkRenderPass RGBA32RP, RGBA8RP, RGBA16RP, RGBA8MSRP; // compatible render passes for creating pipelines + VkRenderPass wrappedRGBA8RP; { VkAttachmentDescription attDesc = { @@ -707,6 +781,9 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) }; vt->CreateRenderPass(Unwrap(dev), &rpinfo, NULL, &RGBA8RP); + + if(m_State < WRITING) + m_pDriver->vkCreateRenderPass(dev, &rpinfo, NULL, &wrappedRGBA8RP); attDesc.format = VK_FORMAT_R32G32B32A32_SFLOAT; @@ -873,6 +950,20 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetResourceManager()->WrapResource(Unwrap(dev), m_OutlinePipeline); + attState.blendEnable = false; + + stages[0].module = module[BLITVS]; + stages[1].module = module[QUADRESOLVEFS]; + + pipeInfo.layout = m_QuadResolvePipeLayout; + pipeInfo.renderPass = wrappedRGBA8RP; + + if(m_State < WRITING) + { + vkr = m_pDriver->vkCreateGraphicsPipelines(dev, VK_NULL_HANDLE, 1, &pipeInfo, NULL, &m_QuadResolvePipeline); + RDCASSERT(vkr == VK_SUCCESS); + } + VkComputePipelineCreateInfo compPipeInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, NULL, 0, { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_COMPUTE_BIT, VK_NULL_HANDLE, "main", NULL }, @@ -905,6 +996,8 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) vt->DestroyRenderPass(Unwrap(dev), RGBA32RP, NULL); vt->DestroyRenderPass(Unwrap(dev), RGBA8RP, NULL); vt->DestroyRenderPass(Unwrap(dev), RGBA8MSRP, NULL); + if(m_State < WRITING) + m_pDriver->vkDestroyRenderPass(dev, wrappedRGBA8RP, NULL); for(size_t i=0; i < ARRAY_COUNT(module); i++) { @@ -1070,6 +1163,13 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) m_TextGlyphUBO.Unmap(vt, dev); } + + m_OverdrawRampUBO.Create(driver, dev, 2048, 1, 0); // no ring needed, fixed data + RDCCOMPILE_ASSERT(sizeof(overdrawRamp) <= 2048, "overdraw ramp uniforms size"); + + void *ramp = m_OverdrawRampUBO.Map(vt, dev, (uint32_t *)NULL); + memcpy(ramp, overdrawRamp, sizeof(overdrawRamp)); + m_OverdrawRampUBO.Unmap(vt, dev); // pick pixel data { @@ -1251,7 +1351,7 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) // don't need to ring this, as we hard-sync for readback anyway m_HistogramUBO.Create(driver, dev, sizeof(HistogramUBOData), 1, 0); - VkDescriptorBufferInfo bufInfo[6]; + VkDescriptorBufferInfo bufInfo[7]; RDCEraseEl(bufInfo); // tex display is updated right before rendering @@ -1262,6 +1362,7 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) m_TextStringUBO.FillDescriptor(bufInfo[3]); m_MeshUBO.FillDescriptor(bufInfo[4]); m_OutlineUBO.FillDescriptor(bufInfo[5]); + m_OverdrawRampUBO.FillDescriptor(bufInfo[6]); VkDescriptorImageInfo atlasImInfo; atlasImInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; @@ -1304,9 +1405,16 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) Unwrap(m_OutlineDescSet), 0, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, NULL, &bufInfo[5], NULL }, + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(m_QuadDescSet), 1, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + NULL, &bufInfo[6], NULL + }, }; - vt->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writeSet), writeSet, 0, NULL); + uint32_t writeCount = ARRAY_COUNT(writeSet); + if(m_State >= WRITING) writeCount--; // don't write to m_QuadDescSet when it's not allocated + vt->UpdateDescriptorSets(Unwrap(dev), writeCount, writeSet, 0, NULL); vt->EndCommandBuffer(Unwrap(cmd)); } @@ -1367,7 +1475,11 @@ VulkanDebugManager::~VulkanDebugManager() GetResourceManager()->ReleaseWrappedResource(m_TextDescSet); GetResourceManager()->ReleaseWrappedResource(m_MeshDescSet); GetResourceManager()->ReleaseWrappedResource(m_OutlineDescSet); - GetResourceManager()->ReleaseWrappedResource(m_MeshFetchDescSet); + if(m_QuadDescSet) + { + GetResourceManager()->ReleaseWrappedResource(m_MeshFetchDescSet); + GetResourceManager()->ReleaseWrappedResource(m_QuadDescSet); + } for(size_t i=0; i < ARRAY_COUNT(m_HistogramDescSet); i++) GetResourceManager()->ReleaseWrappedResource(m_HistogramDescSet[i]); @@ -1597,6 +1709,8 @@ VulkanDebugManager::~VulkanDebugManager() m_HistogramReadback.Destroy(vt, dev); m_HistogramUBO.Destroy(vt, dev); + m_OverdrawRampUBO.Destroy(vt, dev); + // overlay & postvs resources are allocated through driver if(m_MeshFetchDescSetLayout != VK_NULL_HANDLE) m_pDriver->vkDestroyDescriptorSetLayout(dev, m_MeshFetchDescSetLayout, NULL); @@ -1615,6 +1729,15 @@ VulkanDebugManager::~VulkanDebugManager() if(m_OverlayImageMem != VK_NULL_HANDLE) m_pDriver->vkFreeMemory(dev, m_OverlayImageMem, NULL); + + if(m_QuadDescSetLayout != VK_NULL_HANDLE) + m_pDriver->vkDestroyDescriptorSetLayout(dev, m_QuadDescSetLayout, NULL); + + if(m_QuadResolvePipeLayout != VK_NULL_HANDLE) + m_pDriver->vkDestroyPipelineLayout(dev, m_QuadResolvePipeLayout, NULL); + + if(m_QuadResolvePipeline != VK_NULL_HANDLE) + m_pDriver->vkDestroyPipeline(dev, m_QuadResolvePipeline, NULL); } void VulkanDebugManager::BeginText(const TextPrintState &textstate) @@ -2003,6 +2126,203 @@ void VulkanDebugManager::PatchFixedColShader(VkShaderModule &mod, float col[4]) RDCASSERT(vkr == VK_SUCCESS); } +struct QuadOverdrawCallback : public DrawcallCallback +{ + QuadOverdrawCallback(WrappedVulkan *vk, uint32_t frameID, const vector &events) + : m_pDriver(vk) + , m_pDebug(vk->GetDebugManager()) + , m_FrameID(frameID) + , m_Events(events) + , m_PrevState(VulkanCreationInfo()) + { m_pDriver->SetDrawcallCB(this); } + ~QuadOverdrawCallback() + { m_pDriver->SetDrawcallCB(NULL); } + + void PreDraw(uint32_t eid, VkCommandBuffer cmd) + { + if(std::find(m_Events.begin(), m_Events.end(), eid) == m_Events.end()) + return; + + // we customise the pipeline to disable framebuffer writes, but perform normal testing + // and substitute our quad calculation fragment shader that writes to a storage image + // that is bound in a new descriptor set. + + VkResult vkr = VK_SUCCESS; + + m_PrevState = m_pDriver->GetRenderState(); + VulkanRenderState &pipestate = m_pDriver->GetRenderState(); + + // check cache first + VkPipeline pipe = m_PipelineCache[pipestate.graphics.pipeline]; + + // if we don't get a hit, create a modified pipeline + if(pipe == VK_NULL_HANDLE) + { + VulkanCreationInfo &c = pipestate.m_CreationInfo; + + VulkanCreationInfo::Pipeline &p = c.m_Pipeline[pipestate.graphics.pipeline]; + + VkDescriptorSetLayout *descSetLayouts; + + // descSet will be the index of our new descriptor set + uint32_t descSet = (uint32_t)c.m_PipelineLayout[p.layout].descSetLayouts.size(); + + descSetLayouts = new VkDescriptorSetLayout[descSet+1]; + + for(uint32_t i=0; i < descSet; i++) + descSetLayouts[i] = m_pDriver->GetResourceManager()->GetCurrentHandle(c.m_PipelineLayout[p.layout].descSetLayouts[i]); + + // this layout has storage image and + descSetLayouts[descSet] = m_pDebug->m_QuadDescSetLayout; + + const vector &push = c.m_PipelineLayout[p.layout].pushRanges; + + VkPipelineLayoutCreateInfo pipeLayoutInfo = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, NULL, 0, + descSet+1, descSetLayouts, + (uint32_t)push.size(), push.empty() ? NULL : &push[0], + }; + + // create pipeline layout with same descriptor set layouts, plus our mesh output set + VkPipelineLayout pipeLayout; + vkr = m_pDriver->vkCreatePipelineLayout(m_pDriver->GetDev(), &pipeLayoutInfo, NULL, &pipeLayout); + RDCASSERT(vkr == VK_SUCCESS); + + SAFE_DELETE_ARRAY(descSetLayouts); + + VkGraphicsPipelineCreateInfo pipeCreateInfo; + m_pDebug->MakeGraphicsPipelineInfo(pipeCreateInfo, pipestate.graphics.pipeline); + + // repoint pipeline layout + pipeCreateInfo.layout = pipeLayout; + + // disable colour writes/blends + VkPipelineColorBlendStateCreateInfo *cb = (VkPipelineColorBlendStateCreateInfo *)pipeCreateInfo.pColorBlendState; + for(uint32_t i=0; i < cb->attachmentCount; i++) + { + VkPipelineColorBlendAttachmentState *att = (VkPipelineColorBlendAttachmentState *)&cb->pAttachments[i]; + att->blendEnable = false; + att->colorWriteMask = 0x0; + } + + // disable tests and depth/stencil writes + VkPipelineDepthStencilStateCreateInfo *ds = (VkPipelineDepthStencilStateCreateInfo *)pipeCreateInfo.pDepthStencilState; + ds->depthTestEnable = false; + ds->depthWriteEnable = false; + ds->stencilTestEnable = false; + ds->depthBoundsTestEnable = false; + ds->front.passOp = ds->front.failOp = ds->front.depthFailOp = VK_STENCIL_OP_KEEP; + ds->back.passOp = ds->back.failOp = ds->back.depthFailOp = VK_STENCIL_OP_KEEP; + + // don't discard + VkPipelineRasterizationStateCreateInfo *rs = (VkPipelineRasterizationStateCreateInfo *)pipeCreateInfo.pRasterizationState; + rs->rasterizerDiscardEnable = false; + + vector spirv = *m_pDebug->m_QuadSPIRV; + + // patch spirv, change descriptor set to descSet value + size_t it = 5; + while(it < spirv.size()) + { + uint16_t WordCount = spirv[it]>>spv::WordCountShift; + spv::Op opcode = spv::Op(spirv[it]&spv::OpCodeMask); + + if(opcode == spv::OpDecorate && spirv[it+2] == spv::DecorationDescriptorSet) + { + spirv[it+3] = descSet; + break; + } + + it += WordCount; + } + + VkShaderModuleCreateInfo modinfo = { + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, NULL, 0, + spirv.size()*sizeof(uint32_t), &spirv[0], + }; + + VkShaderModule module; + + VkDevice dev = m_pDriver->GetDev(); + + vkr = ObjDisp(dev)->CreateShaderModule(Unwrap(dev), &modinfo, NULL, &module); + RDCASSERT(vkr == VK_SUCCESS); + + m_pDriver->GetResourceManager()->WrapResource(Unwrap(dev), module); + + bool found = false; + for(uint32_t i=0; i < pipeCreateInfo.stageCount; i++) + { + VkPipelineShaderStageCreateInfo &sh = (VkPipelineShaderStageCreateInfo &)pipeCreateInfo.pStages[i]; + if(sh.stage == VK_SHADER_STAGE_FRAGMENT_BIT) + { + sh.module = module; + sh.pName = "main"; + found = true; + break; + } + } + + if(!found) + { + // we know this is safe because it's pointing to a static array that's + // big enough for all shaders + + VkPipelineShaderStageCreateInfo &sh = (VkPipelineShaderStageCreateInfo &)pipeCreateInfo.pStages[pipeCreateInfo.stageCount++]; + sh.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + sh.pNext = NULL; + sh.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + sh.module = module; + sh.pName = "main"; + sh.pSpecializationInfo = NULL; + } + + vkr = m_pDriver->vkCreateGraphicsPipelines(dev, VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL, &pipe); + RDCASSERT(vkr == VK_SUCCESS); + + ObjDisp(dev)->DestroyShaderModule(Unwrap(dev), Unwrap(module), NULL); + m_pDriver->GetResourceManager()->ReleaseWrappedResource(module); + + m_PipelineCache[pipestate.graphics.pipeline] = pipe; + } + + // modify state for first draw call + pipestate.graphics.pipeline = GetResID(pipe); + pipestate.graphics.descSets.push_back(GetResID(m_pDebug->m_QuadDescSet)); + + if(cmd) + pipestate.BindPipeline(cmd); + } + + bool PostDraw(uint32_t eid, VkCommandBuffer cmd) + { + if(std::find(m_Events.begin(), m_Events.end(), eid) == m_Events.end()) + return false; + + // restore the render state and go ahead with the real draw + m_pDriver->GetRenderState() = m_PrevState; + + RDCASSERT(cmd); + m_pDriver->GetRenderState().BindPipeline(cmd); + + return true; + } + + void PostRedraw(uint32_t eid, VkCommandBuffer cmd) + { + // nothing to do + } + + uint32_t m_FrameID; + WrappedVulkan *m_pDriver; + VulkanDebugManager *m_pDebug; + const vector &m_Events; + + // cache modified pipelines + map m_PipelineCache; + VulkanRenderState m_PrevState; +}; + ResourceId VulkanDebugManager::RenderOverlay(ResourceId texid, TextureDisplayOverlay overlay, uint32_t frameID, uint32_t eventID, const vector &passEvents) { const VkLayerDispatchTable *vt = ObjDisp(m_Device); @@ -2788,6 +3108,195 @@ ResourceId VulkanDebugManager::RenderOverlay(ResourceId texid, TextureDisplayOve m_pDriver->vkDestroyFramebuffer(m_Device, depthFB, NULL); } } + else if(overlay == eTexOverlay_QuadOverdrawPass || overlay == eTexOverlay_QuadOverdrawDraw) + { + VulkanRenderState prevstate = m_pDriver->m_RenderState; + + { + SCOPED_TIMER("Quad Overdraw"); + + float black[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_OverlayImage), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, (VkClearColorValue *)black, 1, &subresourceRange); + + vector events = passEvents; + + if(overlay == eTexOverlay_QuadOverdrawDraw) + events.clear(); + + events.push_back(eventID); + + VkImage quadImg; + VkDeviceMemory quadImgMem; + VkImageView quadImgView; + + VkImageCreateInfo imInfo = { + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0, + VK_IMAGE_TYPE_2D, VK_FORMAT_R32_UINT, + { RDCMAX(1, m_OverlayDim.width>>1), RDCMAX(1, m_OverlayDim.height>>1), 1 }, + 1, 4, VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_STORAGE_BIT|VK_IMAGE_USAGE_SAMPLED_BIT, + VK_SHARING_MODE_EXCLUSIVE, 0, NULL, + VK_IMAGE_LAYOUT_UNDEFINED, + }; + + VkResult vkr = m_pDriver->vkCreateImage(m_Device, &imInfo, NULL, &quadImg); + RDCASSERT(vkr == VK_SUCCESS); + + VkMemoryRequirements mrq = {0}; + + m_pDriver->vkGetImageMemoryRequirements(m_Device, quadImg, &mrq); + + VkMemoryAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, + mrq.size, m_pDriver->GetGPULocalMemoryIndex(mrq.memoryTypeBits), + }; + + vkr = m_pDriver->vkAllocateMemory(m_Device, &allocInfo, NULL, &quadImgMem); + RDCASSERT(vkr == VK_SUCCESS); + + vkr = m_pDriver->vkBindImageMemory(m_Device, quadImg, quadImgMem, 0); + RDCASSERT(vkr == VK_SUCCESS); + + VkImageViewCreateInfo viewinfo = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, + 0, quadImg, VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_FORMAT_R32_UINT, + { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 4 }, + }; + + vkr = m_pDriver->vkCreateImageView(m_Device, &viewinfo, NULL, &quadImgView); + RDCASSERT(vkr == VK_SUCCESS); + + // update descriptor to point to our R32 result image + VkDescriptorImageInfo imdesc = { 0 }; + imdesc.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + imdesc.sampler = VK_NULL_HANDLE; + imdesc.imageView = Unwrap(quadImgView); + + VkWriteDescriptorSet write = { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(m_QuadDescSet), 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + &imdesc, NULL, NULL + }; + vt->UpdateDescriptorSets(Unwrap(m_Device), 1, &write, 0, NULL); + + VkImageMemoryBarrier quadImBarrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + Unwrap(quadImg), + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 4 } + }; + + void *barrier = &quadImBarrier; + + // clear all to black + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + vt->CmdClearColorImage(Unwrap(cmd), Unwrap(quadImg), VK_IMAGE_LAYOUT_GENERAL, (VkClearColorValue *)&black, 1, &quadImBarrier.subresourceRange); + + quadImBarrier.srcAccessMask = quadImBarrier.dstAccessMask; + quadImBarrier.oldLayout = quadImBarrier.newLayout; + + quadImBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + + // set to general layout, for load/store operations + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + // end this cmd buffer so the image is in the right state for the next part + vkr = vt->EndCommandBuffer(Unwrap(cmd)); + RDCASSERT(vkr == VK_SUCCESS); + + if(overlay == eTexOverlay_QuadOverdrawPass) + m_pDriver->ReplayLog(frameID, 0, events[0], eReplay_WithoutDraw); + + // declare callback struct here + QuadOverdrawCallback cb(m_pDriver, frameID, events); + + if(overlay == eTexOverlay_QuadOverdrawPass) + { + m_pDriver->ReplayLog(frameID, events.front(), events.back(), eReplay_Full); + } + else + { + // don't have the driver call the callback + m_pDriver->SetDrawcallCB(NULL); + + // call PreDraw to set up per-draw changed state without binding anything + cb.PreDraw(events.back(), NULL); + + // do the single drawcall, which will bind the pipeline above + m_pDriver->ReplayLog(frameID, events.front(), events.back(), eReplay_OnlyDraw); + } + + // resolve pass + { + cmd = m_pDriver->GetNextCmd(); + + vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERT(vkr == VK_SUCCESS); + + quadImBarrier.srcAccessMask = quadImBarrier.dstAccessMask; + quadImBarrier.oldLayout = quadImBarrier.newLayout; + + quadImBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + // wait for writing to finish + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + VkClearValue clearval = {0}; + VkRenderPassBeginInfo rpbegin = { + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, NULL, + Unwrap(m_OverlayNoDepthRP), Unwrap(m_OverlayNoDepthFB), + m_pDriver->m_RenderState.renderArea, + 1, &clearval, + }; + vt->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE); + + vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_QuadResolvePipeline)); + vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_QuadResolvePipeLayout), 0, 1, UnwrapPtr(m_QuadDescSet), 0, NULL); + + VkViewport viewport = { + 0.0f, 0.0f, + (float)m_OverlayDim.width, + (float)m_OverlayDim.height, + 0.0f, 1.0f + }; + vt->CmdSetViewport(Unwrap(cmd), 1, &viewport); + + vt->CmdDraw(Unwrap(cmd), 4, 1, 0, 0); + vt->CmdEndRenderPass(Unwrap(cmd)); + + vkr = vt->EndCommandBuffer(Unwrap(cmd)); + RDCASSERT(vkr == VK_SUCCESS); + } + + m_pDriver->SubmitCmds(); + m_pDriver->FlushQ(); + + m_pDriver->vkDestroyImageView(m_Device, quadImgView, NULL); + m_pDriver->vkDestroyImage(m_Device, quadImg, NULL); + m_pDriver->vkFreeMemory(m_Device, quadImgMem, NULL); + + for(auto it=cb.m_PipelineCache.begin(); it != cb.m_PipelineCache.end(); ++it) + { + m_pDriver->vkDestroyPipeline(m_Device, it->second, NULL); + } + } + + // restore back to normal + if(overlay == eTexOverlay_QuadOverdrawPass) + m_pDriver->ReplayLog(frameID, 0, eventID, eReplay_WithoutDraw); + else + m_pDriver->m_RenderState = prevstate; + + cmd = m_pDriver->GetNextCmd(); + + vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERT(vkr == VK_SUCCESS); + } vkr = vt->EndCommandBuffer(Unwrap(cmd)); RDCASSERT(vkr == VK_SUCCESS); diff --git a/renderdoc/driver/vulkan/vk_debug.h b/renderdoc/driver/vulkan/vk_debug.h index efcb78dbe..58a9c95e3 100644 --- a/renderdoc/driver/vulkan/vk_debug.h +++ b/renderdoc/driver/vulkan/vk_debug.h @@ -197,6 +197,13 @@ class VulkanDebugManager VkRenderPass m_OverlayNoDepthRP; VkExtent2D m_OverlayDim; VkDeviceSize m_OverlayMemSize; + + GPUBuffer m_OverdrawRampUBO; + VkDescriptorSetLayout m_QuadDescSetLayout; + VkDescriptorSet m_QuadDescSet; + VkPipelineLayout m_QuadResolvePipeLayout; + VkPipeline m_QuadResolvePipeline; + vector *m_QuadSPIRV; VkDescriptorSetLayout m_MeshDescSetLayout; VkPipelineLayout m_MeshPipeLayout; @@ -227,7 +234,7 @@ class VulkanDebugManager VkDescriptorSet m_MeshFetchDescSet; MeshDisplayPipelines CacheMeshDisplayPipelines(const MeshFormat &primary, const MeshFormat &secondary); - + void MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &pipeCreateInfo, ResourceId pipeline); private: void InitDebugData(); void ShutdownDebugData(); @@ -245,10 +252,11 @@ class VulkanDebugManager void PatchFixedColShader(VkShaderModule &mod, float col[4]); void RenderTextInternal(const TextPrintState &textstate, float x, float y, const char *text); - void MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &pipeCreateInfo, ResourceId pipeline); static const int FONT_TEX_WIDTH = 256; static const int FONT_TEX_HEIGHT = 128; + LogState m_State; + float m_FontCharAspect; float m_FontCharSize; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index a7e5f8091..4857f9364 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -3787,13 +3787,18 @@ struct InitPostVSCallback : public DrawcallCallback ~InitPostVSCallback() { m_pDriver->SetDrawcallCB(NULL); } - void PreDraw(uint32_t eid) + void PreDraw(uint32_t eid, VkCommandBuffer cmd) { if(std::find(m_Events.begin(), m_Events.end(), eid) != m_Events.end()) m_pReplay->InitPostVSBuffers(m_FrameID, eid); } - void PostDraw(uint32_t eid) + bool PostDraw(uint32_t eid, VkCommandBuffer cmd) + { + return false; + } + + void PostRedraw(uint32_t eid, VkCommandBuffer cmd) { } diff --git a/renderdoc/driver/vulkan/vk_state.cpp b/renderdoc/driver/vulkan/vk_state.cpp index 816f5dab2..9b2e8ece0 100644 --- a/renderdoc/driver/vulkan/vk_state.cpp +++ b/renderdoc/driver/vulkan/vk_state.cpp @@ -109,6 +109,35 @@ void VulkanRenderState::BeginRenderPassAndApplyState(VkCommandBuffer cmd) for(uint32_t i=0; i < subpass; i++) ObjDisp(cmd)->CmdNextSubpass(Unwrap(cmd), VK_SUBPASS_CONTENTS_INLINE); + BindPipeline(cmd); + + if(!views.empty()) + ObjDisp(cmd)->CmdSetViewport(Unwrap(cmd), (uint32_t)views.size(), &views[0]); + if(!scissors.empty()) + ObjDisp(cmd)->CmdSetScissor(Unwrap(cmd), (uint32_t)scissors.size(), &scissors[0]); + + ObjDisp(cmd)->CmdSetBlendConstants(Unwrap(cmd), blendConst); + ObjDisp(cmd)->CmdSetDepthBounds(Unwrap(cmd), mindepth, maxdepth); + ObjDisp(cmd)->CmdSetLineWidth(Unwrap(cmd), lineWidth); + ObjDisp(cmd)->CmdSetDepthBias(Unwrap(cmd), bias.depth, bias.biasclamp, bias.slope); + + ObjDisp(cmd)->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, back.ref); + ObjDisp(cmd)->CmdSetStencilCompareMask(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, back.compare); + ObjDisp(cmd)->CmdSetStencilWriteMask(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, back.write); + + ObjDisp(cmd)->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, front.ref); + ObjDisp(cmd)->CmdSetStencilCompareMask(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, front.compare); + ObjDisp(cmd)->CmdSetStencilWriteMask(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, front.write); + + if(ibuffer.buf != ResourceId()) + ObjDisp(cmd)->CmdBindIndexBuffer(Unwrap(cmd), Unwrap(GetResourceManager()->GetCurrentHandle(ibuffer.buf)), ibuffer.offs, ibuffer.bytewidth == 4 ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16); + + for(size_t i=0; i < vbuffers.size(); i++) + ObjDisp(cmd)->CmdBindVertexBuffers(Unwrap(cmd), (uint32_t)i, 1, UnwrapPtr(GetResourceManager()->GetCurrentHandle(vbuffers[i].buf)), &vbuffers[i].offs); +} + +void VulkanRenderState::BindPipeline(VkCommandBuffer cmd) +{ if(graphics.pipeline != ResourceId()) { ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(GetResourceManager()->GetCurrentHandle(graphics.pipeline))); @@ -150,6 +179,12 @@ void VulkanRenderState::BeginRenderPassAndApplyState(VkCommandBuffer cmd) ResourceId pipeLayoutId = m_CreationInfo.m_Pipeline[compute.pipeline].layout; VkPipelineLayout layout = GetResourceManager()->GetCurrentHandle(pipeLayoutId); + const vector &pushRanges = m_CreationInfo.m_PipelineLayout[pipeLayoutId].pushRanges; + + // only set push constant ranges that the layout uses + for(size_t i=0; i < pushRanges.size(); i++) + ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(layout), pushRanges[i].stageFlags, pushRanges[i].offset, pushRanges[i].size, pushconsts+pushRanges[i].offset); + const vector &descSetLayouts = m_CreationInfo.m_PipelineLayout[pipeLayoutId].descSetLayouts; for(size_t i=0; i < descSetLayouts.size(); i++) @@ -158,34 +193,10 @@ void VulkanRenderState::BeginRenderPassAndApplyState(VkCommandBuffer cmd) if(compute.descSets[i] != ResourceId()) { - ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(layout), (uint32_t)i, + ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(layout), (uint32_t)i, 1, UnwrapPtr(GetResourceManager()->GetCurrentHandle(compute.descSets[i])), descLayout.dynamicCount, descLayout.dynamicCount == 0 ? NULL : &compute.offsets[i][0]); } } } - - if(!views.empty()) - ObjDisp(cmd)->CmdSetViewport(Unwrap(cmd), (uint32_t)views.size(), &views[0]); - if(!scissors.empty()) - ObjDisp(cmd)->CmdSetScissor(Unwrap(cmd), (uint32_t)scissors.size(), &scissors[0]); - - ObjDisp(cmd)->CmdSetBlendConstants(Unwrap(cmd), blendConst); - ObjDisp(cmd)->CmdSetDepthBounds(Unwrap(cmd), mindepth, maxdepth); - ObjDisp(cmd)->CmdSetLineWidth(Unwrap(cmd), lineWidth); - ObjDisp(cmd)->CmdSetDepthBias(Unwrap(cmd), bias.depth, bias.biasclamp, bias.slope); - - ObjDisp(cmd)->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, back.ref); - ObjDisp(cmd)->CmdSetStencilCompareMask(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, back.compare); - ObjDisp(cmd)->CmdSetStencilWriteMask(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, back.write); - - ObjDisp(cmd)->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, front.ref); - ObjDisp(cmd)->CmdSetStencilCompareMask(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, front.compare); - ObjDisp(cmd)->CmdSetStencilWriteMask(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, front.write); - - if(ibuffer.buf != ResourceId()) - ObjDisp(cmd)->CmdBindIndexBuffer(Unwrap(cmd), Unwrap(GetResourceManager()->GetCurrentHandle(ibuffer.buf)), ibuffer.offs, ibuffer.bytewidth == 4 ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16); - - for(size_t i=0; i < vbuffers.size(); i++) - ObjDisp(cmd)->CmdBindVertexBuffers(Unwrap(cmd), (uint32_t)i, 1, UnwrapPtr(GetResourceManager()->GetCurrentHandle(vbuffers[i].buf)), &vbuffers[i].offs); } diff --git a/renderdoc/driver/vulkan/vk_state.h b/renderdoc/driver/vulkan/vk_state.h index 560ca1051..a4fe30508 100644 --- a/renderdoc/driver/vulkan/vk_state.h +++ b/renderdoc/driver/vulkan/vk_state.h @@ -36,6 +36,7 @@ struct VulkanRenderState VulkanRenderState(VulkanCreationInfo &createInfo); VulkanRenderState &operator =(const VulkanRenderState &o); void BeginRenderPassAndApplyState(VkCommandBuffer cmd); + void BindPipeline(VkCommandBuffer cmd); // dynamic state vector views; diff --git a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp index 61c68b840..a35a5bca8 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp @@ -45,12 +45,20 @@ bool WrappedVulkan::Serialise_vkCmdDraw( { if(IsPartialCmd(cmdid) && InPartialRange()) { - if(m_DrawcallCallback) m_DrawcallCallback->PreDraw(m_RootEventID); - commandBuffer = PartialCmdBuf(); + + if(m_DrawcallCallback) m_DrawcallCallback->PreDraw(m_RootEventID, commandBuffer); + ObjDisp(commandBuffer)->CmdDraw(Unwrap(commandBuffer), vtxCount, instCount, firstVtx, firstInst); - if(m_DrawcallCallback) m_DrawcallCallback->PostDraw(m_RootEventID); + if(m_DrawcallCallback) + { + if(m_DrawcallCallback->PostDraw(m_RootEventID, commandBuffer)) + { + ObjDisp(commandBuffer)->CmdDraw(Unwrap(commandBuffer), vtxCount, instCount, firstVtx, firstInst); + m_DrawcallCallback->PostRedraw(m_RootEventID, commandBuffer); + } + } } } else if(m_State == READING) @@ -979,12 +987,20 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexed( { if(IsPartialCmd(cmdid) && InPartialRange()) { - if(m_DrawcallCallback) m_DrawcallCallback->PreDraw(m_RootEventID); - commandBuffer = PartialCmdBuf(); + + if(m_DrawcallCallback) m_DrawcallCallback->PreDraw(m_RootEventID, commandBuffer); + ObjDisp(commandBuffer)->CmdDrawIndexed(Unwrap(commandBuffer), idxCount, instCount, firstIdx, vtxOffs, firstInst); - if(m_DrawcallCallback) m_DrawcallCallback->PostDraw(m_RootEventID); + if(m_DrawcallCallback) + { + if(m_DrawcallCallback->PostDraw(m_RootEventID, commandBuffer)) + { + ObjDisp(commandBuffer)->CmdDrawIndexed(Unwrap(commandBuffer), idxCount, instCount, firstIdx, vtxOffs, firstInst); + m_DrawcallCallback->PostRedraw(m_RootEventID, commandBuffer); + } + } } } else if(m_State == READING) diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index 3749b38d8..e668db525 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -370,6 +370,8 @@ + + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index a799c5be6..4d4e0570d 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -478,6 +478,12 @@ Resources\spv + + Resources\spv + + + Resources\spv +