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

2297 lines
80 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2015-2018 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 "3rdparty/glslang/SPIRV/spirv.hpp"
#include "data/glsl_shaders.h"
#include "maths/camera.h"
#include "maths/formatpacking.h"
#include "maths/matrix.h"
#include "vk_core.h"
#include "vk_shader_cache.h"
#define VULKAN 1
#include "data/glsl/debuguniforms.h"
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());
}
// 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(), VK_NULL_HANDLE, 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());
}
// Create a compute pipeline with a SPIRV Blob (creates a temporary shader module)
static void create(WrappedVulkan *driver, const char *objName, const int line, VkPipeline *pipe,
VkPipelineLayout pipeLayout, SPIRVBlob computeModule)
{
*pipe = VK_NULL_HANDLE;
// 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 == NULL)
return;
VkShaderModule module = VK_NULL_HANDLE;
VkShaderModuleCreateInfo moduleInfo = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
moduleInfo.codeSize = computeModule->size() * sizeof(uint32_t);
moduleInfo.pCode = computeModule->data();
VkResult vkr = driver->vkCreateShaderModule(driver->GetDev(), &moduleInfo, NULL, &module);
if(vkr != VK_SUCCESS)
{
RDCERR("Failed creating temporary shader for object %s at line %i, vkr was %s", objName, line,
ToStr(vkr).c_str());
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,
module, "main", NULL},
pipeLayout,
VK_NULL_HANDLE,
0,
};
vkr = driver->vkCreateComputePipelines(driver->GetDev(), VK_NULL_HANDLE, 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());
driver->vkDestroyShaderModule(driver->GetDev(), module, NULL);
}
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;
};
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
0xf,
};
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(), VK_NULL_HANDLE, 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, "Failed to create vulkan object " #obj, __LINE__, &obj, __VA_ARGS__)
VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver)
{
m_pDriver = driver;
m_Device = m_pDriver->GetDev();
VkDevice dev = m_Device;
VkResult vkr = VK_SUCCESS;
VulkanShaderCache *shaderCache = driver->GetShaderCache();
// we need just one descriptor for MS<->Array
VkDescriptorPoolSize poolTypes[] = {
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2}, {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1},
};
VkDescriptorPoolCreateInfo poolInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
NULL,
0,
1,
ARRAY_COUNT(poolTypes),
&poolTypes[0],
};
CREATE_OBJECT(m_ArrayMSSampler, VK_FILTER_NEAREST);
vkr = m_pDriver->vkCreateDescriptorPool(dev, &poolInfo, NULL, &m_ArrayMSDescriptorPool);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
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},
});
CREATE_OBJECT(m_ArrayMSPipeLayout, m_ArrayMSDescSetLayout, sizeof(Vec4u));
//////////////////////////////////////////////////////////////////
// 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));
//////////////////////////////////////////////////////////////////
// Depth MS to Array copy (via graphics)
CREATE_OBJECT(m_ArrayMSDescSet, m_ArrayMSDescriptorPool, m_ArrayMSDescSetLayout);
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,
};
CREATE_OBJECT(m_DepthMS2ArrayPipe[f], depthPipeInfo);
m_pDriver->vkDestroyRenderPass(dev, depthMS2ArrayRP, NULL);
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);
m_pDriver->vkDestroyRenderPass(dev, depthArray2MSRP, 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);
}
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 < MeshDisplayPipelines::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->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);
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
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,
0,
0, // MULTIDEVICE - need to actually pick the right queue family here maybe?
Unwrap(m_Custom.TexImg),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, 1}};
m_pDriver->m_ImageLayouts[GetResID(m_Custom.TexImg)].subresourceStates[0].newLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
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);
DoPipelineBarrier(cmd, 1, &barrier);
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,
};
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 VkLayerDispatchTable *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;
}
}
MeshPickUBOData *ubo = (MeshPickUBOData *)m_VertexPick.UBO.Map();
ubo->rayPos = rayPos;
ubo->rayDir = rayDir;
ubo->use_indices = cfg.position.indexByteStride ? 1U : 0U;
ubo->numVerts = cfg.position.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:
{
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();
bytebuf idxs;
if(cfg.position.indexByteStride && cfg.position.indexResourceId != ResourceId())
GetBufferData(cfg.position.indexResourceId, cfg.position.indexByteOffset, 0, idxs);
// 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.
if(!idxs.empty())
{
// resize up on demand
if(m_VertexPick.IBSize < cfg.position.numIndices * sizeof(uint32_t))
{
if(m_VertexPick.IBSize > 0)
{
m_VertexPick.IB.Destroy();
m_VertexPick.IBUpload.Destroy();
}
m_VertexPick.IBSize = cfg.position.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();
memset(outidxs, 0, m_VertexPick.IBSize);
uint16_t *idxs16 = (uint16_t *)&idxs[0];
uint32_t *idxs32 = (uint32_t *)&idxs[0];
// if indices are 16-bit, manually upcast them so the shader only
// has to deal with one type
if(cfg.position.indexByteStride == 2)
{
size_t bufsize = idxs.size() / 2;
for(uint32_t i = 0; i < bufsize && i < cfg.position.numIndices; i++)
outidxs[i] = idxs16[i];
}
else
{
size_t bufsize = idxs.size() / 4;
memcpy(outidxs, idxs32, RDCMIN(bufsize, cfg.position.numIndices * sizeof(uint32_t)));
}
m_VertexPick.IBUpload.Unmap();
}
if(m_VertexPick.VBSize < cfg.position.numIndices * sizeof(FloatVector))
{
if(m_VertexPick.VBSize > 0)
{
m_VertexPick.VB.Destroy();
m_VertexPick.VBUpload.Destroy();
}
m_VertexPick.VBSize = cfg.position.numIndices * 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);
}
// unpack and linearise the data
{
bytebuf oldData;
GetBufferData(cfg.position.vertexResourceId, cfg.position.vertexByteOffset, 0, oldData);
byte *data = &oldData[0];
byte *dataEnd = data + oldData.size();
bool valid = true;
FloatVector *vbData = (FloatVector *)m_VertexPick.VBUpload.Map();
uint32_t idxclamp = 0;
if(cfg.position.baseVertex < 0)
idxclamp = uint32_t(-cfg.position.baseVertex);
for(uint32_t i = 0; i < cfg.position.numIndices; i++)
{
uint32_t idx = i;
// apply baseVertex but clamp to 0 (don't allow index to become negative)
if(idx < idxclamp)
idx = 0;
else if(cfg.position.baseVertex < 0)
idx -= idxclamp;
else if(cfg.position.baseVertex > 0)
idx += cfg.position.baseVertex;
vbData[i] = HighlightCache::InterpretVertex(data, idx, cfg, dataEnd, valid);
}
m_VertexPick.VBUpload.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();
return ret;
}
void VulkanDebugManager::GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &ret)
{
VkDevice dev = m_pDriver->GetDev();
const VkLayerDispatchTable *vt = ObjDisp(dev);
VkBuffer srcBuf = m_pDriver->GetResourceManager()->GetCurrentHandle<VkBuffer>(buff);
if(srcBuf == VK_NULL_HANDLE)
{
RDCERR("Getting buffer data for unknown buffer %llu!", buff);
return;
}
uint64_t 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 - offset;
}
if(len > 0 && 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);
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);
RDCASSERT(pData != NULL);
memcpy(&ret[dstoffset], pData, (size_t)chunkSize);
dstoffset += (size_t)chunkSize;
sizeRemaining -= chunkSize;
vt->UnmapMemory(Unwrap(dev), Unwrap(m_ReadbackWindow.mem));
}
vt->DeviceWaitIdle(Unwrap(dev));
}
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_Checkerboard.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.5f);
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.8f);
m_Histogram.Init(m_pDriver, m_General.DescriptorPool);
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 1.0f);
WrappedVulkan *driver = m_pDriver;
CREATE_OBJECT(
m_MeshFetchDescSetLayout,
{
// output buffer
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
// index buffer (if needed)
{1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
// vertex buffers (float type)
{2, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 16, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
// vertex buffers (uint32_t type)
{3, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 16, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
// vertex buffers (int32_t type)
{4, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 16, VK_SHADER_STAGE_COMPUTE_BIT, NULL},
});
CREATE_OBJECT(m_MeshFetchDescSet, m_General.DescriptorPool, m_MeshFetchDescSetLayout);
}
void VulkanReplay::DestroyResources()
{
ClearPostVSCache();
m_pDriver->vkDestroyDescriptorSetLayout(m_pDriver->GetDev(), m_MeshFetchDescSetLayout, NULL);
m_General.Destroy(m_pDriver);
m_TexRender.Destroy(m_pDriver);
m_Overlay.Destroy(m_pDriver);
m_Checkerboard.Destroy(m_pDriver);
m_VertexPick.Destroy(m_pDriver);
m_PixelPick.Destroy(m_pDriver);
m_Histogram.Destroy(m_pDriver);
}
void VulkanReplay::GeneralMisc::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
VkResult vkr = VK_SUCCESS;
VkDescriptorPoolSize descPoolTypes[] = {
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 320},
{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_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)
{
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(LinearSampler, VK_FILTER_LINEAR);
CREATE_OBJECT(DescSetLayout,
{
{0, 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, 1, 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},
});
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");
{
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,
};
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);
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;
VkDeviceSize offsets[ARRAY_COUNT(DummyImages)];
VkDeviceSize curOffset = 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};
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(ARRAY_COUNT(DummyImages) == ARRAY_COUNT(DummyImageViews),
"dummy image arrays mismatched sizes");
RDCCOMPILE_ASSERT(ARRAY_COUNT(DummyImages) == ARRAY_COUNT(DummyWrites),
"dummy image arrays mismatched sizes");
RDCCOMPILE_ASSERT(ARRAY_COUNT(DummyImages) == ARRAY_COUNT(DummyInfos),
"dummy image arrays mismatched sizes");
VkMemoryAllocateInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, 0, ~0U,
};
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_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0,
NULL,
VK_IMAGE_LAYOUT_UNDEFINED,
};
vkr = driver->vkCreateImage(driver->GetDev(), &imInfo, NULL, &DummyImages[index]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
VkMemoryRequirements mrq = {0};
driver->vkGetImageMemoryRequirements(driver->GetDev(), DummyImages[index], &mrq);
uint32_t memIndex = driver->GetGPULocalMemoryIndex(mrq.memoryTypeBits);
// make sure all images can use the same memory type
RDCASSERTMSG("memory type indices don't overlap!",
allocInfo.memoryTypeIndex == ~0U || allocInfo.memoryTypeIndex == memIndex,
allocInfo.memoryTypeIndex, memIndex, fmt, type);
allocInfo.memoryTypeIndex = memIndex;
// align to our alignment, then increment curOffset by our size
curOffset = AlignUp(curOffset, mrq.alignment);
offsets[index] = curOffset;
curOffset += mrq.size;
// fill out the descriptor set write to the write binding - set will be filled out
// on demand when we're actulaly using these writes.
DummyWrites[index].descriptorCount = 1;
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++;
}
}
// align up a bit just to be safe
allocInfo.allocationSize = AlignUp(curOffset, (VkDeviceSize)1024ULL);
// allocate one big block
vkr = driver->vkAllocateMemory(driver->GetDev(), &allocInfo, NULL, &DummyMemory);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
// bind all the image memory
for(index = 0; index < (int)ARRAY_COUNT(DummyImages); index++)
{
vkr = driver->vkBindImageMemory(driver->GetDev(), DummyImages[index], DummyMemory,
offsets[index]);
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(types); type++)
{
VkImageViewCreateInfo viewInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
NULL,
0,
DummyImages[index],
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,
},
};
vkr = driver->vkCreateImageView(driver->GetDev(), &viewInfo, NULL, &DummyImageViews[index]);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
DummyInfos[index].imageView = Unwrap(DummyImageViews[index]);
// 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,
0,
0, // MULTIDEVICE - need to actually pick the right queue family here maybe?
Unwrap(DummyImages[index]),
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
DoPipelineBarrier(cmd, 1, &barrier);
index++;
}
}
ObjDisp(cmd)->EndCommandBuffer(Unwrap(cmd));
}
}
void VulkanReplay::TextureRendering::Destroy(WrappedVulkan *driver)
{
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);
UBO.Destroy();
driver->vkDestroySampler(driver->GetDev(), LinearSampler, NULL);
for(size_t i = 0; i < ARRAY_COUNT(DummyImages); i++)
{
driver->vkDestroyImageView(driver->GetDev(), DummyImageViews[i], NULL);
driver->vkDestroyImage(driver->GetDev(), DummyImages[i], NULL);
}
driver->vkFreeMemory(driver->GetDev(), DummyMemory, NULL);
driver->vkDestroySampler(driver->GetDev(), DummySampler, NULL);
}
void VulkanReplay::OverlayRendering::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
VulkanShaderCache *shaderCache = driver->GetShaderCache();
CREATE_OBJECT(m_OutlineDescSetLayout,
{{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},
{1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
});
CREATE_OBJECT(m_TriSizeDescSetLayout,
{
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL},
{1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL},
{2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, NULL},
});
CREATE_OBJECT(m_QuadResolvePipeLayout, m_QuadDescSetLayout, 0);
CREATE_OBJECT(m_TriSizePipeLayout, m_TriSizeDescSetLayout, 0);
CREATE_OBJECT(m_OutlinePipeLayout, m_OutlineDescSetLayout, 0);
CREATE_OBJECT(m_QuadDescSet, descriptorPool, m_QuadDescSetLayout);
CREATE_OBJECT(m_TriSizeDescSet, descriptorPool, m_TriSizeDescSetLayout);
CREATE_OBJECT(m_OutlineDescSet, descriptorPool, m_OutlineDescSetLayout);
m_OutlineUBO.Create(driver, driver->GetDev(), 128, 10, 0);
RDCCOMPILE_ASSERT(sizeof(OutlineUBOData) <= 128, "outline UBO size");
ConciseGraphicsPipeline outlineInfo = {
VK_NULL_HANDLE,
m_OutlinePipeLayout,
shaderCache->GetBuiltinModule(BuiltinShader::BlitVS),
shaderCache->GetBuiltinModule(BuiltinShader::OutlineFS),
{VK_DYNAMIC_STATE_VIEWPORT},
VK_SAMPLE_COUNT_1_BIT,
false, // sampleRateShading
false, // depthEnable
false, // stencilEnable
VK_STENCIL_OP_KEEP,
true, // colourOutput
true, // blendEnable
VK_BLEND_FACTOR_SRC_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
};
uint32_t samplesHandled = 0;
RDCCOMPILE_ASSERT(ARRAY_COUNT(m_OutlinePipeline) == ARRAY_COUNT(m_OutlinePipeline),
"Arrays are mismatched in size!");
uint32_t supportedSampleCounts = driver->GetDeviceProps().limits.framebufferColorSampleCounts;
for(size_t i = 0; i < ARRAY_COUNT(m_OutlinePipeline); 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
outlineInfo.renderPass = RGBA16MSRP;
outlineInfo.sampleCount = VkSampleCountFlagBits(1 << i);
// set up outline pipeline configuration
outlineInfo.blendEnable = true;
outlineInfo.fragment = shaderCache->GetBuiltinModule(BuiltinShader::OutlineFS);
outlineInfo.pipeLayout = m_OutlinePipeLayout;
CREATE_OBJECT(m_OutlinePipeline[i], outlineInfo);
// set up quad resolve pipeline configuration
outlineInfo.blendEnable = false;
outlineInfo.fragment = shaderCache->GetBuiltinModule(BuiltinShader::QuadResolveFS);
outlineInfo.pipeLayout = m_QuadResolvePipeLayout;
CREATE_OBJECT(m_QuadResolvePipeline[i], outlineInfo);
driver->vkDestroyRenderPass(driver->GetDev(), RGBA16MSRP, NULL);
}
RDCASSERTEQUAL((uint32_t)driver->GetDeviceProps().limits.framebufferColorSampleCounts,
samplesHandled);
OverdrawRampUBO.Create(driver, driver->GetDev(), 2048, 1, 0); // no ring needed, fixed data
RDCCOMPILE_ASSERT(sizeof(overdrawRamp) <= 2048, "overdraw ramp uniforms size");
void *ramp = OverdrawRampUBO.Map();
memcpy(ramp, overdrawRamp, sizeof(overdrawRamp));
OverdrawRampUBO.Unmap();
m_TriSizeUBO.Create(driver, driver->GetDev(), sizeof(Vec4f), 4096, 0);
VkDescriptorBufferInfo outlineUBO = {};
VkDescriptorBufferInfo overdrawramp = {};
m_OutlineUBO.FillDescriptor(outlineUBO);
OverdrawRampUBO.FillDescriptor(overdrawramp);
VkWriteDescriptorSet writes[] = {
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_OutlineDescSet), 0, 0, 1,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, NULL, &outlineUBO, NULL},
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_QuadDescSet), 1, 0, 1,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, NULL, &overdrawramp, NULL},
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_TriSizeDescSet), 1, 0, 1,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, NULL, &overdrawramp, NULL},
};
VkDevice dev = driver->GetDev();
ObjDisp(dev)->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writes), writes, 0, NULL);
}
void VulkanReplay::OverlayRendering::Destroy(WrappedVulkan *driver)
{
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);
OverdrawRampUBO.Destroy();
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_OutlineDescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), m_OutlinePipeLayout, NULL);
for(size_t i = 0; i < ARRAY_COUNT(m_OutlinePipeline); i++)
driver->vkDestroyPipeline(driver->GetDev(), m_OutlinePipeline[i], NULL);
m_OutlineUBO.Destroy();
m_TriSizeUBO.Destroy();
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), m_TriSizeDescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), m_TriSizePipeLayout, NULL);
}
void VulkanReplay::CheckerboardRendering::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(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(), 128, 10, 0);
ConciseGraphicsPipeline checkerInfo = {
SRGBA8RP,
PipeLayout,
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_ONE,
VK_BLEND_FACTOR_ZERO,
};
CREATE_OBJECT(Pipeline, checkerInfo);
checkerInfo.renderPass = SRGBA8MSRP;
checkerInfo.sampleCount = VULKAN_MESH_VIEW_SAMPLES;
CREATE_OBJECT(MSAAPipeline, checkerInfo);
VkDescriptorBufferInfo checkerboard = {};
UBO.FillDescriptor(checkerboard);
VkWriteDescriptorSet writes[] = {
{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(DescSet), 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::CheckerboardRendering::Destroy(WrappedVulkan *driver)
{
driver->vkDestroyDescriptorSetLayout(driver->GetDev(), DescSetLayout, NULL);
driver->vkDestroyPipelineLayout(driver->GetDev(), PipeLayout, NULL);
driver->vkDestroyPipeline(driver->GetDev(), Pipeline, NULL);
driver->vkDestroyPipeline(driver->GetDev(), MSAAPipeline, NULL);
UBO.Destroy();
}
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)
{
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)
{
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,
0,
0, // MULTIDEVICE - need to actually pick the right queue family here maybe?
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)
{
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::HistogramMinMax::Init(WrappedVulkan *driver, VkDescriptorPool descriptorPool)
{
VulkanShaderCache *shaderCache = driver->GetShaderCache();
shaderCache->SetCaching(true);
std::vector<std::string> sources;
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},
{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);
SPIRVCompilationSettings compileSettings;
compileSettings.lang = SPIRVSourceLanguage::VulkanGLSL;
compileSettings.stage = SPIRVShaderStage::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");
for(size_t t = 1; t < ARRAY_COUNT(m_MinMaxTilePipe); t++)
{
for(size_t f = 0; f < ARRAY_COUNT(m_MinMaxTilePipe[0]); f++)
{
SPIRVBlob minmaxtile = NULL;
SPIRVBlob minmaxresult = NULL;
SPIRVBlob histogram = NULL;
std::string err;
std::string defines = "";
if(driver->GetDriverVersion().TexelFetchBrokenDriver())
defines += "#define NO_TEXEL_FETCH\n";
defines += string("#define SHADER_RESTYPE ") + ToStr(t) + "\n";
defines += string("#define UINT_TEX ") + (f == 1 ? "1" : "0") + "\n";
defines += string("#define SINT_TEX ") + (f == 2 ? "1" : "0") + "\n";
GenerateGLSLShader(sources, eShaderVulkan, defines, GetEmbeddedResource(glsl_histogram_comp),
430);
err = shaderCache->GetSPIRVBlob(compileSettings, sources, histogram);
if(!err.empty())
{
RDCERR("Error compiling histogram shader: %s. Defines are:\n%s", err.c_str(),
defines.c_str());
histogram = NULL;
}
GenerateGLSLShader(sources, eShaderVulkan, defines, GetEmbeddedResource(glsl_minmaxtile_comp),
430);
err = shaderCache->GetSPIRVBlob(compileSettings, sources, minmaxtile);
if(!err.empty())
{
RDCERR("Error compiling min/max tile shader: %s. Defines are:\n%s", err.c_str(),
defines.c_str());
minmaxtile = NULL;
}
CREATE_OBJECT(m_MinMaxTilePipe[t][f], m_HistogramPipeLayout, minmaxtile);
CREATE_OBJECT(m_HistogramPipe[t][f], m_HistogramPipeLayout, histogram);
if(t == 1)
{
GenerateGLSLShader(sources, eShaderVulkan, defines,
GetEmbeddedResource(glsl_minmaxresult_comp), 430);
err = shaderCache->GetSPIRVBlob(compileSettings, sources, minmaxresult);
if(!err.empty())
{
RDCERR("Error compiling min/max result shader: %s. Defines are:\n%s", err.c_str(),
defines.c_str());
minmaxresult = NULL;
}
CREATE_OBJECT(m_MinMaxResultPipe[f], m_HistogramPipeLayout, minmaxresult);
}
}
}
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::eGPUBufferSSBO);
m_MinMaxResult.Create(driver, driver->GetDev(), sizeof(Vec4f) * 2, 1, GPUBuffer::eGPUBufferSSBO);
m_MinMaxReadback.Create(driver, driver->GetDev(), sizeof(Vec4f) * 2, 1,
GPUBuffer::eGPUBufferReadback);
m_HistogramBuf.Create(driver, driver->GetDev(), sizeof(uint32_t) * 4 * HGRAM_NUM_BUCKETS, 1,
GPUBuffer::eGPUBufferSSBO);
m_HistogramReadback.Create(driver, driver->GetDev(), sizeof(uint32_t) * 4 * 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)
{
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();
}