From 97c3186e348f8c03a3d335867f8b422987859548 Mon Sep 17 00:00:00 2001 From: Aliya Pazylbekova Date: Tue, 16 Jul 2019 13:43:37 -0400 Subject: [PATCH] Vk Pixel history: Add occlusion query test Create a pipeline for doing occlusion queries --- renderdoc/driver/vulkan/vk_pixelhistory.cpp | 373 ++++++++++++++++++-- 1 file changed, 334 insertions(+), 39 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_pixelhistory.cpp b/renderdoc/driver/vulkan/vk_pixelhistory.cpp index 9de6ccf0c..2ac81457b 100644 --- a/renderdoc/driver/vulkan/vk_pixelhistory.cpp +++ b/renderdoc/driver/vulkan/vk_pixelhistory.cpp @@ -1,7 +1,7 @@ /****************************************************************************** * The MIT License (MIT) * -* Copyright (c) 2016-2019 Baldur Karlsson +* Copyright (c) 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 @@ -22,6 +22,7 @@ * THE SOFTWARE. ******************************************************************************/ +#include #include #include "driver/vulkan/vk_debug.h" #include "driver/vulkan/vk_replay.h" @@ -67,14 +68,20 @@ struct PixelHistoryResources struct PixelHistoryValue { uint8_t color[16]; - uint8_t depth[8]; + union + { + uint32_t udepth; + float fdepth; + } depth; + int8_t stencil; + uint8_t padding[3]; }; struct EventInfo { PixelHistoryValue premod; PixelHistoryValue postmod; - uint8_t shadout[16]; + PixelHistoryValue shadout; uint8_t fixed_stencil[8]; uint8_t original_stencil[8]; }; @@ -92,18 +99,26 @@ std::vector VulkanReplay::PixelHistory(std::vector &events) + VkFormat format, VkExtent3D extent, uint32_t sampleMask, + VkQueryPool occlusionPool, VkImageView colorImageView, + VkImageView stencilImageView, VkImage colorImage, VkImage stencilImage, + VkBuffer dstBuffer, const std::vector &events) : m_pDriver(vk), m_X(x), m_Y(y), m_Image(image), m_Format(format), + m_Extent(extent), m_DstBuffer(dstBuffer), m_SampleMask(sampleMask), m_OcclusionPool(occlusionPool), @@ -118,14 +133,74 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback m_Events.insert(std::make_pair(events[i].eventId, events[i])); } ~VulkanPixelHistoryCallback() { m_pDriver->SetDrawcallCB(NULL); } - void CopyPixel(uint32_t eid, VkCommandBuffer cmd, size_t offset) + // CreateResources creates fixed colour shader, offscreen framebuffer and + // a renderpass for occlusion and stencil tests. + void CreateResources() + { + // Create fixed colour shader + float col[4] = {1.0, 0.0, 0.0, 1.0}; + m_pDriver->GetDebugManager()->PatchFixedColShader(m_FixedColFS, col); + + VkResult vkr; + + // TODO: add depth/stencil attachment + VkAttachmentReference colRef = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; + VkSubpassDescription sub = {}; + sub.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + sub.colorAttachmentCount = 1; + sub.pColorAttachments = &colRef; + + VkAttachmentDescription colDesc = {}; + colDesc.format = m_Format; + colDesc.samples = VK_SAMPLE_COUNT_1_BIT; + colDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + colDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkRenderPassCreateInfo rpinfo = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO}; + rpinfo.attachmentCount = 1; + rpinfo.pAttachments = &colDesc; + rpinfo.subpassCount = 1; + rpinfo.pSubpasses = ⊂ + vkr = m_pDriver->vkCreateRenderPass(m_pDriver->GetDev(), &rpinfo, NULL, &m_RenderPass); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + VkFramebufferCreateInfo fbinfo = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO}; + fbinfo.renderPass = m_RenderPass; + fbinfo.attachmentCount = 1; + fbinfo.pAttachments = &m_ColorImageView; + fbinfo.width = m_Extent.width; + fbinfo.height = m_Extent.height; + fbinfo.layers = 1; + + vkr = m_pDriver->vkCreateFramebuffer(m_pDriver->GetDev(), &fbinfo, NULL, &m_OffscreenFB); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + } + + void DestroyResources() + { + m_pDriver->vkDestroyShaderModule(m_pDriver->GetDev(), m_FixedColFS, NULL); + m_pDriver->vkDestroyFramebuffer(m_pDriver->GetDev(), m_OffscreenFB, NULL); + m_pDriver->vkDestroyRenderPass(m_pDriver->GetDev(), m_RenderPass, NULL); + + for(auto it = m_PipelineCache.begin(); it != m_PipelineCache.end(); ++it) + { + m_pDriver->vkDestroyPipeline(m_pDriver->GetDev(), it->second.occlusion, NULL); + } + } + + void CopyPixel(VkImage srcImage, VkFormat srcFormat, VkImage depthImage, VkFormat depthFormat, + VkCommandBuffer cmd, size_t offset) { CopyPixelParams colourCopyParams = {}; colourCopyParams.multisampled = false; // TODO: multisampled - colourCopyParams.srcImage = m_Image; + colourCopyParams.srcImage = srcImage; colourCopyParams.srcImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // TODO: image layout - colourCopyParams.srcImageFormat = m_Format; + colourCopyParams.srcImageFormat = srcFormat; colourCopyParams.imageOffset.x = int32_t(m_X); colourCopyParams.imageOffset.y = int32_t(m_Y); colourCopyParams.imageOffset.z = 0; @@ -133,24 +208,139 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback m_pDriver->GetDebugManager()->PixelHistoryCopyPixel(cmd, colourCopyParams, offset); - const DrawcallDescription *draw = m_pDriver->GetDrawcall(eid); - if(draw && draw->depthOut != ResourceId()) + if(depthImage != VK_NULL_HANDLE) { - // The draw call had a depth image attachment. - ResourceId depthImage = m_pDriver->GetResourceManager()->GetLiveID(draw->depthOut); - const VulkanCreationInfo::Image &imginfo = - m_pDriver->GetDebugManager()->GetImageInfo(depthImage); CopyPixelParams depthCopyParams = colourCopyParams; depthCopyParams.depthcopy = true; - depthCopyParams.srcImage = - m_pDriver->GetResourceManager()->GetCurrentHandle(depthImage); + depthCopyParams.srcImage = depthImage; depthCopyParams.srcImageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - depthCopyParams.srcImageFormat = imginfo.format; + depthCopyParams.srcImageFormat = depthFormat; m_pDriver->GetDebugManager()->PixelHistoryCopyPixel( cmd, depthCopyParams, offset + offsetof(struct PixelHistoryValue, depth)); } } + // ReplayDraw begins renderpass, executes a single draw defined by the eventId and + // ends the renderpass. + void ReplayDraw(VkCommandBuffer cmd, size_t eventIndex, int eventId, bool doQuery) + { + VulkanRenderState &state = m_pDriver->GetRenderState(); + const DrawcallDescription *drawcall = m_pDriver->GetDrawcall(eventId); + state.BeginRenderPassAndApplyState(cmd, VulkanRenderState::BindGraphics); + + if(doQuery) + ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), m_OcclusionPool, (uint32_t)eventIndex, + VK_QUERY_CONTROL_PRECISE_BIT); + + if(drawcall->flags & DrawFlags::Indexed) + ObjDisp(cmd)->CmdDrawIndexed(Unwrap(cmd), drawcall->numIndices, drawcall->numInstances, + drawcall->indexOffset, drawcall->baseVertex, + drawcall->instanceOffset); + else + ObjDisp(cmd)->CmdDraw(Unwrap(cmd), drawcall->numIndices, drawcall->numInstances, + drawcall->vertexOffset, drawcall->instanceOffset); + + if(doQuery) + ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), m_OcclusionPool, (uint32_t)eventIndex); + + state.EndRenderPass(cmd); + } + + void UpdateScissor(const VkViewport &view, VkRect2D &scissor) + { + float fx = (float)m_X; + float fy = (float)m_Y; + float y_start = view.y; + float y_end = view.y + view.height; + if(view.height < 0) + { + y_start = view.y + view.height; + y_end = view.y; + } + + if(fx < view.x || fy < y_start || fx >= view.x + view.width || fy >= y_end) + { + scissor.offset.x = scissor.offset.y = scissor.extent.width = scissor.extent.height = 0; + } + else + { + scissor.offset.x = m_X; + scissor.offset.y = m_Y; + scissor.extent.width = scissor.extent.height = 1; + } + } + + // CreatePipelineForOcclusion creates a graphics VkPipeline to use for occlusion + // queries. The pipeline uses a fixed colour shader, disables all tests, and + // scissors to just the pixel we are interested in. + VkPipeline CreatePipelineForOcclusion(ResourceId pipeline) + { + VkGraphicsPipelineCreateInfo pipeCreateInfo = {}; + m_pDriver->GetShaderCache()->MakeGraphicsPipelineInfo(pipeCreateInfo, pipeline); + VkPipelineRasterizationStateCreateInfo *rs = + (VkPipelineRasterizationStateCreateInfo *)pipeCreateInfo.pRasterizationState; + VkPipelineDepthStencilStateCreateInfo *ds = + (VkPipelineDepthStencilStateCreateInfo *)pipeCreateInfo.pDepthStencilState; + VkPipelineViewportStateCreateInfo *vs = + (VkPipelineViewportStateCreateInfo *)pipeCreateInfo.pViewportState; + const VulkanCreationInfo::Pipeline &p = + m_pDriver->GetRenderState().m_CreationInfo->m_Pipeline[pipeline]; + + std::vector prevStages; + prevStages.resize(pipeCreateInfo.stageCount); + memcpy(prevStages.data(), pipeCreateInfo.pStages, + sizeof(VkPipelineShaderStageCreateInfo) * pipeCreateInfo.stageCount); + + std::vector stages; + stages.resize(pipeCreateInfo.stageCount); + memcpy(stages.data(), pipeCreateInfo.pStages, + sizeof(VkPipelineShaderStageCreateInfo) * pipeCreateInfo.stageCount); + + for(uint32_t i = 0; i < pipeCreateInfo.stageCount; i++) + { + if(stages[i].stage == VK_SHADER_STAGE_FRAGMENT_BIT) + { + stages[i].module = m_FixedColFS; + stages[i].pName = "main"; + } + } + pipeCreateInfo.pStages = stages.data(); + + VkRect2D newScissors[16]; + memset(newScissors, 0, sizeof(newScissors)); + { + // Disable all tests + rs->cullMode = VK_CULL_MODE_NONE; + rs->rasterizerDiscardEnable = VK_FALSE; + ds->depthTestEnable = VK_FALSE; + ds->depthWriteEnable = VK_FALSE; + ds->stencilTestEnable = VK_FALSE; + ds->depthBoundsTestEnable = VK_FALSE; + + if(m_pDriver->GetDeviceFeatures().depthClamp) + rs->depthClampEnable = true; + + // Change scissors unless they are set dynamically. + if(!p.dynamicStates[VkDynamicViewport]) + { + for(uint32_t i = 0; i < vs->viewportCount; i++) + { + UpdateScissor(vs->pViewports[i], newScissors[i]); + } + vs->pScissors = newScissors; + } + } + // TODO: Disable discard rectangles from VK_EXT_discard_rectangles. + + pipeCreateInfo.renderPass = m_RenderPass; + + VkPipeline pipe; + VkResult vkr = m_pDriver->vkCreateGraphicsPipelines(m_pDriver->GetDev(), VK_NULL_HANDLE, 1, + &pipeCreateInfo, NULL, &pipe); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + return pipe; + } + void PreDraw(uint32_t eid, VkCommandBuffer cmd) { auto it = m_Events.find(eid); @@ -163,9 +353,66 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback m_pDriver->GetRenderState().EndRenderPass(cmd); // Get pre-modification values size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - CopyPixel(eid, cmd, storeOffset); + VkImage depthImage = VK_NULL_HANDLE; + VkFormat depthFormat = VK_FORMAT_UNDEFINED; + const DrawcallDescription *draw = m_pDriver->GetDrawcall(eid); + if(draw && draw->depthOut != ResourceId()) + { + ResourceId resId = m_pDriver->GetResourceManager()->GetLiveID(draw->depthOut); + depthImage = m_pDriver->GetResourceManager()->GetCurrentHandle(resId); + const VulkanCreationInfo::Image &imginfo = m_pDriver->GetDebugManager()->GetImageInfo(resId); + depthFormat = imginfo.format; + } + + CopyPixel(m_Image, m_Format, depthImage, depthFormat, cmd, storeOffset); // TODO: add all logic for draw calls. + VulkanRenderState &pipestate = m_pDriver->GetRenderState(); + ResourceId prevState = pipestate.graphics.pipeline; + ResourceId prevRenderpass = pipestate.renderPass; + ResourceId prevFramebuffer = pipestate.framebuffer; + uint32_t prevSubpass = pipestate.subpass; + + { + auto pipeIt = m_PipelineCache.find(pipestate.graphics.pipeline); + PipelineReplacements replacements; + + if(pipeIt != m_PipelineCache.end()) + { + replacements = pipeIt->second; + } + else + { + // TODO: Create other pipeline replacements + replacements.occlusion = CreatePipelineForOcclusion(pipestate.graphics.pipeline); + + m_PipelineCache.insert(std::make_pair(pipestate.graphics.pipeline, replacements)); + } + + const VulkanCreationInfo::Pipeline &p = + pipestate.m_CreationInfo->m_Pipeline[pipestate.graphics.pipeline]; + if(p.dynamicStates[VkDynamicViewport]) + for(uint32_t i = 0; i < pipestate.views.size(); i++) + UpdateScissor(pipestate.views[i], pipestate.scissors[i]); + + pipestate.framebuffer = GetResID(m_OffscreenFB); + pipestate.renderPass = GetResID(m_RenderPass); + pipestate.subpass = 0; + pipestate.graphics.pipeline = GetResID(replacements.occlusion); + // TODO: Draws might have side-effects (writing to storage images/buffers). + // Consider re-writing SPIR-V for all shaders except fragment shader + // to remove all stores.s + ReplayDraw(cmd, (uint32_t)m_OcclusionQueries.size(), eid, true); + + m_OcclusionQueries.insert( + std::pair(eid, (uint32_t)m_OcclusionQueries.size())); + } + + // Restore the state. + m_pDriver->GetRenderState() = m_PrevState; + pipestate.framebuffer = prevFramebuffer; + pipestate.renderPass = prevRenderpass; + pipestate.subpass = prevSubpass; if(m_PrevState.graphics.pipeline != ResourceId()) m_pDriver->GetRenderState().BeginRenderPassAndApplyState(cmd, VulkanRenderState::BindGraphics); @@ -179,7 +426,19 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback m_pDriver->GetRenderState().EndRenderPass(cmd); size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - CopyPixel(eid, cmd, storeOffset + offsetof(struct EventInfo, postmod)); + VkImage depthImage = VK_NULL_HANDLE; + VkFormat depthFormat = VK_FORMAT_UNDEFINED; + const DrawcallDescription *draw = m_pDriver->GetDrawcall(eid); + if(draw && draw->depthOut != ResourceId()) + { + ResourceId resId = m_pDriver->GetResourceManager()->GetLiveID(draw->depthOut); + depthImage = m_pDriver->GetResourceManager()->GetCurrentHandle(resId); + const VulkanCreationInfo::Image &imginfo = m_pDriver->GetDebugManager()->GetImageInfo(resId); + depthFormat = imginfo.format; + } + + CopyPixel(m_Image, m_Format, depthImage, depthFormat, cmd, + storeOffset + offsetof(struct EventInfo, postmod)); m_pDriver->GetRenderState().BeginRenderPassAndApplyState(cmd, VulkanRenderState::BindGraphics); @@ -199,7 +458,7 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback return; size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - CopyPixel(eid, cmd, storeOffset); + CopyPixel(m_Image, m_Format, VK_NULL_HANDLE, VK_FORMAT_UNDEFINED, cmd, storeOffset); } bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) { @@ -207,7 +466,8 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback return false; size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - CopyPixel(eid, cmd, storeOffset + offsetof(struct EventInfo, postmod)); + CopyPixel(m_Image, m_Format, VK_NULL_HANDLE, VK_FORMAT_UNDEFINED, cmd, + storeOffset + offsetof(struct EventInfo, postmod)); m_EventIndices.insert(std::make_pair(eid, m_EventIndices.size())); return false; } @@ -218,7 +478,7 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback return; size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - CopyPixel(eid, cmd, storeOffset); + CopyPixel(m_Image, m_Format, VK_NULL_HANDLE, VK_FORMAT_UNDEFINED, cmd, storeOffset); } bool PostMisc(uint32_t eid, DrawFlags flags, VkCommandBuffer cmd) { @@ -226,7 +486,8 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback return false; size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo); - CopyPixel(eid, cmd, storeOffset + offsetof(struct EventInfo, postmod)); + CopyPixel(m_Image, m_Format, VK_NULL_HANDLE, VK_FORMAT_UNDEFINED, cmd, + storeOffset + offsetof(struct EventInfo, postmod)); m_EventIndices.insert(std::make_pair(eid, m_EventIndices.size())); return false; } @@ -241,8 +502,10 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback WrappedVulkan *m_pDriver; VkImage m_Image; VkFormat m_Format; + VkExtent3D m_Extent; std::map m_Events; std::map m_EventIndices; + std::map m_OcclusionQueries; VkBuffer m_DstBuffer; uint32_t m_X, m_Y; @@ -253,6 +516,11 @@ struct VulkanPixelHistoryCallback : public VulkanDrawcallCallback VkImage m_ColorImage; VkImage m_StencilImage; + VkShaderModule m_FixedColFS; + VkRenderPass m_RenderPass; + VkFramebuffer m_OffscreenFB; + std::map m_PipelineCache; + VulkanRenderState m_PrevState; }; @@ -283,7 +551,9 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou // Device local resources: imgInfo.format = format; - imgInfo.extent = {extent.width, extent.height, 1}; + imgInfo.extent.width = extent.width; + imgInfo.extent.height = extent.height; + imgInfo.extent.depth = 1; imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imgInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; @@ -328,7 +598,7 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou viewInfo.image = stencilImage; viewInfo.format = VK_FORMAT_D32_SFLOAT_S8_UINT; - viewInfo.subresourceRange = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1}; + viewInfo.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1}; vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &stencilImageView); RDCASSERTEQUAL(vkr, VK_SUCCESS); @@ -521,6 +791,7 @@ std::vector VulkanReplay::PixelHistory(std::vector history; VkResult vkr; VkDevice dev = m_pDriver->GetDev(); @@ -564,11 +835,24 @@ std::vector VulkanReplay::PixelHistory(std::vectorGetCurrentHandle(target), imginfo.format, - sampleMask, occlusionPool, resources.colorImageView, resources.stencilImageView, + imginfo.extent, sampleMask, occlusionPool, resources.colorImageView, resources.stencilImageView, resources.colorImage, resources.stencilImage, resources.dstBuffer, events); + cb.CreateResources(); m_pDriver->ReplayLog(0, events.back().eventId, eReplay_Full); m_pDriver->SubmitCmds(); m_pDriver->FlushQ(); + cb.DestroyResources(); + + std::vector occlusionResults; + if(cb.m_OcclusionQueries.size() > 0) + { + occlusionResults.resize(cb.m_OcclusionQueries.size()); + vkr = ObjDisp(dev)->GetQueryPoolResults( + Unwrap(dev), occlusionPool, 0, (uint32_t)occlusionResults.size(), + occlusionResults.size() * sizeof(occlusionResults[0]), occlusionResults.data(), + sizeof(uint64_t), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + } for(size_t ev = 0; ev < events.size(); ev++) { @@ -576,7 +860,19 @@ std::vector VulkanReplay::PixelHistory(std::vectorflags & DrawFlags::Clear); bool directWrite = isDirectWrite(events[ev].usage); // TODO: actually do occlusion query. - uint64_t occlData = 1; + uint64_t occlData = 0; + if(!directWrite) + { + auto it = cb.m_OcclusionQueries.find((uint32_t)events[ev].eventId); + if(it == cb.m_OcclusionQueries.end() || it->second > occlusionResults.size()) + { + RDCERR("Failed to find occlusion query result for event id %u", events[ev].eventId); + } + occlData = occlusionResults[it->second]; + } + + RDCDEBUG("PixelHistory event id: %u, clear = %u, directWrite = %u, occlData = %u", + events[ev].eventId, clear, directWrite, occlData); if(events[ev].view != ResourceId()) { @@ -621,15 +917,14 @@ std::vector VulkanReplay::PixelHistory(std::vector VulkanReplay::PixelHistory(std::vector