From 6ec36d6695541eb31babceb650b524eceba1e5ad Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 31 Dec 2015 22:36:10 +0100 Subject: [PATCH] Implement drawcall highlight and wireframe texture overlays --- ReleaseNotes.md | 3 +- renderdoc/Makefile | 1 + renderdoc/data/embedded_files.h | 1 + renderdoc/data/renderdoc.rc | 1 + renderdoc/data/resource.h | 1 + renderdoc/data/spv/fixedcol.frag | 36 ++ renderdoc/driver/vulkan/vk_debug.cpp | 476 +++++++++++++++++++++++++-- renderdoc/driver/vulkan/vk_debug.h | 5 + renderdoc/renderdoc.vcxproj | 2 + renderdoc/renderdoc.vcxproj.filters | 6 + 10 files changed, 501 insertions(+), 31 deletions(-) create mode 100644 renderdoc/data/spv/fixedcol.frag diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 60e6ee07c..de1575275 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -18,6 +18,7 @@ Current Support * The pipeline state will be displayed at each command, showing the data contained in each member of the pipeline createinfo struct, as well as dynamic state. * Simple disassembly/reflection of SPIR-V to determine which descriptors to read for read-only resources and uniform buffers. The uniform buffers will be listed separately and the member variables filled out. * Simple display of most 2D textures with mips in the texture viewer. +* Drawcall highlight and wireframe overlays * Threading should be pretty efficient - no heavy locks on common paths (outside of creation/deletion) Known Issues @@ -42,7 +43,7 @@ On replay: * Only 2D non-array non-integer textures can currently be displayed. * Auto texture range-fit or histogram display is not implemented. -* Debug overlays aren't implemented. +* Debug overlays other than drawcall highlight and wireframe aren't implemented. * Saving textures is not supported. * Queue-level API events are not properly listed. API calls between draw-type vkCmd... are listed. * Meshes are not rendered as a preview. diff --git a/renderdoc/Makefile b/renderdoc/Makefile index 7c4487366..e1920c642 100644 --- a/renderdoc/Makefile +++ b/renderdoc/Makefile @@ -70,6 +70,7 @@ data/spv/textvs.spvo \ data/spv/textfs.spvo \ data/spv/genericvs.spvo \ data/spv/genericfs.spvo \ +data/spv/fixedcolfs.spvo \ data/sourcecodepro.ttfo .PHONY: all diff --git a/renderdoc/data/embedded_files.h b/renderdoc/data/embedded_files.h index 3a4c4e962..caf344bbd 100644 --- a/renderdoc/data/embedded_files.h +++ b/renderdoc/data/embedded_files.h @@ -54,5 +54,6 @@ DECLARE_EMBED(textvs_spv); DECLARE_EMBED(textfs_spv); DECLARE_EMBED(genericvs_spv); DECLARE_EMBED(genericfs_spv); +DECLARE_EMBED(fixedcolfs_spv); #undef DECLARE_EMBED diff --git a/renderdoc/data/renderdoc.rc b/renderdoc/data/renderdoc.rc index c6aedd3f8..b37da86ab 100644 --- a/renderdoc/data/renderdoc.rc +++ b/renderdoc/data/renderdoc.rc @@ -138,6 +138,7 @@ RESOURCE_textvs_spv TYPE_EMBED "spv/textvs.spv" RESOURCE_textfs_spv TYPE_EMBED "spv/textfs.spv" RESOURCE_genericvs_spv TYPE_EMBED "spv/genericvs.spv" RESOURCE_genericfs_spv TYPE_EMBED "spv/genericfs.spv" +RESOURCE_fixedcolfs_spv TYPE_EMBED "spv/fixedcolfs.spv" #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// diff --git a/renderdoc/data/resource.h b/renderdoc/data/resource.h index 1d8b18fff..999ef0d85 100644 --- a/renderdoc/data/resource.h +++ b/renderdoc/data/resource.h @@ -40,6 +40,7 @@ #define RESOURCE_textfs_spv 405 #define RESOURCE_genericvs_spv 406 #define RESOURCE_genericfs_spv 407 +#define RESOURCE_fixedcolfs_spv 408 #if !defined(STRINGIZE) #define STRINGIZE2(a) #a diff --git a/renderdoc/data/spv/fixedcol.frag b/renderdoc/data/spv/fixedcol.frag new file mode 100644 index 000000000..2907ca6a8 --- /dev/null +++ b/renderdoc/data/spv/fixedcol.frag @@ -0,0 +1,36 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2014 Crytek + * + * 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. + ******************************************************************************/ + +#version 420 core + +layout (location = 0) out vec4 color_out; + +void main(void) +{ + // used to have a shader-replacement pixel shader + // that outputs a fixed colour, without needing a + // slot in a descriptor set. We re-write the SPIR-V + // on the fly to replace these constants + color_out = vec4(1.1f, 2.2f, 3.3f, 4.4f); +} diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index b31ac7bda..dabd72aa1 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -27,6 +27,8 @@ #include "stb/stb_truetype.h" +#include "3rdparty/glslang/SPIRV/spirv.hpp" + // VKTODOMED should share this between shader and C++ - need #include support in glslang struct displayuniforms { @@ -230,6 +232,8 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) m_OverlayImageMem = VK_NULL_HANDLE; m_OverlayImage = VK_NULL_HANDLE; m_OverlayImageView = VK_NULL_HANDLE; + m_OverlayNoDepthFB = VK_NULL_HANDLE; + m_OverlayNoDepthRP = VK_NULL_HANDLE; RDCEraseEl(m_OverlayDim); m_OverlayMemSize = 0; @@ -1130,25 +1134,22 @@ VulkanDebugManager::~VulkanDebugManager() m_OutlineStripVBO.Destroy(vt, dev); m_GenericUBO.Destroy(vt, dev); + + // overlay resources are allocated through driver + if(m_OverlayNoDepthFB != VK_NULL_HANDLE) + m_pDriver->vkDestroyFramebuffer(dev, m_OverlayNoDepthFB); + if(m_OverlayNoDepthRP != VK_NULL_HANDLE) + m_pDriver->vkDestroyRenderPass(dev, m_OverlayNoDepthRP); + if(m_OverlayImageView != VK_NULL_HANDLE) - { - vt->DestroyImageView(Unwrap(dev), Unwrap(m_OverlayImageView)); - VKMGR()->ReleaseWrappedResource(m_OverlayImageView); - } + m_pDriver->vkDestroyImageView(dev, m_OverlayImageView); if(m_OverlayImage != VK_NULL_HANDLE) - { - // overlay image was allocated through driver so that its ID is - // registered, so go back through the same path to destroy m_pDriver->vkDestroyImage(dev, m_OverlayImage); - } if(m_OverlayImageMem != VK_NULL_HANDLE) - { - vt->FreeMemory(Unwrap(dev), Unwrap(m_OverlayImageMem)); - VKMGR()->ReleaseWrappedResource(m_OverlayImageMem); - } + m_pDriver->vkFreeMemory(dev, m_OverlayImageMem); } void VulkanDebugManager::BeginText(const TextPrintState &textstate) @@ -1233,6 +1234,229 @@ void VulkanDebugManager::EndText(const TextPrintState &textstate) ObjDisp(textstate.cmd)->EndCommandBuffer(Unwrap(textstate.cmd)); } +void VulkanDebugManager::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &pipeCreateInfo, ResourceId pipeline) +{ + VulkanCreationInfo::Pipeline &pipeInfo = m_pDriver->m_CreationInfo.m_Pipeline[pipeline]; + + static VkPipelineShaderStageCreateInfo stages[6]; + + uint32_t stageCount = 0; + + for(uint32_t i=0; i < 6; i++) + { + if(pipeInfo.shaders[i] != ResourceId()) + { + stages[stageCount].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stages[stageCount].stage = (VkShaderStage)i; + stages[stageCount].shader = VKMGR()->GetCurrentHandle(pipeInfo.shaders[i]); + stages[stageCount].pNext = NULL; + stages[stageCount].pSpecializationInfo = NULL; + stageCount++; + } + } + + static VkPipelineVertexInputStateCreateInfo vi = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + + static VkVertexInputAttributeDescription viattr[128] = {0}; + static VkVertexInputBindingDescription vibind[128] = {0}; + + vi.pVertexAttributeDescriptions = viattr; + vi.pVertexBindingDescriptions = vibind; + + vi.attributeCount = (uint32_t)pipeInfo.vertexAttrs.size(); + vi.bindingCount = (uint32_t)pipeInfo.vertexBindings.size(); + + for(uint32_t i=0; i < vi.attributeCount; i++) + { + viattr[i].binding = pipeInfo.vertexAttrs[i].binding; + viattr[i].offsetInBytes = pipeInfo.vertexAttrs[i].byteoffset; + viattr[i].format = pipeInfo.vertexAttrs[i].format; + viattr[i].location = pipeInfo.vertexAttrs[i].location; + } + + for(uint32_t i=0; i < vi.bindingCount; i++) + { + vibind[i].binding = pipeInfo.vertexBindings[i].vbufferBinding; + vibind[i].strideInBytes = pipeInfo.vertexBindings[i].bytestride; + vibind[i].stepRate = pipeInfo.vertexBindings[i].perInstance ? VK_VERTEX_INPUT_STEP_RATE_INSTANCE : VK_VERTEX_INPUT_STEP_RATE_VERTEX; + } + + RDCASSERT(ARRAY_COUNT(viattr) >= pipeInfo.vertexAttrs.size()); + RDCASSERT(ARRAY_COUNT(vibind) >= pipeInfo.vertexBindings.size()); + + static VkPipelineInputAssemblyStateCreateInfo ia = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + + ia.topology = pipeInfo.topology; + ia.primitiveRestartEnable = pipeInfo.primitiveRestartEnable; + + static VkPipelineTessellationStateCreateInfo tess = { VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO }; + + tess.patchControlPoints = pipeInfo.patchControlPoints; + + static VkPipelineViewportStateCreateInfo vp = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; + + static VkViewport views[32] = {0}; + static VkRect2D scissors[32] = {0}; + + memcpy(views, &pipeInfo.viewports[0], pipeInfo.viewports.size()*sizeof(VkViewport)); + + vp.pViewports = &views[0]; + vp.viewportCount = (uint32_t)pipeInfo.viewports.size(); + + memcpy(views, &pipeInfo.scissors[0], pipeInfo.scissors.size()*sizeof(VkRect2D)); + + vp.pScissors = &scissors[0]; + vp.scissorCount = (uint32_t)pipeInfo.scissors.size(); + + RDCASSERT(ARRAY_COUNT(views) >= pipeInfo.viewports.size()); + RDCASSERT(ARRAY_COUNT(scissors) >= pipeInfo.scissors.size()); + + static VkPipelineRasterStateCreateInfo rs = { VK_STRUCTURE_TYPE_PIPELINE_RASTER_STATE_CREATE_INFO }; + + rs.depthClipEnable = pipeInfo.depthClipEnable; + rs.rasterizerDiscardEnable = pipeInfo.rasterizerDiscardEnable, + rs.fillMode = pipeInfo.fillMode; + rs.cullMode = pipeInfo.cullMode; + rs.frontFace = pipeInfo.frontFace; + rs.depthBiasEnable = pipeInfo.depthBiasEnable; + rs.depthBias = pipeInfo.depthBias; + rs.depthBiasClamp = pipeInfo.depthBiasClamp; + rs.slopeScaledDepthBias = pipeInfo.slopeScaledDepthBias; + rs.lineWidth = pipeInfo.lineWidth; + + static VkPipelineMultisampleStateCreateInfo msaa = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + + msaa.rasterSamples = pipeInfo.rasterSamples; + msaa.sampleShadingEnable = pipeInfo.sampleShadingEnable; + msaa.minSampleShading = pipeInfo.minSampleShading; + msaa.pSampleMask = &pipeInfo.sampleMask; + + static VkPipelineDepthStencilStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; + + ds.depthTestEnable = pipeInfo.depthTestEnable; + ds.depthWriteEnable = pipeInfo.depthWriteEnable; + ds.depthCompareOp = pipeInfo.depthCompareOp; + ds.depthBoundsTestEnable = pipeInfo.depthBoundsEnable; + ds.stencilTestEnable = pipeInfo.stencilTestEnable; + ds.front = pipeInfo.front; ds.back = pipeInfo.back; + ds.minDepthBounds = pipeInfo.minDepthBounds; + ds.maxDepthBounds = pipeInfo.maxDepthBounds; + + static VkPipelineColorBlendStateCreateInfo cb = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; + + cb.alphaToCoverageEnable = pipeInfo.alphaToCoverageEnable; + cb.alphaToOneEnable = pipeInfo.alphaToOneEnable; + cb.logicOpEnable = pipeInfo.logicOpEnable; + cb.logicOp = pipeInfo.logicOp; + memcpy(cb.blendConst, pipeInfo.blendConst, sizeof(cb.blendConst)); + + static VkPipelineColorBlendAttachmentState atts[32] = { 0 }; + + cb.attachmentCount = (uint32_t)pipeInfo.attachments.size(); + cb.pAttachments = atts; + + for(uint32_t i=0; i < cb.attachmentCount; i++) + { + atts[i].blendEnable = pipeInfo.attachments[i].blendEnable; + atts[i].channelWriteMask = pipeInfo.attachments[i].channelWriteMask; + atts[i].blendOpAlpha = pipeInfo.attachments[i].alphaBlend.Operation; + atts[i].srcBlendAlpha = pipeInfo.attachments[i].alphaBlend.Source; + atts[i].destBlendAlpha = pipeInfo.attachments[i].alphaBlend.Destination; + atts[i].blendOpColor = pipeInfo.attachments[i].blend.Operation; + atts[i].srcBlendColor = pipeInfo.attachments[i].blend.Source; + atts[i].destBlendColor = pipeInfo.attachments[i].blend.Destination; + } + + RDCASSERT(ARRAY_COUNT(atts) >= pipeInfo.attachments.size()); + + static VkDynamicState dynSt[VK_DYNAMIC_STATE_NUM]; + + static VkPipelineDynamicStateCreateInfo dyn = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; + + dyn.dynamicStateCount = 0; + dyn.pDynamicStates = dynSt; + + for(uint32_t i=0; i < VK_DYNAMIC_STATE_NUM; i++) + if(pipeInfo.dynamicStates[i]) + dynSt[dyn.dynamicStateCount++] = (VkDynamicState)i; + + // since we don't have to worry about threading, we point everything at the above static structs + + VkGraphicsPipelineCreateInfo ret = { + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, NULL, + stageCount, stages, + &vi, + &ia, + &tess, + &vp, + &rs, + &msaa, + &ds, + &cb, + &dyn, + pipeInfo.flags, + VKMGR()->GetCurrentHandle(pipeInfo.layout), + VKMGR()->GetCurrentHandle(pipeInfo.renderpass), + pipeInfo.subpass, + VK_NULL_HANDLE, // base pipeline handle + 0, // base pipeline index + }; + + pipeCreateInfo = ret; +} + +void VulkanDebugManager::PatchFixedColShader(VkShaderModule &mod, VkShader &shad, float col[4]) +{ + string fixedcol = GetEmbeddedResource(fixedcolfs_spv); + + union + { + char *str; + uint32_t *spirv; + float *data; + } alias; + + alias.str = &fixedcol[0]; + + uint32_t *spirv = alias.spirv; + size_t spirvLength = fixedcol.size()/sizeof(uint32_t); + + size_t it = 5; + while(it < spirvLength) + { + uint16_t WordCount = spirv[it]>>16; + spv::Op opcode = spv::Op(spirv[it]&0xffff); + + if(opcode == spv::OpConstant) + { + if(alias.data[it+3] == 1.1f) alias.data[it+3] = col[0]; + else if(alias.data[it+3] == 2.2f) alias.data[it+3] = col[1]; + else if(alias.data[it+3] == 3.3f) alias.data[it+3] = col[2]; + else if(alias.data[it+3] == 4.4f) alias.data[it+3] = col[3]; + else RDCERR("Unexpected constant value"); + } + + it += WordCount; + } + + VkShaderModuleCreateInfo modinfo = { + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, NULL, + fixedcol.size(), (void *)spirv, 0, + }; + + VkResult vkr = m_pDriver->vkCreateShaderModule(m_Device, &modinfo, &mod); + RDCASSERT(vkr == VK_SUCCESS); + + VkShaderCreateInfo shadinfo = { + VK_STRUCTURE_TYPE_SHADER_CREATE_INFO, NULL, + mod, "main", 0, + VK_SHADER_STAGE_FRAGMENT, + }; + + vkr = m_pDriver->vkCreateShader(m_Device, &shadinfo, &shad); + RDCASSERT(vkr == VK_SUCCESS); +} + ResourceId VulkanDebugManager::RenderOverlay(ResourceId texid, TextureDisplayOverlay overlay, uint32_t frameID, uint32_t eventID, const vector &passEvents) { const VkLayerDispatchTable *vt = ObjDisp(m_Device); @@ -1244,22 +1468,26 @@ ResourceId VulkanDebugManager::RenderOverlay(ResourceId texid, TextureDisplayOve VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT }; VkResult vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERT(vkr == VK_SUCCESS); // if the overlay image is the wrong size, free it if(m_OverlayImage != VK_NULL_HANDLE && (iminfo.extent.width != m_OverlayDim.width || iminfo.extent.height != m_OverlayDim.height)) { - vt->DestroyImageView(Unwrap(m_Device), Unwrap(m_OverlayImageView)); - VKMGR()->ReleaseWrappedResource(m_OverlayImageView); - + m_pDriver->vkDestroyRenderPass(Unwrap(m_Device), Unwrap(m_OverlayNoDepthRP)); + m_pDriver->vkDestroyFramebuffer(Unwrap(m_Device), Unwrap(m_OverlayNoDepthFB)); + m_pDriver->vkDestroyImageView(Unwrap(m_Device), Unwrap(m_OverlayImageView)); m_pDriver->vkDestroyImage(m_Device, m_OverlayImage); m_OverlayImage = VK_NULL_HANDLE; m_OverlayImageView = VK_NULL_HANDLE; + m_OverlayNoDepthRP = VK_NULL_HANDLE; + m_OverlayNoDepthFB = VK_NULL_HANDLE; } // create the overlay image if we don't have one already - // we go through the driver's vkCreateImage so a live resource gets registered - // for this ID (just identity ID->ID map) + // we go through the driver's creation functions so creation info + // is saved and the resources are registered as live resources for + // their IDs. if(m_OverlayImage == VK_NULL_HANDLE) { m_OverlayDim.width = iminfo.extent.width; @@ -1281,7 +1509,7 @@ ResourceId VulkanDebugManager::RenderOverlay(ResourceId texid, TextureDisplayOve RDCASSERT(vkr == VK_SUCCESS); VkMemoryRequirements mrq; - vkr = vt->GetImageMemoryRequirements(Unwrap(m_Device), Unwrap(m_OverlayImage), &mrq); + vkr = m_pDriver->vkGetImageMemoryRequirements(m_Device, m_OverlayImage, &mrq); RDCASSERT(vkr == VK_SUCCESS); // if no memory is allocated, or it's not enough, @@ -1290,8 +1518,7 @@ ResourceId VulkanDebugManager::RenderOverlay(ResourceId texid, TextureDisplayOve { if(m_OverlayImageMem != VK_NULL_HANDLE) { - vt->FreeMemory(Unwrap(m_Device), Unwrap(m_OverlayImageMem)); - VKMGR()->ReleaseWrappedResource(m_OverlayImageMem); + m_pDriver->vkFreeMemory(m_Device, m_OverlayImageMem); } VkMemoryAllocInfo allocInfo = { @@ -1299,30 +1526,27 @@ ResourceId VulkanDebugManager::RenderOverlay(ResourceId texid, TextureDisplayOve mrq.size, m_pDriver->GetGPULocalMemoryIndex(mrq.memoryTypeBits), }; - vkr = vt->AllocMemory(Unwrap(m_Device), &allocInfo, &m_OverlayImageMem); + vkr = m_pDriver->vkAllocMemory(m_Device, &allocInfo, &m_OverlayImageMem); RDCASSERT(vkr == VK_SUCCESS); - VKMGR()->WrapResource(Unwrap(m_Device), m_OverlayImageMem); m_OverlayMemSize = mrq.size; } - vkr = vt->BindImageMemory(Unwrap(m_Device), Unwrap(m_OverlayImage), Unwrap(m_OverlayImageMem), 0); + vkr = m_pDriver->vkBindImageMemory(m_Device, m_OverlayImage, m_OverlayImageMem, 0); RDCASSERT(vkr == VK_SUCCESS); VkImageViewCreateInfo viewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, - Unwrap(m_OverlayImage), VK_IMAGE_VIEW_TYPE_2D, + m_OverlayImage, VK_IMAGE_VIEW_TYPE_2D, imInfo.format, { VK_CHANNEL_SWIZZLE_R, VK_CHANNEL_SWIZZLE_G, VK_CHANNEL_SWIZZLE_B, VK_CHANNEL_SWIZZLE_A }, { VK_IMAGE_ASPECT_COLOR, 0, 1, 0, 1, }, 0, }; - vkr = vt->CreateImageView(Unwrap(m_Device), &viewInfo, &m_OverlayImageView); + vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, &m_OverlayImageView); RDCASSERT(vkr == VK_SUCCESS); - VKMGR()->WrapResource(Unwrap(m_Device), m_OverlayImageView); - // need to transition image into valid state VkImageMemoryBarrier trans = { @@ -1339,14 +1563,206 @@ ResourceId VulkanDebugManager::RenderOverlay(ResourceId texid, TextureDisplayOve void *barrier = (void *)&trans; vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); - } + + VkAttachmentDescription colDesc = { + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION, NULL, + imInfo.format, 1, + VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + }; - float col[] = { 0.1f, 0.2f, 0.3f, 0.4f }; + VkAttachmentReference colRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkSubpassDescription sub = { + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION, NULL, + VK_PIPELINE_BIND_POINT_GRAPHICS, 0, + 0, NULL, // inputs + 1, &colRef, // color + NULL, // resolve + { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED }, // depth-stencil + 0, NULL, // preserve + }; + + VkRenderPassCreateInfo rpinfo = { + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, + 1, &colDesc, + 1, &sub, + 0, NULL, // dependencies + }; + + vkr = m_pDriver->vkCreateRenderPass(m_Device, &rpinfo, &m_OverlayNoDepthRP); + RDCASSERT(vkr == VK_SUCCESS); + + // Create framebuffer rendering just to overlay image, no depth + VkFramebufferCreateInfo fbinfo = { + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, + m_OverlayNoDepthRP, + 1, &m_OverlayImageView, + m_OverlayDim.width, m_OverlayDim.height, 1, + }; + + vkr = m_pDriver->vkCreateFramebuffer(m_Device, &fbinfo, &m_OverlayNoDepthFB); + RDCASSERT(vkr == VK_SUCCESS); + + // can't create a framebuffer or renderpass for overlay image + depth as that + // needs to match the depth texture type wherever our draw is. + } + VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_OverlayImage), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, (VkClearColorValue *)col, 1, &subresourceRange); + if(!m_pDriver->m_PartialReplayData.renderPassActive) + { + // don't do anything, no drawcall capable of making overlays selected + 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); + } + else if(overlay == eTexOverlay_NaN || overlay == eTexOverlay_Clipping) + { + 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); + } + else if(overlay == eTexOverlay_Drawcall || overlay == eTexOverlay_Wireframe) + { + float highlightCol[] = { 0.8f, 0.1f, 0.8f, 0.0f }; + + if(overlay == eTexOverlay_Wireframe) + { + highlightCol[0] = 200/255.0f; + highlightCol[1] = 1.0f; + highlightCol[2] = 0.0f; + } + + vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_OverlayImage), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, (VkClearColorValue *)highlightCol, 1, &subresourceRange); + + highlightCol[3] = 1.0f; + + // backup state + WrappedVulkan::PartialReplayData::StateVector prevstate = m_pDriver->m_PartialReplayData.state; + + // make patched shader + VkShaderModule mod = VK_NULL_HANDLE; + VkShader shad = VK_NULL_HANDLE; + + PatchFixedColShader(mod, shad, highlightCol); + + // make patched pipeline + VkGraphicsPipelineCreateInfo pipeCreateInfo; + + MakeGraphicsPipelineInfo(pipeCreateInfo, prevstate.graphics.pipeline); + + // disable all tests possible + VkPipelineDepthStencilStateCreateInfo *ds = (VkPipelineDepthStencilStateCreateInfo *)pipeCreateInfo.pDepthStencilState; + ds->depthTestEnable = false; + ds->depthWriteEnable = false; + ds->stencilTestEnable = false; + ds->depthBoundsTestEnable = false; + + VkPipelineRasterStateCreateInfo *rs = (VkPipelineRasterStateCreateInfo *)pipeCreateInfo.pRasterState; + rs->cullMode = VK_CULL_MODE_NONE; + rs->rasterizerDiscardEnable = false; + rs->depthClipEnable = false; + + if(overlay == eTexOverlay_Wireframe && m_pDriver->GetDeviceFeatures().fillModeNonSolid) + { + rs->fillMode = VK_FILL_MODE_WIREFRAME; + rs->lineWidth = 1.0f; + } + + VkPipelineColorBlendStateCreateInfo *cb = (VkPipelineColorBlendStateCreateInfo *)pipeCreateInfo.pColorBlendState; + cb->logicOpEnable = false; + cb->attachmentCount = 1; // only one colour attachment + for(uint32_t i=0; i < cb->attachmentCount; cb++) + { + VkPipelineColorBlendAttachmentState *att = (VkPipelineColorBlendAttachmentState *)&cb->pAttachments[i]; + att->blendEnable = false; + att->channelWriteMask = 0xf; + } + + // set scissors to max + for(size_t i=0; i < pipeCreateInfo.pViewportState->scissorCount; i++) + { + VkRect2D &sc = (VkRect2D &)pipeCreateInfo.pViewportState->pScissors[i]; + sc.offset.x = 0; + sc.offset.y = 0; + sc.extent.width = 4096; + sc.extent.height = 4096; + } + + // set our renderpass and shader + pipeCreateInfo.renderPass = m_OverlayNoDepthRP; + + 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) + { + sh.shader = shad; + 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.pNext = NULL; + sh.pSpecializationInfo = NULL; + sh.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + sh.shader = shad; + sh.stage = VK_SHADER_STAGE_FRAGMENT; + } + + vkr = vt->EndCommandBuffer(Unwrap(cmd)); + RDCASSERT(vkr == VK_SUCCESS); + + VkPipeline pipe = VK_NULL_HANDLE; + + vkr = m_pDriver->vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1, &pipeCreateInfo, &pipe); + RDCASSERT(vkr == VK_SUCCESS); + + // modify state + m_pDriver->m_PartialReplayData.state.renderPass = GetResID(m_OverlayNoDepthRP); + m_pDriver->m_PartialReplayData.state.framebuffer = GetResID(m_OverlayNoDepthFB); + + m_pDriver->m_PartialReplayData.state.graphics.pipeline = GetResID(pipe); + + // set dynamic scissors in case pipeline was using them + for(size_t i=0; i < m_pDriver->m_PartialReplayData.state.scissors.size(); i++) + { + m_pDriver->m_PartialReplayData.state.scissors[i].offset.x = 0; + m_pDriver->m_PartialReplayData.state.scissors[i].offset.x = 0; + m_pDriver->m_PartialReplayData.state.scissors[i].extent.width = 4096; + m_pDriver->m_PartialReplayData.state.scissors[i].extent.height = 4096; + } + + if(overlay == eTexOverlay_Wireframe) + m_pDriver->m_PartialReplayData.state.lineWidth = 1.0f; + + m_pDriver->ReplayLog(frameID, 0, eventID, eReplay_OnlyDraw); + + // submit & flush so that we don't have to keep pipeline around for a while + m_pDriver->SubmitCmds(); + m_pDriver->FlushQ(); + + cmd = m_pDriver->GetNextCmd(); + + vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + RDCASSERT(vkr == VK_SUCCESS); + + // restore state + m_pDriver->m_PartialReplayData.state = prevstate; + + m_pDriver->vkDestroyPipeline(m_Device, pipe); + m_pDriver->vkDestroyShaderModule(m_Device, mod); + m_pDriver->vkDestroyShader(m_Device, shad); + } 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 876b0f296..5acd26044 100644 --- a/renderdoc/driver/vulkan/vk_debug.h +++ b/renderdoc/driver/vulkan/vk_debug.h @@ -125,6 +125,8 @@ class VulkanDebugManager VkDeviceMemory m_OverlayImageMem; VkImage m_OverlayImage; VkImageView m_OverlayImageView; + VkFramebuffer m_OverlayNoDepthFB; + VkRenderPass m_OverlayNoDepthRP; VkExtent2D m_OverlayDim; VkDeviceSize m_OverlayMemSize; @@ -132,7 +134,10 @@ class VulkanDebugManager void InitDebugData(); void ShutdownDebugData(); + void PatchFixedColShader(VkShaderModule &mod, VkShader &shad, 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; diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index ed8ce97fd..74db12c46 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -361,6 +361,8 @@ + + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index 9d7f80528..e967ba72b 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -472,6 +472,12 @@ Resources\spv + + Resources\spv + + + Resources\spv +