From d5aa1da377021a8df1a8538fa41cf7b23dcb7990 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 31 Dec 2015 22:39:46 +0100 Subject: [PATCH] Implement histogram and min/max fetching via compute --- renderdoc/data/embedded_files.h | 3 + renderdoc/data/renderdoc.rc | 3 + renderdoc/data/resource.h | 137 +++++---- renderdoc/data/spv/histogram.comp | 124 ++++++++ renderdoc/data/spv/minmaxresult.comp | 96 ++++++ renderdoc/data/spv/minmaxtile.comp | 104 +++++++ renderdoc/driver/vulkan/vk_debug.cpp | 166 +++++++++- renderdoc/driver/vulkan/vk_debug.h | 12 + renderdoc/driver/vulkan/vk_replay.cpp | 426 +++++++++++++++++++++++++- renderdoc/renderdoc.vcxproj | 6 + renderdoc/renderdoc.vcxproj.filters | 18 ++ 11 files changed, 1023 insertions(+), 72 deletions(-) create mode 100644 renderdoc/data/spv/histogram.comp create mode 100644 renderdoc/data/spv/minmaxresult.comp create mode 100644 renderdoc/data/spv/minmaxtile.comp diff --git a/renderdoc/data/embedded_files.h b/renderdoc/data/embedded_files.h index a3e0d600b..c29542d84 100644 --- a/renderdoc/data/embedded_files.h +++ b/renderdoc/data/embedded_files.h @@ -58,5 +58,8 @@ DECLARE_EMBED(fixedcolfs_spv); DECLARE_EMBED(meshvs_spv); DECLARE_EMBED(meshgs_spv); DECLARE_EMBED(meshfs_spv); +DECLARE_EMBED(minmaxtilecs_spv); +DECLARE_EMBED(minmaxresultcs_spv); +DECLARE_EMBED(histogramcs_spv); #undef DECLARE_EMBED diff --git a/renderdoc/data/renderdoc.rc b/renderdoc/data/renderdoc.rc index 5a3c9e433..07347493a 100644 --- a/renderdoc/data/renderdoc.rc +++ b/renderdoc/data/renderdoc.rc @@ -142,6 +142,9 @@ RESOURCE_fixedcolfs_spv TYPE_EMBED "spv/fixedcolfs.spv" RESOURCE_meshvs_spv TYPE_EMBED "spv/meshvs.spv" RESOURCE_meshgs_spv TYPE_EMBED "spv/meshgs.spv" RESOURCE_meshfs_spv TYPE_EMBED "spv/meshfs.spv" +RESOURCE_minmaxtilecs_spv TYPE_EMBED "spv/minmaxtilecs.spv" +RESOURCE_minmaxresultcs_spv TYPE_EMBED "spv/minmaxresultcs.spv" +RESOURCE_histogramcs_spv TYPE_EMBED "spv/histogramcs.spv" #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// diff --git a/renderdoc/data/resource.h b/renderdoc/data/resource.h index 852722b58..05122b341 100644 --- a/renderdoc/data/resource.h +++ b/renderdoc/data/resource.h @@ -1,67 +1,70 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by renderdoc.rc -// -#define TYPE_EMBED 256 - -#define RESOURCE_debugdisplay_hlsl 101 -#define RESOURCE_debugtext_hlsl 102 -#define RESOURCE_debugcbuffers_h 103 -#define RESOURCE_debugcommon_hlsl 104 -#define RESOURCE_histogram_hlsl 105 -#define RESOURCE_multisample_hlsl 106 -#define RESOURCE_mesh_hlsl 107 - -#define RESOURCE_blit_vert 201 -#define RESOURCE_blit_frag 202 -#define RESOURCE_texdisplay_frag 203 -#define RESOURCE_checkerboard_frag 204 -#define RESOURCE_generic_vert 205 -#define RESOURCE_generic_frag 206 -#define RESOURCE_mesh_vert 207 -#define RESOURCE_debuguniforms_h 208 -#define RESOURCE_text_vert 209 -#define RESOURCE_text_frag 210 -#define RESOURCE_texsample_h 211 -#define RESOURCE_histogram_comp 212 -#define RESOURCE_mesh_frag 213 -#define RESOURCE_mesh_geom 214 -#define RESOURCE_mesh_comp 215 -#define RESOURCE_arraymscopy_comp 216 -#define RESOURCE_quadoverdraw_frag 217 -#define RESOURCE_outline_frag 218 - -#define RESOURCE_sourcecodepro_ttf 301 - -#define RESOURCE_blitvs_spv 401 -#define RESOURCE_checkerboardfs_spv 402 -#define RESOURCE_texdisplayfs_spv 403 -#define RESOURCE_textvs_spv 404 -#define RESOURCE_textfs_spv 405 -#define RESOURCE_genericvs_spv 406 -#define RESOURCE_genericfs_spv 407 -#define RESOURCE_fixedcolfs_spv 408 -#define RESOURCE_meshvs_spv 409 -#define RESOURCE_meshgs_spv 410 -#define RESOURCE_meshfs_spv 411 - -#if !defined(STRINGIZE) -#define STRINGIZE2(a) #a -#define STRINGIZE(a) STRINGIZE2(a) -#endif - -#define GIT_COMMIT_HASH "NO_GIT_COMMIT_HASH_DEFINED" -//#define RENDERDOC_OFFICIAL_BUILD // used to determine whether to submit auto crash reports - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 114 -#define _APS_NEXT_COMMAND_VALUE 40029 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif - -#include "version.h" +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by renderdoc.rc +// +#define TYPE_EMBED 256 + +#define RESOURCE_debugdisplay_hlsl 101 +#define RESOURCE_debugtext_hlsl 102 +#define RESOURCE_debugcbuffers_h 103 +#define RESOURCE_debugcommon_hlsl 104 +#define RESOURCE_histogram_hlsl 105 +#define RESOURCE_multisample_hlsl 106 +#define RESOURCE_mesh_hlsl 107 + +#define RESOURCE_blit_vert 201 +#define RESOURCE_blit_frag 202 +#define RESOURCE_texdisplay_frag 203 +#define RESOURCE_checkerboard_frag 204 +#define RESOURCE_generic_vert 205 +#define RESOURCE_generic_frag 206 +#define RESOURCE_mesh_vert 207 +#define RESOURCE_debuguniforms_h 208 +#define RESOURCE_text_vert 209 +#define RESOURCE_text_frag 210 +#define RESOURCE_texsample_h 211 +#define RESOURCE_histogram_comp 212 +#define RESOURCE_mesh_frag 213 +#define RESOURCE_mesh_geom 214 +#define RESOURCE_mesh_comp 215 +#define RESOURCE_arraymscopy_comp 216 +#define RESOURCE_quadoverdraw_frag 217 +#define RESOURCE_outline_frag 218 + +#define RESOURCE_sourcecodepro_ttf 301 + +#define RESOURCE_blitvs_spv 401 +#define RESOURCE_checkerboardfs_spv 402 +#define RESOURCE_texdisplayfs_spv 403 +#define RESOURCE_textvs_spv 404 +#define RESOURCE_textfs_spv 405 +#define RESOURCE_genericvs_spv 406 +#define RESOURCE_genericfs_spv 407 +#define RESOURCE_fixedcolfs_spv 408 +#define RESOURCE_meshvs_spv 409 +#define RESOURCE_meshgs_spv 410 +#define RESOURCE_meshfs_spv 411 +#define RESOURCE_minmaxtilecs_spv 412 +#define RESOURCE_minmaxresultcs_spv 413 +#define RESOURCE_histogramcs_spv 414 + +#if !defined(STRINGIZE) +#define STRINGIZE2(a) #a +#define STRINGIZE(a) STRINGIZE2(a) +#endif + +#define GIT_COMMIT_HASH "NO_GIT_COMMIT_HASH_DEFINED" +//#define RENDERDOC_OFFICIAL_BUILD // used to determine whether to submit auto crash reports + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 114 +#define _APS_NEXT_COMMAND_VALUE 40029 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + +#include "version.h" diff --git a/renderdoc/data/spv/histogram.comp b/renderdoc/data/spv/histogram.comp new file mode 100644 index 000000000..da6af0b3e --- /dev/null +++ b/renderdoc/data/spv/histogram.comp @@ -0,0 +1,124 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2014 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. + ******************************************************************************/ + +// compute shaders that figure out the min/max values or histogram in a texture heirarchically +// note that we have to conditionally compile this shader for float/uint/sint as doing that +// dynamically produces a shader with too many temp registers unfortunately. + +#version 430 core + +#define HGRAM_PIXELS_PER_TILE 64u +#define HGRAM_TILES_PER_BLOCK 32u + +#define HGRAM_NUM_BUCKETS 256u + +layout(binding=0, std140) writeonly buffer minmaxresultdest +{ + uint result[HGRAM_NUM_BUCKETS]; +} dest; + +layout(binding=2, std140) uniform HistogramCBufferData +{ + uint HistogramChannels; + float HistogramMin; + float HistogramMax; + uint HistogramFlags; + + float HistogramSlice; + int HistogramMip; + int HistogramSample; + int HistogramNumSamples; + + vec3 HistogramTextureResolution; + float Padding3; +} histogram; + +layout (binding = 3) uniform sampler2D tex; + +layout (local_size_x = HGRAM_TILES_PER_BLOCK, local_size_y = HGRAM_TILES_PER_BLOCK) in; + +void main() +{ + uvec3 tid = gl_LocalInvocationID; + uvec3 gid = gl_WorkGroupID; + + uvec3 texDim = uvec3(histogram.HistogramTextureResolution); + + uint blocksX = uint(ceil(float(texDim.x)/float(HGRAM_PIXELS_PER_TILE*HGRAM_TILES_PER_BLOCK))); + + uvec2 topleft = (gid.xy*HGRAM_TILES_PER_BLOCK + tid.xy)*HGRAM_PIXELS_PER_TILE; + + int i=0; + + for(uint y=topleft.y; y < min(texDim.y, topleft.y + HGRAM_PIXELS_PER_TILE); y++) + { + for(uint x=topleft.x; x < min(texDim.x, topleft.x + HGRAM_PIXELS_PER_TILE); x++) + { + uint bucketIdx = HGRAM_NUM_BUCKETS+1; + + { + vec4 data = textureLod(tex, vec2(x, y) / histogram.HistogramTextureResolution.xy, float(histogram.HistogramMip)); + + float divisor = 0.0f; + float sum = 0.0f; + if((histogram.HistogramChannels & 0x1u) > 0) + { + sum += data.x; + divisor += 1.0f; + } + if((histogram.HistogramChannels & 0x2u) > 0) + { + sum += data.y; + divisor += 1.0f; + } + if((histogram.HistogramChannels & 0x4u) > 0) + { + sum += data.z; + divisor += 1.0f; + } + if((histogram.HistogramChannels & 0x8u) > 0) + { + sum += data.w; + divisor += 1.0f; + } + + if(divisor > 0.0f) + { + float val = sum/divisor; + + float normalisedVal = (val - histogram.HistogramMin)/(histogram.HistogramMax - histogram.HistogramMin); + + if(normalisedVal < 0.0f) + normalisedVal = 2.0f; + + bucketIdx = uint(floor(normalisedVal*HGRAM_NUM_BUCKETS)); + } + } + + if(bucketIdx >= 0 && bucketIdx < HGRAM_NUM_BUCKETS) + atomicAdd(dest.result[bucketIdx], 1U); + } + } +} + diff --git a/renderdoc/data/spv/minmaxresult.comp b/renderdoc/data/spv/minmaxresult.comp new file mode 100644 index 000000000..fab12ddbf --- /dev/null +++ b/renderdoc/data/spv/minmaxresult.comp @@ -0,0 +1,96 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2014 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. + ******************************************************************************/ + +// compute shaders that figure out the min/max values or histogram in a texture heirarchically +// note that we have to conditionally compile this shader for float/uint/sint as doing that +// dynamically produces a shader with too many temp registers unfortunately. + +#version 430 core + +#define HGRAM_PIXELS_PER_TILE 64u +#define HGRAM_TILES_PER_BLOCK 32u + +#define HGRAM_NUM_BUCKETS 256u + +layout(binding=0, std140) writeonly buffer minmaxresultdest +{ + vec4 result[2]; +} dest; + +layout(binding=1, std140) readonly buffer minmaxtilesrc +{ + vec4 tiles[]; +} src; + +layout(binding=2, std140) uniform HistogramCBufferData +{ + uint HistogramChannels; + float HistogramMin; + float HistogramMax; + uint HistogramFlags; + + float HistogramSlice; + int HistogramMip; + int HistogramSample; + int HistogramNumSamples; + + vec3 HistogramTextureResolution; + float Padding3; +} minmax; + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + uvec3 texDim = uvec3(minmax.HistogramTextureResolution); + + uint blocksX = uint(ceil(float(texDim.x)/float(HGRAM_PIXELS_PER_TILE*HGRAM_TILES_PER_BLOCK))); + uint blocksY = uint(ceil(float(texDim.y)/float(HGRAM_PIXELS_PER_TILE*HGRAM_TILES_PER_BLOCK))); + + vec4 minvalF = src.tiles[0]; + vec4 maxvalF = src.tiles[1]; + + // i is the tile we're looking at + for(uint i=1; i < blocksX*blocksY*HGRAM_TILES_PER_BLOCK*HGRAM_TILES_PER_BLOCK; i++) + { + uint blockIdx = i/(HGRAM_TILES_PER_BLOCK*HGRAM_TILES_PER_BLOCK); + uint tileIdx = i%(HGRAM_TILES_PER_BLOCK*HGRAM_TILES_PER_BLOCK); + + // which block and tile is this in + uvec2 blockXY = uvec2(blockIdx % blocksX, blockIdx / blocksX); + uvec2 tileXY = uvec2(tileIdx % HGRAM_TILES_PER_BLOCK, tileIdx / HGRAM_TILES_PER_BLOCK); + + // if this is at least partially within the texture, include it. + if(blockXY.x*(HGRAM_TILES_PER_BLOCK*HGRAM_TILES_PER_BLOCK) + tileXY.x*HGRAM_PIXELS_PER_TILE < texDim.x && + blockXY.y*(HGRAM_TILES_PER_BLOCK*HGRAM_TILES_PER_BLOCK) + tileXY.y*HGRAM_PIXELS_PER_TILE < texDim.y) + { + minvalF = min(minvalF, src.tiles[i*2 + 0]); + maxvalF = max(maxvalF, src.tiles[i*2 + 1]); + } + } + + dest.result[0] = minvalF; + dest.result[1] = maxvalF; +} + diff --git a/renderdoc/data/spv/minmaxtile.comp b/renderdoc/data/spv/minmaxtile.comp new file mode 100644 index 000000000..372909fba --- /dev/null +++ b/renderdoc/data/spv/minmaxtile.comp @@ -0,0 +1,104 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2014 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. + ******************************************************************************/ + +// compute shaders that figure out the min/max values or histogram in a texture heirarchically +// note that we have to conditionally compile this shader for float/uint/sint as doing that +// dynamically produces a shader with too many temp registers unfortunately. + +#version 430 core + +#define HGRAM_PIXELS_PER_TILE 64u +#define HGRAM_TILES_PER_BLOCK 32u + +#define HGRAM_NUM_BUCKETS 256u + +layout(binding=0, std140) writeonly buffer minmaxtiledest +{ + vec4 tiles[]; +} dest; + +layout(binding=2, std140) uniform HistogramCBufferData +{ + uint HistogramChannels; + float HistogramMin; + float HistogramMax; + uint HistogramFlags; + + float HistogramSlice; + int HistogramMip; + int HistogramSample; + int HistogramNumSamples; + + vec3 HistogramTextureResolution; + float Padding3; +} minmax; + +layout (binding = 3) uniform sampler2D tex; + +layout (local_size_x = HGRAM_TILES_PER_BLOCK, local_size_y = HGRAM_TILES_PER_BLOCK) in; + +void main() +{ + uvec3 tid = gl_LocalInvocationID; + uvec3 gid = gl_WorkGroupID; + + uvec3 texDim = uvec3(minmax.HistogramTextureResolution); + + uint blocksX = uint(ceil(float(texDim.x)/float(HGRAM_PIXELS_PER_TILE*HGRAM_TILES_PER_BLOCK))); + + uvec2 topleft = (gid.xy*HGRAM_TILES_PER_BLOCK + tid.xy)*HGRAM_PIXELS_PER_TILE; + + uint outIdx = (tid.y*HGRAM_TILES_PER_BLOCK + tid.x) + (gid.y*blocksX + gid.x)*(HGRAM_TILES_PER_BLOCK*HGRAM_TILES_PER_BLOCK); + + int i=0; + + { + vec4 minval = vec4(0,0,0,0); + vec4 maxval = vec4(0,0,0,0); + + for(uint y=topleft.y; y < min(texDim.y, topleft.y + HGRAM_PIXELS_PER_TILE); y++) + { + for(uint x=topleft.x; x < min(texDim.x, topleft.x + HGRAM_PIXELS_PER_TILE); x++) + { + vec4 data = textureLod(tex, vec2(x, y) / minmax.HistogramTextureResolution.xy, float(minmax.HistogramMip)); + + if(i == 0) + { + minval = maxval = data; + } + else + { + minval = min(minval, data); + maxval = max(maxval, data); + } + + i++; + } + } + + dest.tiles[outIdx*2+0] = minval; + dest.tiles[outIdx*2+1] = maxval; + } +} + diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index 93424eb5e..1ffa4459b 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -99,6 +99,35 @@ struct meshuniforms Vec2f pointSpriteSize; }; +// histogram/minmax is calculated in blocks of NxN each with MxM tiles. +// e.g. a tile is 32x32 pixels, then this is arranged in blocks of 32x32 tiles. +// 1 compute thread = 1 tile, 1 compute group = 1 block +// +// NOTE because of this a block can cover more than the texture (think of a 1280x720 +// texture covered by 2x1 blocks) +// +// these values are in each dimension +#define HGRAM_PIXELS_PER_TILE 64 +#define HGRAM_TILES_PER_BLOCK 32 + +#define HGRAM_NUM_BUCKETS 256 + +struct histogramuniforms +{ + uint32_t HistogramChannels; + float HistogramMin; + float HistogramMax; + uint32_t HistogramFlags; + + float HistogramSlice; + uint32_t HistogramMip; + int HistogramSample; + int HistogramNumSamples; + + Vec3f HistogramTextureResolution; + float Padding3; +}; + void VulkanDebugManager::GPUBuffer::Create(WrappedVulkan *driver, VkDevice dev, VkDeviceSize size, uint32_t ringSize, uint32_t flags) { const VkLayerDispatchTable *vt = ObjDisp(dev); @@ -125,6 +154,9 @@ void VulkanDebugManager::GPUBuffer::Create(WrappedVulkan *driver, VkDevice dev, if(flags & eGPUBufferVBuffer) bufInfo.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + + if(flags & eGPUBufferSSBO) + bufInfo.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; VkResult vkr = vt->CreateBuffer(Unwrap(dev), &bufInfo, &buf); RDCASSERT(vkr == VK_SUCCESS); @@ -365,6 +397,25 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetResourceManager()->WrapResource(Unwrap(dev), m_TextDescSetLayout); } + { + VkDescriptorSetLayoutBinding layoutBinding[] = { + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL, }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL, }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL, }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, NULL, } + }; + + VkDescriptorSetLayoutCreateInfo descsetLayoutInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, NULL, + ARRAY_COUNT(layoutBinding), &layoutBinding[0], + }; + + vkr = vt->CreateDescriptorSetLayout(Unwrap(dev), &descsetLayoutInfo, &m_HistogramDescSetLayout); + RDCASSERT(vkr == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(dev), m_HistogramDescSetLayout); + } + VkPipelineLayoutCreateInfo pipeLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, NULL, 1, UnwrapPtr(m_TexDisplayDescSetLayout), @@ -404,15 +455,23 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetResourceManager()->WrapResource(Unwrap(dev), m_MeshPipeLayout); + pipeLayoutInfo.pSetLayouts = UnwrapPtr(m_HistogramDescSetLayout); + + vkr = vt->CreatePipelineLayout(Unwrap(dev), &pipeLayoutInfo, &m_HistogramPipeLayout); + RDCASSERT(vkr == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(dev), m_HistogramPipeLayout); + VkDescriptorTypeCount descPoolTypes[] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1024, }, { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1024, }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1024, }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1024, }, }; VkDescriptorPoolCreateInfo descpoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, NULL, - VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 4+ARRAY_COUNT(m_TexDisplayDescSet), + VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 6+ARRAY_COUNT(m_TexDisplayDescSet), ARRAY_COUNT(descPoolTypes), &descPoolTypes[0], }; @@ -453,6 +512,18 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) RDCASSERT(vkr == VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(dev), m_MeshDescSet); + + vkr = vt->AllocDescriptorSets(Unwrap(dev), Unwrap(m_DescriptorPool), VK_DESCRIPTOR_SET_USAGE_STATIC, 1, + UnwrapPtr(m_HistogramDescSetLayout), &m_HistogramDescSet[0]); + RDCASSERT(vkr == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(dev), m_HistogramDescSet[0]); + + vkr = vt->AllocDescriptorSets(Unwrap(dev), Unwrap(m_DescriptorPool), VK_DESCRIPTOR_SET_USAGE_STATIC, 1, + UnwrapPtr(m_HistogramDescSetLayout), &m_HistogramDescSet[1]); + RDCASSERT(vkr == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(dev), m_HistogramDescSet[1]); m_GenericUBO.Create(driver, dev, 128, 10, 0); RDCCOMPILE_ASSERT(sizeof(genericuniforms) <= 128, "outline strip VBO size"); @@ -504,6 +575,9 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetEmbeddedResource(meshvs_spv), GetEmbeddedResource(meshgs_spv), GetEmbeddedResource(meshfs_spv), + GetEmbeddedResource(minmaxtilecs_spv), + GetEmbeddedResource(minmaxresultcs_spv), + GetEmbeddedResource(histogramcs_spv), }; VkShaderStage shaderStages[] = { @@ -517,6 +591,9 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) VK_SHADER_STAGE_VERTEX, VK_SHADER_STAGE_GEOMETRY, VK_SHADER_STAGE_FRAGMENT, + VK_SHADER_STAGE_COMPUTE, + VK_SHADER_STAGE_COMPUTE, + VK_SHADER_STAGE_COMPUTE, }; enum shaderIdx @@ -531,6 +608,9 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) MESHVS, MESHGS, MESHFS, + MINMAXTILECS, + MINMAXRESULTCS, + HISTOGRAMCS, NUM_SHADERS, }; @@ -756,6 +836,35 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) GetResourceManager()->WrapResource(Unwrap(dev), m_HighlightBoxPipeline); + VkComputePipelineCreateInfo compPipeInfo = { + VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, NULL, + { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, VK_SHADER_STAGE_COMPUTE, VK_NULL_HANDLE, NULL }, + 0, // flags + Unwrap(m_HistogramPipeLayout), + VK_NULL_HANDLE, 0, // base pipeline VkPipeline + }; + + compPipeInfo.stage.shader = Unwrap(shader[MINMAXTILECS]); + + vkr = vt->CreateComputePipelines(Unwrap(dev), VK_NULL_HANDLE, 1, &compPipeInfo, &m_MinMaxTilePipe); + RDCASSERT(vkr == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(dev), m_MinMaxTilePipe); + + compPipeInfo.stage.shader = Unwrap(shader[MINMAXRESULTCS]); + + vkr = vt->CreateComputePipelines(Unwrap(dev), VK_NULL_HANDLE, 1, &compPipeInfo, &m_MinMaxResultPipe); + RDCASSERT(vkr == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(dev), m_MinMaxResultPipe); + + compPipeInfo.stage.shader = Unwrap(shader[HISTOGRAMCS]); + + vkr = vt->CreateComputePipelines(Unwrap(dev), VK_NULL_HANDLE, 1, &compPipeInfo, &m_HistogramPipe); + RDCASSERT(vkr == VK_SUCCESS); + + GetResourceManager()->WrapResource(Unwrap(dev), m_HistogramPipe); + vt->DestroyRenderPass(Unwrap(dev), RGBA32RP); vt->DestroyRenderPass(Unwrap(dev), RGBA8RP); @@ -1099,6 +1208,21 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver, VkDevice dev) memcpy(axisData, axisFrustum, sizeof(axisFrustum)); m_MeshAxisFrustumVB.Unmap(vt, dev); + + 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, dev, byteSize, 1, GPUBuffer::eGPUBufferSSBO); + m_MinMaxResult.Create(driver, dev, sizeof(Vec4f)*2, 1, GPUBuffer::eGPUBufferSSBO); + m_MinMaxReadback.Create(driver, dev, sizeof(Vec4f)*2, 1, GPUBuffer::eGPUBufferReadback); + m_HistogramBuf.Create(driver, dev, sizeof(uint32_t)*HGRAM_NUM_BUCKETS, 1, GPUBuffer::eGPUBufferSSBO); + m_HistogramReadback.Create(driver, dev, 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, dev, sizeof(histogramuniforms), 1, 0); VkDescriptorInfo desc[7]; RDCEraseEl(desc); @@ -1194,6 +1318,9 @@ VulkanDebugManager::~VulkanDebugManager() GetResourceManager()->ReleaseWrappedResource(m_TextDescSet); GetResourceManager()->ReleaseWrappedResource(m_MeshDescSet); + for(size_t i=0; i < ARRAY_COUNT(m_HistogramDescSet); i++) + GetResourceManager()->ReleaseWrappedResource(m_HistogramDescSet[i]); + for(size_t i=0; i < ARRAY_COUNT(m_TexDisplayDescSet); i++) GetResourceManager()->ReleaseWrappedResource(m_TexDisplayDescSet[i]); @@ -1375,6 +1502,43 @@ VulkanDebugManager::~VulkanDebugManager() m_OutlineStripVBO.Destroy(vt, dev); m_GenericUBO.Destroy(vt, dev); + if(m_HistogramDescSetLayout != VK_NULL_HANDLE) + { + vt->DestroyDescriptorSetLayout(Unwrap(dev), Unwrap(m_HistogramDescSetLayout)); + GetResourceManager()->ReleaseWrappedResource(m_HistogramDescSetLayout); + } + + if(m_HistogramPipeLayout != VK_NULL_HANDLE) + { + vt->DestroyPipelineLayout(Unwrap(dev), Unwrap(m_HistogramPipeLayout)); + GetResourceManager()->ReleaseWrappedResource(m_HistogramPipeLayout); + } + + if(m_MinMaxResultPipe != VK_NULL_HANDLE) + { + vt->DestroyPipeline(Unwrap(dev), Unwrap(m_MinMaxResultPipe)); + GetResourceManager()->ReleaseWrappedResource(m_MinMaxResultPipe); + } + + if(m_MinMaxTilePipe != VK_NULL_HANDLE) + { + vt->DestroyPipeline(Unwrap(dev), Unwrap(m_MinMaxTilePipe)); + GetResourceManager()->ReleaseWrappedResource(m_MinMaxTilePipe); + } + + if(m_HistogramPipe != VK_NULL_HANDLE) + { + vt->DestroyPipeline(Unwrap(dev), Unwrap(m_HistogramPipe)); + GetResourceManager()->ReleaseWrappedResource(m_HistogramPipe); + } + + m_MinMaxTileResult.Destroy(vt, dev); + m_MinMaxResult.Destroy(vt, dev); + m_MinMaxReadback.Destroy(vt, dev); + m_HistogramBuf.Destroy(vt, dev); + m_HistogramReadback.Destroy(vt, dev); + m_HistogramUBO.Destroy(vt, dev); + // overlay resources are allocated through driver if(m_OverlayNoDepthFB != VK_NULL_HANDLE) m_pDriver->vkDestroyFramebuffer(dev, m_OverlayNoDepthFB); diff --git a/renderdoc/driver/vulkan/vk_debug.h b/renderdoc/driver/vulkan/vk_debug.h index 39dd906d9..d1deea892 100644 --- a/renderdoc/driver/vulkan/vk_debug.h +++ b/renderdoc/driver/vulkan/vk_debug.h @@ -74,6 +74,7 @@ class VulkanDebugManager { eGPUBufferReadback = 0x1, eGPUBufferVBuffer = 0x2, + eGPUBufferSSBO = 0x4, }; GPUBuffer() : buf(VK_NULL_HANDLE), mem(VK_NULL_HANDLE) {} void Create(WrappedVulkan *driver, VkDevice dev, VkDeviceSize size, uint32_t ringSize, uint32_t flags); @@ -162,6 +163,17 @@ class VulkanDebugManager GPUBuffer m_MeshUBO, m_MeshBBoxVB, m_MeshAxisFrustumVB; VkShader m_MeshShaders[3]; VkShaderModule m_MeshModules[3]; + + GPUBuffer m_MinMaxTileResult; // tile result buffer + GPUBuffer m_MinMaxResult, m_MinMaxReadback; // Vec4f[2] final result buffer + GPUBuffer m_HistogramBuf, m_HistogramReadback; // uint32_t * num buckets buffer + VkDescriptorSetLayout m_HistogramDescSetLayout; + VkPipelineLayout m_HistogramPipeLayout; + VkDescriptorSet m_HistogramDescSet[2]; + GPUBuffer m_HistogramUBO; + VkPipeline m_MinMaxResultPipe; + VkPipeline m_MinMaxTilePipe; + VkPipeline m_HistogramPipe; MeshDisplayPipelines CacheMeshDisplayPipelines(const MeshFormat &primary, const MeshFormat &secondary); diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index ab14badf0..9a6d32756 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -80,6 +80,27 @@ struct meshuniforms #define MESHDISPLAY_SECONDARY 0x3 #define MESHDISPLAY_SECONDARY_ALPHA 0x4 +#define HGRAM_PIXELS_PER_TILE 64 +#define HGRAM_TILES_PER_BLOCK 32 + +#define HGRAM_NUM_BUCKETS 256 + +struct histogramuniforms +{ + uint32_t HistogramChannels; + float HistogramMin; + float HistogramMax; + uint32_t HistogramFlags; + + float HistogramSlice; + uint32_t HistogramMip; + int HistogramSample; + int HistogramNumSamples; + + Vec3f HistogramTextureResolution; + float Padding3; +}; + VulkanReplay::OutputWindow::OutputWindow() : wnd(NULL_WND_HANDLE), width(0), height(0), dsimg(VK_NULL_HANDLE), dsmem(VK_NULL_HANDLE) { @@ -3133,14 +3154,411 @@ void VulkanReplay::FillCBufferVariables(ResourceId shader, uint32_t cbufSlot, ve bool VulkanReplay::GetMinMax(ResourceId texid, uint32_t sliceFace, uint32_t mip, uint32_t sample, float *minval, float *maxval) { - RDCUNIMPLEMENTED("GetMinMax"); - return false; + VkDevice dev = m_pDriver->GetDev(); + VkCmdBuffer cmd = m_pDriver->GetNextCmd(); + const VkLayerDispatchTable *vt = ObjDisp(dev); + + ImageLayouts &layouts = m_pDriver->m_ImageLayouts[texid]; + VulkanCreationInfo::Image &iminfo = m_pDriver->m_CreationInfo.m_Image[texid]; + VkImage liveIm = m_pDriver->GetResourceManager()->GetCurrentHandle(texid); + + // VKTODOMED handle multiple subresources with different layouts etc + VkImageLayout origLayout = layouts.subresourceStates[0].newLayout; + VkImageView liveImView = iminfo.view; + + if(liveImView == VK_NULL_HANDLE) + { + VkImageViewCreateInfo viewInfo = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, + Unwrap(liveIm), VK_IMAGE_VIEW_TYPE_2D, + iminfo.format, + { VK_CHANNEL_SWIZZLE_R, VK_CHANNEL_SWIZZLE_G, VK_CHANNEL_SWIZZLE_B, VK_CHANNEL_SWIZZLE_A }, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, RDCMAX(1U, (uint32_t)iminfo.mipLevels), 0, 1, }, + 0 + }; + + // Only needed on AMD - does the wrong thing on nvidia - so commented for now while AMD + // drivers aren't on 0.9.2 + //if(iminfo.format == VK_FORMAT_B8G8R8A8_UNORM || iminfo.format == VK_FORMAT_B8G8R8A8_SRGB) + //std::swap(viewInfo.channels.r, viewInfo.channels.b); + + VkResult vkr = ObjDisp(dev)->CreateImageView(Unwrap(dev), &viewInfo, &iminfo.view); + RDCASSERT(vkr == VK_SUCCESS); + + ResourceId viewid = m_pDriver->GetResourceManager()->WrapResource(Unwrap(dev), iminfo.view); + // register as a live-only resource, so it is cleaned up properly + m_pDriver->GetResourceManager()->AddLiveResource(viewid, iminfo.view); + + liveImView = iminfo.view; + } + + VkDescriptorInfo desc = {0}; + desc.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + desc.imageView = Unwrap(liveImView); + desc.sampler = Unwrap(GetDebugManager()->m_PointSampler); + + VkDescriptorInfo bufdescs[3]; + RDCEraseEl(bufdescs); + GetDebugManager()->m_MinMaxTileResult.FillDescriptor(bufdescs[0]); + GetDebugManager()->m_MinMaxResult.FillDescriptor(bufdescs[1]); + GetDebugManager()->m_HistogramUBO.FillDescriptor(bufdescs[2]); + + VkWriteDescriptorSet writeSet[] = { + + // first pass on tiles + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[0]), + 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufdescs[0] // destination = tile result + }, + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[0]), + 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufdescs[0] // source = unused, bind tile result + }, + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[0]), + 2, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &bufdescs[2] + }, + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[0]), + 3, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &desc + }, + + // second pass from tiles to result + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[1]), + 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufdescs[1] // destination = result + }, + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[1]), + 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufdescs[0] // source = tile result + }, + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[1]), + 2, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &bufdescs[2] + }, + }; + + vt->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writeSet), writeSet, 0, NULL); + + histogramuniforms *data = (histogramuniforms *)GetDebugManager()->m_HistogramUBO.Map(vt, dev, NULL); + + data->HistogramTextureResolution.x = (float)RDCMAX(uint32_t(iminfo.extent.width)>>mip, 1U); + data->HistogramTextureResolution.y = (float)RDCMAX(uint32_t(iminfo.extent.height)>>mip, 1U); + data->HistogramTextureResolution.z = (float)RDCMAX(uint32_t(iminfo.arraySize)>>mip, 1U); + data->HistogramSlice = (float)sliceFace; + data->HistogramMip = (int)mip; + data->HistogramNumSamples = iminfo.samples; + data->HistogramSample = (int)RDCCLAMP(sample, 0U, uint32_t(iminfo.samples)-1); + if(sample == ~0U) data->HistogramSample = -iminfo.samples; + data->HistogramMin = 0.0f; + data->HistogramMax = 1.0f; + data->HistogramChannels = 0xf; + + if(iminfo.type == VK_IMAGE_TYPE_3D) + data->HistogramSlice = float(sliceFace)/float(iminfo.extent.depth); + + GetDebugManager()->m_HistogramUBO.Unmap(vt, dev); + + VkImageMemoryBarrier srcimTrans = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, + 0, 0, origLayout, VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + Unwrap(liveIm), + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }; + + // ensure all previous writes have completed + srcimTrans.outputMask = + VK_MEMORY_OUTPUT_COLOR_ATTACHMENT_BIT| + VK_MEMORY_OUTPUT_SHADER_WRITE_BIT| + VK_MEMORY_OUTPUT_DEPTH_STENCIL_ATTACHMENT_BIT| + VK_MEMORY_OUTPUT_TRANSFER_BIT; + // before we go reading + srcimTrans.inputMask = VK_MEMORY_INPUT_SHADER_READ_BIT; + + VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT }; + + vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + + void *barrier = (void *)&srcimTrans; + + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + srcimTrans.oldLayout = srcimTrans.newLayout; + + srcimTrans.outputMask = 0; + srcimTrans.inputMask = 0; + + int blocksX = (int)ceil(iminfo.extent.width/float(HGRAM_PIXELS_PER_TILE*HGRAM_TILES_PER_BLOCK)); + int blocksY = (int)ceil(iminfo.extent.height/float(HGRAM_PIXELS_PER_TILE*HGRAM_TILES_PER_BLOCK)); + + vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(GetDebugManager()->m_MinMaxTilePipe)); + vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(GetDebugManager()->m_HistogramPipeLayout), + 0, 1, UnwrapPtr(GetDebugManager()->m_HistogramDescSet[0]), 0, NULL); + + vt->CmdDispatch(Unwrap(cmd), blocksX, blocksY, 1); + + VkBufferMemoryBarrier tilebarrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, NULL, + VK_MEMORY_OUTPUT_SHADER_WRITE_BIT, VK_MEMORY_INPUT_SHADER_READ_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + Unwrap(GetDebugManager()->m_MinMaxTileResult.buf), + 0, GetDebugManager()->m_MinMaxTileResult.totalsize, + }; + + // image layout back to normal + srcimTrans.newLayout = origLayout; + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + // ensure shader writes complete before coalescing the tiles + barrier = (void *)&tilebarrier; + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(GetDebugManager()->m_MinMaxResultPipe)); + vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(GetDebugManager()->m_HistogramPipeLayout), + 0, 1, UnwrapPtr(GetDebugManager()->m_HistogramDescSet[1]), 0, NULL); + + vt->CmdDispatch(Unwrap(cmd), 1, 1, 1); + + // ensure shader writes complete before copying back to readback buffer + tilebarrier.inputMask = VK_MEMORY_INPUT_TRANSFER_BIT; + tilebarrier.buffer = Unwrap(GetDebugManager()->m_MinMaxResult.buf); + tilebarrier.size = GetDebugManager()->m_MinMaxResult.totalsize; + + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + VkBufferCopy bufcopy = { + 0, 0, GetDebugManager()->m_MinMaxResult.totalsize, + }; + + vt->CmdCopyBuffer(Unwrap(cmd), Unwrap(GetDebugManager()->m_MinMaxResult.buf), Unwrap(GetDebugManager()->m_MinMaxReadback.buf), 1, &bufcopy); + + // wait for copy to complete before mapping + tilebarrier.outputMask = VK_MEMORY_OUTPUT_TRANSFER_BIT; + tilebarrier.inputMask = VK_MEMORY_INPUT_HOST_READ_BIT; + tilebarrier.buffer = Unwrap(GetDebugManager()->m_MinMaxReadback.buf); + tilebarrier.size = GetDebugManager()->m_MinMaxResult.totalsize; + + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + vt->EndCommandBuffer(Unwrap(cmd)); + + // submit cmds and wait for idle so we can readback + m_pDriver->SubmitCmds(); + m_pDriver->FlushQ(); + + Vec4f *minmax = (Vec4f *)GetDebugManager()->m_MinMaxReadback.Map(vt, dev, NULL); + + minval[0] = minmax[0].x; + minval[1] = minmax[0].y; + minval[2] = minmax[0].z; + minval[3] = minmax[0].w; + + maxval[0] = minmax[1].x; + maxval[1] = minmax[1].y; + maxval[2] = minmax[1].z; + maxval[3] = minmax[1].w; + + GetDebugManager()->m_MinMaxReadback.Unmap(vt, dev); + + return true; } bool VulkanReplay::GetHistogram(ResourceId texid, uint32_t sliceFace, uint32_t mip, uint32_t sample, float minval, float maxval, bool channels[4], vector &histogram) { - RDCUNIMPLEMENTED("GetHistogram"); - return false; + if(minval >= maxval) return false; + + VkDevice dev = m_pDriver->GetDev(); + VkCmdBuffer cmd = m_pDriver->GetNextCmd(); + const VkLayerDispatchTable *vt = ObjDisp(dev); + + ImageLayouts &layouts = m_pDriver->m_ImageLayouts[texid]; + VulkanCreationInfo::Image &iminfo = m_pDriver->m_CreationInfo.m_Image[texid]; + VkImage liveIm = m_pDriver->GetResourceManager()->GetCurrentHandle(texid); + + // VKTODOMED handle multiple subresources with different layouts etc + VkImageLayout origLayout = layouts.subresourceStates[0].newLayout; + VkImageView liveImView = iminfo.view; + + if(liveImView == VK_NULL_HANDLE) + { + VkImageViewCreateInfo viewInfo = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, + Unwrap(liveIm), VK_IMAGE_VIEW_TYPE_2D, + iminfo.format, + { VK_CHANNEL_SWIZZLE_R, VK_CHANNEL_SWIZZLE_G, VK_CHANNEL_SWIZZLE_B, VK_CHANNEL_SWIZZLE_A }, + { VK_IMAGE_ASPECT_COLOR_BIT, 0, RDCMAX(1U, (uint32_t)iminfo.mipLevels), 0, 1, }, + 0 + }; + + // Only needed on AMD - does the wrong thing on nvidia - so commented for now while AMD + // drivers aren't on 0.9.2 + //if(iminfo.format == VK_FORMAT_B8G8R8A8_UNORM || iminfo.format == VK_FORMAT_B8G8R8A8_SRGB) + //std::swap(viewInfo.channels.r, viewInfo.channels.b); + + VkResult vkr = ObjDisp(dev)->CreateImageView(Unwrap(dev), &viewInfo, &iminfo.view); + RDCASSERT(vkr == VK_SUCCESS); + + ResourceId viewid = m_pDriver->GetResourceManager()->WrapResource(Unwrap(dev), iminfo.view); + // register as a live-only resource, so it is cleaned up properly + m_pDriver->GetResourceManager()->AddLiveResource(viewid, iminfo.view); + + liveImView = iminfo.view; + } + + VkDescriptorInfo desc = {0}; + desc.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + desc.imageView = Unwrap(liveImView); + desc.sampler = Unwrap(GetDebugManager()->m_PointSampler); + + VkDescriptorInfo bufdescs[2]; + RDCEraseEl(bufdescs); + GetDebugManager()->m_HistogramBuf.FillDescriptor(bufdescs[0]); + GetDebugManager()->m_HistogramUBO.FillDescriptor(bufdescs[1]); + + VkWriteDescriptorSet writeSet[] = { + + // histogram pass + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[0]), + 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufdescs[0] // destination = histogram result + }, + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[0]), + 1, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufdescs[0] // source = unused, bind histogram result + }, + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[0]), + 2, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &bufdescs[1] + }, + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, + Unwrap(GetDebugManager()->m_HistogramDescSet[0]), + 3, 0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &desc + }, + }; + + vt->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writeSet), writeSet, 0, NULL); + + histogramuniforms *data = (histogramuniforms *)GetDebugManager()->m_HistogramUBO.Map(vt, dev, NULL); + + data->HistogramTextureResolution.x = (float)RDCMAX(uint32_t(iminfo.extent.width)>>mip, 1U); + data->HistogramTextureResolution.y = (float)RDCMAX(uint32_t(iminfo.extent.height)>>mip, 1U); + data->HistogramTextureResolution.z = (float)RDCMAX(uint32_t(iminfo.arraySize)>>mip, 1U); + data->HistogramSlice = (float)sliceFace; + data->HistogramMip = (int)mip; + data->HistogramNumSamples = iminfo.samples; + data->HistogramSample = (int)RDCCLAMP(sample, 0U, uint32_t(iminfo.samples)-1); + if(sample == ~0U) data->HistogramSample = -iminfo.samples; + data->HistogramMin = minval; + data->HistogramMax = maxval; + + uint32_t chans = 0; + if(channels[0]) chans |= 0x1; + if(channels[1]) chans |= 0x2; + if(channels[2]) chans |= 0x4; + if(channels[3]) chans |= 0x8; + + data->HistogramChannels = chans; + data->HistogramFlags = 0; + + if(iminfo.type == VK_IMAGE_TYPE_3D) + data->HistogramSlice = float(sliceFace)/float(iminfo.extent.depth); + + GetDebugManager()->m_HistogramUBO.Unmap(vt, dev); + + VkImageMemoryBarrier srcimTrans = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, + 0, 0, origLayout, VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + Unwrap(liveIm), + { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }; + + // ensure all previous writes have completed + srcimTrans.outputMask = + VK_MEMORY_OUTPUT_COLOR_ATTACHMENT_BIT| + VK_MEMORY_OUTPUT_SHADER_WRITE_BIT| + VK_MEMORY_OUTPUT_DEPTH_STENCIL_ATTACHMENT_BIT| + VK_MEMORY_OUTPUT_TRANSFER_BIT; + // before we go reading + srcimTrans.inputMask = VK_MEMORY_INPUT_SHADER_READ_BIT; + + VkCmdBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, NULL, VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT }; + + vt->BeginCommandBuffer(Unwrap(cmd), &beginInfo); + + void *barrier = (void *)&srcimTrans; + + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + srcimTrans.oldLayout = srcimTrans.newLayout; + + srcimTrans.outputMask = 0; + srcimTrans.inputMask = 0; + + int blocksX = (int)ceil(iminfo.extent.width/float(HGRAM_PIXELS_PER_TILE*HGRAM_TILES_PER_BLOCK)); + int blocksY = (int)ceil(iminfo.extent.height/float(HGRAM_PIXELS_PER_TILE*HGRAM_TILES_PER_BLOCK)); + + vt->CmdFillBuffer(Unwrap(cmd), Unwrap(GetDebugManager()->m_HistogramBuf.buf), 0, GetDebugManager()->m_HistogramBuf.totalsize, 0); + + vt->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(GetDebugManager()->m_HistogramPipe)); + vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_COMPUTE, Unwrap(GetDebugManager()->m_HistogramPipeLayout), + 0, 1, UnwrapPtr(GetDebugManager()->m_HistogramDescSet[0]), 0, NULL); + + vt->CmdDispatch(Unwrap(cmd), blocksX, blocksY, 1); + + VkBufferMemoryBarrier tilebarrier = { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, NULL, + VK_MEMORY_OUTPUT_SHADER_WRITE_BIT, VK_MEMORY_INPUT_TRANSFER_BIT, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + Unwrap(GetDebugManager()->m_HistogramBuf.buf), + 0, GetDebugManager()->m_HistogramBuf.totalsize, + }; + + // image layout back to normal + srcimTrans.newLayout = origLayout; + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + // ensure shader writes complete before copying to readback buf + barrier = (void *)&tilebarrier; + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + VkBufferCopy bufcopy = { + 0, 0, GetDebugManager()->m_HistogramBuf.totalsize, + }; + + vt->CmdCopyBuffer(Unwrap(cmd), Unwrap(GetDebugManager()->m_HistogramBuf.buf), Unwrap(GetDebugManager()->m_HistogramReadback.buf), 1, &bufcopy); + + // wait for copy to complete before mapping + tilebarrier.outputMask = VK_MEMORY_OUTPUT_TRANSFER_BIT; + tilebarrier.inputMask = VK_MEMORY_INPUT_HOST_READ_BIT; + tilebarrier.buffer = Unwrap(GetDebugManager()->m_HistogramReadback.buf); + tilebarrier.size = GetDebugManager()->m_HistogramReadback.totalsize; + + vt->CmdPipelineBarrier(Unwrap(cmd), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, false, 1, &barrier); + + vt->EndCommandBuffer(Unwrap(cmd)); + + // submit cmds and wait for idle so we can readback + m_pDriver->SubmitCmds(); + m_pDriver->FlushQ(); + + uint32_t *buckets = (uint32_t *)GetDebugManager()->m_HistogramReadback.Map(vt, dev, NULL); + + histogram.assign(buckets, buckets+HGRAM_NUM_BUCKETS); + + GetDebugManager()->m_HistogramReadback.Unmap(vt, dev); + + return true; } void VulkanReplay::InitPostVSBuffers(uint32_t frameID, uint32_t eventID) diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index 7e72e5b83..68beae831 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -367,12 +367,18 @@ + + + + + + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index c1106db9d..8ff83203b 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -496,6 +496,24 @@ Resources\spv + + Resources\spv + + + Resources\spv + + + Resources\spv + + + Resources\spv + + + Resources\spv + + + Resources\spv +