Files
renderdoc/renderdoc/driver/vulkan/vk_debug.cpp
T

4064 lines
147 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "vk_debug.h"
#include <float.h>
#include "core/settings.h"
#include "data/glsl_shaders.h"
#include "driver/ihv/amd/amd_counters.h"
#include "driver/ihv/amd/official/GPUPerfAPI/Include/GPUPerfAPI-VK.h"
#include "driver/shaders/spirv/spirv_compile.h"
#include "maths/camera.h"
#include "maths/formatpacking.h"
#include "maths/matrix.h"
#include "vk_core.h"
#include "vk_replay.h"
#include "vk_shader_cache.h"
#define VULKAN 1
#include "data/glsl/glsl_ubos_cpp.h"
RDOC_CONFIG(bool, Vulkan_HardwareCounters, true,
"Enable support for IHV-specific hardware counters on Vulkan.");
const VkDeviceSize STAGE_BUFFER_BYTE_SIZE = 16 * 1024 * 1024ULL;
static void create(WrappedVulkan *driver, const char *objName, const int line, VkSampler *sampler,
VkFilter samplerFilter)
{
VkSamplerCreateInfo sampInfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
sampInfo.minFilter = sampInfo.magFilter = samplerFilter;
sampInfo.mipmapMode = samplerFilter == VK_FILTER_NEAREST ? VK_SAMPLER_MIPMAP_MODE_NEAREST
: VK_SAMPLER_MIPMAP_MODE_LINEAR;
sampInfo.addressModeU = sampInfo.addressModeV = sampInfo.addressModeW =
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
sampInfo.maxLod = 128.0f;
VkResult vkr = driver->vkCreateSampler(driver->GetDev(), &sampInfo, NULL, sampler);
if(vkr != VK_SUCCESS)
RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str());
}
static void create(WrappedVulkan *driver, const char *objName, const int line,
VkDescriptorSetLayout *descLayout,
std::initializer_list<VkDescriptorSetLayoutBinding> bindings)
{
VkDescriptorSetLayoutCreateInfo descsetLayoutInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
NULL,
0,
(uint32_t)bindings.size(),
bindings.begin(),
};
VkResult vkr =
driver->vkCreateDescriptorSetLayout(driver->GetDev(), &descsetLayoutInfo, NULL, descLayout);
if(vkr != VK_SUCCESS)
RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str());
}
static void create(WrappedVulkan *driver, const char *objName, const int line,
VkPipelineLayout *pipeLayout, VkDescriptorSetLayout setLayout, uint32_t pushBytes)
{
VkPipelineLayoutCreateInfo pipeLayoutInfo = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
VkPushConstantRange push = {VK_SHADER_STAGE_ALL, 0, pushBytes};
if(pushBytes > 0)
{
pipeLayoutInfo.pPushConstantRanges = &push;
pipeLayoutInfo.pushConstantRangeCount = 1;
}
pipeLayoutInfo.pSetLayouts = &setLayout;
pipeLayoutInfo.setLayoutCount = 1;
VkResult vkr = driver->vkCreatePipelineLayout(driver->GetDev(), &pipeLayoutInfo, NULL, pipeLayout);
if(vkr != VK_SUCCESS)
RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str());
}
// create a single subpass renderpass with a single attachment
static void create(WrappedVulkan *driver, const char *objName, const int line,
VkRenderPass *renderPass, VkFormat attachFormat,
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT,
VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
{
VkAttachmentDescription attDesc = {0,
attachFormat,
sampleCount,
VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
layout,
layout};
VkAttachmentReference attRef = {0, layout};
VkSubpassDescription sub = {
0, VK_PIPELINE_BIND_POINT_GRAPHICS,
0, NULL, // inputs
1, &attRef, // color
};
if(IsDepthOrStencilFormat(attachFormat))
{
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
sub.colorAttachmentCount = 0;
sub.pColorAttachments = NULL;
sub.pDepthStencilAttachment = &attRef;
}
VkRenderPassCreateInfo rpinfo = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 1, &attDesc, 1, &sub,
};
VkResult vkr = driver->vkCreateRenderPass(driver->GetDev(), &rpinfo, NULL, renderPass);
if(vkr != VK_SUCCESS)
RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str());
driver->GetResourceManager()->SetInternalResource(GetResID(*renderPass));
}
// Create a compute pipeline with a shader module
static void create(WrappedVulkan *driver, const char *objName, const int line, VkPipeline *pipe,
VkPipelineLayout pipeLayout, VkShaderModule computeModule)
{
// if the module didn't compile, this pipeline is not be supported. Silently don't create it, code
// later should handle the missing pipeline as indicating lack of support
if(computeModule == VK_NULL_HANDLE)
{
*pipe = VK_NULL_HANDLE;
return;
}
VkComputePipelineCreateInfo compPipeInfo = {
VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
NULL,
0,
{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_COMPUTE_BIT,
computeModule, "main", NULL},
pipeLayout,
VK_NULL_HANDLE,
0,
};
VkResult vkr = driver->vkCreateComputePipelines(
driver->GetDev(), driver->GetShaderCache()->GetPipeCache(), 1, &compPipeInfo, NULL, pipe);
if(vkr != VK_SUCCESS)
RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str());
}
static void create(WrappedVulkan *driver, const char *objName, const int line,
VkDescriptorSet *descSet, VkDescriptorPool pool, VkDescriptorSetLayout setLayout)
{
VkDescriptorSetAllocateInfo descSetAllocInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, NULL, pool, 1, &setLayout,
};
// don't expect this to fail (or if it does then it should be immediately obvious, not transient).
VkResult vkr = driver->vkAllocateDescriptorSets(driver->GetDev(), &descSetAllocInfo, descSet);
if(vkr != VK_SUCCESS)
RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str());
}
// a simpler one-shot descriptor containing anything we might want to vary in a graphics pipeline
struct ConciseGraphicsPipeline
{
// misc
VkRenderPass renderPass;
VkPipelineLayout pipeLayout;
VkShaderModule vertex;
VkShaderModule fragment;
// dynamic state
std::initializer_list<VkDynamicState> dynstates;
// msaa
VkSampleCountFlagBits sampleCount;
bool sampleRateShading;
// depth stencil
bool depthEnable;
bool stencilEnable;
VkStencilOp stencilOp;
// color blend
bool colourOutput;
bool blendEnable;
VkBlendFactor srcBlend;
VkBlendFactor dstBlend;
uint32_t writeMask;
};
static void create(WrappedVulkan *driver, const char *objName, const int line, VkPipeline *pipe,
const ConciseGraphicsPipeline &info)
{
// if the module didn't compile, this pipeline is not be supported. Silently don't create it, code
// later should handle the missing pipeline as indicating lack of support
if(info.vertex == VK_NULL_HANDLE || info.fragment == VK_NULL_HANDLE)
return;
// first configure the structs that contain parameters derived from the info parameter
const VkPipelineShaderStageCreateInfo shaderStages[2] = {
{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_VERTEX_BIT,
info.vertex, "main", NULL},
{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0, VK_SHADER_STAGE_FRAGMENT_BIT,
info.fragment, "main", NULL},
};
const VkPipelineDynamicStateCreateInfo dynamicState = {
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
NULL,
0,
(uint32_t)info.dynstates.size(),
info.dynstates.begin(),
};
VkPipelineMultisampleStateCreateInfo msaa = {
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
};
msaa.rasterizationSamples = info.sampleCount;
if(info.sampleRateShading)
{
msaa.minSampleShading = 1.0f;
msaa.sampleShadingEnable = true;
}
const VkPipelineDepthStencilStateCreateInfo depthStencil = {
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
NULL,
0,
info.depthEnable,
info.depthEnable,
VK_COMPARE_OP_ALWAYS,
false,
info.stencilEnable,
{info.stencilOp, info.stencilOp, info.stencilOp, VK_COMPARE_OP_ALWAYS, 0xff, 0xff, 0},
{info.stencilOp, info.stencilOp, info.stencilOp, VK_COMPARE_OP_ALWAYS, 0xff, 0xff, 0},
0.0f,
1.0f,
};
const VkPipelineColorBlendAttachmentState colAttach = {
info.blendEnable,
// colour blending
info.srcBlend, info.dstBlend, VK_BLEND_OP_ADD,
// alpha blending
info.srcBlend, info.dstBlend, VK_BLEND_OP_ADD,
// write mask
info.writeMask,
};
const VkPipelineColorBlendStateCreateInfo colorBlend = {
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
NULL,
0,
false,
VK_LOGIC_OP_NO_OP,
info.colourOutput ? 1U : 0U,
&colAttach,
{1.0f, 1.0f, 1.0f, 1.0f},
};
// below this point, structs are not affected by the info
const VkPipelineVertexInputStateCreateInfo vertexInput = {
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
};
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
};
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
VkPipelineViewportStateCreateInfo viewScissor = {
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
viewScissor.viewportCount = viewScissor.scissorCount = 1;
// add default scissor, if scissor is dynamic this will be ignored.
VkRect2D scissor = {{0, 0}, {16384, 16384}};
viewScissor.pScissors = &scissor;
// can't really make a sensible one-size-fits-all default viewport like we can with scissors, so
// make it small.
VkViewport viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
viewScissor.pViewports = &viewport;
VkPipelineRasterizationStateCreateInfo raster = {
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
};
raster.frontFace = VK_FRONT_FACE_CLOCKWISE;
raster.lineWidth = 1.0f;
const VkGraphicsPipelineCreateInfo graphicsPipeInfo = {
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
NULL,
0,
2,
shaderStages,
&vertexInput,
&inputAssembly,
NULL, // tess
&viewScissor,
&raster,
&msaa,
&depthStencil,
&colorBlend,
&dynamicState,
info.pipeLayout,
info.renderPass,
0, // sub pass
VK_NULL_HANDLE, // base pipeline handle
-1, // base pipeline index
};
VkResult vkr = driver->vkCreateGraphicsPipelines(
driver->GetDev(), driver->GetShaderCache()->GetPipeCache(), 1, &graphicsPipeInfo, NULL, pipe);
if(vkr != VK_SUCCESS)
RDCERR("Failed creating object %s at line %i, vkr was %s", objName, line, ToStr(vkr).c_str());
}
// utility macro that lets us check for VkResult failures inside the utility helpers while
// preserving context from outside
#define CREATE_OBJECT(obj, ...) create(driver, #obj, __LINE__, &obj, __VA_ARGS__)
VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver)
{
if(RenderDoc::Inst().GetCrashHandler())
RenderDoc::Inst().GetCrashHandler()->RegisterMemoryRegion(this, sizeof(VulkanDebugManager));
m_pDriver = driver;
m_Device = m_pDriver->GetDev();
VkDevice dev = m_Device;
VulkanResourceManager *rm = driver->GetResourceManager();
VkResult vkr = VK_SUCCESS;
VulkanShaderCache *shaderCache = driver->GetShaderCache();
VkDescriptorPoolSize poolTypes[] = {
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2 * ARRAY_COUNT(m_ArrayMSDescSet)},
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 * ARRAY_COUNT(m_ArrayMSDescSet)},
};
VkDescriptorPoolCreateInfo poolInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
NULL,
0,
ARRAY_COUNT(m_ArrayMSDescSet),
ARRAY_COUNT(poolTypes),
&poolTypes[0],
};
CREATE_OBJECT(m_ArrayMSSampler, VK_FILTER_NEAREST);
rm->SetInternalResource(GetResID(m_ArrayMSSampler));
vkr = m_pDriver->vkCreateDescriptorPool(dev, &poolInfo, NULL, &m_ArrayMSDescriptorPool);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
rm->SetInternalResource(GetResID(m_ArrayMSDescriptorPool));
CREATE_OBJECT(m_ArrayMSDescSetLayout,
{
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_ALL, NULL},
});
rm->SetInternalResource(GetResID(m_ArrayMSDescSetLayout));
CREATE_OBJECT(m_ArrayMSPipeLayout, m_ArrayMSDescSetLayout, sizeof(Vec4u));
rm->SetInternalResource(GetResID(m_ArrayMSPipeLayout));
//////////////////////////////////////////////////////////////////
// Color MS to Array copy (via compute)
CREATE_OBJECT(m_MS2ArrayPipe, m_ArrayMSPipeLayout,
shaderCache->GetBuiltinModule(BuiltinShader::MS2ArrayCS));
CREATE_OBJECT(m_Array2MSPipe, m_ArrayMSPipeLayout,
shaderCache->GetBuiltinModule(BuiltinShader::Array2MSCS));
rm->SetInternalResource(GetResID(m_MS2ArrayPipe));
rm->SetInternalResource(GetResID(m_Array2MSPipe));
//////////////////////////////////////////////////////////////////
// Depth MS to Array copy (via graphics)
// need a dummy UINT texture to fill the binding when we don't have a stencil aspect to copy.
// unfortunately there's no single guaranteed UINT format that can be sampled as MSAA, so we try a
// few since hopefully we'll find one that will work.
VkFormat attemptFormats[] = {VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8_UINT,
VK_FORMAT_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT};
for(VkFormat f : attemptFormats)
{
VkImageAspectFlags viewAspectMask =
IsStencilFormat(f) ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
VkImageAspectFlags barrierAspectMask = viewAspectMask;
if(IsDepthAndStencilFormat(f) && (barrierAspectMask & VK_IMAGE_ASPECT_STENCIL_BIT))
barrierAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
VkFormatProperties props = {};
driver->vkGetPhysicalDeviceFormatProperties(driver->GetPhysDev(), f, &props);
if(!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
continue;
VkImageCreateInfo imInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
NULL,
0,
VK_IMAGE_TYPE_2D,
f,
{1, 1, 1},
1,
1,
VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0,
NULL,
VK_IMAGE_LAYOUT_UNDEFINED,
};
VkImageFormatProperties imgprops = {};
vkr = driver->vkGetPhysicalDeviceImageFormatProperties(driver->GetPhysDev(), f,
imInfo.imageType, imInfo.tiling,
imInfo.usage, imInfo.flags, &imgprops);
if(vkr == VK_ERROR_FORMAT_NOT_SUPPORTED)
continue;
// if it doesn't support MSAA, bail out
if(imgprops.sampleCounts == VK_SAMPLE_COUNT_1_BIT)
continue;
vkr = driver->vkCreateImage(driver->GetDev(), &imInfo, NULL, &m_DummyStencilImage[0]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
rm->SetInternalResource(GetResID(m_DummyStencilImage[0]));
imInfo.samples = VK_SAMPLE_COUNT_2_BIT;
// MoltenVK seems to only support 4/8 samples and not 2...
if(imgprops.sampleCounts & VK_SAMPLE_COUNT_2_BIT)
imInfo.samples = VK_SAMPLE_COUNT_2_BIT;
else if(imgprops.sampleCounts & VK_SAMPLE_COUNT_4_BIT)
imInfo.samples = VK_SAMPLE_COUNT_4_BIT;
else if(imgprops.sampleCounts & VK_SAMPLE_COUNT_8_BIT)
imInfo.samples = VK_SAMPLE_COUNT_8_BIT;
else if(imgprops.sampleCounts & VK_SAMPLE_COUNT_16_BIT)
imInfo.samples = VK_SAMPLE_COUNT_16_BIT;
else if(imgprops.sampleCounts & VK_SAMPLE_COUNT_32_BIT)
imInfo.samples = VK_SAMPLE_COUNT_32_BIT;
else
RDCWARN("Can't find supported MSAA sample count");
RDCASSERT(imgprops.sampleCounts & imInfo.samples, imgprops.sampleCounts, imInfo.samples);
vkr = driver->vkCreateImage(driver->GetDev(), &imInfo, NULL, &m_DummyStencilImage[1]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
rm->SetInternalResource(GetResID(m_DummyStencilImage[1]));
VkMemoryRequirements mrq[2] = {};
driver->vkGetImageMemoryRequirements(driver->GetDev(), m_DummyStencilImage[0], &mrq[0]);
driver->vkGetImageMemoryRequirements(driver->GetDev(), m_DummyStencilImage[1], &mrq[1]);
uint32_t memoryTypeBits = (mrq[0].memoryTypeBits & mrq[1].memoryTypeBits);
// assume we have some memory type available in common
RDCASSERT(memoryTypeBits, mrq[0].memoryTypeBits, mrq[1].memoryTypeBits);
// allocate memory
VkMemoryAllocateInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL,
AlignUp(mrq[0].size, mrq[1].alignment) + mrq[1].size,
driver->GetGPULocalMemoryIndex(memoryTypeBits),
};
vkr = driver->vkAllocateMemory(driver->GetDev(), &allocInfo, NULL, &m_DummyStencilMemory);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
rm->SetInternalResource(GetResID(m_DummyStencilMemory));
vkr =
driver->vkBindImageMemory(driver->GetDev(), m_DummyStencilImage[0], m_DummyStencilMemory, 0);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
vkr = driver->vkBindImageMemory(driver->GetDev(), m_DummyStencilImage[1], m_DummyStencilMemory,
AlignUp(mrq[0].size, mrq[1].alignment));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkImageViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
NULL,
0,
m_DummyStencilImage[0],
VK_IMAGE_VIEW_TYPE_2D_ARRAY,
f,
{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY},
{
viewAspectMask, 0, 1, 0, 1,
},
};
vkr = driver->vkCreateImageView(driver->GetDev(), &viewInfo, NULL, &m_DummyStencilView[0]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
rm->SetInternalResource(GetResID(m_DummyStencilView[0]));
viewInfo.image = m_DummyStencilImage[1];
vkr = driver->vkCreateImageView(driver->GetDev(), &viewInfo, NULL, &m_DummyStencilView[1]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
rm->SetInternalResource(GetResID(m_DummyStencilView[1]));
VkCommandBuffer cmd = driver->GetNextCmd();
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// need to update image layout into valid state
VkImageMemoryBarrier barrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
0,
VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_DummyStencilImage[0]),
{barrierAspectMask, 0, 1, 0, 1},
};
DoPipelineBarrier(cmd, 1, &barrier);
barrier.image = Unwrap(m_DummyStencilImage[1]);
DoPipelineBarrier(cmd, 1, &barrier);
ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
break;
}
if(m_DummyStencilImage[0] == VK_NULL_HANDLE)
{
RDCERR("Couldn't find any integer format we could generate a dummy multisampled image with");
}
for(size_t i = 0; i < ARRAY_COUNT(m_ArrayMSDescSet); i++)
{
CREATE_OBJECT(m_ArrayMSDescSet[i], m_ArrayMSDescriptorPool, m_ArrayMSDescSetLayout);
rm->SetInternalResource(GetResID(m_ArrayMSDescSet[i]));
}
VkFormat formats[] = {
VK_FORMAT_D16_UNORM, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_X8_D24_UNORM_PACK32,
VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT,
};
VkSampleCountFlagBits sampleCounts[] = {
VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_16_BIT,
};
RDCCOMPILE_ASSERT(ARRAY_COUNT(m_DepthMS2ArrayPipe) == ARRAY_COUNT(formats),
"Array count mismatch");
RDCCOMPILE_ASSERT(ARRAY_COUNT(m_DepthArray2MSPipe) == ARRAY_COUNT(formats),
"Array count mismatch");
RDCCOMPILE_ASSERT(ARRAY_COUNT(m_DepthArray2MSPipe[0]) == ARRAY_COUNT(sampleCounts),
"Array count mismatch");
// we use VK_IMAGE_LAYOUT_GENERAL here because it matches the expected layout for the
// non-depth copy, which uses a storage image.
VkImageLayout rpLayout = VK_IMAGE_LAYOUT_GENERAL;
for(size_t f = 0; f < ARRAY_COUNT(formats); f++)
{
// if the format isn't supported at all, bail out and don't try to create anything
if(!(m_pDriver->GetFormatProperties(formats[f]).optimalTilingFeatures &
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
{
RDCDEBUG("Depth copies MSAA -> Array not supported for format %s", ToStr(formats[f]).c_str());
continue;
}
VkRenderPass depthMS2ArrayRP = VK_NULL_HANDLE;
CREATE_OBJECT(depthMS2ArrayRP, formats[f], VK_SAMPLE_COUNT_1_BIT, rpLayout);
ConciseGraphicsPipeline depthPipeInfo = {
depthMS2ArrayRP,
m_ArrayMSPipeLayout,
shaderCache->GetBuiltinModule(BuiltinShader::BlitVS),
shaderCache->GetBuiltinModule(BuiltinShader::DepthMS2ArrayFS),
{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_STENCIL_REFERENCE},
VK_SAMPLE_COUNT_1_BIT,
false, // sampleRateShading
true, // depthEnable
true, // stencilEnable
VK_STENCIL_OP_REPLACE,
false, // colourOutput
false, // blendEnable
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ZERO,
0xf, // writeMask
};
CREATE_OBJECT(m_DepthMS2ArrayPipe[f], depthPipeInfo);
rm->SetInternalResource(GetResID(m_DepthMS2ArrayPipe[f]));
m_pDriver->vkDestroyRenderPass(dev, depthMS2ArrayRP, NULL);
if(!m_pDriver->GetDeviceEnabledFeatures().sampleRateShading)
{
RDCDEBUG("No depth Array -> MSAA copies can be supported without sample rate shading");
continue;
}
for(size_t s = 0; s < ARRAY_COUNT(sampleCounts); s++)
{
// if this sample count isn't supported, don't create it
if(!(m_pDriver->GetDeviceProps().limits.framebufferDepthSampleCounts &
(uint32_t)sampleCounts[s]))
{
RDCDEBUG("Depth copies Array -> MSAA not supported for sample count %u on format %s",
sampleCounts[s], ToStr(formats[f]).c_str());
continue;
}
VkRenderPass depthArray2MSRP;
CREATE_OBJECT(depthArray2MSRP, formats[f], sampleCounts[s], rpLayout);
depthPipeInfo.fragment = shaderCache->GetBuiltinModule(BuiltinShader::DepthArray2MSFS);
depthPipeInfo.renderPass = depthArray2MSRP;
depthPipeInfo.sampleCount = sampleCounts[s];
depthPipeInfo.sampleRateShading = true;
CREATE_OBJECT(m_DepthArray2MSPipe[f][s], depthPipeInfo);
rm->SetInternalResource(GetResID(m_DepthArray2MSPipe[f][s]));
m_pDriver->vkDestroyRenderPass(dev, depthArray2MSRP, NULL);
}
}
if(RenderDoc::Inst().IsReplayApp())
{
VkDescriptorPoolSize descPoolTypes[] = {
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, ARRAY_COUNT(m_DiscardSet)},
};
VkDescriptorPoolCreateInfo descPoolInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
NULL,
0,
ARRAY_COUNT(m_DiscardSet),
ARRAY_COUNT(descPoolTypes),
&descPoolTypes[0],
};
// create descriptor pool
vkr = driver->vkCreateDescriptorPool(driver->GetDev(), &descPoolInfo, NULL, &m_DiscardPool);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
CREATE_OBJECT(m_DiscardSetLayout,
{
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
});
CREATE_OBJECT(m_DiscardLayout, m_DiscardSetLayout, 4);
ResourceFormat fmt;
fmt.type = ResourceFormatType::Regular;
fmt.compType = CompType::Float;
fmt.compByteWidth = 4;
fmt.compCount = 1;
for(size_t i = 0; i < ARRAY_COUNT(m_DiscardSet); i++)
{
CREATE_OBJECT(m_DiscardSet[i], m_DiscardPool, m_DiscardSetLayout);
bytebuf pattern = GetDiscardPattern(DiscardType(i), fmt);
m_DiscardCB[i].Create(m_pDriver, m_Device, pattern.size(), 1, 0);
memcpy(m_DiscardCB[i].Map(), pattern.data(), pattern.size());
m_DiscardCB[i].Unmap();
VkDescriptorBufferInfo bufInfo = {};
m_DiscardCB[i].FillDescriptor(bufInfo);
VkWriteDescriptorSet writes[] = {
{
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_DiscardSet[i]), 0, 0, 1,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, NULL, &bufInfo, NULL,
},
};
ObjDisp(dev)->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writes), writes, 0, NULL);
}
}
// we only need this during replay, so don't create otherwise.
if(RenderDoc::Inst().IsReplayApp())
{
m_ReadbackWindow.Create(driver, dev, STAGE_BUFFER_BYTE_SIZE, 1, GPUBuffer::eGPUBufferReadback);
}
else
{
m_ReadbackWindow.Create(driver, dev, 256 * 1024 * 1024ULL, 1, GPUBuffer::eGPUBufferReadback);
vkr = ObjDisp(dev)->MapMemory(Unwrap(dev), Unwrap(m_ReadbackWindow.mem), 0, VK_WHOLE_SIZE, 0,
(void **)&m_ReadbackPtr);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
}
VulkanDebugManager::~VulkanDebugManager()
{
VkDevice dev = m_Device;
m_Custom.Destroy(m_pDriver);
m_ReadbackWindow.Destroy();
for(auto it = m_CachedMeshPipelines.begin(); it != m_CachedMeshPipelines.end(); ++it)
for(uint32_t i = 0; i < VKMeshDisplayPipelines::ePipe_Count; i++)
m_pDriver->vkDestroyPipeline(dev, it->second.pipes[i], NULL);
m_pDriver->vkDestroyDescriptorPool(dev, m_ArrayMSDescriptorPool, NULL);
m_pDriver->vkDestroySampler(dev, m_ArrayMSSampler, NULL);
m_pDriver->vkDestroyImageView(dev, m_DummyStencilView[0], NULL);
m_pDriver->vkDestroyImageView(dev, m_DummyStencilView[1], NULL);
m_pDriver->vkDestroyImage(dev, m_DummyStencilImage[0], NULL);
m_pDriver->vkDestroyImage(dev, m_DummyStencilImage[1], NULL);
m_pDriver->vkFreeMemory(dev, m_DummyStencilMemory, NULL);
m_pDriver->vkDestroyDescriptorSetLayout(dev, m_ArrayMSDescSetLayout, NULL);
m_pDriver->vkDestroyPipelineLayout(dev, m_ArrayMSPipeLayout, NULL);
m_pDriver->vkDestroyPipeline(dev, m_Array2MSPipe, NULL);
m_pDriver->vkDestroyPipeline(dev, m_MS2ArrayPipe, NULL);
m_pDriver->vkDestroyDescriptorPool(dev, m_DiscardPool, NULL);
m_pDriver->vkDestroyPipelineLayout(dev, m_DiscardLayout, NULL);
m_pDriver->vkDestroyDescriptorSetLayout(dev, m_DiscardSetLayout, NULL);
for(size_t i = 0; i < ARRAY_COUNT(m_DiscardCB); i++)
m_DiscardCB[i].Destroy();
for(auto it = m_DiscardImages.begin(); it != m_DiscardImages.end(); it++)
{
for(VkImageView view : it->second.views)
m_pDriver->vkDestroyImageView(dev, view, NULL);
for(VkFramebuffer fb : it->second.fbs)
m_pDriver->vkDestroyFramebuffer(dev, fb, NULL);
}
for(auto it = m_DiscardPipes.begin(); it != m_DiscardPipes.end(); it++)
{
m_pDriver->vkDestroyPipeline(dev, it->second.pso, NULL);
m_pDriver->vkDestroyRenderPass(dev, it->second.rp, NULL);
}
for(auto it = m_DiscardPatterns.begin(); it != m_DiscardPatterns.end(); it++)
m_pDriver->vkDestroyBuffer(dev, it->second, NULL);
for(size_t i = 0; i < ARRAY_COUNT(m_DepthMS2ArrayPipe); i++)
m_pDriver->vkDestroyPipeline(dev, m_DepthMS2ArrayPipe[i], NULL);
for(size_t f = 0; f < ARRAY_COUNT(m_DepthArray2MSPipe); f++)
for(size_t s = 0; s < ARRAY_COUNT(m_DepthArray2MSPipe[0]); s++)
m_pDriver->vkDestroyPipeline(dev, m_DepthArray2MSPipe[f][s], NULL);
}
void VulkanDebugManager::CreateCustomShaderTex(uint32_t width, uint32_t height, uint32_t mip)
{
WrappedVulkan *driver = m_pDriver;
VkDevice dev = m_Device;
VkResult vkr = VK_SUCCESS;
if(m_Custom.TexImg != VK_NULL_HANDLE)
{
if(width == m_Custom.TexWidth && height == m_Custom.TexHeight)
{
// recreate framebuffer for this mip
// Create framebuffer rendering just to overlay image, no depth
VkFramebufferCreateInfo fbinfo = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
NULL,
0,
m_Custom.TexRP,
1,
&m_Custom.TexImgView[mip],
RDCMAX(1U, width >> mip),
RDCMAX(1U, height >> mip),
1,
};
vkr = m_pDriver->vkCreateFramebuffer(m_Device, &fbinfo, NULL, &m_Custom.TexFB);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
return;
}
m_pDriver->vkDestroyRenderPass(dev, m_Custom.TexRP, NULL);
m_pDriver->vkDestroyFramebuffer(dev, m_Custom.TexFB, NULL);
for(size_t i = 0; i < ARRAY_COUNT(m_Custom.TexImgView); i++)
m_pDriver->vkDestroyImageView(dev, m_Custom.TexImgView[i], NULL);
RDCEraseEl(m_Custom.TexImgView);
m_pDriver->vkDestroyImage(dev, m_Custom.TexImg, NULL);
}
m_Custom.TexWidth = width;
m_Custom.TexHeight = height;
VkImageCreateInfo imInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
NULL,
0,
VK_IMAGE_TYPE_2D,
VK_FORMAT_R16G16B16A16_SFLOAT,
{width, height, 1},
CalcNumMips((int)width, (int)height, 1),
1,
VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0,
NULL,
VK_IMAGE_LAYOUT_UNDEFINED,
};
vkr = m_pDriver->vkCreateImage(m_Device, &imInfo, NULL, &m_Custom.TexImg);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkMemoryRequirements mrq = {0};
m_pDriver->vkGetImageMemoryRequirements(m_Device, m_Custom.TexImg, &mrq);
// if no memory is allocated, or it's not enough,
// then allocate
if(m_Custom.TexMem == VK_NULL_HANDLE || mrq.size > m_Custom.TexMemSize)
{
if(m_Custom.TexMem != VK_NULL_HANDLE)
m_pDriver->vkFreeMemory(m_Device, m_Custom.TexMem, 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_Custom.TexMem);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_Custom.TexMemSize = mrq.size;
}
vkr = m_pDriver->vkBindImageMemory(m_Device, m_Custom.TexImg, m_Custom.TexMem, 0);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkImageViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
NULL,
0,
m_Custom.TexImg,
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,
},
};
for(uint32_t i = 0; i < imInfo.mipLevels; i++)
{
viewInfo.subresourceRange.baseMipLevel = i;
vkr = m_pDriver->vkCreateImageView(m_Device, &viewInfo, NULL, &m_Custom.TexImgView[i]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
// need to update image layout into valid state
VkCommandBuffer cmd = m_pDriver->GetNextCmd();
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
ObjDisp(dev)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
m_pDriver->FindImageState(GetResID(m_Custom.TexImg))
->InlineTransition(cmd, m_pDriver->m_QueueFamilyIdx, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
m_pDriver->GetImageTransitionInfo());
vkr = ObjDisp(dev)->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
#if ENABLED(SINGLE_FLUSH_VALIDATE)
m_pDriver->SubmitCmds();
#endif
CREATE_OBJECT(m_Custom.TexRP, imInfo.format, imInfo.samples);
// Create framebuffer rendering just to overlay image, no depth
VkFramebufferCreateInfo fbinfo = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
NULL,
0,
m_Custom.TexRP,
1,
&m_Custom.TexImgView[mip],
RDCMAX(1U, width >> mip),
RDCMAX(1U, height >> mip),
1,
};
vkr = m_pDriver->vkCreateFramebuffer(m_Device, &fbinfo, NULL, &m_Custom.TexFB);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
void VulkanDebugManager::CreateCustomShaderPipeline(ResourceId shader, VkPipelineLayout pipeLayout)
{
WrappedVulkan *driver = m_pDriver;
if(shader == ResourceId())
return;
if(m_Custom.TexPipeline != VK_NULL_HANDLE)
{
if(m_Custom.TexShader == shader)
return;
m_pDriver->vkDestroyPipeline(m_Device, m_Custom.TexPipeline, NULL);
}
m_Custom.TexShader = shader;
ConciseGraphicsPipeline customPipe = {
m_Custom.TexRP,
pipeLayout,
m_pDriver->GetShaderCache()->GetBuiltinModule(BuiltinShader::BlitVS),
m_pDriver->GetResourceManager()->GetCurrentHandle<VkShaderModule>(shader),
{VK_DYNAMIC_STATE_VIEWPORT},
VK_SAMPLE_COUNT_1_BIT,
false, // sampleRateShading
false, // depthEnable
false, // stencilEnable
VK_STENCIL_OP_KEEP,
true, // colourOutput
false, // blendEnable
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ZERO,
0xf, // writeMask
};
CREATE_OBJECT(m_Custom.TexPipeline, customPipe);
}
// TODO: Point meshes don't pick correctly
uint32_t VulkanReplay::PickVertex(uint32_t eventId, int32_t w, int32_t h, const MeshDisplay &cfg,
uint32_t x, uint32_t y)
{
VkDevice dev = m_pDriver->GetDev();
const VkDevDispatchTable *vt = ObjDisp(dev);
VkMarkerRegion::Begin(StringFormat::Fmt("VulkanReplay::PickVertex(%u, %u)", x, y));
Matrix4f projMat = Matrix4f::Perspective(90.0f, 0.1f, 100000.0f, float(w) / float(h));
Matrix4f camMat = cfg.cam ? ((Camera *)cfg.cam)->GetMatrix() : Matrix4f::Identity();
Matrix4f pickMVP = projMat.Mul(camMat);
Matrix4f pickMVPProj;
if(cfg.position.unproject)
{
// the derivation of the projection matrix might not be right (hell, it could be an
// orthographic projection). But it'll be close enough likely.
Matrix4f guessProj =
cfg.position.farPlane != FLT_MAX
? Matrix4f::Perspective(cfg.fov, cfg.position.nearPlane, cfg.position.farPlane, cfg.aspect)
: Matrix4f::ReversePerspective(cfg.fov, cfg.position.nearPlane, cfg.aspect);
if(cfg.ortho)
guessProj = Matrix4f::Orthographic(cfg.position.nearPlane, cfg.position.farPlane);
pickMVPProj = projMat.Mul(camMat.Mul(guessProj.Inverse()));
}
vec3 rayPos;
vec3 rayDir;
// convert mouse pos to world space ray
{
Matrix4f inversePickMVP = pickMVP.Inverse();
float pickX = ((float)x) / ((float)w);
float pickXCanonical = RDCLERP(-1.0f, 1.0f, pickX);
float pickY = ((float)y) / ((float)h);
// flip the Y axis
float pickYCanonical = RDCLERP(1.0f, -1.0f, pickY);
vec3 cameraToWorldNearPosition =
inversePickMVP.Transform(Vec3f(pickXCanonical, pickYCanonical, -1), 1);
vec3 cameraToWorldFarPosition =
inversePickMVP.Transform(Vec3f(pickXCanonical, pickYCanonical, 1), 1);
vec3 testDir = (cameraToWorldFarPosition - cameraToWorldNearPosition);
testDir.Normalise();
/* Calculate the ray direction first in the regular way (above), so we can use the
the output for testing if the ray we are picking is negative or not. This is similar
to checking against the forward direction of the camera, but more robust
*/
if(cfg.position.unproject)
{
Matrix4f inversePickMVPGuess = pickMVPProj.Inverse();
vec3 nearPosProj = inversePickMVPGuess.Transform(Vec3f(pickXCanonical, pickYCanonical, -1), 1);
vec3 farPosProj = inversePickMVPGuess.Transform(Vec3f(pickXCanonical, pickYCanonical, 1), 1);
rayDir = (farPosProj - nearPosProj);
rayDir.Normalise();
if(testDir.z < 0)
{
rayDir = -rayDir;
}
rayPos = nearPosProj;
}
else
{
rayDir = testDir;
rayPos = cameraToWorldNearPosition;
}
}
const bool fandecode =
(cfg.position.topology == Topology::TriangleFan && cfg.position.allowRestart);
uint32_t numIndices = cfg.position.numIndices;
bytebuf idxs;
uint32_t minIndex = 0;
uint32_t maxIndex = cfg.position.numIndices;
if(cfg.position.indexByteStride && cfg.position.indexResourceId != ResourceId())
GetBufferData(cfg.position.indexResourceId, cfg.position.indexByteOffset, 0, idxs);
uint32_t idxclamp = 0;
if(cfg.position.baseVertex < 0)
idxclamp = uint32_t(-cfg.position.baseVertex);
// We copy into our own buffers to promote to the target type (uint32) that the shader expects.
// Most IBs will be 16-bit indices, most VBs will not be float4. We also apply baseVertex here
if(!idxs.empty())
{
rdcarray<uint32_t> idxtmp;
// if it's a triangle fan that allows restart, we'll have to unpack it.
// Allocate enough space for the list on the GPU, and enough temporary space to upcast into
// first
if(fandecode)
{
idxtmp.resize(numIndices);
numIndices *= 3;
}
// resize up on demand
if(m_VertexPick.IBSize < numIndices * sizeof(uint32_t))
{
if(m_VertexPick.IBSize > 0)
{
m_VertexPick.IB.Destroy();
m_VertexPick.IBUpload.Destroy();
}
m_VertexPick.IBSize = numIndices * sizeof(uint32_t);
m_VertexPick.IB.Create(m_pDriver, dev, m_VertexPick.IBSize, 1,
GPUBuffer::eGPUBufferGPULocal | GPUBuffer::eGPUBufferSSBO);
m_VertexPick.IBUpload.Create(m_pDriver, dev, m_VertexPick.IBSize, 1, 0);
}
uint32_t *outidxs = (uint32_t *)m_VertexPick.IBUpload.Map();
uint32_t *mappedPtr = outidxs;
memset(outidxs, 0, m_VertexPick.IBSize);
// if we're decoding a fan, we write into our temporary vector first
if(fandecode)
outidxs = idxtmp.data();
uint16_t *idxs16 = (uint16_t *)&idxs[0];
uint32_t *idxs32 = (uint32_t *)&idxs[0];
size_t idxcount = 0;
if(cfg.position.indexByteStride == 2)
{
size_t bufsize = idxs.size() / 2;
for(uint32_t i = 0; i < bufsize && i < cfg.position.numIndices; i++)
{
uint32_t idx = idxs16[i];
if(idx < idxclamp)
idx = 0;
else if(cfg.position.baseVertex < 0)
idx -= idxclamp;
else if(cfg.position.baseVertex > 0)
idx += cfg.position.baseVertex;
if(i == 0)
{
minIndex = maxIndex = idx;
}
else
{
minIndex = RDCMIN(idx, minIndex);
maxIndex = RDCMAX(idx, maxIndex);
}
outidxs[i] = idx;
idxcount++;
}
}
else
{
uint32_t bufsize = uint32_t(idxs.size() / 4);
minIndex = maxIndex = idxs32[0];
for(uint32_t i = 0; i < RDCMIN(bufsize, cfg.position.numIndices); i++)
{
uint32_t idx = idxs32[i];
if(idx < idxclamp)
idx = 0;
else if(cfg.position.baseVertex < 0)
idx -= idxclamp;
else if(cfg.position.baseVertex > 0)
idx += cfg.position.baseVertex;
minIndex = RDCMIN(idx, minIndex);
maxIndex = RDCMAX(idx, maxIndex);
outidxs[i] = idx;
idxcount++;
}
}
// if it's a triangle fan that allows restart, unpack it
if(cfg.position.topology == Topology::TriangleFan && cfg.position.allowRestart)
{
// resize to how many indices were actually read
idxtmp.resize(idxcount);
// patch the index buffer
PatchTriangleFanRestartIndexBufer(idxtmp, cfg.position.restartIndex);
for(uint32_t &idx : idxtmp)
{
if(idx == cfg.position.restartIndex)
idx = 0;
}
numIndices = (uint32_t)idxtmp.size();
// now copy the decoded list to the GPU
memcpy(mappedPtr, idxtmp.data(), idxtmp.size() * sizeof(uint32_t));
}
m_VertexPick.IBUpload.Unmap();
}
// unpack and linearise the data
{
bytebuf oldData;
GetBufferData(cfg.position.vertexResourceId, cfg.position.vertexByteOffset, 0, oldData);
// clamp maxIndex to upper bound in case we got invalid indices or primitive restart indices
maxIndex = RDCMIN(maxIndex, uint32_t(oldData.size() / RDCMAX(1U, cfg.position.vertexByteStride)));
if(m_VertexPick.VBSize < (maxIndex + 1) * sizeof(FloatVector))
{
if(m_VertexPick.VBSize > 0)
{
m_VertexPick.VB.Destroy();
m_VertexPick.VBUpload.Destroy();
}
m_VertexPick.VBSize = (maxIndex + 1) * sizeof(FloatVector);
m_VertexPick.VB.Create(m_pDriver, dev, m_VertexPick.VBSize, 1,
GPUBuffer::eGPUBufferGPULocal | GPUBuffer::eGPUBufferSSBO);
m_VertexPick.VBUpload.Create(m_pDriver, dev, m_VertexPick.VBSize, 1, 0);
}
byte *data = &oldData[0];
byte *dataEnd = data + oldData.size();
bool valid = true;
FloatVector *vbData = (FloatVector *)m_VertexPick.VBUpload.Map();
// the index buffer may refer to vertices past the start of the vertex buffer, so we can't just
// conver the first N vertices we'll need.
// Instead we grab min and max above, and convert every vertex in that range. This might
// slightly over-estimate but not as bad as 0-max or the whole buffer.
for(uint32_t idx = minIndex; idx <= maxIndex; idx++)
vbData[idx] = HighlightCache::InterpretVertex(data, idx, cfg.position.vertexByteStride,
cfg.position.format, dataEnd, valid);
m_VertexPick.VBUpload.Unmap();
}
MeshPickUBOData *ubo = (MeshPickUBOData *)m_VertexPick.UBO.Map();
ubo->rayPos = rayPos;
ubo->rayDir = rayDir;
ubo->use_indices = cfg.position.indexByteStride ? 1U : 0U;
ubo->numVerts = numIndices;
bool isTriangleMesh = true;
switch(cfg.position.topology)
{
case Topology::TriangleList:
{
ubo->meshMode = MESH_TRIANGLE_LIST;
break;
};
case Topology::TriangleStrip:
{
ubo->meshMode = MESH_TRIANGLE_STRIP;
break;
};
case Topology::TriangleFan:
{
if(fandecode)
ubo->meshMode = MESH_TRIANGLE_LIST;
else
ubo->meshMode = MESH_TRIANGLE_FAN;
break;
};
case Topology::TriangleList_Adj:
{
ubo->meshMode = MESH_TRIANGLE_LIST_ADJ;
break;
};
case Topology::TriangleStrip_Adj:
{
ubo->meshMode = MESH_TRIANGLE_STRIP_ADJ;
break;
};
default: // points, lines, patchlists, unknown
{
ubo->meshMode = MESH_OTHER;
isTriangleMesh = false;
};
}
// line/point data
ubo->unproject = cfg.position.unproject;
ubo->mvp = cfg.position.unproject ? pickMVPProj : pickMVP;
ubo->coords = Vec2f((float)x, (float)y);
ubo->viewport = Vec2f((float)w, (float)h);
m_VertexPick.UBO.Unmap();
VkDescriptorBufferInfo ibInfo = {};
VkDescriptorBufferInfo vbInfo = {};
m_VertexPick.VB.FillDescriptor(vbInfo);
m_VertexPick.IB.FillDescriptor(ibInfo);
VkWriteDescriptorSet writes[] = {
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_VertexPick.DescSet), 1, 0, 1,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, NULL, &vbInfo, NULL},
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_VertexPick.DescSet), 2, 0, 1,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, NULL, &ibInfo, NULL},
};
if(!idxs.empty())
vt->UpdateDescriptorSets(Unwrap(m_Device), 2, writes, 0, NULL);
else
vt->UpdateDescriptorSets(Unwrap(m_Device), 1, writes, 0, NULL);
VkCommandBuffer cmd = m_pDriver->GetNextCmd();
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
VkBufferCopy bufCopy = {0, 0, 0};
vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
// reset first uint (used as atomic counter) to 0
vt->CmdFillBuffer(Unwrap(cmd), Unwrap(m_VertexPick.Result.buf), 0, sizeof(uint32_t) * 4, 0);
VkBufferMemoryBarrier bufBarrier = {
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
NULL,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(m_VertexPick.Result.buf),
0,
VK_WHOLE_SIZE,
};
// wait for zero to be written to atomic counter before using in shader
DoPipelineBarrier(cmd, 1, &bufBarrier);
// copy uploaded VB and if needed IB
if(!idxs.empty())
{
// wait for writes
bufBarrier.buffer = Unwrap(m_VertexPick.IBUpload.buf);
bufBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
DoPipelineBarrier(cmd, 1, &bufBarrier);
// do copy
bufCopy.size = m_VertexPick.IBSize;
vt->CmdCopyBuffer(Unwrap(cmd), Unwrap(m_VertexPick.IBUpload.buf), Unwrap(m_VertexPick.IB.buf),
1, &bufCopy);
// wait for copy
bufBarrier.buffer = Unwrap(m_VertexPick.IB.buf);
bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
bufBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
DoPipelineBarrier(cmd, 1, &bufBarrier);
}
// wait for writes
bufBarrier.buffer = Unwrap(m_VertexPick.VBUpload.buf);
bufBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
DoPipelineBarrier(cmd, 1, &bufBarrier);
// do copy
bufCopy.size = m_VertexPick.VBSize;
vt->CmdCopyBuffer(Unwrap(cmd), Unwrap(m_VertexPick.VBUpload.buf), Unwrap(m_VertexPick.VB.buf), 1,
&bufCopy);
// wait for copy
bufBarrier.buffer = Unwrap(m_VertexPick.VB.buf);
bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
bufBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
DoPipelineBarrier(cmd, 1, &bufBarrier);
vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(m_VertexPick.Pipeline));
vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(m_VertexPick.Layout),
0, 1, UnwrapPtr(m_VertexPick.DescSet), 0, NULL);
uint32_t workgroupx = uint32_t(cfg.position.numIndices / 128 + 1);
vt->CmdDispatch(Unwrap(cmd), workgroupx, 1, 1);
// wait for shader to finish writing before transferring to readback buffer
bufBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
bufBarrier.buffer = Unwrap(m_VertexPick.Result.buf);
DoPipelineBarrier(cmd, 1, &bufBarrier);
bufCopy.size = m_VertexPick.Result.totalsize;
// copy to readback buffer
vt->CmdCopyBuffer(Unwrap(cmd), Unwrap(m_VertexPick.Result.buf),
Unwrap(m_VertexPick.ResultReadback.buf), 1, &bufCopy);
// wait for transfer to finish before reading on CPU
bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
bufBarrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
bufBarrier.buffer = Unwrap(m_VertexPick.ResultReadback.buf);
DoPipelineBarrier(cmd, 1, &bufBarrier);
VkResult vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
#if ENABLED(SINGLE_FLUSH_VALIDATE)
m_pDriver->SubmitCmds();
#endif
m_pDriver->SubmitCmds();
m_pDriver->FlushQ();
uint32_t *pickResultData = (uint32_t *)m_VertexPick.ResultReadback.Map();
uint32_t numResults = *pickResultData;
uint32_t ret = ~0U;
if(numResults > 0)
{
if(isTriangleMesh)
{
struct PickResult
{
uint32_t vertid;
vec3 intersectionPoint;
};
PickResult *pickResults = (PickResult *)(pickResultData + 4);
PickResult *closest = pickResults;
// distance from raycast hit to nearest worldspace position of the mouse
float closestPickDistance = (closest->intersectionPoint - rayPos).Length();
// min with size of results buffer to protect against overflows
for(uint32_t i = 1; i < RDCMIN((uint32_t)VertexPicking::MaxMeshPicks, numResults); i++)
{
float pickDistance = (pickResults[i].intersectionPoint - rayPos).Length();
if(pickDistance < closestPickDistance)
{
closest = pickResults + i;
}
}
ret = closest->vertid;
}
else
{
struct PickResult
{
uint32_t vertid;
uint32_t idx;
float len;
float depth;
};
PickResult *pickResults = (PickResult *)(pickResultData + 4);
PickResult *closest = pickResults;
// min with size of results buffer to protect against overflows
for(uint32_t i = 1; i < RDCMIN((uint32_t)VertexPicking::MaxMeshPicks, numResults); i++)
{
// We need to keep the picking order consistent in the face
// of random buffer appends, when multiple vertices have the
// identical position (e.g. if UVs or normals are different).
//
// We could do something to try and disambiguate, but it's
// never going to be intuitive, it's just going to flicker
// confusingly.
if(pickResults[i].len < closest->len ||
(pickResults[i].len == closest->len && pickResults[i].depth < closest->depth) ||
(pickResults[i].len == closest->len && pickResults[i].depth == closest->depth &&
pickResults[i].vertid < closest->vertid))
closest = pickResults + i;
}
ret = closest->vertid;
}
}
m_VertexPick.ResultReadback.Unmap();
VkMarkerRegion::Set(StringFormat::Fmt("Result is %u", ret));
VkMarkerRegion::End();
if(fandecode)
{
// undo the triangle list expansion
if(ret > 2)
ret = (ret + 3) / 3 + 1;
}
return ret;
}
const VulkanCreationInfo::Image &VulkanDebugManager::GetImageInfo(ResourceId img) const
{
auto it = m_pDriver->m_CreationInfo.m_Image.find(img);
RDCASSERT(it != m_pDriver->m_CreationInfo.m_Image.end());
return it->second;
}
const VulkanCreationInfo::ImageView &VulkanDebugManager::GetImageViewInfo(ResourceId imgView) const
{
auto it = m_pDriver->m_CreationInfo.m_ImageView.find(imgView);
RDCASSERT(it != m_pDriver->m_CreationInfo.m_ImageView.end());
return it->second;
}
const VulkanCreationInfo::Pipeline &VulkanDebugManager::GetPipelineInfo(ResourceId pipe) const
{
auto it = m_pDriver->m_CreationInfo.m_Pipeline.find(pipe);
RDCASSERT(it != m_pDriver->m_CreationInfo.m_Pipeline.end());
return it->second;
}
const VulkanCreationInfo::ShaderModule &VulkanDebugManager::GetShaderInfo(ResourceId shader) const
{
auto it = m_pDriver->m_CreationInfo.m_ShaderModule.find(shader);
RDCASSERT(it != m_pDriver->m_CreationInfo.m_ShaderModule.end());
return it->second;
}
const VulkanCreationInfo::Framebuffer &VulkanDebugManager::GetFramebufferInfo(ResourceId fb) const
{
auto it = m_pDriver->m_CreationInfo.m_Framebuffer.find(fb);
RDCASSERT(it != m_pDriver->m_CreationInfo.m_Framebuffer.end());
return it->second;
}
const VulkanCreationInfo::RenderPass &VulkanDebugManager::GetRenderPassInfo(ResourceId rp) const
{
auto it = m_pDriver->m_CreationInfo.m_RenderPass.find(rp);
RDCASSERT(it != m_pDriver->m_CreationInfo.m_RenderPass.end());
return it->second;
}
const VulkanCreationInfo::PipelineLayout &VulkanDebugManager::GetPipelineLayoutInfo(ResourceId rp) const
{
auto it = m_pDriver->m_CreationInfo.m_PipelineLayout.find(rp);
RDCASSERT(it != m_pDriver->m_CreationInfo.m_PipelineLayout.end());
return it->second;
}
const DescSetLayout &VulkanDebugManager::GetDescSetLayout(ResourceId dsl) const
{
auto it = m_pDriver->m_CreationInfo.m_DescSetLayout.find(dsl);
RDCASSERT(it != m_pDriver->m_CreationInfo.m_DescSetLayout.end());
return it->second;
}
const WrappedVulkan::DescriptorSetInfo &VulkanDebugManager::GetDescSetInfo(ResourceId ds) const
{
auto it = m_pDriver->m_DescriptorSetState.find(ds);
RDCASSERT(it != m_pDriver->m_DescriptorSetState.end());
return it->second;
}
void VulkanDebugManager::GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &ret)
{
VkDevice dev = m_pDriver->GetDev();
const VkDevDispatchTable *vt = ObjDisp(dev);
WrappedVkRes *res = m_pDriver->GetResourceManager()->GetCurrentResource(buff);
if(res == VK_NULL_HANDLE)
{
RDCERR("Getting buffer data for unknown buffer/memory %s!", ToStr(buff).c_str());
return;
}
VkBuffer srcBuf = VK_NULL_HANDLE;
uint64_t bufsize = 0;
if(WrappedVkDeviceMemory::IsAlloc(res))
{
srcBuf = m_pDriver->m_CreationInfo.m_Memory[buff].wholeMemBuf;
bufsize = m_pDriver->m_CreationInfo.m_Memory[buff].size;
if(srcBuf == VK_NULL_HANDLE)
{
RDCLOG(
"Memory doesn't have wholeMemBuf, either non-buffer accessible (non-linear) or dedicated "
"image memory");
return;
}
}
else
{
srcBuf = m_pDriver->GetResourceManager()->GetCurrentHandle<VkBuffer>(buff);
bufsize = m_pDriver->m_CreationInfo.m_Buffer[buff].size;
}
if(offset >= bufsize)
{
// can't read past the end of the buffer, return empty
return;
}
if(len == 0 || len > bufsize)
{
len = bufsize - offset;
}
if(VkDeviceSize(offset + len) > bufsize)
{
RDCWARN("Attempting to read off the end of the buffer (%llu %llu). Will be clamped (%llu)",
offset, len, bufsize);
len = RDCMIN(len, bufsize - offset);
}
ret.resize((size_t)len);
VkDeviceSize srcoffset = (VkDeviceSize)offset;
size_t dstoffset = 0;
VkDeviceSize sizeRemaining = (VkDeviceSize)len;
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);
VkBufferMemoryBarrier bufBarrier = {
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
NULL,
0,
VK_ACCESS_TRANSFER_READ_BIT,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(srcBuf),
srcoffset,
sizeRemaining,
};
bufBarrier.srcAccessMask = VK_ACCESS_ALL_WRITE_BITS;
// wait for previous writes to happen before we copy to our window buffer
DoPipelineBarrier(cmd, 1, &bufBarrier);
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
#if ENABLED(SINGLE_FLUSH_VALIDATE)
m_pDriver->SubmitCmds();
#endif
while(sizeRemaining > 0)
{
VkDeviceSize chunkSize = RDCMIN(sizeRemaining, STAGE_BUFFER_BYTE_SIZE);
cmd = m_pDriver->GetNextCmd();
vkr = vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkBufferCopy region = {srcoffset, 0, chunkSize};
vt->CmdCopyBuffer(Unwrap(cmd), Unwrap(srcBuf), Unwrap(m_ReadbackWindow.buf), 1, &region);
bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
bufBarrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
bufBarrier.buffer = Unwrap(m_ReadbackWindow.buf);
bufBarrier.offset = 0;
bufBarrier.size = chunkSize;
// wait for transfer to happen before we read
DoPipelineBarrier(cmd, 1, &bufBarrier);
vkr = vt->EndCommandBuffer(Unwrap(cmd));
RDCASSERTEQUAL(vkr, VK_SUCCESS);
m_pDriver->SubmitCmds();
m_pDriver->FlushQ();
byte *pData = NULL;
vkr = vt->MapMemory(Unwrap(dev), Unwrap(m_ReadbackWindow.mem), 0, VK_WHOLE_SIZE, 0,
(void **)&pData);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkMappedMemoryRange range = {
VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, NULL, Unwrap(m_ReadbackWindow.mem), 0, VK_WHOLE_SIZE,
};
vkr = vt->InvalidateMappedMemoryRanges(Unwrap(dev), 1, &range);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
RDCASSERT(pData != NULL);
memcpy(&ret[dstoffset], pData, (size_t)chunkSize);
srcoffset += chunkSize;
dstoffset += (size_t)chunkSize;
sizeRemaining -= chunkSize;
vt->UnmapMemory(Unwrap(dev), Unwrap(m_ReadbackWindow.mem));
}
vt->DeviceWaitIdle(Unwrap(dev));
}
void VulkanDebugManager::FillWithDiscardPattern(VkCommandBuffer cmd, DiscardType type,
VkImage image, VkImageLayout curLayout,
VkImageSubresourceRange discardRange,
VkRect2D discardRect)
{
VkDevice dev = m_Device;
const VkDevDispatchTable *vt = ObjDisp(dev);
const VulkanCreationInfo::Image &imInfo = GetImageInfo(GetResID(image));
VkMarkerRegion marker(
cmd, StringFormat::Fmt("FillWithDiscardPattern %s", ToStr(GetResID(image)).c_str()));
if(imInfo.samples > 1)
{
WrappedVulkan *driver = m_pDriver;
bool depth = false;
if(IsDepthOrStencilFormat(imInfo.format))
depth = true;
VkImageAspectFlags imAspects = FormatImageAspects(imInfo.format);
rdcpair<VkFormat, VkSampleCountFlagBits> key = {imInfo.format, imInfo.samples};
DiscardPassData &passdata = m_DiscardPipes[key];
// create and cache a pipeline and RP that writes to this format and sample count
if(passdata.pso == VK_NULL_HANDLE)
{
VkAttachmentReference attRef = {
0, depth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
VkAttachmentDescription attDesc = {
0,
imInfo.format,
imInfo.samples,
VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE,
attRef.layout,
attRef.layout,
};
VkSubpassDescription sub = {
0, VK_PIPELINE_BIND_POINT_GRAPHICS,
};
if(depth)
{
sub.pDepthStencilAttachment = &attRef;
}
else
{
sub.pColorAttachments = &attRef;
sub.colorAttachmentCount = 1;
}
VkRenderPassCreateInfo rpinfo = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 1, &attDesc, 1, &sub, 0, NULL,
};
VkResult vkr = m_pDriver->vkCreateRenderPass(m_pDriver->GetDev(), &rpinfo, NULL, &passdata.rp);
if(vkr != VK_SUCCESS)
RDCERR("Failed to create shader debug render pass: %s", ToStr(vkr).c_str());
ConciseGraphicsPipeline pipeInfo = {
passdata.rp,
m_DiscardLayout,
m_pDriver->GetShaderCache()->GetBuiltinModule(BuiltinShader::BlitVS),
m_pDriver->GetShaderCache()->GetBuiltinModule(BuiltinShader::DiscardFS),
{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR},
imInfo.samples,
false, // sampleRateShading
true, // depthEnable
true, // stencilEnable
VK_STENCIL_OP_REPLACE,
true, // colourOutput
false, // blendEnable
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ZERO,
0xf, // writeMask
};
CREATE_OBJECT(passdata.pso, pipeInfo);
}
if(passdata.pso == VK_NULL_HANDLE)
return;
DiscardImgData &imgdata = m_DiscardImages[GetResID(image)];
// create and cache views and framebuffers for every slice in this image
if(imgdata.fbs.empty())
{
VkImageAspectFlags aspectMask = imAspects;
for(int pass = 0; pass < 3; pass++)
{
// only depth/stencil images need multiple sets of views to mask out one aspect or the other
if(pass > 0)
{
if(imAspects != (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
break;
if(pass == 1)
aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
else if(pass == 2)
aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
}
for(uint32_t a = 0; a < imInfo.arrayLayers; a++)
{
VkImageViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
NULL,
0,
image,
VK_IMAGE_VIEW_TYPE_2D,
imInfo.format,
{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY},
{
aspectMask, 0, 1, a, 1,
},
};
VkImageView view;
VkResult vkr = driver->vkCreateImageView(driver->GetDev(), &viewInfo, NULL, &view);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
imgdata.views.push_back(view);
// create framebuffer
VkFramebufferCreateInfo fbinfo = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
NULL,
0,
passdata.rp,
1,
&view,
imInfo.extent.width,
imInfo.extent.height,
1,
};
VkFramebuffer fb;
vkr = driver->vkCreateFramebuffer(driver->GetDev(), &fbinfo, NULL, &fb);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
imgdata.fbs.push_back(fb);
}
}
}
if(imgdata.fbs.empty())
return;
ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(passdata.pso));
ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS,
Unwrap(m_DiscardLayout), 0, 1,
UnwrapPtr(m_DiscardSet[(size_t)type]), 0, NULL);
VkViewport viewport = {0.0f, 0.0f, (float)imInfo.extent.width, (float)imInfo.extent.height, 1.0f};
ObjDisp(cmd)->CmdSetViewport(Unwrap(cmd), 0, 1U, &viewport);
ObjDisp(cmd)->CmdSetScissor(Unwrap(cmd), 0, 1U, &discardRect);
discardRect.extent.width =
RDCMIN(discardRect.extent.width, imInfo.extent.width - discardRect.offset.x);
discardRect.extent.height =
RDCMIN(discardRect.extent.height, imInfo.extent.height - discardRect.offset.y);
discardRange.layerCount =
RDCMIN(discardRange.layerCount, imInfo.arrayLayers - discardRange.baseArrayLayer);
VkRenderPassBeginInfo rpbegin = {
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
NULL,
Unwrap(passdata.rp),
VK_NULL_HANDLE,
discardRect,
};
uint32_t pass = 0;
VkImageMemoryBarrier dstimBarrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL,
VK_ACCESS_ALL_READ_BITS | VK_ACCESS_ALL_WRITE_BITS, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
curLayout, depth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, Unwrap(image), discardRange,
};
DoPipelineBarrier(cmd, 1, &dstimBarrier);
ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_DiscardLayout), VK_SHADER_STAGE_ALL, 0, 4,
&pass);
uint32_t offset = 0;
if(imAspects != discardRange.aspectMask)
{
// if we're only discarding one of depth or stencil in a depth/stencil image, pick a
// framebuffer that only targets that aspect.
if(discardRange.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT)
offset = imInfo.arrayLayers;
else
offset = imInfo.arrayLayers * 2;
}
for(uint32_t slice = discardRange.baseArrayLayer;
slice < discardRange.baseArrayLayer + discardRange.layerCount; slice++)
{
rpbegin.framebuffer = Unwrap(imgdata.fbs[slice + offset]);
ObjDisp(cmd)->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE);
if(depth && discardRange.aspectMask != VK_IMAGE_ASPECT_DEPTH_BIT)
{
pass = 1;
ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_DiscardLayout), VK_SHADER_STAGE_ALL, 0,
4, &pass);
ObjDisp(cmd)->CmdSetStencilReference(
Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, 0x00);
ObjDisp(cmd)->CmdDraw(Unwrap(cmd), 4, 1, 0, 0);
pass = 2;
ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_DiscardLayout), VK_SHADER_STAGE_ALL, 0,
4, &pass);
ObjDisp(cmd)->CmdSetStencilReference(
Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, 0xff);
ObjDisp(cmd)->CmdDraw(Unwrap(cmd), 4, 1, 0, 0);
}
else
{
ObjDisp(cmd)->CmdDraw(Unwrap(cmd), 4, 1, 0, 0);
}
ObjDisp(cmd)->CmdEndRenderPass(Unwrap(cmd));
}
dstimBarrier.oldLayout = dstimBarrier.newLayout;
dstimBarrier.newLayout = curLayout;
dstimBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dstimBarrier.dstAccessMask = VK_ACCESS_ALL_WRITE_BITS | VK_ACCESS_ALL_READ_BITS;
DoPipelineBarrier(cmd, 1, &dstimBarrier);
/*
for(UINT sub = 0; sub < region->NumSubresources; sub++)
{
UINT subresource = region->FirstSubresource + sub;
if(depth)
{
dsvDesc.Texture2DMSArray.FirstArraySlice = GetSliceForSubresource(res, subresource);
m_pDevice->CreateDepthStencilView(res, &dsvDesc, dsv);
cmd->OMSetRenderTargets(0, NULL, FALSE, &dsv);
}
else
{
rtvDesc.Texture2DMSArray.FirstArraySlice = GetSliceForSubresource(res, subresource);
m_pDevice->CreateRenderTargetView(res, &rtvDesc, rtv);
cmd->OMSetRenderTargets(1, &rtv, FALSE, NULL);
}
UINT mip = GetMipForSubresource(res, subresource);
UINT plane = GetPlaneForSubresource(res, subresource);
for(D3D12_RECT r : rects)
{
r.right = RDCMIN(LONG(RDCMAX(1U, (UINT)desc.Width >> mip)), r.right);
r.bottom = RDCMIN(LONG(RDCMAX(1U, (UINT)desc.Height >> mip)), r.bottom);
cmd->RSSetScissorRects(1, &r);
}
}
*/
m_pDriver->GetCmdRenderState().BindPipeline(m_pDriver, cmd, VulkanRenderState::BindGraphics,
false);
return;
}
rdcpair<VkFormat, DiscardType> key = {imInfo.format, type};
if(key.first == VK_FORMAT_S8_UINT)
key.first = VK_FORMAT_D32_SFLOAT_S8_UINT;
VkBuffer buf = m_DiscardPatterns[key];
VkResult vkr = VK_SUCCESS;
if(buf == VK_NULL_HANDLE)
{
bytebuf pattern = GetDiscardPattern(key.second, MakeResourceFormat(key.first));
VkBufferCreateInfo bufInfo = {
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
NULL,
0,
pattern.size(),
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
};
vkr = m_pDriver->vkCreateBuffer(dev, &bufInfo, NULL, &buf);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
MemoryAllocation alloc = m_pDriver->AllocateMemoryForResource(
buf, MemoryScope::ImmutableReplayDebug, MemoryType::GPULocal);
vkr = vt->BindBufferMemory(Unwrap(dev), Unwrap(buf), Unwrap(alloc.mem), alloc.offs);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
vt->CmdUpdateBuffer(Unwrap(cmd), Unwrap(buf), 0, pattern.size(), pattern.data());
m_DiscardPatterns[key] = buf;
VkBufferMemoryBarrier bufBarrier = {
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
NULL,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_TRANSFER_READ_BIT,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(buf),
0,
VK_WHOLE_SIZE,
};
DoPipelineBarrier(cmd, 1, &bufBarrier);
}
VkImageAspectFlags aspectFlags = discardRange.aspectMask & FormatImageAspects(imInfo.format);
rdcarray<VkBufferImageCopy> mainCopies, stencilCopies;
VkExtent3D extent;
// copy each slice/mip individually
for(uint32_t a = 0; a < imInfo.arrayLayers; a++)
{
if(a < discardRange.baseArrayLayer || a >= discardRange.baseArrayLayer + discardRange.layerCount)
continue;
extent = imInfo.extent;
extent.width = RDCMIN(extent.width, discardRect.offset.x + discardRect.extent.width);
extent.height = RDCMIN(extent.height, discardRect.offset.y + discardRect.extent.height);
for(uint32_t m = 0; m < imInfo.mipLevels; m++)
{
if(m >= discardRange.baseMipLevel && m < discardRange.baseMipLevel + discardRange.levelCount)
{
for(uint32_t z = 0; z < extent.depth; z++)
{
for(uint32_t y = discardRect.offset.y; y < extent.height; y += DiscardPatternHeight)
{
for(uint32_t x = discardRect.offset.x; x < extent.width; x += DiscardPatternWidth)
{
VkBufferImageCopy region = {
0,
0,
0,
{aspectFlags, m, a, 1},
{
(int)x, (int)y, (int)z,
},
};
region.imageExtent.width = RDCMIN(DiscardPatternWidth, extent.width - x);
region.imageExtent.height = RDCMIN(DiscardPatternHeight, extent.height - y);
region.imageExtent.depth = 1;
region.bufferRowLength = DiscardPatternWidth;
// for depth/stencil copies, write depth first
if(aspectFlags == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if(aspectFlags != VK_IMAGE_ASPECT_STENCIL_BIT)
mainCopies.push_back(region);
if(aspectFlags & VK_IMAGE_ASPECT_STENCIL_BIT)
{
uint32_t depthStride = (imInfo.format == VK_FORMAT_D16_UNORM_S8_UINT ? 2 : 4);
// if it's a depth/stencil format, write stencil separately
region.bufferOffset = DiscardPatternWidth * DiscardPatternHeight * depthStride;
region.bufferRowLength = DiscardPatternWidth * depthStride;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
stencilCopies.push_back(region);
}
}
}
}
}
// update the extent for the next mip
extent.width = RDCMAX(extent.width >> 1, 1U);
extent.height = RDCMAX(extent.height >> 1, 1U);
extent.depth = RDCMAX(extent.depth >> 1, 1U);
}
}
VkImageMemoryBarrier dstimBarrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
VK_ACCESS_ALL_READ_BITS | VK_ACCESS_ALL_WRITE_BITS,
VK_ACCESS_TRANSFER_WRITE_BIT,
curLayout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(image),
discardRange,
};
DoPipelineBarrier(cmd, 1, &dstimBarrier);
if(!mainCopies.empty())
ObjDisp(cmd)->CmdCopyBufferToImage(Unwrap(cmd), Unwrap(buf), Unwrap(image),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
(uint32_t)mainCopies.size(), mainCopies.data());
if(!stencilCopies.empty())
ObjDisp(cmd)->CmdCopyBufferToImage(Unwrap(cmd), Unwrap(buf), Unwrap(image),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
(uint32_t)stencilCopies.size(), stencilCopies.data());
dstimBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
dstimBarrier.newLayout = curLayout;
dstimBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
dstimBarrier.dstAccessMask = VK_ACCESS_ALL_WRITE_BITS | VK_ACCESS_ALL_READ_BITS;
DoPipelineBarrier(cmd, 1, &dstimBarrier);
}
void VulkanDebugManager::InitReadbackBuffer()
{
if(m_ReadbackWindow.buf == VK_NULL_HANDLE)
{
VkDevice dev = m_pDriver->GetDev();
m_ReadbackWindow.Create(m_pDriver, dev, 256 * 1024 * 1024ULL, 1, GPUBuffer::eGPUBufferReadback);
VkResult vkr = ObjDisp(dev)->MapMemory(Unwrap(dev), Unwrap(m_ReadbackWindow.mem), 0,
VK_WHOLE_SIZE, 0, (void **)&m_ReadbackPtr);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
}
void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe,
VkDescriptorPool &descpool,
rdcarray<VkDescriptorSetLayout> &setLayouts,
rdcarray<VkDescriptorSet> &descSets,
VkShaderStageFlagBits patchedBindingStage,
const VkDescriptorSetLayoutBinding *newBindings,
size_t newBindingsCount)
{
VkDevice dev = m_Device;
VulkanCreationInfo &creationInfo = m_pDriver->m_CreationInfo;
const VulkanCreationInfo::Pipeline &pipeInfo = creationInfo.m_Pipeline[pipe.pipeline];
VkResult vkr = VK_SUCCESS;
rdcarray<VkWriteDescriptorSet> descWrites;
struct AllocedWrites
{
~AllocedWrites()
{
for(VkDescriptorImageInfo *a : imgWrites)
delete[] a;
for(VkDescriptorBufferInfo *a : bufWrites)
delete[] a;
for(VkBufferView *a : bufViewWrites)
delete[] a;
for(VkWriteDescriptorSetInlineUniformBlockEXT *a : inlineWrites)
delete a;
}
rdcarray<VkDescriptorImageInfo *> imgWrites;
rdcarray<VkDescriptorBufferInfo *> bufWrites;
rdcarray<VkBufferView *> bufViewWrites;
rdcarray<VkWriteDescriptorSetInlineUniformBlockEXT *> inlineWrites;
} alloced;
rdcarray<VkDescriptorImageInfo *> &allocImgWrites = alloced.imgWrites;
rdcarray<VkDescriptorBufferInfo *> &allocBufWrites = alloced.bufWrites;
rdcarray<VkBufferView *> &allocBufViewWrites = alloced.bufViewWrites;
rdcarray<VkWriteDescriptorSetInlineUniformBlockEXT *> &allocInlineWrites = alloced.inlineWrites;
// one for each descriptor type. 1 of each to start with, we then increment for each descriptor
// we need to allocate
VkDescriptorPoolSize poolSizes[12] = {
{VK_DESCRIPTOR_TYPE_SAMPLER, 1},
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1},
{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1},
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1},
{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1},
{VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1},
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1},
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1},
{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1},
{VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, 0},
};
VkDescriptorPoolInlineUniformBlockCreateInfoEXT inlineCreateInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT,
};
static const uint32_t InlinePoolIndex = 11;
uint32_t poolSizeCount = InlinePoolIndex;
// count up our own
for(size_t i = 0; i < newBindingsCount; i++)
{
RDCASSERT(newBindings[i].descriptorType < ARRAY_COUNT(poolSizes), newBindings[i].descriptorType);
poolSizes[newBindings[i].descriptorType].descriptorCount += newBindings[i].descriptorCount;
}
const rdcarray<ResourceId> &pipeDescSetLayouts =
creationInfo.m_PipelineLayout[pipeInfo.layout].descSetLayouts;
// need to add our added bindings to the first descriptor set
rdcarray<VkDescriptorSetLayoutBinding> bindings(newBindings, newBindingsCount);
// if there are fewer sets bound than were declared in the pipeline layout, only process the
// bound sets (as otherwise we'd fail to copy from them). Assume the application knew what it
// was doing and the other sets are statically unused.
setLayouts.resize(RDCMIN(pipe.descSets.size(), pipeDescSetLayouts.size()));
size_t boundDescs = setLayouts.size();
// need at least one set, if the shader isn't using any we'll just make our own
if(setLayouts.empty())
setLayouts.resize(1);
// start with the limits as they are, and subtract off them incrementally. When any limit would
// drop below 0, we fail.
uint32_t maxPerStageDescriptorSamplers[6] = {
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSamplers,
};
uint32_t maxPerStageDescriptorUniformBuffers[6] = {
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorUniformBuffers,
};
uint32_t maxPerStageDescriptorStorageBuffers[6] = {
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageBuffers,
};
uint32_t maxPerStageDescriptorSampledImages[6] = {
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorSampledImages,
};
uint32_t maxPerStageDescriptorStorageImages[6] = {
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorStorageImages,
};
uint32_t maxPerStageDescriptorInputAttachments[6] = {
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments,
m_pDriver->GetDeviceProps().limits.maxPerStageDescriptorInputAttachments,
};
uint32_t maxPerStageResources[6] = {
m_pDriver->GetDeviceProps().limits.maxPerStageResources,
m_pDriver->GetDeviceProps().limits.maxPerStageResources,
m_pDriver->GetDeviceProps().limits.maxPerStageResources,
m_pDriver->GetDeviceProps().limits.maxPerStageResources,
m_pDriver->GetDeviceProps().limits.maxPerStageResources,
m_pDriver->GetDeviceProps().limits.maxPerStageResources,
};
uint32_t maxDescriptorSetSamplers = m_pDriver->GetDeviceProps().limits.maxDescriptorSetSamplers;
uint32_t maxDescriptorSetUniformBuffers =
m_pDriver->GetDeviceProps().limits.maxDescriptorSetUniformBuffers;
uint32_t maxDescriptorSetUniformBuffersDynamic =
m_pDriver->GetDeviceProps().limits.maxDescriptorSetUniformBuffersDynamic;
uint32_t maxDescriptorSetStorageBuffers =
m_pDriver->GetDeviceProps().limits.maxDescriptorSetStorageBuffers;
uint32_t maxDescriptorSetStorageBuffersDynamic =
m_pDriver->GetDeviceProps().limits.maxDescriptorSetStorageBuffersDynamic;
uint32_t maxDescriptorSetSampledImages =
m_pDriver->GetDeviceProps().limits.maxDescriptorSetSampledImages;
uint32_t maxDescriptorSetStorageImages =
m_pDriver->GetDeviceProps().limits.maxDescriptorSetStorageImages;
uint32_t maxDescriptorSetInputAttachments =
m_pDriver->GetDeviceProps().limits.maxDescriptorSetInputAttachments;
uint32_t maxDescriptorSetInlineUniformBlocks = 0;
uint32_t maxPerStageDescriptorInlineUniformBlocks[6] = {};
if(m_pDriver->GetExtensions(NULL).ext_EXT_inline_uniform_block)
{
VkPhysicalDeviceInlineUniformBlockPropertiesEXT inlineProps = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT,
};
VkPhysicalDeviceProperties2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
availBase.pNext = &inlineProps;
m_pDriver->vkGetPhysicalDeviceProperties2(m_pDriver->GetPhysDev(), &availBase);
maxDescriptorSetInlineUniformBlocks = inlineProps.maxDescriptorSetInlineUniformBlocks;
for(size_t i = 0; i < ARRAY_COUNT(maxPerStageDescriptorInlineUniformBlocks); i++)
maxPerStageDescriptorInlineUniformBlocks[i] =
inlineProps.maxPerStageDescriptorInlineUniformBlocks;
}
bool error = false;
#define UPDATE_AND_CHECK_LIMIT(maxLimit) \
if(!error) \
{ \
if(descriptorCount > maxLimit) \
{ \
error = true; \
RDCWARN("Limit %s is exceeded. Cannot patch in required descriptor(s).", #maxLimit); \
} \
else \
{ \
maxLimit -= descriptorCount; \
} \
}
#define UPDATE_AND_CHECK_STAGE_LIMIT(maxLimit) \
if(!error) \
{ \
for(uint32_t sbit = 0; sbit < 6; sbit++) \
{ \
if(newBind.stageFlags & (1U << sbit)) \
{ \
if(descriptorCount > maxLimit[sbit]) \
{ \
error = true; \
RDCWARN("Limit %s is exceeded. Cannot patch in required descriptor(s).", #maxLimit); \
} \
else \
{ \
maxLimit[sbit] -= descriptorCount; \
} \
} \
} \
}
for(size_t i = 0; !error && i < setLayouts.size(); i++)
{
bool hasImmutableSamplers = false;
// except for the first layout we need to start from scratch
if(i > 0)
bindings.clear();
// if the shader had no descriptor sets at all, i will be invalid, so just skip and add a set
// with only our own bindings.
if(i < pipeDescSetLayouts.size() && i < pipe.descSets.size() &&
pipe.descSets[i].pipeLayout != ResourceId())
{
// use the descriptor set layout from when it was bound. If the pipeline layout declared a
// descriptor set layout for this set, but it's statically unused, it may be complete
// garbage and doesn't match what the shader uses. However the pipeline layout at descriptor
// set bind time must have been compatible and valid so we can use it. If this set *is* used
// then the pipeline layout at bind time must be compatible with the pipeline's pipeline
// layout, so we're fine too.
const DescSetLayout &origLayout =
creationInfo.m_DescSetLayout[creationInfo.m_PipelineLayout[pipe.descSets[i].pipeLayout]
.descSetLayouts[i]];
WrappedVulkan::DescriptorSetInfo &setInfo =
m_pDriver->m_DescriptorSetState[pipe.descSets[i].descSet];
for(size_t b = 0; !error && b < origLayout.bindings.size(); b++)
{
const DescSetLayout::Binding &bind = origLayout.bindings[b];
// skip empty bindings
if(bind.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM)
continue;
uint32_t descriptorCount = bind.descriptorCount;
if(bind.variableSize)
descriptorCount = setInfo.data.variableDescriptorCount;
// make room in the pool
if(bind.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)
{
poolSizes[InlinePoolIndex].descriptorCount += descriptorCount;
inlineCreateInfo.maxInlineUniformBlockBindings++;
}
else
{
poolSizes[bind.descriptorType].descriptorCount += descriptorCount;
}
VkDescriptorSetLayoutBinding newBind;
// offset the binding. We offset all sets to make it easier for patching - don't need to
// conditionally patch shader bindings depending on which set they're in.
newBind.binding = uint32_t(b + newBindingsCount);
newBind.descriptorCount = descriptorCount;
newBind.descriptorType = bind.descriptorType;
// we only need it available for compute, just make all bindings visible otherwise dynamic
// buffer offsets could be indexed wrongly. Consider the case where we have binding 0 as a
// fragment UBO, and binding 1 as a vertex UBO. Then there are two dynamic offsets, and
// the second is the one we want to use with ours. If we only add the compute visibility
// bit to the second UBO, then suddenly it's the *first* offset that we must provide.
// Instead of trying to remap offsets to match, we simply make every binding compute
// visible so the ordering is still the same. Since compute and graphics are disjoint this
// is safe.
if(patchedBindingStage != 0)
newBind.stageFlags = patchedBindingStage;
else
newBind.stageFlags = bind.stageFlags;
switch(bind.descriptorType)
{
case VK_DESCRIPTOR_TYPE_SAMPLER:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSamplers);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSamplers);
break;
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSampledImages);
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSamplers);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSamplers);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSampledImages);
break;
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSampledImages);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSampledImages);
break;
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageImages);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageImages);
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetSampledImages);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorSampledImages);
break;
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageImages);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageImages);
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetUniformBuffers);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorUniformBuffers);
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageBuffers);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageBuffers);
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetUniformBuffersDynamic);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorUniformBuffers);
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetStorageBuffersDynamic);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorStorageBuffers);
break;
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetInputAttachments);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorInputAttachments);
break;
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT:
descriptorCount = 1;
UPDATE_AND_CHECK_LIMIT(maxDescriptorSetInlineUniformBlocks);
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageDescriptorInlineUniformBlocks);
break;
default: break;
}
UPDATE_AND_CHECK_STAGE_LIMIT(maxPerStageResources);
if(bind.immutableSampler)
{
hasImmutableSamplers = true;
VkSampler *samplers = new VkSampler[bind.descriptorCount];
newBind.pImmutableSamplers = samplers;
for(uint32_t s = 0; s < bind.descriptorCount; s++)
samplers[s] = GetResourceManager()->GetCurrentHandle<VkSampler>(bind.immutableSampler[s]);
}
else
{
newBind.pImmutableSamplers = NULL;
}
bindings.push_back(newBind);
}
}
VkDescriptorSetLayoutCreateInfo descsetLayoutInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
NULL,
0,
(uint32_t)bindings.size(),
bindings.data(),
};
if(!error)
{
// create new offseted descriptor layout
vkr = m_pDriver->vkCreateDescriptorSetLayout(dev, &descsetLayoutInfo, NULL, &setLayouts[i]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
if(hasImmutableSamplers)
{
for(const VkDescriptorSetLayoutBinding &bind : bindings)
delete[] bind.pImmutableSamplers;
}
}
// if we hit an error, we can't create the descriptor set so bail out now
if(error)
return;
VkDescriptorPoolCreateInfo poolCreateInfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
// 1 set for each layout
poolCreateInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
poolCreateInfo.maxSets = (uint32_t)setLayouts.size();
poolCreateInfo.poolSizeCount = poolSizeCount;
poolCreateInfo.pPoolSizes = poolSizes;
if(inlineCreateInfo.maxInlineUniformBlockBindings > 0)
{
poolCreateInfo.poolSizeCount++;
poolCreateInfo.pNext = &inlineCreateInfo;
}
// create descriptor pool with enough space for our descriptors
vkr = m_pDriver->vkCreateDescriptorPool(dev, &poolCreateInfo, NULL, &descpool);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// allocate all the descriptors
VkDescriptorSetAllocateInfo descSetAllocInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
NULL,
descpool,
(uint32_t)setLayouts.size(),
setLayouts.data(),
};
descSets.resize(setLayouts.size());
m_pDriver->vkAllocateDescriptorSets(dev, &descSetAllocInfo, descSets.data());
// copy the data across from the real descriptors into our adjusted bindings
for(size_t i = 0; i < boundDescs; i++)
{
if(pipe.descSets[i].descSet == ResourceId())
continue;
// as above we use the pipeline layout that was originally used to bind this descriptor set
// and not the pipeline layout from the pipeline, in case the pipeline statically doesn't use
// this set and so its descriptor set layout is garbage (doesn't match the actual bound
// descriptor set)
const DescSetLayout &origLayout =
creationInfo.m_DescSetLayout[creationInfo.m_PipelineLayout[pipe.descSets[i].pipeLayout]
.descSetLayouts[i]];
WrappedVulkan::DescriptorSetInfo &setInfo =
m_pDriver->m_DescriptorSetState[pipe.descSets[i].descSet];
{
// push descriptors don't have a source to copy from, we need to add writes
VkWriteDescriptorSet write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
write.dstSet = descSets[i];
// Only write bindings that actually exist in the current descriptor
// set. If there are bindings that aren't set, assume the app knows
// what it's doing and the remaining bindings are unused.
for(size_t b = 0; b < setInfo.data.binds.size(); b++)
{
const DescSetLayout::Binding &bind = origLayout.bindings[b];
// skip empty bindings
if(bind.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM)
continue;
uint32_t descriptorCount = bind.descriptorCount;
if(bind.variableSize)
descriptorCount = setInfo.data.variableDescriptorCount;
DescriptorSetSlot *slot = setInfo.data.binds[b];
write.dstBinding = uint32_t(b + newBindingsCount);
write.dstArrayElement = 0;
write.descriptorCount = descriptorCount;
write.descriptorType = bind.descriptorType;
switch(write.descriptorType)
{
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
{
VkDescriptorImageInfo *out = new VkDescriptorImageInfo[write.descriptorCount];
for(uint32_t w = 0; w < write.descriptorCount; w++)
{
const DescriptorSetSlotImageInfo &src = slot[w].imageInfo;
out[w].imageLayout = src.imageLayout;
out[w].sampler = GetResourceManager()->GetCurrentHandle<VkSampler>(src.sampler);
out[w].imageView = GetResourceManager()->GetCurrentHandle<VkImageView>(src.imageView);
}
write.pImageInfo = out;
allocImgWrites.push_back(out);
break;
}
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
{
VkBufferView *out = new VkBufferView[write.descriptorCount];
for(uint32_t w = 0; w < write.descriptorCount; w++)
out[w] = GetResourceManager()->GetCurrentHandle<VkBufferView>(slot[w].texelBufferView);
write.pTexelBufferView = out;
allocBufViewWrites.push_back(out);
break;
}
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
{
VkDescriptorBufferInfo *out = new VkDescriptorBufferInfo[write.descriptorCount];
for(uint32_t w = 0; w < write.descriptorCount; w++)
{
const DescriptorSetSlotBufferInfo &src = slot[w].bufferInfo;
out[w].offset = src.offset;
out[w].range = src.range;
out[w].buffer = GetResourceManager()->GetCurrentHandle<VkBuffer>(src.buffer);
}
write.pBufferInfo = out;
allocBufWrites.push_back(out);
break;
}
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT:
{
allocInlineWrites.push_back(new VkWriteDescriptorSetInlineUniformBlockEXT);
VkWriteDescriptorSetInlineUniformBlockEXT *inlineWrite = allocInlineWrites.back();
inlineWrite->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT;
inlineWrite->pNext = NULL;
inlineWrite->dataSize = descriptorCount;
inlineWrite->pData = setInfo.data.inlineBytes.data() + slot[0].inlineOffset;
write.pNext = inlineWrite;
break;
}
default: RDCERR("Unexpected descriptor type %d", write.descriptorType);
}
// skip validity check for inline uniform block as the descriptor count means something
// different
if(write.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)
{
write.descriptorCount = descriptorCount;
descWrites.push_back(write);
continue;
}
// start with no descriptors
write.descriptorCount = 0;
for(uint32_t w = 0; w < descriptorCount; w++)
{
// if this write is valid, we increment the descriptor count and continue
if(IsValid(m_pDriver->NULLDescriptorsAllowed(), write, w - write.dstArrayElement))
{
write.descriptorCount++;
}
else
{
// if this write isn't valid, then we first check to see if we had any previous
// pending writes in the array we were going to batch together, if so we add them.
if(write.descriptorCount > 0)
descWrites.push_back(write);
// skip past any previous descriptors we just wrote, as well as the current invalid
// one
if(write.pBufferInfo)
write.pBufferInfo += write.descriptorCount + 1;
if(write.pImageInfo)
write.pImageInfo += write.descriptorCount + 1;
if(write.pTexelBufferView)
write.pTexelBufferView += write.descriptorCount + 1;
// now start again from 0 descriptors, at the next array element
write.dstArrayElement += write.descriptorCount + 1;
write.descriptorCount = 0;
}
}
// if there are any left, add them here
if(write.descriptorCount > 0)
descWrites.push_back(write);
// don't leak the arrays and cause double deletes, NULL them after each time
write.pImageInfo = NULL;
write.pBufferInfo = NULL;
write.pTexelBufferView = NULL;
}
}
}
m_pDriver->vkUpdateDescriptorSets(dev, (uint32_t)descWrites.size(), descWrites.data(), 0, NULL);
}
void VulkanDebugManager::CustomShaderRendering::Destroy(WrappedVulkan *driver)
{
driver->vkDestroyRenderPass(driver->GetDev(), TexRP, NULL);
driver->vkDestroyFramebuffer(driver->GetDev(), TexFB, NULL);
driver->vkDestroyImage(driver->GetDev(), TexImg, NULL);
for(size_t i = 0; i < ARRAY_COUNT(TexImgView); i++)
driver->vkDestroyImageView(driver->GetDev(), TexImgView[i], NULL);
driver->vkFreeMemory(driver->GetDev(), TexMem, NULL);
driver->vkDestroyPipeline(driver->GetDev(), TexPipeline, NULL);
}
void VulkanReplay::CreateResources()
{
m_Device = m_pDriver->GetDev();
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.0f);
m_General.Init(m_pDriver, VK_NULL_HANDLE);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.1f);
m_TexRender.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.3f);
m_Overlay.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.4f);
m_MeshRender.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.6f);
m_VertexPick.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.7f);
m_PixelPick.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.75f);
m_PixelHistory.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.8f);
m_Histogram.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.9f);
m_ShaderDebugData.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 1.0f);
GPA_vkContextOpenInfo context = {Unwrap(m_pDriver->GetInstance()),
Unwrap(m_pDriver->GetPhysDev()), Unwrap(m_pDriver->GetDev())};
if(!m_pDriver->GetReplay()->IsRemoteProxy() && Vulkan_HardwareCounters())
{
AMDCounters *counters = NULL;
GPUVendor vendor = m_pDriver->GetDriverInfo().Vendor();
if(vendor == GPUVendor::AMD)
{
RDCLOG("AMD GPU detected - trying to initialise AMD counters");
counters = new AMDCounters();
}
else
{
RDCLOG("%s GPU detected - no counters available", ToStr(vendor).c_str());
}
if(counters && counters->Init(AMDCounters::ApiType::Vk, (void *)&context))
{
m_pAMDCounters = counters;
}
else
{
delete counters;
m_pAMDCounters = NULL;
}
}
}
void VulkanReplay::DestroyResources()
{
ClearPostVSCache();
ClearFeedbackCache();
m_General.Destroy(m_pDriver);
m_TexRender.Destroy(m_pDriver);
m_Overlay.Destroy(m_pDriver);
m_VertexPick.Destroy(m_pDriver);
m_PixelPick.Destroy(m_pDriver);
m_PixelHistory.Destroy(m_pDriver);
m_Histogram.Destroy(m_pDriver);
m_PostVS.Destroy(m_pDriver);
SAFE_DELETE(m_pAMDCounters);
}
void VulkanReplay::GeneralMisc::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
VkResult vkr = VK_SUCCESS;
VkDescriptorPoolSize descPoolTypes[] = {
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 320},
{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 128},
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 32},
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 128},
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 128},
{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 64},
{VK_DESCRIPTOR_TYPE_SAMPLER, 64},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 32},
};
VkDescriptorPoolCreateInfo descPoolInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
NULL,
0,
32,
ARRAY_COUNT(descPoolTypes),
&descPoolTypes[0],
};
// create descriptor pool
vkr = driver->vkCreateDescriptorPool(driver->GetDev(), &descPoolInfo, NULL, &DescriptorPool);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
CREATE_OBJECT(PointSampler, VK_FILTER_NEAREST);
}
void VulkanReplay::GeneralMisc::Destroy(WrappedVulkan *driver)
{
if(DescriptorPool == VK_NULL_HANDLE)
return;
driver->vkDestroyDescriptorPool(driver->GetDev(), DescriptorPool, NULL);
driver->vkDestroySampler(driver->GetDev(), PointSampler, NULL);
}
void VulkanReplay::TextureRendering::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
VkResult vkr = VK_SUCCESS;
VulkanShaderCache *shaderCache = driver->GetShaderCache();
CREATE_OBJECT(PointSampler, VK_FILTER_NEAREST);
CREATE_OBJECT(LinearSampler, VK_FILTER_LINEAR);
CREATE_OBJECT(DescSetLayout,
{
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL},
{1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL},
{6, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{7, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{8, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{9, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{10, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, VK_SHADER_STAGE_ALL, NULL},
{11, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{12, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{13, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{14, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{15, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{16, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{17, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{18, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{19, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{20, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{50, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_ALL, &PointSampler},
{51, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_ALL, &LinearSampler},
});
CREATE_OBJECT(PipeLayout, DescSetLayout, 0);
for(size_t i = 0; i < ARRAY_COUNT(DescSet); i++)
{
CREATE_OBJECT(DescSet[i], descriptorPool, DescSetLayout);
}
UBO.Create(driver, driver->GetDev(), 128, 10, 0);
RDCCOMPILE_ASSERT(sizeof(TexDisplayUBOData) <= 128, "tex display size");
HeatmapUBO.Create(driver, driver->GetDev(), 512, 10, 0);
RDCCOMPILE_ASSERT(sizeof(HeatmapData) <= 512, "tex display size");
{
VkRenderPass SRGBA8RP = VK_NULL_HANDLE;
VkRenderPass RGBA16RP = VK_NULL_HANDLE;
VkRenderPass RGBA32RP = VK_NULL_HANDLE;
CREATE_OBJECT(SRGBA8RP, VK_FORMAT_R8G8B8A8_SRGB);
CREATE_OBJECT(RGBA16RP, VK_FORMAT_R16G16B16A16_SFLOAT);
CREATE_OBJECT(RGBA32RP, VK_FORMAT_R32G32B32A32_SFLOAT);
ConciseGraphicsPipeline texDisplayInfo = {
SRGBA8RP,
PipeLayout,
shaderCache->GetBuiltinModule(BuiltinShader::BlitVS),
shaderCache->GetBuiltinModule(BuiltinShader::TexDisplayFS),
{VK_DYNAMIC_STATE_VIEWPORT},
VK_SAMPLE_COUNT_1_BIT,
false, // sampleRateShading
false, // depthEnable
false, // stencilEnable
VK_STENCIL_OP_KEEP,
true, // colourOutput
false, // blendEnable
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ZERO,
0xf, // writeMask
};
ConciseGraphicsPipeline texRemapInfo = texDisplayInfo;
CREATE_OBJECT(Pipeline, texDisplayInfo);
texDisplayInfo.renderPass = RGBA32RP;
CREATE_OBJECT(F32Pipeline, texDisplayInfo);
texDisplayInfo.renderPass = RGBA16RP;
CREATE_OBJECT(F16Pipeline, texDisplayInfo);
texDisplayInfo.renderPass = SRGBA8RP;
texDisplayInfo.blendEnable = true;
texDisplayInfo.srcBlend = VK_BLEND_FACTOR_SRC_ALPHA;
texDisplayInfo.dstBlend = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
CREATE_OBJECT(BlendPipeline, texDisplayInfo);
VkFormat formats[3] = {VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R16G16B16A16_UINT,
VK_FORMAT_R32G32B32A32_UINT};
CompType cast[3] = {CompType::Float, CompType::UInt, CompType::SInt};
for(int f = 0; f < 3; f++)
{
for(int i = 0; i < 3; i++)
{
texRemapInfo.fragment =
shaderCache->GetBuiltinModule(BuiltinShader::TexRemap, BuiltinShaderBaseType(i));
CREATE_OBJECT(texRemapInfo.renderPass, GetViewCastedFormat(formats[f], cast[i]));
CREATE_OBJECT(RemapPipeline[f][i][0], texRemapInfo);
driver->vkDestroyRenderPass(driver->GetDev(), texRemapInfo.renderPass, NULL);
}
}
// make versions that only write to green, for doing two-pass stencil writes
texRemapInfo.writeMask = texDisplayInfo.writeMask = 0x2;
for(int f = 0; f < 3; f++)
{
for(int i = 0; i < 3; i++)
{
texRemapInfo.fragment =
shaderCache->GetBuiltinModule(BuiltinShader::TexRemap, BuiltinShaderBaseType(i));
CREATE_OBJECT(texRemapInfo.renderPass, GetViewCastedFormat(formats[f], cast[i]));
CREATE_OBJECT(RemapPipeline[f][i][1], texRemapInfo);
driver->vkDestroyRenderPass(driver->GetDev(), texRemapInfo.renderPass, NULL);
}
}
texDisplayInfo.renderPass = SRGBA8RP;
CREATE_OBJECT(PipelineGreenOnly, texDisplayInfo);
texDisplayInfo.renderPass = RGBA32RP;
CREATE_OBJECT(F32PipelineGreenOnly, texDisplayInfo);
texDisplayInfo.renderPass = RGBA16RP;
CREATE_OBJECT(F16PipelineGreenOnly, texDisplayInfo);
driver->vkDestroyRenderPass(driver->GetDev(), SRGBA8RP, NULL);
driver->vkDestroyRenderPass(driver->GetDev(), RGBA16RP, NULL);
driver->vkDestroyRenderPass(driver->GetDev(), RGBA32RP, NULL);
}
// create dummy images for filling out the texdisplay descriptors
// in slots that are skipped by dynamic branching (e.g. 3D texture
// when we're displaying a 2D, etc).
{
VkCommandBuffer cmd = driver->GetNextCmd();
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
int index = 0;
// we pick RGBA8 formats to be guaranteed they will be supported
VkFormat formats[] = {VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_SINT};
VkImageType types[] = {VK_IMAGE_TYPE_1D, VK_IMAGE_TYPE_2D, VK_IMAGE_TYPE_3D, VK_IMAGE_TYPE_2D};
VkImageViewType viewtypes[] = {
VK_IMAGE_VIEW_TYPE_1D_ARRAY, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_VIEW_TYPE_3D,
VK_IMAGE_VIEW_TYPE_2D_ARRAY,
driver->GetDeviceEnabledFeatures().imageCubeArray ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY
: VK_IMAGE_VIEW_TYPE_CUBE,
};
VkSampleCountFlagBits sampleCounts[] = {VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_1_BIT,
VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_4_BIT};
// type max is one higher than the last RESTYPE, and RESTYPES are 1-indexed
RDCCOMPILE_ASSERT(RESTYPE_TEXTYPEMAX - 1 == ARRAY_COUNT(types),
"RESTYPE values don't match formats for dummy images");
RDCCOMPILE_ASSERT(sizeof(DummyImages) == sizeof(DummyImageViews),
"dummy image arrays mismatched sizes");
RDCCOMPILE_ASSERT(ARRAY_COUNT(DummyImages) == ARRAY_COUNT(formats),
"dummy image arrays mismatched sizes");
// types + 1 for cube
RDCCOMPILE_ASSERT(ARRAY_COUNT(DummyImages[0]) == ARRAY_COUNT(types) + 1,
"dummy image arrays mismatched sizes");
RDCCOMPILE_ASSERT(ARRAY_COUNT(DummyImages[0]) == ARRAY_COUNT(viewtypes),
"dummy image arrays mismatched sizes");
RDCCOMPILE_ASSERT(ARRAY_COUNT(DummyWrites) == ARRAY_COUNT(DummyInfos),
"dummy image arrays mismatched sizes");
CREATE_OBJECT(DummySampler, VK_FILTER_NEAREST);
for(size_t fmt = 0; fmt < ARRAY_COUNT(formats); fmt++)
{
for(size_t type = 0; type < ARRAY_COUNT(types); type++)
{
// create 1x1 image of the right size
VkImageCreateInfo imInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
NULL,
0,
types[type],
formats[fmt],
{1, 1, 1},
1,
1,
sampleCounts[type],
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0,
NULL,
VK_IMAGE_LAYOUT_UNDEFINED,
};
// make the 2D image cube-compatible
if(type == 1)
{
imInfo.arrayLayers = 6;
imInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
}
vkr = driver->vkCreateImage(driver->GetDev(), &imInfo, NULL, &DummyImages[fmt][type]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
MemoryAllocation alloc = driver->AllocateMemoryForResource(
DummyImages[fmt][type], MemoryScope::ImmutableReplayDebug, MemoryType::GPULocal);
vkr = driver->vkBindImageMemory(driver->GetDev(), DummyImages[fmt][type], alloc.mem,
alloc.offs);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// fill out the descriptor set write to the write binding - set will be filled out
// on demand when we're actually using these writes.
DummyWrites[index].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
DummyWrites[index].pNext = NULL;
DummyWrites[index].dstSet = VK_NULL_HANDLE;
DummyWrites[index].dstBinding =
5 * uint32_t(fmt + 1) + uint32_t(type) + 1; // 5 + RESTYPE_x
DummyWrites[index].dstArrayElement = 0;
DummyWrites[index].descriptorCount = 1;
DummyWrites[index].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
DummyWrites[index].pImageInfo = &DummyInfos[index];
DummyWrites[index].pBufferInfo = NULL;
DummyWrites[index].pTexelBufferView = NULL;
DummyInfos[index].sampler = Unwrap(DummySampler);
DummyInfos[index].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
index++;
}
}
// add the last one for the odd-one-out YUV texture
DummyWrites[index].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
DummyWrites[index].pNext = NULL;
DummyWrites[index].dstSet = VK_NULL_HANDLE;
DummyWrites[index].dstBinding = 10; // texYUV
DummyWrites[index].dstArrayElement = 0;
DummyWrites[index].descriptorCount = 1;
DummyWrites[index].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
DummyWrites[index].pImageInfo = &DummyInfos[index];
DummyWrites[index].pBufferInfo = NULL;
DummyWrites[index].pTexelBufferView = NULL;
DummyWrites[index + 1] = DummyWrites[index];
DummyWrites[index + 1].dstArrayElement = 1;
DummyInfos[index].sampler = Unwrap(DummySampler);
DummyInfos[index].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
DummyInfos[index + 1].sampler = Unwrap(DummySampler);
DummyInfos[index + 1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// align up for the dummy buffer
{
VkBufferCreateInfo bufInfo = {
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL, 0, 16,
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT,
};
vkr = driver->vkCreateBuffer(driver->GetDev(), &bufInfo, NULL, &DummyBuffer);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
MemoryAllocation alloc = driver->AllocateMemoryForResource(
DummyBuffer, MemoryScope::ImmutableReplayDebug, MemoryType::GPULocal);
vkr = driver->vkBindBufferMemory(driver->GetDev(), DummyBuffer, alloc.mem, alloc.offs);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
// now that the image memory is bound, we can create the image views and fill the descriptor
// set writes.
index = 0;
for(size_t fmt = 0; fmt < ARRAY_COUNT(formats); fmt++)
{
for(size_t type = 0; type < ARRAY_COUNT(viewtypes); type++)
{
size_t imType = type;
// the cubemap view re-uses the 2D image
bool cube = false;
if(viewtypes[type] == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY ||
viewtypes[type] == VK_IMAGE_VIEW_TYPE_CUBE)
{
imType = 1;
cube = true;
}
VkImageViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
NULL,
0,
DummyImages[fmt][imType],
viewtypes[type],
formats[fmt],
{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,
},
};
if(cube)
viewInfo.subresourceRange.layerCount = 6;
vkr = driver->vkCreateImageView(driver->GetDev(), &viewInfo, NULL,
&DummyImageViews[fmt][type]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// the cubemap view we don't create an info for it, and the image is already transitioned
if(cube)
continue;
RDCASSERT((size_t)index < ARRAY_COUNT(DummyInfos), index);
DummyInfos[index].imageView = Unwrap(DummyImageViews[fmt][type]);
// need to update image layout into valid state
VkImageMemoryBarrier barrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
NULL,
0,
VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
Unwrap(DummyImages[fmt][imType]),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS},
};
DoPipelineBarrier(cmd, 1, &barrier);
index++;
}
}
// duplicate 2D dummy image into YUV
DummyInfos[index].imageView = DummyInfos[1].imageView;
DummyInfos[index + 1].imageView = DummyInfos[1].imageView;
if(DummyBuffer != VK_NULL_HANDLE)
{
VkFormat bufViewTypes[] = {
VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_R32G32B32A32_SINT,
};
for(size_t i = 0; i < ARRAY_COUNT(bufViewTypes); i++)
{
VkBufferViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, NULL, 0, DummyBuffer, bufViewTypes[i], 0, 16,
};
vkr = driver->vkCreateBufferView(driver->GetDev(), &viewInfo, NULL, &DummyBufferView[i]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
}
}
ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
}
}
void VulkanReplay::TextureRendering::Destroy(WrappedVulkan *driver)
{
if(DescSetLayout == VK_NULL_HANDLE)
return;
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), DescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), PipeLayout, NULL);
driver->vkDestroyPipeline(driver->GetDev(), Pipeline, NULL);
driver->vkDestroyPipeline(driver->GetDev(), BlendPipeline, NULL);
driver->vkDestroyPipeline(driver->GetDev(), F16Pipeline, NULL);
driver->vkDestroyPipeline(driver->GetDev(), F32Pipeline, NULL);
for(size_t f = 0; f < 3; f++)
for(size_t i = 0; i < 3; i++)
for(size_t g = 0; g < 2; g++)
driver->vkDestroyPipeline(driver->GetDev(), RemapPipeline[f][i][g], NULL);
driver->vkDestroyPipeline(driver->GetDev(), PipelineGreenOnly, NULL);
driver->vkDestroyPipeline(driver->GetDev(), F16PipelineGreenOnly, NULL);
driver->vkDestroyPipeline(driver->GetDev(), F32PipelineGreenOnly, NULL);
UBO.Destroy();
HeatmapUBO.Destroy();
driver->vkDestroySampler(driver->GetDev(), PointSampler, NULL);
driver->vkDestroySampler(driver->GetDev(), LinearSampler, NULL);
for(size_t fmt = 0; fmt < ARRAY_COUNT(DummyImages); fmt++)
{
for(size_t type = 0; type < ARRAY_COUNT(DummyImages[0]); type++)
{
driver->vkDestroyImageView(driver->GetDev(), DummyImageViews[fmt][type], NULL);
driver->vkDestroyImage(driver->GetDev(), DummyImages[fmt][type], NULL);
}
}
for(size_t fmt = 0; fmt < ARRAY_COUNT(DummyBufferView); fmt++)
driver->vkDestroyBufferView(driver->GetDev(), DummyBufferView[fmt], NULL);
driver->vkDestroyBuffer(driver->GetDev(), DummyBuffer, NULL);
driver->vkDestroySampler(driver->GetDev(), DummySampler, NULL);
}
void VulkanReplay::OverlayRendering::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
VulkanShaderCache *shaderCache = driver->GetShaderCache();
VkRenderPass SRGBA8RP = VK_NULL_HANDLE;
VkRenderPass SRGBA8MSRP = VK_NULL_HANDLE;
CREATE_OBJECT(SRGBA8RP, VK_FORMAT_R8G8B8A8_SRGB);
CREATE_OBJECT(SRGBA8MSRP, VK_FORMAT_R8G8B8A8_SRGB, VULKAN_MESH_VIEW_SAMPLES);
CREATE_OBJECT(m_CheckerDescSetLayout,
{{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL}});
CREATE_OBJECT(m_QuadDescSetLayout,
{
{0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_ALL, NULL},
});
CREATE_OBJECT(m_TriSizeDescSetLayout,
{
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL},
{2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL},
});
CREATE_OBJECT(m_CheckerPipeLayout, m_CheckerDescSetLayout, 0);
CREATE_OBJECT(m_QuadResolvePipeLayout, m_QuadDescSetLayout, 0);
CREATE_OBJECT(m_TriSizePipeLayout, m_TriSizeDescSetLayout, 0);
CREATE_OBJECT(m_QuadDescSet, descriptorPool, m_QuadDescSetLayout);
CREATE_OBJECT(m_TriSizeDescSet, descriptorPool, m_TriSizeDescSetLayout);
CREATE_OBJECT(m_CheckerDescSet, descriptorPool, m_CheckerDescSetLayout);
m_CheckerUBO.Create(driver, driver->GetDev(), 128, 10, 0);
RDCCOMPILE_ASSERT(sizeof(CheckerboardUBOData) <= 128, "checkerboard UBO size");
m_TriSizeUBO.Create(driver, driver->GetDev(), sizeof(Vec4f), 4096, 0);
ConciseGraphicsPipeline pipeInfo = {
SRGBA8RP,
m_CheckerPipeLayout,
shaderCache->GetBuiltinModule(BuiltinShader::BlitVS),
shaderCache->GetBuiltinModule(BuiltinShader::CheckerboardFS),
{VK_DYNAMIC_STATE_VIEWPORT},
VK_SAMPLE_COUNT_1_BIT,
false, // sampleRateShading
false, // depthEnable
false, // stencilEnable
VK_STENCIL_OP_KEEP,
true, // colourOutput
false, // blendEnable
VK_BLEND_FACTOR_SRC_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
0xf, // writeMask
};
CREATE_OBJECT(m_CheckerPipeline, pipeInfo);
pipeInfo.renderPass = SRGBA8MSRP;
pipeInfo.sampleCount = VULKAN_MESH_VIEW_SAMPLES;
CREATE_OBJECT(m_CheckerMSAAPipeline, pipeInfo);
uint32_t samplesHandled = 0;
RDCCOMPILE_ASSERT(ARRAY_COUNT(m_CheckerF16Pipeline) == ARRAY_COUNT(m_QuadResolvePipeline),
"Arrays are mismatched in size!");
uint32_t supportedSampleCounts = driver->GetDeviceProps().limits.framebufferColorSampleCounts;
for(size_t i = 0; i < ARRAY_COUNT(m_CheckerF16Pipeline); i++)
{
VkSampleCountFlagBits samples = VkSampleCountFlagBits(1 << i);
if((supportedSampleCounts & (uint32_t)samples) == 0)
continue;
VkRenderPass RGBA16MSRP = VK_NULL_HANDLE;
CREATE_OBJECT(RGBA16MSRP, VK_FORMAT_R16G16B16A16_SFLOAT, samples);
if(RGBA16MSRP != VK_NULL_HANDLE)
samplesHandled |= (uint32_t)samples;
else
continue;
// if we this sample count is supported then create a pipeline
pipeInfo.renderPass = RGBA16MSRP;
pipeInfo.sampleCount = VkSampleCountFlagBits(1 << i);
// set up outline pipeline configuration
pipeInfo.blendEnable = true;
pipeInfo.fragment = shaderCache->GetBuiltinModule(BuiltinShader::CheckerboardFS);
pipeInfo.pipeLayout = m_CheckerPipeLayout;
CREATE_OBJECT(m_CheckerF16Pipeline[i], pipeInfo);
// set up quad resolve pipeline configuration
pipeInfo.blendEnable = false;
pipeInfo.fragment = shaderCache->GetBuiltinModule(BuiltinShader::QuadResolveFS);
pipeInfo.pipeLayout = m_QuadResolvePipeLayout;
if(pipeInfo.fragment != VK_NULL_HANDLE &&
shaderCache->GetBuiltinModule(BuiltinShader::QuadWriteFS) != VK_NULL_HANDLE)
{
CREATE_OBJECT(m_QuadResolvePipeline[i], pipeInfo);
}
driver->vkDestroyRenderPass(driver->GetDev(), RGBA16MSRP, NULL);
}
RDCASSERTEQUAL((uint32_t)driver->GetDeviceProps().limits.framebufferColorSampleCounts,
samplesHandled);
VkDescriptorBufferInfo checkerboard = {};
m_CheckerUBO.FillDescriptor(checkerboard);
VkWriteDescriptorSet writes[] = {
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_CheckerDescSet), 0, 0, 1,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, NULL, &checkerboard, NULL},
};
VkDevice dev = driver->GetDev();
ObjDisp(dev)->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writes), writes, 0, NULL);
driver->vkDestroyRenderPass(driver->GetDev(), SRGBA8RP, NULL);
driver->vkDestroyRenderPass(driver->GetDev(), SRGBA8MSRP, NULL);
}
void VulkanReplay::OverlayRendering::Destroy(WrappedVulkan *driver)
{
if(ImageMem == VK_NULL_HANDLE)
return;
driver->vkFreeMemory(driver->GetDev(), ImageMem, NULL);
driver->vkDestroyImage(driver->GetDev(), Image, NULL);
driver->vkDestroyImageView(driver->GetDev(), ImageView, NULL);
driver->vkDestroyFramebuffer(driver->GetDev(), NoDepthFB, NULL);
driver->vkDestroyRenderPass(driver->GetDev(), NoDepthRP, NULL);
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), m_QuadDescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), m_QuadResolvePipeLayout, NULL);
for(size_t i = 0; i < ARRAY_COUNT(m_QuadResolvePipeline); i++)
driver->vkDestroyPipeline(driver->GetDev(), m_QuadResolvePipeline[i], NULL);
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), m_CheckerDescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), m_CheckerPipeLayout, NULL);
for(size_t i = 0; i < ARRAY_COUNT(m_CheckerF16Pipeline); i++)
driver->vkDestroyPipeline(driver->GetDev(), m_CheckerF16Pipeline[i], NULL);
driver->vkDestroyPipeline(driver->GetDev(), m_CheckerPipeline, NULL);
driver->vkDestroyPipeline(driver->GetDev(), m_CheckerMSAAPipeline, NULL);
m_CheckerUBO.Destroy();
m_TriSizeUBO.Destroy();
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), m_TriSizeDescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), m_TriSizePipeLayout, NULL);
}
void VulkanReplay::MeshRendering::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
CREATE_OBJECT(DescSetLayout,
{{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL}});
CREATE_OBJECT(PipeLayout, DescSetLayout, 0);
CREATE_OBJECT(DescSet, descriptorPool, DescSetLayout);
UBO.Create(driver, driver->GetDev(), sizeof(MeshUBOData), 16, 0);
BBoxVB.Create(driver, driver->GetDev(), sizeof(Vec4f) * 128, 16, GPUBuffer::eGPUBufferVBuffer);
Vec4f TLN = Vec4f(-1.0f, 1.0f, 0.0f, 1.0f); // TopLeftNear, etc...
Vec4f TRN = Vec4f(1.0f, 1.0f, 0.0f, 1.0f);
Vec4f BLN = Vec4f(-1.0f, -1.0f, 0.0f, 1.0f);
Vec4f BRN = Vec4f(1.0f, -1.0f, 0.0f, 1.0f);
Vec4f TLF = Vec4f(-1.0f, 1.0f, 1.0f, 1.0f);
Vec4f TRF = Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
Vec4f BLF = Vec4f(-1.0f, -1.0f, 1.0f, 1.0f);
Vec4f BRF = Vec4f(1.0f, -1.0f, 1.0f, 1.0f);
Vec4f axisFrustum[] = {
// axis marker vertices
Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec4f(1.0f, 0.0f, 0.0f, 1.0f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f),
Vec4f(0.0f, 1.0f, 0.0f, 1.0f), Vec4f(0.0f, 0.0f, 0.0f, 1.0f), Vec4f(0.0f, 0.0f, 1.0f, 1.0f),
// frustum vertices
TLN, TRN, TRN, BRN, BRN, BLN, BLN, TLN,
TLN, TLF, TRN, TRF, BLN, BLF, BRN, BRF,
TLF, TRF, TRF, BRF, BRF, BLF, BLF, TLF,
};
// doesn't need to be ring'd as it's immutable
AxisFrustumVB.Create(driver, driver->GetDev(), sizeof(axisFrustum), 1,
GPUBuffer::eGPUBufferVBuffer);
Vec4f *axisData = (Vec4f *)AxisFrustumVB.Map();
memcpy(axisData, axisFrustum, sizeof(axisFrustum));
AxisFrustumVB.Unmap();
VkDescriptorBufferInfo meshrender = {};
UBO.FillDescriptor(meshrender);
VkWriteDescriptorSet writes[] = {
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(DescSet), 0, 0, 1,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, NULL, &meshrender, NULL},
};
VkDevice dev = driver->GetDev();
ObjDisp(dev)->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writes), writes, 0, NULL);
}
void VulkanReplay::MeshRendering::Destroy(WrappedVulkan *driver)
{
if(DescSetLayout == VK_NULL_HANDLE)
return;
UBO.Destroy();
BBoxVB.Destroy();
AxisFrustumVB.Destroy();
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), DescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), PipeLayout, NULL);
}
void VulkanReplay::VertexPicking::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
VulkanShaderCache *shaderCache = driver->GetShaderCache();
CREATE_OBJECT(DescSetLayout,
{
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
{1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
{2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
{3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
});
CREATE_OBJECT(Layout, DescSetLayout, 0);
CREATE_OBJECT(DescSet, descriptorPool, DescSetLayout);
// sizes are always 0 so that these buffers are created on demand
IBSize = 0;
VBSize = 0;
UBO.Create(driver, driver->GetDev(), 128, 1, 0);
RDCCOMPILE_ASSERT(sizeof(MeshPickUBOData) <= 128, "mesh pick UBO size");
const size_t meshPickResultSize = MaxMeshPicks * sizeof(FloatVector) + sizeof(uint32_t);
Result.Create(driver, driver->GetDev(), meshPickResultSize, 1,
GPUBuffer::eGPUBufferGPULocal | GPUBuffer::eGPUBufferSSBO);
ResultReadback.Create(driver, driver->GetDev(), meshPickResultSize, 1,
GPUBuffer::eGPUBufferReadback);
CREATE_OBJECT(Pipeline, Layout, shaderCache->GetBuiltinModule(BuiltinShader::MeshCS));
VkDescriptorBufferInfo vertexpickUBO = {};
VkDescriptorBufferInfo vertexpickResult = {};
UBO.FillDescriptor(vertexpickUBO);
Result.FillDescriptor(vertexpickResult);
VkWriteDescriptorSet writes[] = {
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(DescSet), 0, 0, 1,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, NULL, &vertexpickUBO, NULL},
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(DescSet), 3, 0, 1,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, NULL, &vertexpickResult, NULL},
};
VkDevice dev = driver->GetDev();
ObjDisp(dev)->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writes), writes, 0, NULL);
}
void VulkanReplay::VertexPicking::Destroy(WrappedVulkan *driver)
{
if(DescSetLayout == VK_NULL_HANDLE)
return;
UBO.Destroy();
IB.Destroy();
IBUpload.Destroy();
VB.Destroy();
VBUpload.Destroy();
Result.Destroy();
ResultReadback.Destroy();
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), DescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), Layout, NULL);
driver->vkDestroyPipeline(driver->GetDev(), Pipeline, NULL);
}
void VulkanReplay::PixelPicking::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
VkResult vkr = VK_SUCCESS;
VkImageCreateInfo imInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
NULL,
0,
VK_IMAGE_TYPE_2D,
VK_FORMAT_R32G32B32A32_SFLOAT,
{1, 1, 1},
1,
1,
VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0,
NULL,
VK_IMAGE_LAYOUT_UNDEFINED,
};
vkr = driver->vkCreateImage(driver->GetDev(), &imInfo, NULL, &Image);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkMemoryRequirements mrq = {0};
driver->vkGetImageMemoryRequirements(driver->GetDev(), Image, &mrq);
// allocate readback memory
VkMemoryAllocateInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size,
driver->GetGPULocalMemoryIndex(mrq.memoryTypeBits),
};
vkr = driver->vkAllocateMemory(driver->GetDev(), &allocInfo, NULL, &ImageMem);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
vkr = driver->vkBindImageMemory(driver->GetDev(), Image, ImageMem, 0);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkImageViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
NULL,
0,
Image,
VK_IMAGE_VIEW_TYPE_2D,
VK_FORMAT_R32G32B32A32_SFLOAT,
{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 = driver->vkCreateImageView(driver->GetDev(), &viewInfo, NULL, &ImageView);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// need to update image layout into valid state
VkCommandBuffer cmd = driver->GetNextCmd();
VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
vkr = ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &beginInfo);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
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(Image),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
};
DoPipelineBarrier(cmd, 1, &barrier);
ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
CREATE_OBJECT(RP, VK_FORMAT_R32G32B32A32_SFLOAT);
// create framebuffer
VkFramebufferCreateInfo fbinfo = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, 0, RP, 1, &ImageView, 1, 1, 1,
};
vkr = driver->vkCreateFramebuffer(driver->GetDev(), &fbinfo, NULL, &FB);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// since we always sync for readback, doesn't need to be ring'd
ReadbackBuffer.Create(driver, driver->GetDev(), sizeof(float) * 4, 1,
GPUBuffer::eGPUBufferReadback);
}
void VulkanReplay::PixelPicking::Destroy(WrappedVulkan *driver)
{
if(Image == VK_NULL_HANDLE)
return;
driver->vkDestroyImage(driver->GetDev(), Image, NULL);
driver->vkFreeMemory(driver->GetDev(), ImageMem, NULL);
driver->vkDestroyImageView(driver->GetDev(), ImageView, NULL);
ReadbackBuffer.Destroy();
driver->vkDestroyFramebuffer(driver->GetDev(), FB, NULL);
driver->vkDestroyRenderPass(driver->GetDev(), RP, NULL);
}
void VulkanReplay::PixelHistory::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
CREATE_OBJECT(MSCopyDescSetLayout,
{
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
});
VkResult vkr = VK_SUCCESS;
VkDescriptorPoolSize descPoolTypes[] = {
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 64}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 32},
};
VkDescriptorPoolCreateInfo descPoolInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
NULL,
0,
32,
ARRAY_COUNT(descPoolTypes),
&descPoolTypes[0],
};
// create descriptor pool
vkr = driver->vkCreateDescriptorPool(driver->GetDev(), &descPoolInfo, NULL, &MSCopyDescPool);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
CREATE_OBJECT(MSCopyPipeLayout, MSCopyDescSetLayout, 32);
CREATE_OBJECT(MSCopyPipe, MSCopyPipeLayout,
driver->GetShaderCache()->GetBuiltinModule(BuiltinShader::PixelHistoryMSCopyCS));
CREATE_OBJECT(MSCopyDepthPipe, MSCopyPipeLayout,
driver->GetShaderCache()->GetBuiltinModule(BuiltinShader::PixelHistoryMSCopyDepthCS));
}
void VulkanReplay::PixelHistory::Destroy(WrappedVulkan *driver)
{
if(MSCopyPipe != VK_NULL_HANDLE)
driver->vkDestroyPipeline(driver->GetDev(), MSCopyPipe, NULL);
if(MSCopyPipeLayout != VK_NULL_HANDLE)
driver->vkDestroyPipelineLayout(driver->GetDev(), MSCopyPipeLayout, NULL);
if(MSCopyDescSetLayout != VK_NULL_HANDLE)
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), MSCopyDescSetLayout, NULL);
if(MSCopyDescPool != VK_NULL_HANDLE)
driver->vkDestroyDescriptorPool(driver->GetDev(), MSCopyDescPool, NULL);
}
void VulkanReplay::HistogramMinMax::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
VulkanShaderCache *shaderCache = driver->GetShaderCache();
shaderCache->SetCaching(true);
rdcstr glsl;
CREATE_OBJECT(m_HistogramDescSetLayout,
{
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
{1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
{2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
{6, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{7, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{8, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{9, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{10, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, VK_SHADER_STAGE_ALL, NULL},
{11, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{12, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{13, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{14, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{16, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{17, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{18, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
{19, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL},
});
CREATE_OBJECT(m_HistogramPipeLayout, m_HistogramDescSetLayout, 0);
for(size_t i = 0; i < ARRAY_COUNT(m_HistogramDescSet); i++)
CREATE_OBJECT(m_HistogramDescSet[i], descriptorPool, m_HistogramDescSetLayout);
rdcspv::CompilationSettings compileSettings;
compileSettings.lang = rdcspv::InputLanguage::VulkanGLSL;
compileSettings.stage = rdcspv::ShaderStage::Compute;
// type max is one higher than the last RESTYPE, and RESTYPES are 1-indexed
RDCCOMPILE_ASSERT(RESTYPE_TEXTYPEMAX == ARRAY_COUNT(m_MinMaxTilePipe),
"RESTYPE values don't match formats for dummy images");
RDCCOMPILE_ASSERT(ARRAY_COUNT(m_MinMaxTilePipe) == arraydim<BuiltinShaderTextureType>(),
"Array size doesn't match parameter enum");
RDCCOMPILE_ASSERT(ARRAY_COUNT(m_MinMaxTilePipe[0]) == arraydim<BuiltinShaderBaseType>(),
"Array size doesn't match parameter enum");
for(BuiltinShaderTextureType t = BuiltinShaderTextureType::First;
t < BuiltinShaderTextureType::Count; ++t)
{
for(BuiltinShaderBaseType f = BuiltinShaderBaseType::First; f < BuiltinShaderBaseType::Count; ++f)
{
CREATE_OBJECT(m_HistogramPipe[(size_t)t][(size_t)f], m_HistogramPipeLayout,
shaderCache->GetBuiltinModule(BuiltinShader::HistogramCS, f, t));
CREATE_OBJECT(m_MinMaxTilePipe[(size_t)t][(size_t)f], m_HistogramPipeLayout,
shaderCache->GetBuiltinModule(BuiltinShader::MinMaxTileCS, f, t));
if(t == BuiltinShaderTextureType::First)
{
CREATE_OBJECT(m_MinMaxResultPipe[(size_t)f], m_HistogramPipeLayout,
shaderCache->GetBuiltinModule(BuiltinShader::MinMaxResultCS, f));
}
}
}
shaderCache->SetCaching(false);
const uint32_t maxTexDim = 16384;
const uint32_t blockPixSize = HGRAM_PIXELS_PER_TILE * HGRAM_TILES_PER_BLOCK;
const uint32_t maxBlocksNeeded = (maxTexDim * maxTexDim) / (blockPixSize * blockPixSize);
const size_t byteSize =
2 * sizeof(Vec4f) * HGRAM_TILES_PER_BLOCK * HGRAM_TILES_PER_BLOCK * maxBlocksNeeded;
m_MinMaxTileResult.Create(driver, driver->GetDev(), byteSize, 1,
GPUBuffer::eGPUBufferGPULocal | GPUBuffer::eGPUBufferSSBO);
m_MinMaxResult.Create(driver, driver->GetDev(), sizeof(Vec4f) * 2, 1,
GPUBuffer::eGPUBufferGPULocal | GPUBuffer::eGPUBufferSSBO);
m_MinMaxReadback.Create(driver, driver->GetDev(), sizeof(Vec4f) * 2, 1,
GPUBuffer::eGPUBufferReadback);
m_HistogramBuf.Create(driver, driver->GetDev(), sizeof(uint32_t) * HGRAM_NUM_BUCKETS, 1,
GPUBuffer::eGPUBufferGPULocal | GPUBuffer::eGPUBufferSSBO);
m_HistogramReadback.Create(driver, driver->GetDev(), sizeof(uint32_t) * HGRAM_NUM_BUCKETS, 1,
GPUBuffer::eGPUBufferReadback);
// don't need to ring this, as we hard-sync for readback anyway
m_HistogramUBO.Create(driver, driver->GetDev(), sizeof(HistogramUBOData), 1, 0);
}
void VulkanReplay::HistogramMinMax::Destroy(WrappedVulkan *driver)
{
if(m_HistogramDescSetLayout == VK_NULL_HANDLE)
return;
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), m_HistogramDescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), m_HistogramPipeLayout, NULL);
for(size_t t = 1; t < ARRAY_COUNT(m_MinMaxTilePipe); t++)
{
for(size_t f = 0; f < ARRAY_COUNT(m_MinMaxTilePipe[0]); f++)
{
driver->vkDestroyPipeline(driver->GetDev(), m_MinMaxTilePipe[t][f], NULL);
driver->vkDestroyPipeline(driver->GetDev(), m_HistogramPipe[t][f], NULL);
if(t == 1)
driver->vkDestroyPipeline(driver->GetDev(), m_MinMaxResultPipe[f], NULL);
}
}
m_MinMaxTileResult.Destroy();
m_MinMaxResult.Destroy();
m_MinMaxReadback.Destroy();
m_HistogramBuf.Destroy();
m_HistogramReadback.Destroy();
m_HistogramUBO.Destroy();
}
void VulkanReplay::PostVS::Destroy(WrappedVulkan *driver)
{
if(XFBQueryPool != VK_NULL_HANDLE)
driver->vkDestroyQueryPool(driver->GetDev(), XFBQueryPool, NULL);
}
void VulkanReplay::Feedback::Destroy(WrappedVulkan *driver)
{
FeedbackBuffer.Destroy();
}
void ShaderDebugData::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
// should match the enum ShaderDebugBind
CREATE_OBJECT(
DescSetLayout,
{
// ShaderDebugBind::Tex1D
{1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, NULL},
// ShaderDebugBind::Tex2D
{2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, NULL},
// ShaderDebugBind::Tex3D
{3, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, NULL},
// ShaderDebugBind::Tex2DMS
{4, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, NULL},
// ShaderDebugBind::TexCube
{5, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, NULL},
// ShaderDebugBind::Buffer
{6, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, NULL},
// ShaderDebugBind::Sampler
{7, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, NULL},
// ShaderDebugBind::Constants
{8, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, NULL},
});
CREATE_OBJECT(PipeLayout, DescSetLayout, sizeof(Vec4f) * 3 + sizeof(uint32_t));
CREATE_OBJECT(DescSet, descriptorPool, DescSetLayout);
VkResult vkr = VK_SUCCESS;
VkImageCreateInfo imInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
NULL,
0,
VK_IMAGE_TYPE_2D,
VK_FORMAT_R32G32B32A32_SFLOAT,
{1, 1, 1},
1,
1,
VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0,
NULL,
VK_IMAGE_LAYOUT_UNDEFINED,
};
vkr = driver->vkCreateImage(driver->GetDev(), &imInfo, NULL, &Image);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkMemoryRequirements mrq = {0};
driver->vkGetImageMemoryRequirements(driver->GetDev(), Image, &mrq);
// allocate readback memory
VkMemoryAllocateInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, mrq.size,
driver->GetGPULocalMemoryIndex(mrq.memoryTypeBits),
};
vkr = driver->vkAllocateMemory(driver->GetDev(), &allocInfo, NULL, &ImageMemory);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
vkr = driver->vkBindImageMemory(driver->GetDev(), Image, ImageMemory, 0);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkImageViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
NULL,
0,
Image,
VK_IMAGE_VIEW_TYPE_2D,
VK_FORMAT_R32G32B32A32_SFLOAT,
{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 = driver->vkCreateImageView(driver->GetDev(), &viewInfo, NULL, &ImageView);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkAttachmentDescription attDesc = {
0,
VK_FORMAT_R32G32B32A32_SFLOAT,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_GENERAL,
};
VkAttachmentReference attRef = {0, VK_IMAGE_LAYOUT_GENERAL};
VkSubpassDescription sub = {
0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, NULL, 1, &attRef,
};
VkSubpassDependency deps[2] = {
{
VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0,
},
{
0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_READ_BIT, 0,
},
};
VkRenderPassCreateInfo rpinfo = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 1, &attDesc, 1, &sub, 2, deps,
};
vkr = driver->vkCreateRenderPass(driver->GetDev(), &rpinfo, NULL, &RenderPass);
if(vkr != VK_SUCCESS)
RDCERR("Failed to create shader debug render pass: %s", ToStr(vkr).c_str());
// create framebuffer
VkFramebufferCreateInfo fbinfo = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, 0, RenderPass, 1, &ImageView, 1, 1, 1,
};
vkr = driver->vkCreateFramebuffer(driver->GetDev(), &fbinfo, NULL, &Framebuffer);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// don't need to ring this, as we hard-sync for readback anyway
ReadbackBuffer.Create(driver, driver->GetDev(), sizeof(Vec4f), 1, GPUBuffer::eGPUBufferReadback);
ConstantsBuffer.Create(driver, driver->GetDev(), 1024, 1, 0);
}
void ShaderDebugData::Destroy(WrappedVulkan *driver)
{
ConstantsBuffer.Destroy();
ReadbackBuffer.Destroy();
driver->vkDestroyPipeline(driver->GetDev(), MathPipe, NULL);
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), DescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), PipeLayout, NULL);
driver->vkDestroyImage(driver->GetDev(), Image, NULL);
driver->vkFreeMemory(driver->GetDev(), ImageMemory, NULL);
driver->vkDestroyImageView(driver->GetDev(), ImageView, NULL);
driver->vkDestroyFramebuffer(driver->GetDev(), Framebuffer, NULL);
driver->vkDestroyRenderPass(driver->GetDev(), RenderPass, NULL);
// one module each for float, uint, sint.
driver->vkDestroyShaderModule(driver->GetDev(), Module[0], NULL);
driver->vkDestroyShaderModule(driver->GetDev(), Module[1], NULL);
driver->vkDestroyShaderModule(driver->GetDev(), Module[2], NULL);
for(auto it = m_Pipelines.begin(); it != m_Pipelines.end(); it++)
driver->vkDestroyPipeline(driver->GetDev(), it->second, NULL);
}