Files
renderdoc/renderdoc/driver/vulkan/vk_overlay.cpp
T
baldurk 1107462f05 Use pipeline layout from descriptor set binds to iterate descriptor sets
* If a pipeline doesn't statically access a descriptor set, the corresponding
  descriptor set layout in its pipeline layout can be essentially anything and
  it doesn't have to match the actual descriptor set bound at a draw. It's just
  ignored.
* Rather than check for static access ourselves we take advantage of another
  fact - when the descriptor set is bound it must be compatible with the set
  layout from the bind call's pipeline layout. If the pipeline *does* statically
  use the descriptor set, its pipeline layout must be compatible with the bind
  call's pipeline layout for that set.
* So the end result is that we can safely use the bind call's pipeline layout
  for iterating over bound descriptors, secure in the knowledge that it's always
  valid for that data, and if the pipeline uses it then it's also valid for the
  pipeline.
2019-09-02 12:19:37 +01:00

2378 lines
86 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2018-2019 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include <float.h>
#include "data/glsl_shaders.h"
#include "driver/shaders/spirv/spirv_common.h"
#include "driver/shaders/spirv/spirv_gen.h"
#include "maths/camera.h"
#include "maths/formatpacking.h"
#include "maths/matrix.h"
#include "strings/string_utils.h"
#include "vk_core.h"
#include "vk_debug.h"
#include "vk_shader_cache.h"
#define VULKAN 1
#include "data/glsl/glsl_ubos_cpp.h"
struct VulkanQuadOverdrawCallback : public VulkanDrawcallCallback
{
VulkanQuadOverdrawCallback(WrappedVulkan *vk, VkDescriptorSetLayout descSetLayout,
VkDescriptorSet descSet, const std::vector<uint32_t> &events)
: m_pDriver(vk),
m_DescSetLayout(descSetLayout),
m_DescSet(descSet),
m_Events(events),
m_PrevState(vk, NULL)
{
m_pDriver->SetDrawcallCB(this);
}
~VulkanQuadOverdrawCallback() { 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
CachedPipeline pipe = m_PipelineCache[pipestate.graphics.pipeline];
// if we don't get a hit, create a modified pipeline
if(pipe.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<VkDescriptorSetLayout>(
c.m_PipelineLayout[p.layout].descSetLayouts[i]);
// this layout has storage image and
descSetLayouts[descSet] = m_DescSetLayout;
const std::vector<VkPushConstantRange> &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
vkr = m_pDriver->vkCreatePipelineLayout(m_pDriver->GetDev(), &pipeLayoutInfo, NULL,
&pipe.pipeLayout);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
SAFE_DELETE_ARRAY(descSetLayouts);
VkGraphicsPipelineCreateInfo pipeCreateInfo;
m_pDriver->GetShaderCache()->MakeGraphicsPipelineInfo(pipeCreateInfo,
pipestate.graphics.pipeline);
// repoint pipeline layout
pipeCreateInfo.layout = pipe.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 depth/stencil writes but keep any tests enabled
VkPipelineDepthStencilStateCreateInfo *ds =
(VkPipelineDepthStencilStateCreateInfo *)pipeCreateInfo.pDepthStencilState;
ds->depthWriteEnable = 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;
std::vector<uint32_t> spirv =
*m_pDriver->GetShaderCache()->GetBuiltinBlob(BuiltinShader::QuadWriteFS);
// patch spirv, change descriptor set to descSet value
size_t it = 5;
while(it < spirv.size())
{
uint16_t WordCount = spirv[it] >> rdcspv::WordCountShift;
rdcspv::Op opcode = rdcspv::Op(spirv[it] & rdcspv::OpCodeMask);
if(opcode == rdcspv::Op::Decorate &&
spirv[it + 2] == (uint32_t)rdcspv::Decoration::DescriptorSet)
{
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 = m_pDriver->vkCreateShaderModule(dev, &modinfo, NULL, &module);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
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.pipe);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_pDriver->vkDestroyShaderModule(dev, module, NULL);
pipe.descSet = descSet;
m_PipelineCache[pipestate.graphics.pipeline] = pipe;
}
// modify state for first draw call
pipestate.graphics.pipeline = GetResID(pipe.pipe);
RDCASSERT(pipestate.graphics.descSets.size() >= pipe.descSet);
pipestate.graphics.descSets.resize(pipe.descSet + 1);
pipestate.graphics.descSets[pipe.descSet].pipeLayout = GetResID(pipe.pipeLayout);
pipestate.graphics.descSets[pipe.descSet].descSet = GetResID(m_DescSet);
if(cmd)
pipestate.BindPipeline(cmd, VulkanRenderState::BindGraphics, false);
}
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, VulkanRenderState::BindGraphics, false);
return true;
}
void PostRedraw(uint32_t eid, VkCommandBuffer cmd)
{
// nothing to do
}
// Dispatches don't rasterize, so do nothing
void PreDispatch(uint32_t eid, VkCommandBuffer cmd) {}
bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) { return false; }
void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) {}
// Ditto copy/etc
void PreMisc(uint32_t eid, DrawFlags flags, VkCommandBuffer cmd) {}
bool PostMisc(uint32_t eid, DrawFlags flags, VkCommandBuffer cmd) { return false; }
void PostRemisc(uint32_t eid, DrawFlags flags, VkCommandBuffer cmd) {}
void PreEndCommandBuffer(VkCommandBuffer cmd) {}
void AliasEvent(uint32_t primary, uint32_t alias)
{
// don't care
}
WrappedVulkan *m_pDriver;
VkDescriptorSetLayout m_DescSetLayout;
VkDescriptorSet m_DescSet;
const std::vector<uint32_t> &m_Events;
// cache modified pipelines
struct CachedPipeline
{
uint32_t descSet;
VkPipelineLayout pipeLayout;
VkPipeline pipe;
};
std::map<ResourceId, CachedPipeline> m_PipelineCache;
VulkanRenderState m_PrevState;
};
void VulkanDebugManager::PatchFixedColShader(VkShaderModule &mod, float col[4])
{
union
{
uint32_t *spirv;
float *data;
} alias;
std::vector<uint32_t> spv = *m_pDriver->GetShaderCache()->GetBuiltinBlob(BuiltinShader::FixedColFS);
alias.spirv = &spv[0];
size_t spirvLength = spv.size();
int patched = 0;
size_t it = 5;
while(it < spirvLength)
{
uint16_t WordCount = alias.spirv[it] >> rdcspv::WordCountShift;
rdcspv::Op opcode = rdcspv::Op(alias.spirv[it] & rdcspv::OpCodeMask);
if(opcode == rdcspv::Op::Constant)
{
if(alias.data[it + 3] >= 1.0f && alias.data[it + 3] <= 1.5f)
alias.data[it + 3] = col[0];
else if(alias.data[it + 3] >= 2.0f && alias.data[it + 3] <= 2.5f)
alias.data[it + 3] = col[1];
else if(alias.data[it + 3] >= 3.0f && alias.data[it + 3] <= 3.5f)
alias.data[it + 3] = col[2];
else if(alias.data[it + 3] >= 4.0f && alias.data[it + 3] <= 4.5f)
alias.data[it + 3] = col[3];
else
RDCERR("Unexpected constant value");
patched++;
}
it += WordCount;
}
if(patched != 4)
RDCERR("Didn't patch all constants");
VkShaderModuleCreateInfo modinfo = {
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
NULL,
0,
spv.size() * sizeof(uint32_t),
alias.spirv,
};
VkResult vkr = m_pDriver->vkCreateShaderModule(m_Device, &modinfo, NULL, &mod);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
void VulkanDebugManager::PatchLineStripIndexBuffer(const DrawcallDescription *draw,
GPUBuffer &indexBuffer, uint32_t &indexCount)
{
VulkanRenderState &rs = m_pDriver->m_RenderState;
bytebuf indices;
uint8_t *idx8 = NULL;
uint16_t *idx16 = NULL;
uint32_t *idx32 = NULL;
if(draw->flags & DrawFlags::Indexed)
{
GetBufferData(rs.ibuffer.buf,
rs.ibuffer.offs + uint64_t(draw->indexOffset) * draw->indexByteWidth,
uint64_t(draw->numIndices) * draw->indexByteWidth, indices);
if(rs.ibuffer.bytewidth == 4)
idx32 = (uint32_t *)indices.data();
else if(rs.ibuffer.bytewidth == 1)
idx8 = (uint8_t *)indices.data();
else
idx16 = (uint16_t *)indices.data();
}
// we just patch up to 32-bit since we'll be adding more indices and we might overflow 16-bit.
std::vector<uint32_t> patchedIndices;
::PatchLineStripIndexBuffer(draw, idx8, idx16, idx32, patchedIndices);
indexBuffer.Create(m_pDriver, m_Device, patchedIndices.size() * sizeof(uint32_t), 1,
GPUBuffer::eGPUBufferIBuffer);
void *ptr = indexBuffer.Map(0, patchedIndices.size() * sizeof(uint32_t));
memcpy(ptr, patchedIndices.data(), patchedIndices.size() * sizeof(uint32_t));
indexBuffer.Unmap();
rs.ibuffer.offs = 0;
rs.ibuffer.bytewidth = 4;
rs.ibuffer.buf = GetResID(indexBuffer.buf);
VkBufferMemoryBarrier uploadbarrier = {
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
NULL,
VK_ACCESS_HOST_WRITE_BIT,
VK_ACCESS_INDEX_READ_BIT,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(indexBuffer.buf),
0,
indexBuffer.totalsize,
};
VkCommandBuffer cmd = m_pDriver->GetNextCmd();
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
VkResult vkr = ObjDisp(m_Device)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// ensure host writes finish before using as index buffer
DoPipelineBarrier(cmd, 1, &uploadbarrier);
ObjDisp(m_Device)->EndCommandBuffer(Unwrap(cmd));
indexCount = (uint32_t)patchedIndices.size();
}
ResourceId VulkanReplay::RenderOverlay(ResourceId texid, CompType typeHint, FloatVector clearCol,
DebugOverlay overlay, uint32_t eventId,
const std::vector<uint32_t> &passEvents)
{
const VkDevDispatchTable *vt = ObjDisp(m_Device);
VulkanShaderCache *shaderCache = m_pDriver->GetShaderCache();
VulkanCreationInfo::Image &iminfo = m_pDriver->m_CreationInfo.m_Image[texid];
// bail out if the framebuffer dimensions don't match the current framebuffer, or draws will fail.
// This is an order-of-operations problem, if the overlay is set when the event is changed it is
// refreshed before the UI layer can update the current texture.
{
const VulkanCreationInfo::Framebuffer &fb =
m_pDriver->m_CreationInfo.m_Framebuffer[m_pDriver->m_RenderState.GetFramebuffer()];
if(fb.width != iminfo.extent.width || fb.height != iminfo.extent.height)
return GetResID(m_Overlay.Image);
}
VkCommandBuffer cmd = m_pDriver->GetNextCmd();
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
VkResult vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkMarkerRegion::Begin(StringFormat::Fmt("RenderOverlay %d", overlay), cmd);
// if the overlay image is the wrong size, free it
if(m_Overlay.Image != VK_NULL_HANDLE && (iminfo.extent.width != m_Overlay.ImageDim.width ||
iminfo.extent.height != m_Overlay.ImageDim.height))
{
m_pDriver->vkDestroyRenderPass(m_Device, m_Overlay.NoDepthRP, NULL);
m_pDriver->vkDestroyFramebuffer(m_Device, m_Overlay.NoDepthFB, NULL);
m_pDriver->vkDestroyImageView(m_Device, m_Overlay.ImageView, NULL);
m_pDriver->vkDestroyImage(m_Device, m_Overlay.Image, NULL);
m_Overlay.Image = VK_NULL_HANDLE;
m_Overlay.ImageView = VK_NULL_HANDLE;
m_Overlay.NoDepthRP = VK_NULL_HANDLE;
m_Overlay.NoDepthFB = VK_NULL_HANDLE;
}
// create the overlay image if we don't have one already
// 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_Overlay.Image == VK_NULL_HANDLE)
{
m_Overlay.ImageDim.width = iminfo.extent.width;
m_Overlay.ImageDim.height = iminfo.extent.height;
VkImageCreateInfo imInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
NULL,
0,
VK_IMAGE_TYPE_2D,
VK_FORMAT_R16G16B16A16_SFLOAT,
{m_Overlay.ImageDim.width, m_Overlay.ImageDim.height, 1},
1,
1,
iminfo.samples,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0,
NULL,
VK_IMAGE_LAYOUT_UNDEFINED,
};
vkr = m_pDriver->vkCreateImage(m_Device, &imInfo, NULL, &m_Overlay.Image);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkMemoryRequirements mrq = {0};
m_pDriver->vkGetImageMemoryRequirements(m_Device, m_Overlay.Image, &mrq);
// if no memory is allocated, or it's not enough,
// then allocate
if(m_Overlay.ImageMem == VK_NULL_HANDLE || mrq.size > m_Overlay.ImageMemSize)
{
if(m_Overlay.ImageMem != VK_NULL_HANDLE)
{
m_pDriver->vkFreeMemory(m_Device, m_Overlay.ImageMem, NULL);
}
VkMemoryAllocateInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size,
m_pDriver->GetGPULocalMemoryIndex(mrq.memoryTypeBits),
};
vkr = m_pDriver->vkAllocateMemory(m_Device, &allocInfo, NULL, &m_Overlay.ImageMem);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_Overlay.ImageMemSize = mrq.size;
}
vkr = m_pDriver->vkBindImageMemory(m_Device, m_Overlay.Image, m_Overlay.ImageMem, 0);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkImageViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
NULL,
0,
m_Overlay.Image,
VK_IMAGE_VIEW_TYPE_2D,
imInfo.format,
{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY},
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
};
vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &m_Overlay.ImageView);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// need to update image layout into valid state
VkImageMemoryBarrier barrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
0,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
};
m_pDriver->m_ImageLayouts[GetResID(m_Overlay.Image)].subresourceStates[0].newLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
DoPipelineBarrier(cmd, 1, &barrier);
VkAttachmentDescription colDesc = {
0,
imInfo.format,
imInfo.samples,
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,
};
VkAttachmentReference colRef = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
VkSubpassDescription sub = {
0, VK_PIPELINE_BIND_POINT_GRAPHICS,
0, NULL, // inputs
1, &colRef, // color
NULL, // resolve
NULL, // depth-stencil
0, NULL, // preserve
};
VkRenderPassCreateInfo rpinfo = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
NULL,
0,
1,
&colDesc,
1,
&sub,
0,
NULL, // dependencies
};
vkr = m_pDriver->vkCreateRenderPass(m_Device, &rpinfo, NULL, &m_Overlay.NoDepthRP);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// Create framebuffer rendering just to overlay image, no depth
VkFramebufferCreateInfo fbinfo = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
NULL,
0,
m_Overlay.NoDepthRP,
1,
&m_Overlay.ImageView,
(uint32_t)m_Overlay.ImageDim.width,
(uint32_t)m_Overlay.ImageDim.height,
1,
};
vkr = m_pDriver->vkCreateFramebuffer(m_Device, &fbinfo, NULL, &m_Overlay.NoDepthFB);
RDCASSERTEQUAL(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};
const DrawcallDescription *mainDraw = m_pDriver->GetDrawcall(eventId);
// Secondary commands can't have render passes
if((mainDraw && !(mainDraw->flags & DrawFlags::Drawcall)) ||
!m_pDriver->m_Partial[WrappedVulkan::Primary].renderPassActive)
{
// don't do anything, no drawcall capable of making overlays selected
float black[] = {0.0f, 0.0f, 0.0f, 0.0f};
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_Overlay.Image), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
(VkClearColorValue *)black, 1, &subresourceRange);
std::swap(barrier.oldLayout, barrier.newLayout);
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
DoPipelineBarrier(cmd, 1, &barrier);
}
else if(overlay == DebugOverlay::NaN || overlay == DebugOverlay::Clipping)
{
float black[] = {0.0f, 0.0f, 0.0f, 0.0f};
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_Overlay.Image), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
(VkClearColorValue *)black, 1, &subresourceRange);
std::swap(barrier.oldLayout, barrier.newLayout);
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
DoPipelineBarrier(cmd, 1, &barrier);
}
else if(overlay == DebugOverlay::Drawcall || overlay == DebugOverlay::Wireframe)
{
float highlightCol[] = {0.8f, 0.1f, 0.8f, 1.0f};
float bgclearCol[] = {0.0f, 0.0f, 0.0f, 0.5f};
if(overlay == DebugOverlay::Wireframe)
{
highlightCol[0] = 200 / 255.0f;
highlightCol[1] = 1.0f;
highlightCol[2] = 0.0f;
bgclearCol[0] = 200 / 255.0f;
bgclearCol[1] = 1.0f;
bgclearCol[2] = 0.0f;
bgclearCol[3] = 0.0f;
}
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_Overlay.Image), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
(VkClearColorValue *)bgclearCol, 1, &subresourceRange);
std::swap(barrier.oldLayout, barrier.newLayout);
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
DoPipelineBarrier(cmd, 1, &barrier);
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// backup state
VulkanRenderState prevstate = m_pDriver->m_RenderState;
// make patched shader
VkShaderModule mod = VK_NULL_HANDLE;
GetDebugManager()->PatchFixedColShader(mod, highlightCol);
// make patched pipeline
VkGraphicsPipelineCreateInfo pipeCreateInfo;
m_pDriver->GetShaderCache()->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;
VkPipelineRasterizationStateCreateInfo *rs =
(VkPipelineRasterizationStateCreateInfo *)pipeCreateInfo.pRasterizationState;
rs->cullMode = VK_CULL_MODE_NONE;
rs->rasterizerDiscardEnable = false;
// disable all discard rectangles
RemoveNextStruct(&pipeCreateInfo,
VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT);
if(m_pDriver->GetDeviceFeatures().depthClamp)
{
rs->depthClampEnable = true;
}
// disable line stipple
VkPipelineRasterizationLineStateCreateInfoEXT *lineRasterState =
(VkPipelineRasterizationLineStateCreateInfoEXT *)FindNextStruct(
rs, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT);
if(lineRasterState)
{
lineRasterState->stippledLineEnable = VK_FALSE;
}
uint32_t patchedIndexCount = 0;
GPUBuffer patchedIB;
if(overlay == DebugOverlay::Wireframe)
{
rs->lineWidth = 1.0f;
if(mainDraw == NULL)
{
// do nothing
}
else if(m_pDriver->GetDeviceFeatures().fillModeNonSolid)
{
rs->polygonMode = VK_POLYGON_MODE_LINE;
}
else if(mainDraw->topology == Topology::TriangleList ||
mainDraw->topology == Topology::TriangleStrip ||
mainDraw->topology == Topology::TriangleFan ||
mainDraw->topology == Topology::TriangleList_Adj ||
mainDraw->topology == Topology::TriangleStrip_Adj)
{
// bad drivers (aka mobile) won't have non-solid fill mode, so we have to fall back to
// manually patching the index buffer and using a line list. This doesn't work with
// adjacency or patchlist topologies since those imply a vertex processing pipeline that
// requires a particular topology, or can't be implicitly converted to lines at input stage.
// It's unlikely those features will be used on said poor hw, so this should still catch
// most cases.
VkPipelineInputAssemblyStateCreateInfo *ia =
(VkPipelineInputAssemblyStateCreateInfo *)pipeCreateInfo.pInputAssemblyState;
ia->topology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
// thankfully, primitive restart is always supported! This makes the index buffer a bit more
// compact in the common cases where we don't need to repeat two indices for a triangle's
// three lines, instead we have a single restart index after each triangle.
ia->primitiveRestartEnable = true;
GetDebugManager()->PatchLineStripIndexBuffer(mainDraw, patchedIB, patchedIndexCount);
}
else
{
RDCWARN("Unable to draw wireframe overlay for %s topology draw via software patching",
ToStr(mainDraw->topology).c_str());
}
}
VkPipelineColorBlendStateCreateInfo *cb =
(VkPipelineColorBlendStateCreateInfo *)pipeCreateInfo.pColorBlendState;
cb->logicOpEnable = false;
cb->attachmentCount = 1; // only one colour attachment
for(uint32_t i = 0; i < cb->attachmentCount; i++)
{
VkPipelineColorBlendAttachmentState *att =
(VkPipelineColorBlendAttachmentState *)&cb->pAttachments[i];
att->blendEnable = false;
att->colorWriteMask = 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 = 16384;
sc.extent.height = 16384;
}
// set our renderpass and shader
pipeCreateInfo.renderPass = m_Overlay.NoDepthRP;
pipeCreateInfo.subpass = 0;
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 = mod;
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 = mod;
sh.pName = "main";
sh.pSpecializationInfo = NULL;
}
VkPipeline pipe = VK_NULL_HANDLE;
vkr = m_pDriver->vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL,
&pipe);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// modify state
m_pDriver->m_RenderState.renderPass = GetResID(m_Overlay.NoDepthRP);
m_pDriver->m_RenderState.subpass = 0;
m_pDriver->m_RenderState.SetFramebuffer(GetResID(m_Overlay.NoDepthFB));
m_pDriver->m_RenderState.graphics.pipeline = GetResID(pipe);
// set dynamic scissors in case pipeline was using them
for(size_t i = 0; i < m_pDriver->m_RenderState.scissors.size(); i++)
{
m_pDriver->m_RenderState.scissors[i].offset.x = 0;
m_pDriver->m_RenderState.scissors[i].offset.x = 0;
m_pDriver->m_RenderState.scissors[i].extent.width = 16384;
m_pDriver->m_RenderState.scissors[i].extent.height = 16384;
}
if(overlay == DebugOverlay::Wireframe)
m_pDriver->m_RenderState.lineWidth = 1.0f;
if(overlay == DebugOverlay::Drawcall || overlay == DebugOverlay::Wireframe)
m_pDriver->m_RenderState.conditionalRendering.forceDisable = true;
if(patchedIndexCount == 0)
{
m_pDriver->ReplayLog(0, eventId, eReplay_OnlyDraw);
}
else
{
// if we patched the index buffer we need to manually play the draw with a higher index count
// and no index offset.
cmd = m_pDriver->GetNextCmd();
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// do single draw
m_pDriver->m_RenderState.BeginRenderPassAndApplyState(cmd, VulkanRenderState::BindGraphics);
ObjDisp(cmd)->CmdDrawIndexed(Unwrap(cmd), patchedIndexCount, mainDraw->numInstances, 0, 0,
mainDraw->instanceOffset);
m_pDriver->m_RenderState.EndRenderPass(cmd);
vkr = ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
// 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);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// restore state
m_pDriver->m_RenderState = prevstate;
patchedIB.Destroy();
m_pDriver->vkDestroyPipeline(m_Device, pipe, NULL);
m_pDriver->vkDestroyShaderModule(m_Device, mod, NULL);
}
else if(overlay == DebugOverlay::ViewportScissor)
{
// clear the whole image to opaque black. We'll overwite the render area with transparent black
// before rendering the viewport/scissors
float black[] = {0.0f, 0.0f, 0.0f, 1.0f};
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_Overlay.Image), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
(VkClearColorValue *)black, 1, &subresourceRange);
std::swap(barrier.oldLayout, barrier.newLayout);
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
DoPipelineBarrier(cmd, 1, &barrier);
black[3] = 0.0f;
{
VkClearValue clearval = {};
VkRenderPassBeginInfo rpbegin = {
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
NULL,
Unwrap(m_Overlay.NoDepthRP),
Unwrap(m_Overlay.NoDepthFB),
m_pDriver->m_RenderState.renderArea,
1,
&clearval,
};
vt->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE);
VkClearRect rect = {
{
{
m_pDriver->m_RenderState.renderArea.offset.x,
m_pDriver->m_RenderState.renderArea.offset.y,
},
{
m_pDriver->m_RenderState.renderArea.extent.width,
m_pDriver->m_RenderState.renderArea.extent.height,
},
},
0,
1,
};
VkClearAttachment blackclear = {VK_IMAGE_ASPECT_COLOR_BIT, 0, {}};
vt->CmdClearAttachments(Unwrap(cmd), 1, &blackclear, 1, &rect);
VkViewport viewport = m_pDriver->m_RenderState.views[0];
vt->CmdSetViewport(Unwrap(cmd), 0, 1, &viewport);
uint32_t uboOffs = 0;
CheckerboardUBOData *ubo = (CheckerboardUBOData *)m_Overlay.m_CheckerUBO.Map(&uboOffs);
ubo->BorderWidth = 3;
ubo->CheckerSquareDimension = 16.0f;
// set primary/secondary to the same to 'disable' checkerboard
ubo->PrimaryColor = ubo->SecondaryColor = Vec4f(0.1f, 0.1f, 0.1f, 1.0f);
ubo->InnerColor = Vec4f(0.2f, 0.2f, 0.9f, 0.7f);
// set viewport rect
ubo->RectPosition = Vec2f(viewport.x, viewport.y);
ubo->RectSize = Vec2f(viewport.width, viewport.height);
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_AMD_negative_viewport_height ||
m_pDriver->GetExtensions(GetRecord(m_Device)).ext_KHR_maintenance1)
{
ubo->RectSize.y = fabs(viewport.height);
// VK_KHR_maintenance1 requires the position to be adjusted as well
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_KHR_maintenance1 &&
viewport.height < 0.0f)
ubo->RectPosition.y += viewport.height;
}
m_Overlay.m_CheckerUBO.Unmap();
vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS,
Unwrap(m_Overlay.m_CheckerF16Pipeline[SampleIndex(iminfo.samples)]));
vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS,
Unwrap(m_Overlay.m_CheckerPipeLayout), 0, 1,
UnwrapPtr(m_Overlay.m_CheckerDescSet), 1, &uboOffs);
vt->CmdDraw(Unwrap(cmd), 4, 1, 0, 0);
if(!m_pDriver->m_RenderState.scissors.empty())
{
Vec4f scissor((float)m_pDriver->m_RenderState.scissors[0].offset.x,
(float)m_pDriver->m_RenderState.scissors[0].offset.y,
(float)m_pDriver->m_RenderState.scissors[0].extent.width,
(float)m_pDriver->m_RenderState.scissors[0].extent.height);
ubo = (CheckerboardUBOData *)m_Overlay.m_CheckerUBO.Map(&uboOffs);
ubo->BorderWidth = 3;
ubo->CheckerSquareDimension = 16.0f;
// black/white checkered border
ubo->PrimaryColor = Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
ubo->SecondaryColor = Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
// nothing at all inside
ubo->InnerColor = Vec4f(0.0f, 0.0f, 0.0f, 0.0f);
ubo->RectPosition = Vec2f(scissor.x, scissor.y);
ubo->RectSize = Vec2f(scissor.z, scissor.w);
m_Overlay.m_CheckerUBO.Unmap();
viewport.x = scissor.x;
viewport.y = scissor.y;
viewport.width = scissor.z;
viewport.height = scissor.w;
vt->CmdSetViewport(Unwrap(cmd), 0, 1, &viewport);
vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS,
Unwrap(m_Overlay.m_CheckerPipeLayout), 0, 1,
UnwrapPtr(m_Overlay.m_CheckerDescSet), 1, &uboOffs);
vt->CmdDraw(Unwrap(cmd), 4, 1, 0, 0);
}
vt->CmdEndRenderPass(Unwrap(cmd));
}
}
else if(overlay == DebugOverlay::BackfaceCull)
{
float highlightCol[] = {0.0f, 0.0f, 0.0f, 0.0f};
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_Overlay.Image), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
(VkClearColorValue *)highlightCol, 1, &subresourceRange);
std::swap(barrier.oldLayout, barrier.newLayout);
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
DoPipelineBarrier(cmd, 1, &barrier);
highlightCol[0] = 1.0f;
highlightCol[3] = 1.0f;
// backup state
VulkanRenderState prevstate = m_pDriver->m_RenderState;
// make patched shader
VkShaderModule mod[2] = {0};
VkPipeline pipe[2] = {0};
// first shader, no culling, writes red
GetDebugManager()->PatchFixedColShader(mod[0], highlightCol);
highlightCol[0] = 0.0f;
highlightCol[1] = 1.0f;
// second shader, normal culling, writes green
GetDebugManager()->PatchFixedColShader(mod[1], highlightCol);
// make patched pipeline
VkGraphicsPipelineCreateInfo pipeCreateInfo;
m_pDriver->GetShaderCache()->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;
VkPipelineRasterizationStateCreateInfo *rs =
(VkPipelineRasterizationStateCreateInfo *)pipeCreateInfo.pRasterizationState;
VkCullModeFlags origCullMode = rs->cullMode;
rs->cullMode = VK_CULL_MODE_NONE; // first render without any culling
rs->rasterizerDiscardEnable = false;
if(m_pDriver->GetDeviceFeatures().depthClamp)
rs->depthClampEnable = true;
VkPipelineColorBlendStateCreateInfo *cb =
(VkPipelineColorBlendStateCreateInfo *)pipeCreateInfo.pColorBlendState;
cb->logicOpEnable = false;
cb->attachmentCount = 1; // only one colour attachment
for(uint32_t i = 0; i < cb->attachmentCount; i++)
{
VkPipelineColorBlendAttachmentState *att =
(VkPipelineColorBlendAttachmentState *)&cb->pAttachments[i];
att->blendEnable = false;
att->colorWriteMask = 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 = 16384;
sc.extent.height = 16384;
}
// set our renderpass and shader
pipeCreateInfo.renderPass = m_Overlay.NoDepthRP;
pipeCreateInfo.subpass = 0;
VkPipelineShaderStageCreateInfo *fragShader = NULL;
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 = mod[0];
sh.pName = "main";
fragShader = &sh;
break;
}
}
if(fragShader == NULL)
{
// 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 = mod[0];
sh.pName = "main";
sh.pSpecializationInfo = NULL;
fragShader = &sh;
}
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
vkr = m_pDriver->vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL,
&pipe[0]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
fragShader->module = mod[1];
rs->cullMode = origCullMode;
vkr = m_pDriver->vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL,
&pipe[1]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// modify state
m_pDriver->m_RenderState.renderPass = GetResID(m_Overlay.NoDepthRP);
m_pDriver->m_RenderState.subpass = 0;
m_pDriver->m_RenderState.SetFramebuffer(GetResID(m_Overlay.NoDepthFB));
m_pDriver->m_RenderState.graphics.pipeline = GetResID(pipe[0]);
// set dynamic scissors in case pipeline was using them
for(size_t i = 0; i < m_pDriver->m_RenderState.scissors.size(); i++)
{
m_pDriver->m_RenderState.scissors[i].offset.x = 0;
m_pDriver->m_RenderState.scissors[i].offset.x = 0;
m_pDriver->m_RenderState.scissors[i].extent.width = 16384;
m_pDriver->m_RenderState.scissors[i].extent.height = 16384;
}
m_pDriver->ReplayLog(0, eventId, eReplay_OnlyDraw);
m_pDriver->m_RenderState.graphics.pipeline = GetResID(pipe[1]);
m_pDriver->ReplayLog(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);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// restore state
m_pDriver->m_RenderState = prevstate;
for(int i = 0; i < 2; i++)
{
m_pDriver->vkDestroyPipeline(m_Device, pipe[i], NULL);
m_pDriver->vkDestroyShaderModule(m_Device, mod[i], NULL);
}
}
else if(overlay == DebugOverlay::Depth || overlay == DebugOverlay::Stencil)
{
float highlightCol[] = {0.0f, 0.0f, 0.0f, 0.0f};
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_Overlay.Image), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
(VkClearColorValue *)highlightCol, 1, &subresourceRange);
std::swap(barrier.oldLayout, barrier.newLayout);
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
DoPipelineBarrier(cmd, 1, &barrier);
VkFramebuffer depthFB = VK_NULL_HANDLE;
VkRenderPass depthRP = VK_NULL_HANDLE;
const VulkanRenderState &state = m_pDriver->m_RenderState;
VulkanCreationInfo &createinfo = m_pDriver->m_CreationInfo;
RDCASSERT(state.subpass < createinfo.m_RenderPass[state.renderPass].subpasses.size());
int32_t dsIdx =
createinfo.m_RenderPass[state.renderPass].subpasses[state.subpass].depthstencilAttachment;
// make a renderpass and framebuffer for rendering to overlay color and using
// depth buffer from the orignial render
if(dsIdx >= 0 &&
dsIdx < (int32_t)createinfo.m_Framebuffer[state.GetFramebuffer()].attachments.size())
{
VkAttachmentDescription attDescs[] = {
{0, VK_FORMAT_R16G16B16A16_SFLOAT, VK_SAMPLE_COUNT_1_BIT, 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},
{0, VK_FORMAT_UNDEFINED, VK_SAMPLE_COUNT_1_BIT, // will patch this just below
VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL},
};
ResourceId depthView = state.GetFramebufferAttachments()[dsIdx];
VulkanCreationInfo::ImageView &depthViewInfo = createinfo.m_ImageView[depthView];
ResourceId depthIm = depthViewInfo.image;
VulkanCreationInfo::Image &depthImageInfo = createinfo.m_Image[depthIm];
attDescs[1].format = depthImageInfo.format;
attDescs[0].samples = attDescs[1].samples = iminfo.samples;
std::vector<ImageRegionState> &depthStates =
m_pDriver->m_ImageLayouts[depthIm].subresourceStates;
for(ImageRegionState &ds : depthStates)
{
// find the state that overlaps the view's subresource range start. We assume all
// subresources are correctly in the same state (as they should be) so we just need to find
// the first match.
if(ds.subresourceRange.baseArrayLayer <= depthViewInfo.range.baseArrayLayer &&
ds.subresourceRange.baseArrayLayer + 1 > depthViewInfo.range.baseArrayLayer &&
ds.subresourceRange.baseMipLevel <= depthViewInfo.range.baseMipLevel &&
ds.subresourceRange.baseMipLevel + ds.subresourceRange.levelCount + 1 >
depthViewInfo.range.baseMipLevel)
{
attDescs[1].initialLayout = attDescs[1].finalLayout = ds.newLayout;
break;
}
}
VkAttachmentReference colRef = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
VkAttachmentReference dsRef = {1, attDescs[1].initialLayout};
VkSubpassDescription sub = {
0, VK_PIPELINE_BIND_POINT_GRAPHICS,
0, NULL, // inputs
1, &colRef, // color
NULL, // resolve
&dsRef, // depth-stencil
0, NULL, // preserve
};
VkRenderPassCreateInfo rpinfo = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
NULL,
0,
2,
attDescs,
1,
&sub,
0,
NULL, // dependencies
};
vkr = m_pDriver->vkCreateRenderPass(m_Device, &rpinfo, NULL, &depthRP);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkImageView views[] = {
m_Overlay.ImageView,
m_pDriver->GetResourceManager()->GetCurrentHandle<VkImageView>(depthView),
};
// Create framebuffer rendering just to overlay image, no depth
VkFramebufferCreateInfo fbinfo = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
NULL,
0,
depthRP,
2,
views,
(uint32_t)m_Overlay.ImageDim.width,
(uint32_t)m_Overlay.ImageDim.height,
1,
};
vkr = m_pDriver->vkCreateFramebuffer(m_Device, &fbinfo, NULL, &depthFB);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
// if depthRP is NULL, so is depthFB, and it means no depth buffer was
// bound, so we just render green.
highlightCol[0] = 1.0f;
highlightCol[3] = 1.0f;
// backup state
VulkanRenderState prevstate = m_pDriver->m_RenderState;
// make patched shader
VkShaderModule failmod = {}, passmod = {};
VkPipeline failpipe = {}, passpipe = {};
// first shader, no depth/stencil testing, writes red
GetDebugManager()->PatchFixedColShader(failmod, highlightCol);
highlightCol[0] = 0.0f;
highlightCol[1] = 1.0f;
// second shader, enabled depth/stencil testing, writes green
GetDebugManager()->PatchFixedColShader(passmod, highlightCol);
// make patched pipeline
VkGraphicsPipelineCreateInfo pipeCreateInfo;
m_pDriver->GetShaderCache()->MakeGraphicsPipelineInfo(pipeCreateInfo,
prevstate.graphics.pipeline);
// disable all tests possible
VkPipelineDepthStencilStateCreateInfo *ds =
(VkPipelineDepthStencilStateCreateInfo *)pipeCreateInfo.pDepthStencilState;
VkBool32 origDepthTest = ds->depthTestEnable;
ds->depthTestEnable = false;
ds->depthWriteEnable = false;
VkBool32 origStencilTest = ds->stencilTestEnable;
ds->stencilTestEnable = false;
ds->depthBoundsTestEnable = false;
VkPipelineColorBlendStateCreateInfo *cb =
(VkPipelineColorBlendStateCreateInfo *)pipeCreateInfo.pColorBlendState;
cb->logicOpEnable = false;
cb->attachmentCount = 1; // only one colour attachment
for(uint32_t i = 0; i < cb->attachmentCount; i++)
{
VkPipelineColorBlendAttachmentState *att =
(VkPipelineColorBlendAttachmentState *)&cb->pAttachments[i];
att->blendEnable = false;
att->colorWriteMask = 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 = 16384;
sc.extent.height = 16384;
}
// subpass 0 in either render pass
pipeCreateInfo.subpass = 0;
VkPipelineShaderStageCreateInfo *fragShader = NULL;
for(uint32_t i = 0; i < pipeCreateInfo.stageCount; i++)
{
VkPipelineShaderStageCreateInfo &sh =
(VkPipelineShaderStageCreateInfo &)pipeCreateInfo.pStages[i];
if(sh.stage == VK_SHADER_STAGE_FRAGMENT_BIT)
{
sh.pName = "main";
fragShader = &sh;
break;
}
}
if(fragShader == NULL)
{
// 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.pName = "main";
sh.pSpecializationInfo = NULL;
fragShader = &sh;
}
fragShader->module = passmod;
if(depthRP != VK_NULL_HANDLE)
{
if(overlay == DebugOverlay::Depth)
ds->depthTestEnable = origDepthTest;
else
ds->stencilTestEnable = origStencilTest;
pipeCreateInfo.renderPass = depthRP;
}
else
{
pipeCreateInfo.renderPass = m_Overlay.NoDepthRP;
}
vkr = m_pDriver->vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL,
&passpipe);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
fragShader->module = failmod;
// set our renderpass and shader
pipeCreateInfo.renderPass = m_Overlay.NoDepthRP;
// disable culling/discard and enable depth clamp. That way we show any failures due to these
VkPipelineRasterizationStateCreateInfo *rs =
(VkPipelineRasterizationStateCreateInfo *)pipeCreateInfo.pRasterizationState;
rs->cullMode = VK_CULL_MODE_NONE;
rs->rasterizerDiscardEnable = false;
if(m_pDriver->GetDeviceFeatures().depthClamp)
rs->depthClampEnable = true;
vkr = m_pDriver->vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1, &pipeCreateInfo, NULL,
&failpipe);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// modify state
m_pDriver->m_RenderState.renderPass = GetResID(m_Overlay.NoDepthRP);
m_pDriver->m_RenderState.subpass = 0;
m_pDriver->m_RenderState.SetFramebuffer(GetResID(m_Overlay.NoDepthFB));
m_pDriver->m_RenderState.graphics.pipeline = GetResID(failpipe);
// set dynamic scissors in case pipeline was using them
for(size_t i = 0; i < m_pDriver->m_RenderState.scissors.size(); i++)
{
m_pDriver->m_RenderState.scissors[i].offset.x = 0;
m_pDriver->m_RenderState.scissors[i].offset.x = 0;
m_pDriver->m_RenderState.scissors[i].extent.width = 16384;
m_pDriver->m_RenderState.scissors[i].extent.height = 16384;
}
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_pDriver->ReplayLog(0, eventId, eReplay_OnlyDraw);
m_pDriver->m_RenderState.graphics.pipeline = GetResID(passpipe);
if(depthRP != VK_NULL_HANDLE)
{
m_pDriver->m_RenderState.renderPass = GetResID(depthRP);
m_pDriver->m_RenderState.SetFramebuffer(GetResID(depthFB));
}
m_pDriver->ReplayLog(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);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// restore state
m_pDriver->m_RenderState = prevstate;
m_pDriver->vkDestroyPipeline(m_Device, failpipe, NULL);
m_pDriver->vkDestroyShaderModule(m_Device, failmod, NULL);
m_pDriver->vkDestroyPipeline(m_Device, passpipe, NULL);
m_pDriver->vkDestroyShaderModule(m_Device, passmod, NULL);
if(depthRP != VK_NULL_HANDLE)
{
m_pDriver->vkDestroyRenderPass(m_Device, depthRP, NULL);
m_pDriver->vkDestroyFramebuffer(m_Device, depthFB, NULL);
}
}
else if(overlay == DebugOverlay::ClearBeforeDraw || overlay == DebugOverlay::ClearBeforePass)
{
// clear the overlay image itself
float black[] = {0.0f, 0.0f, 0.0f, 0.0f};
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_Overlay.Image), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
(VkClearColorValue *)black, 1, &subresourceRange);
std::swap(barrier.oldLayout, barrier.newLayout);
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
DoPipelineBarrier(cmd, 1, &barrier);
std::vector<uint32_t> events = passEvents;
if(overlay == DebugOverlay::ClearBeforeDraw)
events.clear();
events.push_back(eventId);
{
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
#if ENABLED(SINGLE_FLUSH_VALIDATE)
m_pDriver->SubmitCmds();
#endif
size_t startEvent = 0;
// if we're ClearBeforePass the first event will be a vkBeginRenderPass.
// if there are any other events, we need to play up to right before them
// so that we have all the render state set up to do
// BeginRenderPassAndApplyState and a clear. If it's just the begin, we
// just play including it, do the clear, then we won't replay anything
// in the loop below
if(overlay == DebugOverlay::ClearBeforePass)
{
const DrawcallDescription *draw = m_pDriver->GetDrawcall(events[0]);
if(draw && draw->flags & DrawFlags::BeginPass)
{
if(events.size() == 1)
{
m_pDriver->ReplayLog(0, events[0], eReplay_Full);
}
else
{
startEvent = 1;
m_pDriver->ReplayLog(0, events[1], eReplay_WithoutDraw);
}
}
}
else
{
m_pDriver->ReplayLog(0, events[0], eReplay_WithoutDraw);
}
cmd = m_pDriver->GetNextCmd();
vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_pDriver->m_RenderState.BeginRenderPassAndApplyState(cmd, VulkanRenderState::BindGraphics);
VkClearAttachment clearatt = {VK_IMAGE_ASPECT_COLOR_BIT, 0, {}};
memcpy(clearatt.clearValue.color.float32, &clearCol.x,
sizeof(clearatt.clearValue.color.float32));
std::vector<VkClearAttachment> atts;
VulkanCreationInfo::Framebuffer &fb =
m_pDriver->m_CreationInfo.m_Framebuffer[m_pDriver->m_RenderState.GetFramebuffer()];
VulkanCreationInfo::RenderPass &rp =
m_pDriver->m_CreationInfo.m_RenderPass[m_pDriver->m_RenderState.renderPass];
for(size_t i = 0; i < rp.subpasses[m_pDriver->m_RenderState.subpass].colorAttachments.size();
i++)
{
clearatt.colorAttachment = (uint32_t)i;
atts.push_back(clearatt);
}
VkClearRect rect = {
{{0, 0}, {fb.width, fb.height}}, 0, 1,
};
vt->CmdClearAttachments(Unwrap(cmd), (uint32_t)atts.size(), &atts[0], 1, &rect);
m_pDriver->m_RenderState.EndRenderPass(cmd);
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
for(size_t i = startEvent; i < events.size(); i++)
{
m_pDriver->ReplayLog(events[i], events[i], eReplay_OnlyDraw);
if(overlay == DebugOverlay::ClearBeforePass && i + 1 < events.size())
m_pDriver->ReplayLog(events[i] + 1, events[i + 1], eReplay_WithoutDraw);
}
cmd = m_pDriver->GetNextCmd();
vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
}
else if(overlay == DebugOverlay::QuadOverdrawPass || overlay == DebugOverlay::QuadOverdrawDraw)
{
VulkanRenderState prevstate = m_pDriver->m_RenderState;
if(m_Overlay.m_QuadResolvePipeline[0] != VK_NULL_HANDLE)
{
SCOPED_TIMER("Quad Overdraw");
float black[] = {0.0f, 0.0f, 0.0f, 0.0f};
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_Overlay.Image),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (VkClearColorValue *)black, 1,
&subresourceRange);
std::swap(barrier.oldLayout, barrier.newLayout);
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
DoPipelineBarrier(cmd, 1, &barrier);
std::vector<uint32_t> events = passEvents;
if(overlay == DebugOverlay::QuadOverdrawDraw)
events.clear();
events.push_back(eventId);
// if we're rendering the whole pass, and the first draw is a BeginRenderPass, don't include
// it in the list. We want to start by replaying into the renderpass so that we have the
// correct state being applied.
if(overlay == DebugOverlay::QuadOverdrawPass)
{
const DrawcallDescription *draw = m_pDriver->GetDrawcall(events[0]);
if(draw->flags & DrawFlags::BeginPass)
events.erase(events.begin());
}
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(1U, m_Overlay.ImageDim.width >> 1), RDCMAX(1U, m_Overlay.ImageDim.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,
};
vkr = m_pDriver->vkCreateImage(m_Device, &imInfo, NULL, &quadImg);
RDCASSERTEQUAL(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);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
vkr = m_pDriver->vkBindImageMemory(m_Device, quadImg, quadImgMem, 0);
RDCASSERTEQUAL(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);
RDCASSERTEQUAL(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_Overlay.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},
};
// clear all to black
DoPipelineBarrier(cmd, 1, &quadImBarrier);
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
DoPipelineBarrier(cmd, 1, &quadImBarrier);
VkMemoryBarrier memBarrier = {
VK_STRUCTURE_TYPE_MEMORY_BARRIER, NULL, VK_ACCESS_ALL_WRITE_BITS, VK_ACCESS_ALL_READ_BITS,
};
DoPipelineBarrier(cmd, 1, &memBarrier);
// end this cmd buffer so the image is in the right state for the next part
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
#if ENABLED(SINGLE_FLUSH_VALIDATE)
m_pDriver->SubmitCmds();
#endif
m_pDriver->ReplayLog(0, events[0], eReplay_WithoutDraw);
// declare callback struct here
VulkanQuadOverdrawCallback cb(m_pDriver, m_Overlay.m_QuadDescSetLayout,
m_Overlay.m_QuadDescSet, events);
m_pDriver->ReplayLog(events.front(), events.back(), eReplay_Full);
// resolve pass
{
cmd = m_pDriver->GetNextCmd();
vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
quadImBarrier.srcAccessMask = quadImBarrier.dstAccessMask;
quadImBarrier.oldLayout = quadImBarrier.newLayout;
quadImBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
// wait for writing to finish
DoPipelineBarrier(cmd, 1, &quadImBarrier);
VkClearValue clearval = {};
VkRenderPassBeginInfo rpbegin = {
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
NULL,
Unwrap(m_Overlay.NoDepthRP),
Unwrap(m_Overlay.NoDepthFB),
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_Overlay.m_QuadResolvePipeline[SampleIndex(iminfo.samples)]));
vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS,
Unwrap(m_Overlay.m_QuadResolvePipeLayout), 0, 1,
UnwrapPtr(m_Overlay.m_QuadDescSet), 0, NULL);
VkViewport viewport = {
0.0f, 0.0f, (float)m_Overlay.ImageDim.width, (float)m_Overlay.ImageDim.height,
0.0f, 1.0f};
vt->CmdSetViewport(Unwrap(cmd), 0, 1, &viewport);
vt->CmdDraw(Unwrap(cmd), 4, 1, 0, 0);
vt->CmdEndRenderPass(Unwrap(cmd));
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(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.pipe, NULL);
m_pDriver->vkDestroyPipelineLayout(m_Device, it->second.pipeLayout, NULL);
}
}
// restore back to normal
m_pDriver->ReplayLog(0, eventId, eReplay_WithoutDraw);
cmd = m_pDriver->GetNextCmd();
vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
else if(overlay == DebugOverlay::TriangleSizePass || overlay == DebugOverlay::TriangleSizeDraw)
{
VulkanRenderState prevstate = m_pDriver->m_RenderState;
VkPipelineShaderStageCreateInfo stages[3] = {
{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_VERTEX_BIT,
shaderCache->GetBuiltinModule(BuiltinShader::MeshVS), "main", NULL},
{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_FRAGMENT_BIT,
shaderCache->GetBuiltinModule(BuiltinShader::TrisizeFS), "main", NULL},
{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_GEOMETRY_BIT,
shaderCache->GetBuiltinModule(BuiltinShader::TrisizeGS), "main", NULL},
};
if(stages[0].module != VK_NULL_HANDLE && stages[1].module != VK_NULL_HANDLE &&
stages[2].module != VK_NULL_HANDLE)
{
SCOPED_TIMER("Triangle Size");
float black[] = {0.0f, 0.0f, 0.0f, 0.0f};
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_Overlay.Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
vt->CmdClearColorImage(Unwrap(cmd), Unwrap(m_Overlay.Image),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (VkClearColorValue *)black, 1,
&subresourceRange);
std::swap(barrier.oldLayout, barrier.newLayout);
std::swap(barrier.srcAccessMask, barrier.dstAccessMask);
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
DoPipelineBarrier(cmd, 1, &barrier);
// end this cmd buffer so the image is in the right state for the next part
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
#if ENABLED(SINGLE_FLUSH_VALIDATE)
m_pDriver->SubmitCmds();
#endif
std::vector<uint32_t> events = passEvents;
if(overlay == DebugOverlay::TriangleSizeDraw)
events.clear();
while(!events.empty())
{
const DrawcallDescription *draw = m_pDriver->GetDrawcall(events[0]);
// remove any non-drawcalls, like the pass boundary.
if(!draw || !(draw->flags & DrawFlags::Drawcall))
events.erase(events.begin());
else
break;
}
events.push_back(eventId);
m_pDriver->ReplayLog(0, events[0], eReplay_WithoutDraw);
VulkanRenderState &state = m_pDriver->GetRenderState();
uint32_t meshOffs = 0;
MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&meshOffs);
data->mvp = Matrix4f::Identity();
data->invProj = Matrix4f::Identity();
data->color = Vec4f();
data->homogenousInput = 1;
data->pointSpriteSize = Vec2f(0.0f, 0.0f);
data->displayFormat = 0;
data->rawoutput = 1;
data->padding = Vec3f();
m_MeshRender.UBO.Unmap();
uint32_t viewOffs = 0;
Vec4f *ubo = (Vec4f *)m_Overlay.m_TriSizeUBO.Map(&viewOffs);
*ubo = Vec4f(state.views[0].width, state.views[0].height);
m_Overlay.m_TriSizeUBO.Unmap();
uint32_t offsets[2] = {meshOffs, viewOffs};
VkDescriptorBufferInfo bufdesc;
m_MeshRender.UBO.FillDescriptor(bufdesc);
VkWriteDescriptorSet write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
NULL,
Unwrap(m_Overlay.m_TriSizeDescSet),
0,
0,
1,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
NULL,
&bufdesc,
NULL};
vt->UpdateDescriptorSets(Unwrap(m_Device), 1, &write, 0, NULL);
m_Overlay.m_TriSizeUBO.FillDescriptor(bufdesc);
write.dstBinding = 2;
vt->UpdateDescriptorSets(Unwrap(m_Device), 1, &write, 0, NULL);
VkRenderPass RP = m_Overlay.NoDepthRP;
VkFramebuffer FB = m_Overlay.NoDepthFB;
VulkanCreationInfo &createinfo = m_pDriver->m_CreationInfo;
RDCASSERT(state.subpass < createinfo.m_RenderPass[state.renderPass].subpasses.size());
int32_t dsIdx =
createinfo.m_RenderPass[state.renderPass].subpasses[state.subpass].depthstencilAttachment;
bool depthUsed = false;
// make a renderpass and framebuffer for rendering to overlay color and using
// depth buffer from the orignial render
if(dsIdx >= 0 &&
dsIdx < (int32_t)createinfo.m_Framebuffer[state.GetFramebuffer()].attachments.size())
{
depthUsed = true;
VkAttachmentDescription attDescs[] = {
{0, VK_FORMAT_R16G16B16A16_SFLOAT, VK_SAMPLE_COUNT_1_BIT, 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},
{0, VK_FORMAT_UNDEFINED, VK_SAMPLE_COUNT_1_BIT, // will patch this just below
VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL},
};
ResourceId depthView = state.GetFramebufferAttachments()[dsIdx];
VulkanCreationInfo::ImageView &depthViewInfo = createinfo.m_ImageView[depthView];
ResourceId depthIm = depthViewInfo.image;
VulkanCreationInfo::Image &depthImageInfo = createinfo.m_Image[depthIm];
attDescs[1].format = depthImageInfo.format;
attDescs[0].samples = attDescs[1].samples = iminfo.samples;
std::vector<ImageRegionState> &depthStates =
m_pDriver->m_ImageLayouts[depthIm].subresourceStates;
for(ImageRegionState &ds : depthStates)
{
// find the state that overlaps the view's subresource range start. We assume all
// subresources are correctly in the same state (as they should be) so we just need to
// find the first match.
if(ds.subresourceRange.baseArrayLayer <= depthViewInfo.range.baseArrayLayer &&
ds.subresourceRange.baseArrayLayer + 1 > depthViewInfo.range.baseArrayLayer &&
ds.subresourceRange.baseMipLevel <= depthViewInfo.range.baseMipLevel &&
ds.subresourceRange.baseMipLevel + ds.subresourceRange.levelCount + 1 >
depthViewInfo.range.baseMipLevel)
{
attDescs[1].initialLayout = attDescs[1].finalLayout = ds.newLayout;
break;
}
}
VkAttachmentReference colRef = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
VkAttachmentReference dsRef = {1, attDescs[1].initialLayout};
VkSubpassDescription sub = {
0, VK_PIPELINE_BIND_POINT_GRAPHICS,
0, NULL, // inputs
1, &colRef, // color
NULL, // resolve
&dsRef, // depth-stencil
0, NULL, // preserve
};
VkRenderPassCreateInfo rpinfo = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
NULL,
0,
2,
attDescs,
1,
&sub,
0,
NULL, // dependencies
};
vkr = m_pDriver->vkCreateRenderPass(m_Device, &rpinfo, NULL, &RP);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkImageView views[] = {
m_Overlay.ImageView,
m_pDriver->GetResourceManager()->GetCurrentHandle<VkImageView>(depthView),
};
// Create framebuffer rendering just to overlay image, no depth
VkFramebufferCreateInfo fbinfo = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
NULL,
0,
RP,
2,
views,
(uint32_t)m_Overlay.ImageDim.width,
(uint32_t)m_Overlay.ImageDim.height,
1,
};
vkr = m_pDriver->vkCreateFramebuffer(m_Device, &fbinfo, NULL, &FB);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
VkGraphicsPipelineCreateInfo pipeCreateInfo;
m_pDriver->GetShaderCache()->MakeGraphicsPipelineInfo(pipeCreateInfo, state.graphics.pipeline);
VkPipelineInputAssemblyStateCreateInfo ia = {
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
// most topologies are decomposed into triangle lists on output
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
if(pipeCreateInfo.pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
ia.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
else if(pipeCreateInfo.pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST ||
pipeCreateInfo.pInputAssemblyState->topology ==
VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY ||
pipeCreateInfo.pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP ||
pipeCreateInfo.pInputAssemblyState->topology ==
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY)
ia.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
else if(pipeCreateInfo.pInputAssemblyState->topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN)
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
VkVertexInputBindingDescription binds[] = {
// primary
{0, 0, VK_VERTEX_INPUT_RATE_VERTEX},
// secondary
{1, 0, VK_VERTEX_INPUT_RATE_VERTEX},
};
VkVertexInputAttributeDescription vertAttrs[] = {
{0, 0, VK_FORMAT_R32G32B32A32_SFLOAT, 0}, {1, 0, VK_FORMAT_R32G32B32A32_SFLOAT, 0},
};
VkPipelineVertexInputStateCreateInfo vi = {
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
NULL,
0,
1,
binds,
2,
vertAttrs,
};
VkPipelineColorBlendAttachmentState attState = {
false,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ZERO,
VK_BLEND_OP_ADD,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ZERO,
VK_BLEND_OP_ADD,
0xf,
};
VkPipelineColorBlendStateCreateInfo cb = {
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
NULL,
0,
false,
VK_LOGIC_OP_NO_OP,
1,
&attState,
{1.0f, 1.0f, 1.0f, 1.0f},
};
pipeCreateInfo.stageCount = 3;
pipeCreateInfo.pStages = stages;
pipeCreateInfo.pTessellationState = NULL;
pipeCreateInfo.renderPass = RP;
pipeCreateInfo.subpass = 0;
pipeCreateInfo.layout = m_Overlay.m_TriSizePipeLayout;
pipeCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
pipeCreateInfo.basePipelineIndex = 0;
pipeCreateInfo.pInputAssemblyState = &ia;
pipeCreateInfo.pVertexInputState = &vi;
pipeCreateInfo.pColorBlendState = &cb;
typedef rdcpair<uint32_t, Topology> PipeKey;
std::map<PipeKey, VkPipeline> pipes;
cmd = m_pDriver->GetNextCmd();
vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkClearValue clearval = {};
VkRenderPassBeginInfo rpbegin = {
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
NULL,
Unwrap(RP),
Unwrap(FB),
{{0, 0}, m_Overlay.ImageDim},
1,
&clearval,
};
vt->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE);
VkViewport viewport = {
0.0f, 0.0f, (float)m_Overlay.ImageDim.width, (float)m_Overlay.ImageDim.height,
0.0f, 1.0f};
vt->CmdSetViewport(Unwrap(cmd), 0, 1, &viewport);
for(size_t i = 0; i < events.size(); i++)
{
const DrawcallDescription *draw = m_pDriver->GetDrawcall(events[i]);
for(uint32_t inst = 0; draw && inst < RDCMAX(1U, draw->numInstances); inst++)
{
MeshFormat fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::GSOut);
if(fmt.vertexResourceId == ResourceId())
fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::VSOut);
if(fmt.vertexResourceId != ResourceId())
{
ia.topology = MakeVkPrimitiveTopology(fmt.topology);
binds[0].stride = binds[1].stride = fmt.vertexByteStride;
PipeKey key = make_rdcpair(fmt.vertexByteStride, fmt.topology);
VkPipeline pipe = pipes[key];
if(pipe == VK_NULL_HANDLE)
{
vkr = m_pDriver->vkCreateGraphicsPipelines(m_Device, VK_NULL_HANDLE, 1,
&pipeCreateInfo, NULL, &pipe);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
VkBuffer vb =
m_pDriver->GetResourceManager()->GetCurrentHandle<VkBuffer>(fmt.vertexResourceId);
VkDeviceSize offs = fmt.vertexByteOffset;
vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(vb), &offs);
pipes[key] = pipe;
vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS,
Unwrap(m_Overlay.m_TriSizePipeLayout), 0, 1,
UnwrapPtr(m_Overlay.m_TriSizeDescSet), 2, offsets);
vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(pipe));
const VkPipelineDynamicStateCreateInfo *dyn = pipeCreateInfo.pDynamicState;
for(uint32_t dynState = 0; dyn && dynState < dyn->dynamicStateCount; dynState++)
{
VkDynamicState d = dyn->pDynamicStates[dynState];
if(!state.views.empty() && d == VK_DYNAMIC_STATE_VIEWPORT)
{
vt->CmdSetViewport(Unwrap(cmd), 0, (uint32_t)state.views.size(), &state.views[0]);
}
else if(!state.scissors.empty() && d == VK_DYNAMIC_STATE_SCISSOR)
{
vt->CmdSetScissor(Unwrap(cmd), 0, (uint32_t)state.scissors.size(),
&state.scissors[0]);
}
else if(d == VK_DYNAMIC_STATE_LINE_WIDTH)
{
vt->CmdSetLineWidth(Unwrap(cmd), state.lineWidth);
}
else if(d == VK_DYNAMIC_STATE_DEPTH_BIAS)
{
vt->CmdSetDepthBias(Unwrap(cmd), state.bias.depth, state.bias.biasclamp,
state.bias.slope);
}
else if(d == VK_DYNAMIC_STATE_BLEND_CONSTANTS)
{
vt->CmdSetBlendConstants(Unwrap(cmd), state.blendConst);
}
else if(d == VK_DYNAMIC_STATE_DEPTH_BOUNDS)
{
vt->CmdSetDepthBounds(Unwrap(cmd), state.mindepth, state.maxdepth);
}
else if(d == VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK)
{
vt->CmdSetStencilCompareMask(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT,
state.back.compare);
vt->CmdSetStencilCompareMask(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT,
state.front.compare);
}
else if(d == VK_DYNAMIC_STATE_STENCIL_WRITE_MASK)
{
vt->CmdSetStencilWriteMask(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, state.back.write);
vt->CmdSetStencilWriteMask(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, state.front.write);
}
else if(d == VK_DYNAMIC_STATE_STENCIL_REFERENCE)
{
vt->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FACE_BACK_BIT, state.back.ref);
vt->CmdSetStencilReference(Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT, state.front.ref);
}
}
if(fmt.indexByteStride)
{
VkIndexType idxtype = VK_INDEX_TYPE_UINT16;
if(fmt.indexByteStride == 4)
idxtype = VK_INDEX_TYPE_UINT32;
else if(fmt.indexByteStride == 1)
idxtype = VK_INDEX_TYPE_UINT8_EXT;
if(fmt.indexResourceId != ResourceId())
{
VkBuffer ib =
m_pDriver->GetResourceManager()->GetLiveHandle<VkBuffer>(fmt.indexResourceId);
vt->CmdBindIndexBuffer(Unwrap(cmd), Unwrap(ib), fmt.indexByteOffset, idxtype);
vt->CmdDrawIndexed(Unwrap(cmd), fmt.numIndices, 1, 0, fmt.baseVertex, 0);
}
}
else
{
vt->CmdDraw(Unwrap(cmd), fmt.numIndices, 1, 0, 0);
}
}
}
}
vt->CmdEndRenderPass(Unwrap(cmd));
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_pDriver->SubmitCmds();
m_pDriver->FlushQ();
if(depthUsed)
{
m_pDriver->vkDestroyFramebuffer(m_Device, FB, NULL);
m_pDriver->vkDestroyRenderPass(m_Device, RP, NULL);
}
for(auto it = pipes.begin(); it != pipes.end(); ++it)
m_pDriver->vkDestroyPipeline(m_Device, it->second, NULL);
}
// restore back to normal
m_pDriver->ReplayLog(0, eventId, eReplay_WithoutDraw);
// restore state
m_pDriver->m_RenderState = prevstate;
cmd = m_pDriver->GetNextCmd();
vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
VkMarkerRegion::End(cmd);
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
#if ENABLED(SINGLE_FLUSH_VALIDATE)
m_pDriver->SubmitCmds();
#endif
return GetResID(m_Overlay.Image);
}