From 02a54be6ad35f12aaaff1ad80707af00f67ef98c Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 13 Jul 2020 17:29:11 +0100 Subject: [PATCH] When replaying resource discards fill with explicit pattern. Closes #284 * This helps catches cases where a discarded image is accidentally used and in many cases may still have valid data. Particularly on Vulkan this is relevant for DONT_CARE renderpass load and store ops. --- renderdoc/CMakeLists.txt | 1 + renderdoc/api/replay/rdcstr.h | 2 + renderdoc/data/embedded_files.h | 1 + renderdoc/data/glsl/blit.vert | 8 + renderdoc/data/glsl/discard.frag | 108 ++++ renderdoc/data/hlsl/misc.hlsl | 36 ++ renderdoc/data/renderdoc.rc | 1 + renderdoc/data/resource.h | 1 + renderdoc/driver/d3d11/d3d11_common.cpp | 79 ++- renderdoc/driver/d3d11/d3d11_common.h | 7 +- .../driver/d3d11/d3d11_context1_wrap.cpp | 83 ++- renderdoc/driver/d3d11/d3d11_debug.cpp | 516 ++++++++++++++++ renderdoc/driver/d3d11/d3d11_debug.h | 29 + renderdoc/driver/d3d11/d3d11_resources.cpp | 64 ++ renderdoc/driver/d3d11/d3d11_resources.h | 2 + .../driver/d3d12/d3d12_command_list_wrap.cpp | 8 + renderdoc/driver/d3d12/d3d12_debug.cpp | 396 ++++++++++++ renderdoc/driver/d3d12/d3d12_debug.h | 13 + renderdoc/driver/d3d12/d3d12_resources.cpp | 19 + renderdoc/driver/d3d12/d3d12_resources.h | 1 + renderdoc/driver/gl/gl_debug.cpp | 259 ++++++++ renderdoc/driver/gl/gl_driver.cpp | 19 +- renderdoc/driver/gl/gl_replay.h | 12 + .../driver/gl/wrappers/gl_buffer_funcs.cpp | 154 ++++- renderdoc/driver/gl/wrappers/gl_emulated.cpp | 17 + .../gl/wrappers/gl_framebuffer_funcs.cpp | 136 +++- .../driver/gl/wrappers/gl_texture_funcs.cpp | 296 ++++++++- renderdoc/driver/vulkan/vk_core.cpp | 5 + renderdoc/driver/vulkan/vk_debug.cpp | 502 +++++++++++++++ renderdoc/driver/vulkan/vk_debug.h | 26 + renderdoc/driver/vulkan/vk_info.cpp | 18 + renderdoc/driver/vulkan/vk_info.h | 3 +- renderdoc/driver/vulkan/vk_initstate.cpp | 21 +- renderdoc/driver/vulkan/vk_rendertexture.cpp | 4 +- renderdoc/driver/vulkan/vk_replay.cpp | 8 +- renderdoc/driver/vulkan/vk_replay.h | 2 +- renderdoc/driver/vulkan/vk_shader_cache.cpp | 2 + renderdoc/driver/vulkan/vk_shader_cache.h | 1 + .../driver/vulkan/wrappers/vk_cmd_funcs.cpp | 229 ++++++- .../driver/vulkan/wrappers/vk_draw_funcs.cpp | 5 +- .../driver/vulkan/wrappers/vk_misc_funcs.cpp | 23 +- .../driver/vulkan/wrappers/vk_sync_funcs.cpp | 15 + renderdoc/renderdoc.vcxproj | 1 + renderdoc/renderdoc.vcxproj.filters | 1 + renderdoc/replay/replay_driver.cpp | 456 ++++++++++++++ renderdoc/replay/replay_driver.h | 17 + util/test/demos/CMakeLists.txt | 2 + util/test/demos/d3d11/d3d11_discard_zoo.cpp | 282 +++++++++ util/test/demos/d3d11/d3d11_helpers.cpp | 17 +- util/test/demos/d3d11/d3d11_helpers.h | 2 + util/test/demos/d3d12/d3d12_discard_zoo.cpp | 581 ++++++++++++++++++ util/test/demos/d3d12/d3d12_test.cpp | 7 + util/test/demos/d3d12/d3d12_test.h | 2 + util/test/demos/demos.vcxproj | 4 + util/test/demos/demos.vcxproj.filters | 12 + util/test/demos/gl/gl_discard_zoo.cpp | 454 ++++++++++++++ util/test/demos/vk/vk_discard_zoo.cpp | 520 ++++++++++++++++ util/test/demos/vk/vk_test.cpp | 1 + util/test/demos/vk/vk_test.h | 1 + util/test/rdtest/__init__.py | 1 + util/test/rdtest/shared/Discard_Zoo.py | 210 +++++++ util/test/tests/D3D11/D3D11_Discard_Zoo.py | 55 ++ util/test/tests/D3D12/D3D12_Discard_Zoo.py | 42 ++ util/test/tests/GL/GL_Discard_Zoo.py | 55 ++ util/test/tests/Vulkan/VK_Discard_Zoo.py | 96 +++ 65 files changed, 5855 insertions(+), 96 deletions(-) create mode 100644 renderdoc/data/glsl/discard.frag create mode 100644 util/test/demos/d3d11/d3d11_discard_zoo.cpp create mode 100644 util/test/demos/d3d12/d3d12_discard_zoo.cpp create mode 100644 util/test/demos/gl/gl_discard_zoo.cpp create mode 100644 util/test/demos/vk/vk_discard_zoo.cpp create mode 100644 util/test/rdtest/shared/Discard_Zoo.py create mode 100644 util/test/tests/D3D11/D3D11_Discard_Zoo.py create mode 100644 util/test/tests/D3D12/D3D12_Discard_Zoo.py create mode 100644 util/test/tests/GL/GL_Discard_Zoo.py create mode 100644 util/test/tests/Vulkan/VK_Discard_Zoo.py diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index 61bc4db98..4fdd959b4 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -404,6 +404,7 @@ set(data data/glsl/trisize.geom data/glsl/deptharr2ms.frag data/glsl/depthms2arr.frag + data/glsl/discard.frag data/sourcecodepro.ttf driver/vulkan/renderdoc.json) diff --git a/renderdoc/api/replay/rdcstr.h b/renderdoc/api/replay/rdcstr.h index c56606929..ba7950ccd 100644 --- a/renderdoc/api/replay/rdcstr.h +++ b/renderdoc/api/replay/rdcstr.h @@ -48,6 +48,8 @@ class rdcliteral public: const char *c_str() const { return str; } size_t length() const { return len; } + const char *begin() const { return str; } + const char *end() const { return str + len; } }; inline rdcliteral operator"" _lit(const char *str, size_t len) diff --git a/renderdoc/data/embedded_files.h b/renderdoc/data/embedded_files.h index 560b29026..5e8cae32e 100644 --- a/renderdoc/data/embedded_files.h +++ b/renderdoc/data/embedded_files.h @@ -68,5 +68,6 @@ DECLARE_EMBED(glsl_pixelhistory_mscopy_depth_comp); DECLARE_EMBED(glsl_pixelhistory_primid_frag); DECLARE_EMBED(glsl_shaderdebug_sample_vert); DECLARE_EMBED(glsl_texremap_frag); +DECLARE_EMBED(glsl_discard_frag); #undef DECLARE_EMBED diff --git a/renderdoc/data/glsl/blit.vert b/renderdoc/data/glsl/blit.vert index 41a2b386f..eebac5e04 100644 --- a/renderdoc/data/glsl/blit.vert +++ b/renderdoc/data/glsl/blit.vert @@ -22,8 +22,16 @@ * THE SOFTWARE. ******************************************************************************/ +#if defined(OPENGL_ES) && defined(NUM_VIEWS) +#extension GL_OVR_multiview2 : require +#endif + #include "glsl_globals.h" +#if defined(OPENGL_ES) && defined(NUM_VIEWS) +layout(num_views = NUM_VIEWS) in; +#endif + IO_LOCATION(0) out vec2 uv; void main(void) diff --git a/renderdoc/data/glsl/discard.frag b/renderdoc/data/glsl/discard.frag new file mode 100644 index 000000000..66268556f --- /dev/null +++ b/renderdoc/data/glsl/discard.frag @@ -0,0 +1,108 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#define PATTERN_WIDTH 64 +#define PATTERN_HEIGHT 8 + +#ifdef VULKAN + +#define FRAG_OUT(loc) layout(location = loc) + +layout(set = 0, binding = 0, std140) uniform DiscardUBOData +{ + vec4 pattern[(PATTERN_WIDTH * PATTERN_HEIGHT) / 4]; +} +Pattern; + +layout(push_constant) uniform PushData +{ + uint flags; +}; + +#else // !VULKAN + +// for GLES compatibility where we must match blit.vert +in vec2 uv; + +uniform DiscardUBOData +{ + vec4 pattern[(PATTERN_WIDTH * PATTERN_HEIGHT) / 4]; +} +Pattern; + +uniform uint flags; + +#if defined(OPENGL_ES) + +// GLES requires shader specification of locations +#define FRAG_OUT(loc) layout(location = loc) + +precision highp float; +precision highp int; + +#else + +// desktop GLSL requires a newer version or extensions for shader locations, but we can call +// glBindFragDataLocation +#define FRAG_OUT(loc) + +#endif + +#endif + +FRAG_OUT(0) out vec4 col0; +FRAG_OUT(1) out vec4 col1; +FRAG_OUT(2) out vec4 col2; +FRAG_OUT(3) out vec4 col3; +FRAG_OUT(4) out vec4 col4; +FRAG_OUT(5) out vec4 col5; +FRAG_OUT(6) out vec4 col6; +FRAG_OUT(7) out vec4 col7; + +void main() +{ + int x = int(gl_FragCoord.x) % PATTERN_WIDTH; + int y = int(gl_FragCoord.y) % PATTERN_HEIGHT; + + // invert for 1D textures so that we read the top row first which has data on it + if((flags & 0x10u) != 0u) + y = PATTERN_HEIGHT - 1 - y; + + int idx = ((y * 64) + x); + + float val = Pattern.pattern[idx / 4][idx % 4]; + + uint stencilPass = (flags & 0xfu); + + if(stencilPass == 1u && val >= 0.5f) + discard; + else if(stencilPass == 2u && val < 0.5f) + discard; + + gl_FragDepth = clamp(val, 0.0, 1.0); + + vec4 vecval = vec4(val, val, val, val); + + col0 = col1 = col2 = col3 = col4 = col5 = col6 = col7 = vecval; +} diff --git a/renderdoc/data/hlsl/misc.hlsl b/renderdoc/data/hlsl/misc.hlsl index 668954ded..b1d14e835 100644 --- a/renderdoc/data/hlsl/misc.hlsl +++ b/renderdoc/data/hlsl/misc.hlsl @@ -81,3 +81,39 @@ float4 RENDERDOC_CheckerboardPS(float4 pos : SV_Position) : SV_Target0 // otherwise return checker pattern return checkerVariant ? PrimaryColor : SecondaryColor; } + +#define PATTERN_WIDTH 64 +#define PATTERN_HEIGHT 8 + +cbuffer discarddata : register(b0) +{ + float4 pattern[(PATTERN_WIDTH * PATTERN_HEIGHT) / 4]; +}; + +cbuffer discardopts : register(b1) +{ + uint discardPass; +}; + +MultipleOutput RENDERDOC_DiscardPS(float4 pos : SV_Position, out float depth : SV_Depth) +{ + uint x = uint(pos.x) % PATTERN_WIDTH; + uint y = uint(pos.y) % PATTERN_HEIGHT; + + uint idx = ((y * 64) + x); + + float val = pattern[idx / 4][idx % 4]; + + if(discardPass == 1 && val >= 0.5f) + clip(-1); + else if(discardPass == 2 && val < 0.5f) + clip(-1); + + depth = saturate(val); + + MultipleOutput OUT = (MultipleOutput)0; + + OUT.col0 = OUT.col1 = OUT.col2 = OUT.col3 = OUT.col4 = OUT.col5 = OUT.col6 = OUT.col7 = val.xxxx; + + return OUT; +} diff --git a/renderdoc/data/renderdoc.rc b/renderdoc/data/renderdoc.rc index b11741560..0d8faa1b2 100644 --- a/renderdoc/data/renderdoc.rc +++ b/renderdoc/data/renderdoc.rc @@ -173,6 +173,7 @@ RESOURCE_glsl_gltext_frag TYPE_EMBED "glsl/gltext.frag" RESOURCE_glsl_glsl_globals_h TYPE_EMBED "glsl/glsl_globals.h" RESOURCE_glsl_texremap_frag TYPE_EMBED "glsl/texremap.frag" RESOURCE_glsl_shaderdebug_sample_vert TYPE_EMBED "glsl/shaderdebug_sample.vert" +RESOURCE_glsl_discard_frag TYPE_EMBED "glsl/discard.frag" #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// diff --git a/renderdoc/data/resource.h b/renderdoc/data/resource.h index 0e9dc2079..906e13840 100644 --- a/renderdoc/data/resource.h +++ b/renderdoc/data/resource.h @@ -59,6 +59,7 @@ #define RESOURCE_glsl_pixelhistory_mscopy_depth_comp 443 #define RESOURCE_glsl_pixelhistory_primid_frag 444 #define RESOURCE_glsl_shaderdebug_sample_vert 445 +#define RESOURCE_glsl_discard_frag 446 // Next default values for new objects // diff --git a/renderdoc/driver/d3d11/d3d11_common.cpp b/renderdoc/driver/d3d11/d3d11_common.cpp index c6aa95d76..4fb2740e5 100644 --- a/renderdoc/driver/d3d11/d3d11_common.cpp +++ b/renderdoc/driver/d3d11/d3d11_common.cpp @@ -102,7 +102,7 @@ ResourceRange::ResourceRange(ID3D11ShaderResourceView *srv) ID3D11Resource *res = NULL; srv->GetResource(&res); res->Release(); - resource = (IUnknown *)res; + resource = res; UINT numMips = allMip, numSlices = allSlice; @@ -162,6 +162,8 @@ ResourceRange::ResourceRange(ID3D11ShaderResourceView *srv) case D3D11_SRV_DIMENSION_TEXTURE1D: minMip = srvd.Texture1D.MostDetailedMip; numMips = srvd.Texture1D.MipLevels; + minSlice = 0; + numSlices = 1; break; case D3D11_SRV_DIMENSION_TEXTURE1DARRAY: minMip = srvd.Texture1DArray.MostDetailedMip; @@ -172,6 +174,8 @@ ResourceRange::ResourceRange(ID3D11ShaderResourceView *srv) case D3D11_SRV_DIMENSION_TEXTURE2D: minMip = srvd.Texture2D.MostDetailedMip; numMips = srvd.Texture2D.MipLevels; + minSlice = 0; + numSlices = 1; break; case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: minMip = srvd.Texture2DArray.MostDetailedMip; @@ -179,18 +183,29 @@ ResourceRange::ResourceRange(ID3D11ShaderResourceView *srv) minSlice = srvd.Texture2DArray.FirstArraySlice; numSlices = srvd.Texture2DArray.ArraySize; break; - case D3D11_SRV_DIMENSION_TEXTURE2DMS: break; + case D3D11_SRV_DIMENSION_TEXTURE2DMS: + minMip = 0; + numMips = 1; + minSlice = 0; + numSlices = 1; + break; case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY: + minMip = 0; + numMips = 1; minSlice = srvd.Texture2DMSArray.FirstArraySlice; numSlices = srvd.Texture2DMSArray.ArraySize; break; case D3D11_SRV_DIMENSION_TEXTURE3D: minMip = srvd.Texture3D.MostDetailedMip; numMips = srvd.Texture3D.MipLevels; + minSlice = 0; + numSlices = 1; break; case D3D11_SRV_DIMENSION_TEXTURECUBE: minMip = srvd.TextureCube.MostDetailedMip; numMips = srvd.TextureCube.MipLevels; + minSlice = 0; + numSlices = 1; break; case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY: minMip = srvd.TextureCubeArray.MostDetailedMip; @@ -200,7 +215,10 @@ ResourceRange::ResourceRange(ID3D11ShaderResourceView *srv) break; case D3D11_SRV_DIMENSION_UNKNOWN: case D3D11_SRV_DIMENSION_BUFFER: - case D3D11_SRV_DIMENSION_BUFFEREX: break; + case D3D11_SRV_DIMENSION_BUFFEREX: + minMip = minSlice = 0; + numMips = numSlices = 1; + break; } SetMaxes(numMips, numSlices); @@ -228,7 +246,7 @@ ResourceRange::ResourceRange(ID3D11UnorderedAccessView *uav) ID3D11Resource *res = NULL; uav->GetResource(&res); res->Release(); - resource = (IUnknown *)res; + resource = res; UINT numMips = allMip, numSlices = allSlice; @@ -240,6 +258,8 @@ ResourceRange::ResourceRange(ID3D11UnorderedAccessView *uav) case D3D11_UAV_DIMENSION_TEXTURE1D: minMip = desc.Texture1D.MipSlice; numMips = 1; + minSlice = 0; + numSlices = 1; break; case D3D11_UAV_DIMENSION_TEXTURE1DARRAY: minMip = desc.Texture1DArray.MipSlice; @@ -250,6 +270,8 @@ ResourceRange::ResourceRange(ID3D11UnorderedAccessView *uav) case D3D11_UAV_DIMENSION_TEXTURE2D: minMip = desc.Texture2D.MipSlice; numMips = 1; + minSlice = 0; + numSlices = 1; break; case D3D11_UAV_DIMENSION_TEXTURE2DARRAY: minMip = desc.Texture2DArray.MipSlice; @@ -264,7 +286,10 @@ ResourceRange::ResourceRange(ID3D11UnorderedAccessView *uav) numSlices = desc.Texture3D.WSize; break; case D3D11_UAV_DIMENSION_UNKNOWN: - case D3D11_UAV_DIMENSION_BUFFER: break; + case D3D11_UAV_DIMENSION_BUFFER: + minMip = minSlice = 0; + numMips = numSlices = 1; + break; } SetMaxes(numMips, numSlices); @@ -292,7 +317,7 @@ ResourceRange::ResourceRange(ID3D11RenderTargetView *rtv) ID3D11Resource *res = NULL; rtv->GetResource(&res); res->Release(); - resource = (IUnknown *)res; + resource = res; UINT numMips = allMip, numSlices = allSlice; @@ -304,6 +329,8 @@ ResourceRange::ResourceRange(ID3D11RenderTargetView *rtv) case D3D11_RTV_DIMENSION_TEXTURE1D: minMip = desc.Texture1D.MipSlice; numMips = 1; + minSlice = 0; + numSlices = 1; break; case D3D11_RTV_DIMENSION_TEXTURE1DARRAY: minMip = desc.Texture1DArray.MipSlice; @@ -314,6 +341,8 @@ ResourceRange::ResourceRange(ID3D11RenderTargetView *rtv) case D3D11_RTV_DIMENSION_TEXTURE2D: minMip = desc.Texture2D.MipSlice; numMips = 1; + minSlice = 0; + numSlices = 1; break; case D3D11_RTV_DIMENSION_TEXTURE2DARRAY: minMip = desc.Texture2DArray.MipSlice; @@ -321,8 +350,15 @@ ResourceRange::ResourceRange(ID3D11RenderTargetView *rtv) minSlice = desc.Texture2DArray.FirstArraySlice; numSlices = desc.Texture2DArray.ArraySize; break; - case D3D11_RTV_DIMENSION_TEXTURE2DMS: break; + case D3D11_RTV_DIMENSION_TEXTURE2DMS: + minMip = 0; + numMips = 1; + minSlice = 0; + numSlices = 1; + break; case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY: + minMip = 0; + numMips = 1; minSlice = desc.Texture2DMSArray.FirstArraySlice; numSlices = desc.Texture2DMSArray.ArraySize; break; @@ -333,7 +369,10 @@ ResourceRange::ResourceRange(ID3D11RenderTargetView *rtv) numSlices = desc.Texture3D.WSize; break; case D3D11_RTV_DIMENSION_UNKNOWN: - case D3D11_RTV_DIMENSION_BUFFER: break; + case D3D11_RTV_DIMENSION_BUFFER: + minMip = minSlice = 0; + numMips = numSlices = 1; + break; } SetMaxes(numMips, numSlices); @@ -361,7 +400,7 @@ ResourceRange::ResourceRange(ID3D11DepthStencilView *dsv) ID3D11Resource *res = NULL; dsv->GetResource(&res); res->Release(); - resource = (IUnknown *)res; + resource = res; UINT numMips = allMip, numSlices = allSlice; @@ -378,6 +417,8 @@ ResourceRange::ResourceRange(ID3D11DepthStencilView *dsv) case D3D11_DSV_DIMENSION_TEXTURE1D: minMip = desc.Texture1D.MipSlice; numMips = 1; + minSlice = 0; + numSlices = 1; break; case D3D11_DSV_DIMENSION_TEXTURE1DARRAY: minMip = desc.Texture1DArray.MipSlice; @@ -388,6 +429,8 @@ ResourceRange::ResourceRange(ID3D11DepthStencilView *dsv) case D3D11_DSV_DIMENSION_TEXTURE2D: minMip = desc.Texture2D.MipSlice; numMips = 1; + minSlice = 0; + numSlices = 1; break; case D3D11_DSV_DIMENSION_TEXTURE2DARRAY: minMip = desc.Texture2DArray.MipSlice; @@ -395,12 +438,22 @@ ResourceRange::ResourceRange(ID3D11DepthStencilView *dsv) minSlice = desc.Texture2DArray.FirstArraySlice; numSlices = desc.Texture2DArray.ArraySize; break; - case D3D11_DSV_DIMENSION_TEXTURE2DMS: break; + case D3D11_DSV_DIMENSION_TEXTURE2DMS: + minMip = 0; + numMips = 1; + minSlice = 0; + numSlices = 1; + break; case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY: + minMip = 0; + numMips = 1; minSlice = desc.Texture2DMSArray.FirstArraySlice; numSlices = desc.Texture2DMSArray.ArraySize; break; - case D3D11_DSV_DIMENSION_UNKNOWN: break; + case D3D11_DSV_DIMENSION_UNKNOWN: + minMip = minSlice = 0; + numMips = numSlices = 1; + break; } SetMaxes(numMips, numSlices); @@ -414,8 +467,8 @@ ResourceRange::ResourceRange(ID3D11Buffer *res) resource = res; minMip = minSlice = 0; - maxMip = allMip; - maxSlice = allSlice; + maxMip = 0; + maxSlice = 0; fullRange = true; depthReadOnly = false; stencilReadOnly = false; diff --git a/renderdoc/driver/d3d11/d3d11_common.h b/renderdoc/driver/d3d11/d3d11_common.h index 2fd9aedf5..f83bb0bc0 100644 --- a/renderdoc/driver/d3d11/d3d11_common.h +++ b/renderdoc/driver/d3d11/d3d11_common.h @@ -112,6 +112,11 @@ public: bool IsDepthReadOnly() const { return depthReadOnly; } bool IsStencilReadOnly() const { return stencilReadOnly; } bool IsNull() const { return resource == NULL; } + ID3D11Resource *GetResource() const { return resource; } + UINT GetMinMip() const { return minMip; } + UINT GetMaxMip() const { return maxMip; } + UINT GetMinSlice() const { return minSlice; } + UINT GetMaxSlice() const { return maxSlice; } private: ResourceRange(); @@ -136,7 +141,7 @@ private: static const UINT allMip = 0xf; static const UINT allSlice = 0x7ff; - IUnknown *resource; + ID3D11Resource *resource; UINT minMip : 4; UINT minSlice : 12; UINT maxMip : 4; diff --git a/renderdoc/driver/d3d11/d3d11_context1_wrap.cpp b/renderdoc/driver/d3d11/d3d11_context1_wrap.cpp index 1619a6b35..eb08b3db5 100644 --- a/renderdoc/driver/d3d11/d3d11_context1_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_context1_wrap.cpp @@ -25,6 +25,7 @@ #include "d3d11_context.h" #include "strings/string_utils.h" +#include "d3d11_debug.h" #include "d3d11_renderstate.h" #include "d3d11_resources.h" #include "d3d11_video.h" @@ -1783,14 +1784,16 @@ bool WrappedID3D11DeviceContext::Serialise_DiscardResource(SerialiserType &ser, if(IsReplayingAndReading()) { - if(pResource) + if(m_pDevice->GetReplayOptions().optimisation != ReplayOptimisationLevel::Fastest) { - // don't replay the discard, as it effectively does nothing meaningful but hint - // to the driver that the contents can be discarded. - // Instead we should overwrite the contents with something (during capture too) - // to indicate that the discard has happened visually like a clear. - // This also means we don't have to require/diverge if a 11.1 context is not - // available on replay. + UINT numSubs = GetSubresourceCount(pResource); + for(UINT sub = 0; sub < numSubs; sub++) + m_pDevice->GetDebugManager()->FillWithDiscardPattern(DiscardType::DiscardCall, pResource, + sub, NULL, 0); + } + else if(m_pRealContext1) + { + m_pRealContext1->DiscardResource(GetResourceManager()->UnwrapResource(pResource)); } if(IsLoading(m_State)) @@ -1802,7 +1805,7 @@ bool WrappedID3D11DeviceContext::Serialise_DiscardResource(SerialiserType &ser, DrawcallDescription draw; - draw.name = "DiscardResource()"; + draw.name = StringFormat::Fmt("DiscardResource(%s)", ToStr(dstOrigID).c_str()); draw.flags |= DrawFlags::Clear; draw.copyDestination = dstOrigID; draw.copyDestinationSubresource = Subresource(); @@ -1878,14 +1881,27 @@ bool WrappedID3D11DeviceContext::Serialise_DiscardView(SerialiserType &ser, ID3D if(IsReplayingAndReading()) { - if(pResourceView) + if(m_pDevice->GetReplayOptions().optimisation != ReplayOptimisationLevel::Fastest) { - // don't replay the discard, as it effectively does nothing meaningful but hint - // to the driver that the contents can be discarded. - // Instead we should overwrite the contents with something (during capture too) - // to indicate that the discard has happened visually like a clear. - // This also means we don't have to require/diverge if a 11.1 context is not - // available on replay. + m_pDevice->GetDebugManager()->FillWithDiscardPattern(DiscardType::DiscardCall, pResourceView, + NULL, 0); + } + else if(m_pRealContext1) + { + ID3D11View *real = NULL; + + if(WrappedID3D11RenderTargetView1::IsAlloc(pResourceView)) + real = UNWRAP(WrappedID3D11RenderTargetView1, pResourceView); + else if(WrappedID3D11DepthStencilView::IsAlloc(pResourceView)) + real = UNWRAP(WrappedID3D11DepthStencilView, pResourceView); + else if(WrappedID3D11ShaderResourceView1::IsAlloc(pResourceView)) + real = UNWRAP(WrappedID3D11ShaderResourceView1, pResourceView); + else if(WrappedID3D11UnorderedAccessView1::IsAlloc(pResourceView)) + real = UNWRAP(WrappedID3D11UnorderedAccessView1, pResourceView); + + RDCASSERT(real); + + m_pRealContext1->DiscardView(real); } if(IsLoading(m_State)) @@ -1894,8 +1910,6 @@ bool WrappedID3D11DeviceContext::Serialise_DiscardView(SerialiserType &ser, ID3D DrawcallDescription draw; - draw.name = "DiscardView()"; - draw.flags |= DrawFlags::Clear; draw.copyDestinationSubresource = Subresource(); if(pResourceView) @@ -1951,6 +1965,8 @@ bool WrappedID3D11DeviceContext::Serialise_DiscardView(SerialiserType &ser, ID3D } } + draw.name = StringFormat::Fmt("DiscardView(%s)", ToStr(draw.copyDestination).c_str()); + AddDrawcall(draw, true); } } @@ -2030,6 +2046,7 @@ void WrappedID3D11DeviceContext::DiscardView(ID3D11View *pResourceView) MarkDirtyResource(GetIDForResource(viewRes)); MarkResourceReferenced(GetIDForResource(viewRes), eFrameRef_PartialWrite); + MarkResourceReferenced(GetIDForResource(pResourceView), eFrameRef_Read); SAFE_RELEASE(viewRes); @@ -2064,12 +2081,28 @@ bool WrappedID3D11DeviceContext::Serialise_DiscardView1(SerialiserType &ser, { if(pResourceView) { - // don't replay the discard, as it effectively does nothing meaningful but hint - // to the driver that the contents can be discarded. - // Instead we should overwrite the contents with something (during capture too) - // to indicate that the discard has happened visually like a clear. - // This also means we don't have to require/diverge if a 11.1 context is not - // available on replay. + if(m_pDevice->GetReplayOptions().optimisation != ReplayOptimisationLevel::Fastest) + { + m_pDevice->GetDebugManager()->FillWithDiscardPattern(DiscardType::DiscardCall, + pResourceView, pRect, NumRects); + } + else if(m_pRealContext1) + { + ID3D11View *real = NULL; + + if(WrappedID3D11RenderTargetView1::IsAlloc(pResourceView)) + real = UNWRAP(WrappedID3D11RenderTargetView1, pResourceView); + else if(WrappedID3D11DepthStencilView::IsAlloc(pResourceView)) + real = UNWRAP(WrappedID3D11DepthStencilView, pResourceView); + else if(WrappedID3D11ShaderResourceView1::IsAlloc(pResourceView)) + real = UNWRAP(WrappedID3D11ShaderResourceView1, pResourceView); + else if(WrappedID3D11UnorderedAccessView1::IsAlloc(pResourceView)) + real = UNWRAP(WrappedID3D11UnorderedAccessView1, pResourceView); + + RDCASSERT(real); + + m_pRealContext1->DiscardView1(real, pRect, NumRects); + } } if(IsLoading(m_State)) @@ -2078,7 +2111,6 @@ bool WrappedID3D11DeviceContext::Serialise_DiscardView1(SerialiserType &ser, DrawcallDescription draw; - draw.name = StringFormat::Fmt("DiscardView1(%u)", NumRects); draw.flags |= DrawFlags::Clear; if(pResourceView) @@ -2134,6 +2166,8 @@ bool WrappedID3D11DeviceContext::Serialise_DiscardView1(SerialiserType &ser, } } + draw.name = StringFormat::Fmt("DiscardView1(%s)", ToStr(draw.copyDestination).c_str()); + AddDrawcall(draw, true); } } @@ -2214,6 +2248,7 @@ void WrappedID3D11DeviceContext::DiscardView1(ID3D11View *pResourceView, const D MarkDirtyResource(GetIDForResource(viewRes)); MarkResourceReferenced(GetIDForResource(viewRes), eFrameRef_PartialWrite); + MarkResourceReferenced(GetIDForResource(pResourceView), eFrameRef_Read); SAFE_RELEASE(viewRes); diff --git a/renderdoc/driver/d3d11/d3d11_debug.cpp b/renderdoc/driver/d3d11/d3d11_debug.cpp index 762db6058..72f679da7 100644 --- a/renderdoc/driver/d3d11/d3d11_debug.cpp +++ b/renderdoc/driver/d3d11/d3d11_debug.cpp @@ -273,6 +273,52 @@ void D3D11DebugManager::InitReplayResources() SAFE_RELEASE(dummyTex); } + + { + rdcstr hlsl = GetEmbeddedResource(misc_hlsl); + + m_DiscardVS = shaderCache->MakeVShader(hlsl.c_str(), "RENDERDOC_FullscreenVS", "vs_4_0"); + m_DiscardPS = shaderCache->MakePShader(hlsl.c_str(), "RENDERDOC_DiscardPS", "ps_4_0"); + + ResourceFormat fmt; + fmt.type = ResourceFormatType::Regular; + fmt.compType = CompType::Float; + fmt.compByteWidth = 4; + fmt.compCount = 1; + m_DiscardBytes = GetDiscardPattern(DiscardType::DiscardCall, fmt); + + D3D11_DEPTH_STENCIL_DESC desc; + + desc.BackFace.StencilFailOp = desc.BackFace.StencilPassOp = desc.BackFace.StencilDepthFailOp = + D3D11_STENCIL_OP_REPLACE; + desc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + desc.FrontFace.StencilFailOp = desc.FrontFace.StencilPassOp = + desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_REPLACE; + desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + desc.DepthEnable = TRUE; + desc.DepthFunc = D3D11_COMPARISON_ALWAYS; + desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + desc.StencilReadMask = desc.StencilWriteMask = 0xff; + desc.StencilEnable = TRUE; + + hr = m_pDevice->CreateDepthStencilState(&desc, &m_DiscardDepthState); + + if(FAILED(hr)) + RDCERR("Failed to create m_DiscardDepthState HRESULT: %s", ToStr(hr).c_str()); + + D3D11_RASTERIZER_DESC rastDesc; + RDCEraseEl(rastDesc); + + rastDesc.CullMode = D3D11_CULL_NONE; + rastDesc.FillMode = D3D11_FILL_SOLID; + rastDesc.DepthClipEnable = FALSE; + rastDesc.ScissorEnable = TRUE; + + hr = m_pDevice->CreateRasterizerState(&rastDesc, &m_DiscardRasterState); + + if(FAILED(hr)) + RDCERR("Failed to create m_DiscardRasterState HRESULT: %s", ToStr(hr).c_str()); + } } void D3D11DebugManager::ShutdownResources() @@ -301,6 +347,15 @@ void D3D11DebugManager::ShutdownResources() SAFE_RELEASE(MSArrayCopyVS); m_pDevice->InternalRelease(); + for(auto it = m_DiscardPatterns.begin(); it != m_DiscardPatterns.end(); it++) + if(it->second) + it->second->Release(); + + SAFE_RELEASE(m_DiscardVS); + SAFE_RELEASE(m_DiscardPS); + SAFE_RELEASE(m_DiscardDepthState); + SAFE_RELEASE(m_DiscardRasterState); + for(int i = 0; i < ARRAY_COUNT(PublicCBuffers); i++) { SAFE_RELEASE(PublicCBuffers[i]); @@ -308,6 +363,467 @@ void D3D11DebugManager::ShutdownResources() } } +void D3D11DebugManager::FillWithDiscardPattern(DiscardType type, ID3D11Resource *res, UINT slice, + UINT mip, const D3D11_RECT *pRect, UINT NumRects) +{ + D3D11MarkerRegion region(StringFormat::Fmt("FillWithDiscardPattern %s slice %u mip %u", + ToStr(GetIDForResource(res)).c_str(), slice, mip)); + + D3D11_RECT all = {0, 0, 65536, 65536}; + if(NumRects == 0) + { + NumRects = 1; + pRect = &all; + } + + if(WrappedID3D11Buffer::IsAlloc(res)) + { + D3D11MarkerRegion::Set("Buffer"); + + WrappedID3D11Buffer *buf = (WrappedID3D11Buffer *)res; + + D3D11_BUFFER_DESC desc = {}; + buf->GetDesc(&desc); + + uint32_t value = 0xD15CAD3D; + + for(UINT r = 0; r < NumRects; r++) + { + UINT size = RDCMIN(UINT(pRect[r].right - pRect[r].left), desc.ByteWidth); + + if(desc.Usage == D3D11_USAGE_DYNAMIC || desc.Usage == D3D11_USAGE_STAGING) + { + // dynamic buffers can always be mapped + // staging buffers can be read-only, at which point we can't write to them + if(desc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE) + { + D3D11_MAPPED_SUBRESOURCE mapped = {}; + m_pImmediateContext->Map(res, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + + byte *dst = (byte *)mapped.pData; + dst += pRect[r].left; + for(size_t i = 0; i < size; i++) + { + memcpy(dst, &value, RDCMIN(sizeof(uint32_t), size - i)); + dst += sizeof(uint32_t); + } + + m_pImmediateContext->Unmap(res, 0); + } + } + else if(desc.Usage == D3D11_USAGE_DEFAULT) + { + bytebuf pattern; + pattern.resize(AlignUp4(size)); + + for(size_t i = 0; i < pattern.size(); i += 4) + memcpy(&pattern[i], &value, sizeof(uint32_t)); + + // default buffers can be updated + D3D11_BOX box = {}; + box.bottom = box.back = 1; + box.left = pRect[r].left; + box.right = box.left + size; + m_pImmediateContext->UpdateSubresource(res, 0, &box, pattern.data(), size, size); + } + // IMMUTABLE is the other option, which we can't do anything with + } + } + else + { + DiscardPatternKey key = {}; + UINT numMips = 1; + UINT width = 1, height = 1, depth = 1; + + // for textures we create a template texture with the same format and properties then do + // repeated copies + if(WrappedID3D11Texture1D::IsAlloc(res)) + { + WrappedID3D11Texture1D *tex = (WrappedID3D11Texture1D *)res; + + D3D11_TEXTURE1D_DESC desc = {}; + tex->GetDesc(&desc); + + key.dim = 1; + key.fmt = desc.Format; + numMips = desc.MipLevels; + width = desc.Width; + } + else if(WrappedID3D11Texture2D1::IsAlloc(res)) + { + WrappedID3D11Texture2D1 *tex = (WrappedID3D11Texture2D1 *)res; + + D3D11_TEXTURE2D_DESC desc = {}; + tex->GetDesc(&desc); + + key.dim = 2; + key.fmt = desc.Format; + key.samp = desc.SampleDesc; + numMips = desc.MipLevels; + width = desc.Width; + height = desc.Height; + } + else if(WrappedID3D11Texture3D1::IsAlloc(res)) + { + WrappedID3D11Texture3D1 *tex = (WrappedID3D11Texture3D1 *)res; + + D3D11_TEXTURE3D_DESC desc = {}; + tex->GetDesc(&desc); + + key.dim = 3; + key.fmt = desc.Format; + numMips = desc.MipLevels; + width = desc.Width; + height = desc.Height; + depth = desc.Depth; + } + + UINT subresource = slice * numMips + mip; + if(key.dim == 3) + subresource = mip; + + // depth-stencil resources can't be sub-copied, so we need to render to them + if(IsDepthFormat(key.fmt) || key.samp.Count > 1) + { + D3D11MarkerRegion::Set("Depth texture"); + + D3D11RenderStateTracker tracker(m_pImmediateContext); + + m_pImmediateContext->ClearState(); + + m_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + m_pImmediateContext->OMSetDepthStencilState(m_DiscardDepthState, 0); + m_pImmediateContext->VSSetShader(m_DiscardVS, NULL, 0); + m_pImmediateContext->PSSetShader(m_DiscardPS, NULL, 0); + m_pImmediateContext->RSSetState(m_DiscardRasterState); + + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + rtvDesc.Format = GetFloatTypedFormat(key.fmt); + + D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; + dsvDesc.Flags = 0; + dsvDesc.Format = GetDepthTypedFormat(key.fmt); + + if(key.samp.Count > 1) + { + dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; + dsvDesc.Texture2DMSArray.ArraySize = 1; + dsvDesc.Texture2DMSArray.FirstArraySlice = slice; + + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + rtvDesc.Texture2DMSArray.ArraySize = 1; + rtvDesc.Texture2DMSArray.FirstArraySlice = slice; + } + else if(key.dim == 1) + { + dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE1DARRAY; + dsvDesc.Texture1DArray.ArraySize = 1; + dsvDesc.Texture1DArray.FirstArraySlice = slice; + dsvDesc.Texture1DArray.MipSlice = mip; + } + else if(key.dim == 2) + { + dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + dsvDesc.Texture2DArray.ArraySize = 1; + dsvDesc.Texture2DArray.FirstArraySlice = slice; + dsvDesc.Texture2DArray.MipSlice = mip; + } + + ID3D11RenderTargetView *rtv = NULL; + ID3D11DepthStencilView *dsv = NULL; + if(IsDepthFormat(key.fmt)) + m_pDevice->CreateDepthStencilView(res, &dsvDesc, &dsv); + else + m_pDevice->CreateRenderTargetView(res, &rtvDesc, &rtv); + + m_pImmediateContext->OMSetRenderTargets(1, &rtv, dsv); + + ID3D11Buffer *cbuf = MakeCBuffer(m_DiscardBytes.data(), m_DiscardBytes.size()); + m_pImmediateContext->PSSetConstantBuffers(0, 1, &cbuf); + + D3D11_VIEWPORT viewport = {0, 0, (float)width, (float)height, 0.0f, 1.0f}; + m_pImmediateContext->RSSetViewports(1, &viewport); + + for(UINT r = 0; r < NumRects; r++) + { + m_pImmediateContext->RSSetScissorRects(1, pRect + r); + + if(dsv) + { + uint32_t pass = 1; + cbuf = MakeCBuffer(&pass, sizeof(pass)); + m_pImmediateContext->PSSetConstantBuffers(1, 1, &cbuf); + + m_pImmediateContext->Draw(3, 0); + + m_pImmediateContext->OMSetDepthStencilState(m_DiscardDepthState, 0xff); + pass = 2; + cbuf = MakeCBuffer(&pass, sizeof(pass)); + m_pImmediateContext->PSSetConstantBuffers(1, 1, &cbuf); + + m_pImmediateContext->Draw(3, 0); + } + else + { + uint32_t pass = 0; + cbuf = MakeCBuffer(&pass, sizeof(pass)); + m_pImmediateContext->PSSetConstantBuffers(1, 1, &cbuf); + + m_pImmediateContext->Draw(3, 0); + } + } + + SAFE_RELEASE(rtv); + SAFE_RELEASE(dsv); + } + else + { + ID3D11Resource *patternRes = m_DiscardPatterns[key]; + + if(patternRes == NULL) + { + bytebuf pattern = GetDiscardPattern(type, MakeResourceFormat(key.fmt)); + + if(key.dim == 1) + { + D3D11_TEXTURE1D_DESC desc; + + desc.ArraySize = 1; + desc.Format = key.fmt; + desc.Width = DiscardPatternWidth; + desc.MipLevels = 1; + desc.Usage = D3D11_USAGE_IMMUTABLE; + desc.CPUAccessFlags = 0; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.MiscFlags = 0; + + D3D11_SUBRESOURCE_DATA data = {}; + data.pSysMem = pattern.data(); + data.SysMemSlicePitch = data.SysMemPitch = GetRowPitch(desc.Width, desc.Format, 0); + + ID3D11Texture1D *tex = NULL; + + HRESULT hr = m_pDevice->CreateTexture1D(&desc, &data, &tex); + if(FAILED(hr)) + { + RDCERR("Failed to create discard texture for %s HRESULT: %s", ToStr(key.fmt).c_str(), + ToStr(hr).c_str()); + return; + } + + m_DiscardPatterns[key] = patternRes = tex; + } + else if(key.dim == 2) + { + D3D11_TEXTURE2D_DESC desc; + + desc.ArraySize = 1; + desc.Format = key.fmt; + desc.Width = DiscardPatternWidth; + desc.Height = DiscardPatternHeight; + desc.MipLevels = 1; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_IMMUTABLE; + desc.CPUAccessFlags = 0; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.MiscFlags = 0; + + D3D11_SUBRESOURCE_DATA data = {}; + data.pSysMem = pattern.data(); + data.SysMemPitch = GetRowPitch(desc.Width, desc.Format, 0); + data.SysMemSlicePitch = data.SysMemPitch * desc.Height; + + ID3D11Texture2D *tex = NULL; + + HRESULT hr = m_pDevice->CreateTexture2D(&desc, &data, &tex); + if(FAILED(hr)) + { + RDCERR("Failed to create discard texture for %s HRESULT: %s", ToStr(key.fmt).c_str(), + ToStr(hr).c_str()); + return; + } + + m_DiscardPatterns[key] = patternRes = tex; + } + else + { + D3D11_TEXTURE3D_DESC desc; + + desc.Format = key.fmt; + desc.Width = DiscardPatternWidth; + desc.Height = DiscardPatternHeight; + desc.Depth = 1; + desc.MipLevels = 1; + desc.Usage = D3D11_USAGE_IMMUTABLE; + desc.CPUAccessFlags = 0; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.MiscFlags = 0; + + D3D11_SUBRESOURCE_DATA data = {}; + data.pSysMem = pattern.data(); + data.SysMemPitch = GetRowPitch(desc.Width, desc.Format, 0); + data.SysMemSlicePitch = data.SysMemPitch * desc.Height; + + ID3D11Texture3D *tex = NULL; + + HRESULT hr = m_pDevice->CreateTexture3D(&desc, &data, &tex); + if(FAILED(hr)) + { + RDCERR("Failed to create discard texture for %s HRESULT: %s", ToStr(key.fmt).c_str(), + ToStr(hr).c_str()); + return; + } + + m_DiscardPatterns[key] = patternRes = tex; + } + } + + if(!patternRes) + return; + + UINT z = 0; + if(key.dim == 3) + z = slice; + + for(UINT r = 0; r < NumRects; r++) + { + D3D11_RECT rect = pRect[r]; + + UINT rectWidth = RDCMIN((UINT)rect.right, RDCMAX(1U, width >> mip)); + UINT rectHeight = RDCMIN((UINT)rect.bottom, RDCMAX(1U, height >> mip)); + + for(UINT y = rect.top; y < rectHeight; y += DiscardPatternHeight) + { + for(UINT x = rect.left; x < rectWidth; x += DiscardPatternWidth) + { + D3D11_BOX box = { + 0, + 0, + 0, + RDCMIN(DiscardPatternWidth, uint32_t(rectWidth - x)), + RDCMIN(DiscardPatternHeight, uint32_t(rectHeight - y)), + 1, + }; + m_pImmediateContext->CopySubresourceRegion(res, subresource, x, y, z, patternRes, 0, + &box); + } + } + } + } + } +} + +void D3D11DebugManager::FillWithDiscardPattern(DiscardType type, ID3D11Resource *res, + UINT subresource, const D3D11_RECT *pRect, + UINT NumRects) +{ + if(WrappedID3D11Texture1D::IsAlloc(res)) + { + WrappedID3D11Texture1D *tex = (WrappedID3D11Texture1D *)res; + + D3D11_TEXTURE1D_DESC desc = {}; + tex->GetDesc(&desc); + + // subresource uniquely identifies a slice and mip + FillWithDiscardPattern(type, res, subresource / desc.MipLevels, subresource % desc.MipLevels, + pRect, NumRects); + } + else if(WrappedID3D11Texture2D1::IsAlloc(res)) + { + WrappedID3D11Texture2D1 *tex = (WrappedID3D11Texture2D1 *)res; + + D3D11_TEXTURE2D_DESC desc = {}; + tex->GetDesc(&desc); + + // subresource uniquely identifies a slice and mip + FillWithDiscardPattern(type, res, subresource / desc.MipLevels, subresource % desc.MipLevels, + pRect, NumRects); + } + else if(WrappedID3D11Texture3D1::IsAlloc(res)) + { + WrappedID3D11Texture3D1 *tex = (WrappedID3D11Texture3D1 *)res; + + D3D11_TEXTURE3D_DESC desc = {}; + tex->GetDesc(&desc); + + // fill all slices in this mip + for(UINT z = 0; z < RDCMAX(1U, desc.Depth >> subresource); z++) + FillWithDiscardPattern(type, res, z, subresource, pRect, NumRects); + } + else + { + // buffer + FillWithDiscardPattern(type, res, 0, 0, pRect, NumRects); + } +} + +void D3D11DebugManager::FillWithDiscardPattern(DiscardType type, ID3D11View *view, + const D3D11_RECT *pRect, UINT NumRects) +{ + D3D11MarkerRegion region( + StringFormat::Fmt("FillWithDiscardPattern view %s", ToStr(GetIDForResource(view)).c_str())); + + ResourceRange range = ResourceRange::Null; + + if(WrappedID3D11ShaderResourceView1::IsAlloc(view)) + { + range = ResourceRange((WrappedID3D11ShaderResourceView1 *)view); + } + else if(WrappedID3D11UnorderedAccessView1::IsAlloc(view)) + { + range = ResourceRange((WrappedID3D11UnorderedAccessView1 *)view); + } + else if(WrappedID3D11RenderTargetView1::IsAlloc(view)) + { + range = ResourceRange((WrappedID3D11RenderTargetView1 *)view); + } + else if(WrappedID3D11DepthStencilView::IsAlloc(view)) + { + range = ResourceRange((WrappedID3D11DepthStencilView *)view); + } + + ID3D11Resource *res = range.GetResource(); + UINT numMips = 1; + bool tex3D = false; + + // check for wrapped types first as they will be most common and don't + // require a virtual call + if(WrappedID3D11Texture1D::IsAlloc(res)) + { + D3D11_TEXTURE1D_DESC desc = {}; + ((WrappedID3D11Texture1D *)res)->GetDesc(&desc); + numMips = desc.MipLevels; + } + else if(WrappedID3D11Texture2D1::IsAlloc(res)) + { + D3D11_TEXTURE2D_DESC desc = {}; + ((WrappedID3D11Texture2D1 *)res)->GetDesc(&desc); + numMips = desc.MipLevels; + } + else if(WrappedID3D11Texture3D1::IsAlloc(res)) + { + D3D11_TEXTURE3D_DESC desc = {}; + ((WrappedID3D11Texture3D1 *)res)->GetDesc(&desc); + numMips = desc.MipLevels; + tex3D = true; + } + + rdcarray rects; + rects.assign(pRect, NumRects); + + // DiscardView1 on D3D11 only allows rects to be specified with DSVs and RTVs which should only + // target one mip. + if(NumRects > 0) + RDCASSERTMSG("Rects shouldn't be specified when we have multiple mips", + range.GetMinMip() == range.GetMaxMip(), range.GetMinMip(), range.GetMaxMip(), + NumRects); + + for(UINT slice = range.GetMinSlice(); slice <= range.GetMaxSlice(); slice++) + for(UINT mip = range.GetMinMip(); mip <= range.GetMaxMip(); mip++) + FillWithDiscardPattern(type, res, slice, mip, pRect, NumRects); +} + uint32_t D3D11DebugManager::GetStructCount(ID3D11UnorderedAccessView *uav) { m_pImmediateContext->CopyStructureCount(StageBuffer, 0, uav); diff --git a/renderdoc/driver/d3d11/d3d11_debug.h b/renderdoc/driver/d3d11/d3d11_debug.h index 03b59f6a8..c1f39acf7 100644 --- a/renderdoc/driver/d3d11/d3d11_debug.h +++ b/renderdoc/driver/d3d11/d3d11_debug.h @@ -127,6 +127,13 @@ public: } } + void FillWithDiscardPattern(DiscardType type, ID3D11Resource *res, UINT slice, UINT mip, + const D3D11_RECT *pRect, UINT NumRects); + void FillWithDiscardPattern(DiscardType type, ID3D11Resource *res, UINT subresource, + const D3D11_RECT *pRect, UINT NumRects); + void FillWithDiscardPattern(DiscardType type, ID3D11View *view, const D3D11_RECT *pRect, + UINT NumRects); + uint32_t GetStructCount(ID3D11UnorderedAccessView *uav); void GetBufferData(ID3D11Buffer *buff, uint64_t offset, uint64_t length, bytebuf &retData); @@ -206,4 +213,26 @@ private: // RenderForPredicate ID3D11DepthStencilView *PredicateDSV = NULL; + + struct DiscardPatternKey + { + uint32_t dim; + DXGI_FORMAT fmt; + DXGI_SAMPLE_DESC samp; + + bool operator<(const DiscardPatternKey &o) const + { + if(dim != o.dim) + return dim < o.dim; + if(fmt != o.fmt) + return fmt < o.fmt; + return samp.Count < o.samp.Count; + } + }; + std::map m_DiscardPatterns; + bytebuf m_DiscardBytes; + ID3D11VertexShader *m_DiscardVS = NULL; + ID3D11PixelShader *m_DiscardPS = NULL; + ID3D11DepthStencilState *m_DiscardDepthState = NULL; + ID3D11RasterizerState *m_DiscardRasterState = NULL; }; diff --git a/renderdoc/driver/d3d11/d3d11_resources.cpp b/renderdoc/driver/d3d11/d3d11_resources.cpp index 3d297d9f5..77f82cb70 100644 --- a/renderdoc/driver/d3d11/d3d11_resources.cpp +++ b/renderdoc/driver/d3d11/d3d11_resources.cpp @@ -81,6 +81,68 @@ void WrappedShader::ShaderEntry::BuildReflection() m_Details.resourceId = m_ID; } +UINT GetSubresourceCount(ID3D11Resource *res) +{ + D3D11_RESOURCE_DIMENSION dim; + + // check for wrapped types first as they will be most common and don't + // require a virtual call + if(WrappedID3D11Texture1D::IsAlloc(res)) + dim = D3D11_RESOURCE_DIMENSION_TEXTURE1D; + else if(WrappedID3D11Texture2D1::IsAlloc(res)) + dim = D3D11_RESOURCE_DIMENSION_TEXTURE2D; + else if(WrappedID3D11Texture3D1::IsAlloc(res)) + dim = D3D11_RESOURCE_DIMENSION_TEXTURE3D; + else + res->GetType(&dim); + + if(dim == D3D11_RESOURCE_DIMENSION_BUFFER) + return 1; + + ID3D11Texture1D *tex1 = (dim == D3D11_RESOURCE_DIMENSION_TEXTURE1D) ? (ID3D11Texture1D *)res : NULL; + ID3D11Texture2D *tex2 = (dim == D3D11_RESOURCE_DIMENSION_TEXTURE2D) ? (ID3D11Texture2D *)res : NULL; + ID3D11Texture3D *tex3 = (dim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) ? (ID3D11Texture3D *)res : NULL; + + if(tex1) + { + D3D11_TEXTURE1D_DESC desc; + tex1->GetDesc(&desc); + + int mipLevels = desc.MipLevels; + + if(mipLevels == 0) + mipLevels = CalcNumMips(desc.Width, 1, 1); + + return desc.ArraySize * mipLevels; + } + else if(tex2) + { + D3D11_TEXTURE2D_DESC desc; + tex2->GetDesc(&desc); + + int mipLevels = desc.MipLevels; + + if(mipLevels == 0) + mipLevels = CalcNumMips(desc.Width, desc.Height, 1); + + return desc.ArraySize * mipLevels; + } + else if(tex3) + { + D3D11_TEXTURE3D_DESC desc; + tex3->GetDesc(&desc); + + int mipLevels = desc.MipLevels; + + if(mipLevels == 0) + mipLevels = CalcNumMips(desc.Width, desc.Height, desc.Depth); + + return mipLevels; + } + + return 1; +} + UINT GetMipForSubresource(ID3D11Resource *res, int Subresource) { D3D11_RESOURCE_DIMENSION dim; @@ -93,6 +155,8 @@ UINT GetMipForSubresource(ID3D11Resource *res, int Subresource) dim = D3D11_RESOURCE_DIMENSION_TEXTURE2D; else if(WrappedID3D11Texture3D1::IsAlloc(res)) dim = D3D11_RESOURCE_DIMENSION_TEXTURE3D; + else if(WrappedID3D11Buffer::IsAlloc(res)) + return 0; else res->GetType(&dim); diff --git a/renderdoc/driver/d3d11/d3d11_resources.h b/renderdoc/driver/d3d11/d3d11_resources.h index 091d667b9..2f9641e67 100644 --- a/renderdoc/driver/d3d11/d3d11_resources.h +++ b/renderdoc/driver/d3d11/d3d11_resources.h @@ -40,6 +40,8 @@ UINT GetByteSize(ID3D11Texture1D *tex, int SubResource); UINT GetByteSize(ID3D11Texture2D *tex, int SubResource); UINT GetByteSize(ID3D11Texture3D *tex, int SubResource); +UINT GetSubresourceCount(ID3D11Resource *res); + UINT GetMipForSubresource(ID3D11Resource *res, int Subresource); UINT GetSliceForSubresource(ID3D11Resource *res, int Subresource); UINT GetMipForDsv(const D3D11_DEPTH_STENCIL_VIEW_DESC &dsv); diff --git a/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp index 9ac14fba9..825ba3abf 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp @@ -4872,6 +4872,14 @@ bool WrappedID3D12GraphicsCommandList::Serialise_DiscardResource(SerialiserType { Unwrap(m_Cmd->RerecordCmdList(m_Cmd->m_LastCmdListID)) ->DiscardResource(Unwrap(pResource), pRegion); + + if(m_pDevice->GetReplayOptions().optimisation != ReplayOptimisationLevel::Fastest) + { + m_pDevice->GetDebugManager()->FillWithDiscardPattern( + m_Cmd->RerecordCmdList(m_Cmd->m_LastCmdListID), + m_Cmd->m_BakedCmdListInfo[m_Cmd->m_LastCmdListID].state, DiscardType::DiscardCall, + pResource, pRegion); + } } } else diff --git a/renderdoc/driver/d3d12/d3d12_debug.cpp b/renderdoc/driver/d3d12/d3d12_debug.cpp index 9740459a1..13cd53961 100644 --- a/renderdoc/driver/d3d12/d3d12_debug.cpp +++ b/renderdoc/driver/d3d12/d3d12_debug.cpp @@ -280,6 +280,8 @@ D3D12DebugManager::D3D12DebugManager(WrappedID3D12Device *wrapper) shaderCache->GetShaderBlob(hlsl.c_str(), "RENDERDOC_FullscreenVS", D3DCOMPILE_WARNINGS_ARE_ERRORS, "vs_5_0", &m_FullscreenVS); + shaderCache->GetShaderBlob(hlsl.c_str(), "RENDERDOC_DiscardPS", D3DCOMPILE_WARNINGS_ARE_ERRORS, + "ps_5_0", &m_DiscardPS); } { @@ -368,6 +370,29 @@ D3D12DebugManager::D3D12DebugManager(WrappedID3D12Device *wrapper) if(m_DebugList) m_DebugList->Close(); + + { + ResourceFormat fmt; + fmt.type = ResourceFormatType::Regular; + fmt.compType = CompType::Float; + fmt.compByteWidth = 4; + fmt.compCount = 1; + bytebuf pattern = GetDiscardPattern(DiscardType::DiscardCall, fmt); + m_DiscardConstants = MakeCBuffer(pattern.size()); + FillBuffer(m_DiscardConstants, 0, pattern.data(), pattern.size()); + + ID3DBlob *root = shaderCache->MakeRootSig({ + cbvParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 0), + constParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 1, 1), + }); + + RDCASSERT(root); + + hr = m_pDevice->CreateRootSignature(0, root->GetBufferPointer(), root->GetBufferSize(), + __uuidof(ID3D12RootSignature), (void **)&m_DiscardRootSig); + + SAFE_RELEASE(root); + } } D3D12DebugManager::~D3D12DebugManager() @@ -408,9 +433,24 @@ D3D12DebugManager::~D3D12DebugManager() SAFE_RELEASE(m_TexResource); + SAFE_RELEASE(m_DiscardConstants); + SAFE_RELEASE(m_DiscardRootSig); + SAFE_RELEASE(m_DiscardPS); + SAFE_RELEASE(m_DebugAlloc); SAFE_RELEASE(m_DebugList); + for(auto it = m_DiscardPipes.begin(); it != m_DiscardPipes.end(); it++) + if(it->second) + it->second->Release(); + + for(auto it = m_DiscardPatterns.begin(); it != m_DiscardPatterns.end(); it++) + if(it->second) + it->second->Release(); + + for(size_t i = 0; i < m_DiscardBuffers.size(); i++) + m_DiscardBuffers[i]->Release(); + if(RenderDoc::Inst().GetCrashHandler()) RenderDoc::Inst().GetCrashHandler()->UnregisterMemoryRegion(this); } @@ -641,6 +681,362 @@ void D3D12DebugManager::ResetDebugAlloc() m_DebugAlloc->Reset(); } +void D3D12DebugManager::FillWithDiscardPattern(ID3D12GraphicsCommandListX *cmd, + const D3D12RenderState &state, DiscardType type, + ID3D12Resource *res, + const D3D12_DISCARD_REGION *region) +{ + RDCASSERT(type == DiscardType::DiscardCall); + + D3D12MarkerRegion marker( + cmd, StringFormat::Fmt("FillWithDiscardPattern %s", ToStr(GetResID(res)).c_str())); + + D3D12_RESOURCE_DESC desc = res->GetDesc(); + + rdcarray rects; + + if(region && region->NumRects > 0) + rects.assign(region->pRects, region->NumRects); + else + rects = {{0, 0, (LONG)desc.Width, (LONG)desc.Height}}; + + if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + { + // ignore rects, they are only allowed with 2D resources + size_t size = (size_t)desc.Width; + + ID3D12Resource *patternBuf = NULL; + + // if we have discard buffers, try the last one, it's the biggest we have + if(!m_DiscardBuffers.empty()) + { + patternBuf = m_DiscardBuffers.back(); + + // if it's not big enough, don't use it + if(patternBuf->GetDesc().Width < size) + patternBuf = NULL; + } + + // if we don't have a buffer, make one that's big enough and use that + if(patternBuf == NULL) + { + bytebuf pattern; + // make at least 1K at a time to prevent too much incremental updates if we encounter buffers + // of different sizes + pattern.resize(AlignUp(size, 1024U)); + + uint32_t value = 0xD15CAD3D; + + for(size_t i = 0; i < pattern.size(); i += 4) + memcpy(&pattern[i], &value, sizeof(uint32_t)); + + patternBuf = MakeCBuffer(pattern.size()); + + m_DiscardBuffers.push_back(patternBuf); + + FillBuffer(patternBuf, 0, pattern.data(), size); + } + + // fill the destination with a copy from the pattern buffer + cmd->CopyBufferRegion(res, 0, patternBuf, 0, size); + + return; + } + + if(desc.SampleDesc.Count > 1) + { + // we can't do discard patterns for MSAA on compute comand lists + if(cmd->GetType() == D3D12_COMMAND_LIST_TYPE_COMPUTE) + return; + + bool depth = false; + if(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) + depth = true; + + DXGI_FORMAT fmt = desc.Format; + + if(depth) + fmt = GetDepthTypedFormat(fmt); + else + fmt = GetFloatTypedFormat(fmt); + + rdcpair key = {fmt, desc.SampleDesc.Count}; + rdcpair stencilKey = {DXGI_FORMAT_UNKNOWN, desc.SampleDesc.Count}; + + ID3D12PipelineState *pipe = m_DiscardPipes[key]; + ID3D12PipelineState *stencilpipe = pipe; + + if(fmt == DXGI_FORMAT_D32_FLOAT_S8X24_UINT) + { + stencilKey.first = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; + stencilpipe = m_DiscardPipes[stencilKey]; + } + else if(fmt == DXGI_FORMAT_D24_UNORM_S8_UINT) + { + stencilKey.first = DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + stencilpipe = m_DiscardPipes[stencilKey]; + } + + if(pipe == NULL) + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeDesc = {}; + + pipeDesc.pRootSignature = m_DiscardRootSig; + pipeDesc.VS.BytecodeLength = m_FullscreenVS->GetBufferSize(); + pipeDesc.VS.pShaderBytecode = m_FullscreenVS->GetBufferPointer(); + pipeDesc.PS.BytecodeLength = m_DiscardPS->GetBufferSize(); + pipeDesc.PS.pShaderBytecode = m_DiscardPS->GetBufferPointer(); + pipeDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; + pipeDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; + pipeDesc.SampleMask = 0xFFFFFFFF; + pipeDesc.SampleDesc.Count = desc.SampleDesc.Count; + pipeDesc.IBStripCutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED; + pipeDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + pipeDesc.BlendState.RenderTarget[0].BlendEnable = FALSE; + pipeDesc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; + pipeDesc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; + pipeDesc.BlendState.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; + pipeDesc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA; + pipeDesc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; + pipeDesc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; + pipeDesc.BlendState.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + + pipeDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + pipeDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + + pipeDesc.DepthStencilState.StencilReadMask = 0xFF; + pipeDesc.DepthStencilState.StencilWriteMask = 0xFF; + pipeDesc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + pipeDesc.DepthStencilState.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE; + pipeDesc.DepthStencilState.FrontFace.StencilFailOp = D3D12_STENCIL_OP_REPLACE; + pipeDesc.DepthStencilState.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_REPLACE; + pipeDesc.DepthStencilState.BackFace = pipeDesc.DepthStencilState.FrontFace; + + pipeDesc.DepthStencilState.DepthEnable = FALSE; + pipeDesc.DepthStencilState.StencilEnable = FALSE; + + if(depth) + { + pipeDesc.DSVFormat = fmt; + pipeDesc.DepthStencilState.DepthEnable = TRUE; + } + else + { + pipeDesc.NumRenderTargets = 1; + pipeDesc.RTVFormats[0] = fmt; + } + + HRESULT hr = m_pDevice->CreateGraphicsPipelineState(&pipeDesc, __uuidof(ID3D12PipelineState), + (void **)&pipe); + + if(FAILED(hr)) + RDCERR("Couldn't create MSAA discard pattern pipe! HRESULT: %s", ToStr(hr).c_str()); + + m_DiscardPipes[key] = pipe; + + if(stencilKey.first != DXGI_FORMAT_UNKNOWN) + { + pipeDesc.DepthStencilState.DepthEnable = FALSE; + pipeDesc.DepthStencilState.StencilEnable = TRUE; + + hr = m_pDevice->CreateGraphicsPipelineState(&pipeDesc, __uuidof(ID3D12PipelineState), + (void **)&stencilpipe); + + if(FAILED(hr)) + RDCERR("Couldn't create MSAA discard pattern pipe! HRESULT: %s", ToStr(hr).c_str()); + + m_DiscardPipes[stencilKey] = stencilpipe; + } + } + + if(!pipe) + return; + + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + cmd->SetPipelineState(pipe); + cmd->SetGraphicsRootSignature(m_DiscardRootSig); + cmd->SetGraphicsRootConstantBufferView(0, m_DiscardConstants->GetGPUVirtualAddress()); + D3D12_VIEWPORT viewport = {0, 0, (float)desc.Width, (float)desc.Height, 0.0f, 1.0f}; + cmd->RSSetViewports(1, &viewport); + + if(m_pDevice->GetOpts3().ViewInstancingTier != D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED) + cmd->SetViewInstanceMask(0); + + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; + rtvDesc.Format = fmt; + rtvDesc.Texture2DMSArray.ArraySize = 1; + + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; + dsvDesc.Flags = D3D12_DSV_FLAG_NONE; + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY; + dsvDesc.Format = fmt; + dsvDesc.Texture2DMSArray.ArraySize = 1; + + D3D12_CPU_DESCRIPTOR_HANDLE rtv = GetCPUHandle(MSAA_RTV); + D3D12_CPU_DESCRIPTOR_HANDLE dsv = GetCPUHandle(MSAA_DSV); + + for(UINT sub = 0; sub < region->NumSubresources; sub++) + { + UINT subresource = region->FirstSubresource + sub; + if(depth) + { + dsvDesc.Texture2DMSArray.FirstArraySlice = GetSliceForSubresource(res, subresource); + m_pDevice->CreateDepthStencilView(res, &dsvDesc, dsv); + cmd->OMSetRenderTargets(0, NULL, FALSE, &dsv); + } + else + { + rtvDesc.Texture2DMSArray.FirstArraySlice = GetSliceForSubresource(res, subresource); + m_pDevice->CreateRenderTargetView(res, &rtvDesc, rtv); + cmd->OMSetRenderTargets(1, &rtv, FALSE, NULL); + } + + UINT mip = GetMipForSubresource(res, subresource); + UINT plane = GetPlaneForSubresource(res, subresource); + + for(D3D12_RECT r : rects) + { + r.right = RDCMIN(LONG(RDCMAX(1U, (UINT)desc.Width >> mip)), r.right); + r.bottom = RDCMIN(LONG(RDCMAX(1U, (UINT)desc.Height >> mip)), r.bottom); + + cmd->RSSetScissorRects(1, &r); + + if(depth) + { + if(plane == 0) + { + cmd->SetPipelineState(pipe); + cmd->SetGraphicsRoot32BitConstant(1, 0, 0); + cmd->DrawInstanced(3, 1, 0, 0); + } + else + { + cmd->SetPipelineState(stencilpipe); + cmd->SetGraphicsRoot32BitConstant(1, 1, 0); + cmd->OMSetStencilRef(0x00); + cmd->DrawInstanced(3, 1, 0, 0); + + cmd->SetGraphicsRoot32BitConstant(1, 2, 0); + cmd->OMSetStencilRef(0xff); + cmd->DrawInstanced(3, 1, 0, 0); + } + } + else + { + cmd->SetGraphicsRoot32BitConstant(1, 0, 0); + cmd->DrawInstanced(3, 1, 0, 0); + } + } + } + + state.ApplyState(m_pDevice, cmd); + + return; + } + + // see if we already have a buffer with texels in the desired format, if not then create it + ID3D12Resource *buf = m_DiscardPatterns[desc.Format]; + + if(buf == NULL) + { + bytebuf pattern = GetDiscardPattern(type, MakeResourceFormat(desc.Format), 256); + + buf = MakeCBuffer(pattern.size()); + + FillBuffer(buf, 0, pattern.data(), pattern.size()); + + m_DiscardPatterns[desc.Format] = buf; + } + + UINT firstSub = region ? region->FirstSubresource : 0; + UINT numSubs = region ? region->NumSubresources : GetNumSubresources(m_pDevice, &desc); + + for(UINT sub = firstSub; sub < firstSub + numSubs; sub++) + { + D3D12_RESOURCE_BARRIER b = {}; + b.Transition.pResource = res; + b.Transition.Subresource = sub; + + // TODO can we do better than an educated guess as to what the previous state was? + if(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) + b.Transition.StateBefore = D3D12_RESOURCE_STATE_DEPTH_WRITE; + else if(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) + b.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + else + b.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON; + + b.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + cmd->ResourceBarrier(1, &b); + + D3D12_TEXTURE_COPY_LOCATION dst, src; + + dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dst.pResource = res; + dst.SubresourceIndex = sub; + + UINT mip = GetMipForSubresource(res, sub); + + DXGI_FORMAT fmt = desc.Format; + UINT bufOffset = 0; + + // if this is a depth/stencil format it comes in multiple planes - figure out which format we're + // copying and the appropriate buffer offset + if(IsDepthAndStencilFormat(fmt)) + { + UINT planeSlice = GetPlaneForSubresource(res, sub); + + if(planeSlice == 0) + { + fmt = DXGI_FORMAT_R32_TYPELESS; + } + else + { + fmt = DXGI_FORMAT_R8_TYPELESS; + bufOffset += + GetByteSize(DiscardPatternWidth, DiscardPatternHeight, 1, DXGI_FORMAT_R32_FLOAT, 0); + } + } + + // the user isn't allowed to specify rects for 3D textures, so in that case we'll have our own + // default 0,0->64k,64k one. Similarly we also discard all z slices + uint32_t depth = + desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? desc.DepthOrArraySize : 1U; + for(uint32_t z = 0; z < RDCMAX(1U, depth >> mip); z++) + { + for(D3D12_RECT r : rects) + { + int32_t rectWidth = RDCMIN(LONG(RDCMAX(1U, (UINT)desc.Width >> mip)), r.right); + int32_t rectHeight = RDCMIN(LONG(RDCMAX(1U, (UINT)desc.Height >> mip)), r.bottom); + + for(int32_t y = r.top; y < rectHeight; y += DiscardPatternHeight) + { + for(int32_t x = r.left; x < rectWidth; x += DiscardPatternWidth) + { + src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + src.pResource = buf; + src.PlacedFootprint.Offset = bufOffset; + src.PlacedFootprint.Footprint.Format = fmt; + src.PlacedFootprint.Footprint.RowPitch = + AlignUp(GetRowPitch(DiscardPatternWidth, fmt, 0), 256U); + src.PlacedFootprint.Footprint.Width = + RDCMIN(DiscardPatternWidth, uint32_t(rectWidth - x)); + src.PlacedFootprint.Footprint.Height = + RDCMIN(DiscardPatternHeight, uint32_t(rectHeight - y)); + src.PlacedFootprint.Footprint.Depth = 1; + + cmd->CopyTextureRegion(&dst, x, y, z, &src, NULL); + } + } + } + } + + std::swap(b.Transition.StateBefore, b.Transition.StateAfter); + + cmd->ResourceBarrier(1, &b); + } +} + D3D12_CPU_DESCRIPTOR_HANDLE D3D12DebugManager::GetCPUHandle(CBVUAVSRVSlot slot) { D3D12_CPU_DESCRIPTOR_HANDLE ret = cbvsrvuavHeap->GetCPUDescriptorHandleForHeapStart(); diff --git a/renderdoc/driver/d3d12/d3d12_debug.h b/renderdoc/driver/d3d12/d3d12_debug.h index ddac38d3a..1f4738f10 100644 --- a/renderdoc/driver/d3d12/d3d12_debug.h +++ b/renderdoc/driver/d3d12/d3d12_debug.h @@ -142,6 +142,10 @@ public: ID3D12GraphicsCommandListX *ResetDebugList(); void ResetDebugAlloc(); + void FillWithDiscardPattern(ID3D12GraphicsCommandListX *cmd, const D3D12RenderState &state, + DiscardType type, ID3D12Resource *res, + const D3D12_DISCARD_REGION *region); + D3D12_CPU_DESCRIPTOR_HANDLE GetCPUHandle(CBVUAVSRVSlot slot); D3D12_CPU_DESCRIPTOR_HANDLE GetCPUHandle(RTVSlot slot); D3D12_CPU_DESCRIPTOR_HANDLE GetCPUHandle(DSVSlot slot); @@ -217,6 +221,15 @@ private: // Debug lists ID3D12GraphicsCommandListX *m_DebugList = NULL; ID3D12CommandAllocator *m_DebugAlloc = NULL; + + // Discard pattern rendering + ID3DBlob *m_DiscardPS = NULL; + ID3D12Resource *m_DiscardConstants = NULL; + ID3D12RootSignature *m_DiscardRootSig = NULL; + + std::map, ID3D12PipelineState *> m_DiscardPipes; + std::map m_DiscardPatterns; + rdcarray m_DiscardBuffers; }; void MoveRootSignatureElementsToRegisterSpace(D3D12RootSignature &sig, uint32_t registerSpace, diff --git a/renderdoc/driver/d3d12/d3d12_resources.cpp b/renderdoc/driver/d3d12/d3d12_resources.cpp index deb1bcd85..89793743c 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.cpp +++ b/renderdoc/driver/d3d12/d3d12_resources.cpp @@ -412,6 +412,25 @@ void WrappedID3D12PipelineState::ShaderEntry::BuildReflection() m_Details.resourceId = GetResourceID(); } +UINT GetPlaneForSubresource(ID3D12Resource *res, int Subresource) +{ + D3D12_RESOURCE_DESC desc = res->GetDesc(); + + if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + return 0; + + int mipLevels = desc.MipLevels; + + if(mipLevels == 0) + mipLevels = CalcNumMips((int)desc.Width, 1, 1); + + UINT arraySlices = desc.DepthOrArraySize; + if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) + arraySlices = 1; + + return Subresource / (mipLevels * arraySlices); +} + UINT GetMipForSubresource(ID3D12Resource *res, int Subresource) { D3D12_RESOURCE_DESC desc = res->GetDesc(); diff --git a/renderdoc/driver/d3d12/d3d12_resources.h b/renderdoc/driver/d3d12/d3d12_resources.h index 9c3a1c087..2a1002095 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.h +++ b/renderdoc/driver/d3d12/d3d12_resources.h @@ -28,6 +28,7 @@ #include "d3d12_device.h" #include "d3d12_manager.h" +UINT GetPlaneForSubresource(ID3D12Resource *res, int Subresource); UINT GetMipForSubresource(ID3D12Resource *res, int Subresource); UINT GetSliceForSubresource(ID3D12Resource *res, int Subresource); UINT GetMipForDsv(const D3D12_DEPTH_STENCIL_VIEW_DESC &dsv); diff --git a/renderdoc/driver/gl/gl_debug.cpp b/renderdoc/driver/gl/gl_debug.cpp index 087e1f0e8..e735c3d09 100644 --- a/renderdoc/driver/gl/gl_debug.cpp +++ b/renderdoc/driver/gl/gl_debug.cpp @@ -546,6 +546,45 @@ void GLReplay::InitDebugData() BindUBO(DebugData.checkerProg, "CheckerboardUBOData", 0); + for(size_t numViews = 0; numViews < ARRAY_COUNT(DebugData.discardProg); numViews++) + { + rdcstr defines; + + if(numViews > 0 && IsGLES && HasExt[OVR_multiview]) + defines = StringFormat::Fmt("#define NUM_VIEWS %zu", numViews + 1); + + rdcstr blitvs = + GenerateGLSLShader(GetEmbeddedResource(glsl_blit_vert), shaderType, glslBaseVer, defines); + + fs = GenerateGLSLShader(GetEmbeddedResource(glsl_discard_frag), shaderType, glslBaseVer); + DebugData.discardProg[numViews] = CreateShaderProgram(blitvs, fs); + + BindUBO(DebugData.discardProg[numViews], "DiscardUBOData", 0); + + if(!IsGLES) + { + rdcstr name = "col0"; + for(GLuint i = 0; i < 8; i++) + { + name[3] = char('0' + i); + GL.glBindFragDataLocation(DebugData.discardProg[numViews], i, name.c_str()); + } + } + } + + { + ResourceFormat fmt; + fmt.type = ResourceFormatType::Regular; + fmt.compType = CompType::Float; + fmt.compByteWidth = 4; + fmt.compCount = 1; + bytebuf pattern = GetDiscardPattern(DiscardType::InvalidateCall, fmt, 1, true); + drv.glGenBuffers(1, &DebugData.discardPatternBuffer); + drv.glBindBuffer(eGL_UNIFORM_BUFFER, DebugData.discardPatternBuffer); + drv.glNamedBufferDataEXT(DebugData.discardPatternBuffer, pattern.size(), pattern.data(), + eGL_STATIC_DRAW); + } + if(HasExt[ARB_geometry_shader4]) { vs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_vert), shaderType, glslBaseVer); @@ -1114,6 +1153,10 @@ void GLReplay::DeleteDebugData() if(DebugData.overlayProg) drv.glDeleteProgram(DebugData.overlayProg); + for(size_t i = 0; i < ARRAY_COUNT(DebugData.discardProg); i++) + if(DebugData.discardProg[i]) + drv.glDeleteProgram(DebugData.discardProg[i]); + drv.glDeleteTransformFeedbacks(1, &DebugData.feedbackObj); drv.glDeleteBuffers(1, &DebugData.feedbackBuffer); drv.glDeleteQueries((GLsizei)DebugData.feedbackQueries.size(), DebugData.feedbackQueries.data()); @@ -1283,6 +1326,222 @@ void GLReplay::RestoreSamplerParams(GLenum target, GLuint texname, TextureSample GL.glTextureParameterivEXT(texname, target, eGL_TEXTURE_COMPARE_MODE, (GLint *)&state.compareMode); } +void GLReplay::FillWithDiscardPattern(DiscardType type, GLuint framebuffer, GLsizei numAttachments, + const GLenum *attachments, GLint x, GLint y, GLsizei width, + GLsizei height) +{ + RDCASSERT(type == DiscardType::InvalidateCall); + + WrappedOpenGL &drv = *m_pDriver; + + GLMarkerRegion region("FillWithDiscardPattern FBO"); + + GLRenderState rs; + rs.FetchState(&drv); + + // we use the original framebuffer, and mask off any attachments that shouldn't be discarded + drv.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, framebuffer); + + int numviews = 1; + + if(IsGLES && HasExt[OVR_multiview]) + { + GL.glGetNamedFramebufferAttachmentParameterivEXT( + framebuffer, eGL_COLOR_ATTACHMENT0, eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR, + &numviews); + RDCLOG("Discarding with %u views", numviews); + } + + GLuint prog = DebugData.discardProg[RDCCLAMP(numviews, 1, 4) - 1]; + + drv.glUseProgram(prog); + drv.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, DebugData.discardPatternBuffer); + + drv.glDisable(eGL_BLEND); + drv.glDisable(eGL_CULL_FACE); + drv.glEnable(eGL_DEPTH_TEST); + drv.glEnable(eGL_STENCIL_TEST); + drv.glDepthFunc(eGL_ALWAYS); + drv.glStencilFunc(eGL_ALWAYS, 0, 0xff); + if(!IsGLES) + drv.glPolygonMode(eGL_FRONT_AND_BACK, eGL_FILL); + + // scissor to the rect + drv.glEnable(eGL_SCISSOR_TEST); + drv.glScissor(x, y, width, height); + + GLint maxview[2] = {2048, 2048}; + drv.glGetIntegerv(eGL_MAX_VIEWPORT_DIMS, maxview); + drv.glViewport(0, 0, maxview[0], maxview[1]); + + // disable all masks at first + drv.glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + drv.glDepthMask(GL_FALSE); + drv.glStencilMask(0); + + // enable masks specified by attachments + bool stencil = false; + for(GLsizei i = 0; i < numAttachments; i++) + { + if(attachments[i] == eGL_DEPTH_STENCIL_ATTACHMENT) + { + drv.glDepthMask(GL_TRUE); + drv.glStencilMask(0xff); + stencil = true; + } + else if(attachments[i] == eGL_DEPTH_ATTACHMENT) + { + drv.glDepthMask(GL_TRUE); + } + else if(attachments[i] == eGL_STENCIL_ATTACHMENT) + { + drv.glStencilMask(0xff); + stencil = true; + } + else + { + drv.glColorMaski(attachments[i] - eGL_COLOR_ATTACHMENT0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + } + + GLint loc = drv.glGetUniformLocation(prog, "flags"); + + uint32_t flags = 0U; + + if(height == 1) + flags |= 0x10U; + + if(stencil) + { + drv.glStencilOp(eGL_REPLACE, eGL_REPLACE, eGL_REPLACE); + + drv.glStencilFunc(eGL_ALWAYS, 0, 0xff); + drv.glProgramUniform1ui(prog, loc, flags | 1U); + drv.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4); + + drv.glStencilFunc(eGL_ALWAYS, 0xff, 0xff); + drv.glProgramUniform1ui(prog, loc, flags | 2U); + drv.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4); + } + else + { + drv.glProgramUniform1ui(prog, loc, flags); + + // if we're not writing stencil we can just do one draw + drv.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4); + } + + rs.ApplyState(&drv); +} + +void GLReplay::FillWithDiscardPattern(DiscardType type, ResourceId id, GLuint mip, GLint xoffset, + GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, + GLsizei depth) +{ + RDCASSERT(type == DiscardType::InvalidateCall); + + WrappedOpenGL &drv = *m_pDriver; + + GLMarkerRegion region("FillWithDiscardPattern Texture"); + + auto &texDetails = drv.m_Textures[id]; + + GLenum fmt = texDetails.internalFormat; + + bytebuf &pattern = m_DiscardPatterns[fmt]; + + GLenum target = texDetails.curType; + + if(pattern.empty()) + pattern = GetDiscardPattern(type, MakeResourceFormat(target, fmt), 1, true); + + bool compressed = IsCompressedFormat(fmt); + + if(target == eGL_TEXTURE_CUBE_MAP) + depth = RDCMIN(depth, 6); + else + depth = RDCMIN(texDetails.depth, depth); + height = RDCMIN(texDetails.height, height); + width = RDCMIN(texDetails.width, width); + + GLint dim = texDetails.dimension; + + GLint oldRowLength = 0; + GL.glGetIntegerv(eGL_UNPACK_ROW_LENGTH, &oldRowLength); + + // ensure even if we're uploading a sub-rect that the row length is the same. + GL.glPixelStorei(eGL_UNPACK_ROW_LENGTH, DiscardPatternWidth); + + GLuint tex = texDetails.resource.name; + + GLenum format = eGL_NONE; + GLenum datatype = eGL_NONE; + + if(!compressed) + { + format = GetBaseFormat(fmt); + datatype = GetDataType(fmt); + } + + for(GLsizei zc = 0; zc < depth; zc++) + { + GLsizei z = zoffset + zc; + + if(texDetails.curType == eGL_TEXTURE_CUBE_MAP) + { + GLenum targets[] = { + eGL_TEXTURE_CUBE_MAP_POSITIVE_X, eGL_TEXTURE_CUBE_MAP_NEGATIVE_X, + eGL_TEXTURE_CUBE_MAP_POSITIVE_Y, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + eGL_TEXTURE_CUBE_MAP_POSITIVE_Z, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + }; + + target = targets[z % 6]; + } + + for(GLsizei yc = 0; yc < height; yc += DiscardPatternHeight) + { + GLsizei y = yoffset + yc; + + for(GLsizei xc = 0; xc < width; xc += DiscardPatternWidth) + { + GLsizei x = xoffset + xc; + + GLsizei subwidth = RDCMIN((GLsizei)DiscardPatternWidth, texDetails.width - x); + GLsizei subheight = RDCMIN((GLsizei)DiscardPatternHeight, texDetails.height - y); + + if(compressed) + { + GLsizei dataSize = (GLsizei)GetCompressedByteSize(subwidth, subheight, 1, fmt); + + if(dim == 1) + GL.glCompressedTextureSubImage1DEXT(tex, target, mip, x, subwidth, fmt, dataSize, + pattern.data()); + else if(dim == 2) + GL.glCompressedTextureSubImage2DEXT(tex, target, mip, x, y, subwidth, subheight, fmt, + dataSize, pattern.data()); + else if(dim == 3) + GL.glCompressedTextureSubImage3DEXT(tex, target, mip, x, y, z, subwidth, subheight, 1, + fmt, dataSize, pattern.data()); + } + else + { + if(dim == 1) + GL.glTextureSubImage1DEXT(tex, target, mip, x, subwidth, format, datatype, + pattern.data()); + else if(dim == 2) + GL.glTextureSubImage2DEXT(tex, target, mip, x, y, subwidth, subheight, format, datatype, + pattern.data()); + else if(dim == 3) + GL.glTextureSubImage3DEXT(tex, target, mip, x, y, z, subwidth, subheight, 1, format, + datatype, pattern.data()); + } + } + } + } + + GL.glPixelStorei(eGL_UNPACK_ROW_LENGTH, oldRowLength); +} + void GLReplay::PickPixel(ResourceId texture, uint32_t x, uint32_t y, const Subresource &sub, CompType typeCast, float pixel[4]) { diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 79bba8ccf..49496de3e 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -4648,8 +4648,17 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) case GLChunk::eglSwapBuffers: case GLChunk::eglPostSubBufferNV: case GLChunk::eglSwapBuffersWithDamageEXT: - case GLChunk::eglSwapBuffersWithDamageKHR: - return Serialise_Present(ser); + case GLChunk::eglSwapBuffersWithDamageKHR: return Serialise_Present(ser); + + case GLChunk::glInvalidateNamedFramebufferSubData: + case GLChunk::glInvalidateSubFramebuffer: + return Serialise_glInvalidateNamedFramebufferSubData(ser, 0, 0, NULL, 0, 0, 0, 0); + case GLChunk::glInvalidateTexImage: return Serialise_glInvalidateTexImage(ser, 0, 0); + case GLChunk::glInvalidateTexSubImage: + return Serialise_glInvalidateTexSubImage(ser, 0, 0, 0, 0, 0, 0, 0, 0); + case GLChunk::glInvalidateBufferData: return Serialise_glInvalidateBufferData(ser, 0); + case GLChunk::glInvalidateBufferSubData: + return Serialise_glInvalidateBufferSubData(ser, 0, 0, 0); // these functions are not currently serialised - they do nothing on replay and are not // serialised for information (it would be harmless and perhaps useful for the user to see @@ -4934,12 +4943,6 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) case GLChunk::glProgramBinary: case GLChunk::glReleaseShaderCompiler: case GLChunk::glFrameTerminatorGREMEDY: - case GLChunk::glInvalidateBufferData: - case GLChunk::glInvalidateBufferSubData: - case GLChunk::glInvalidateNamedFramebufferSubData: - case GLChunk::glInvalidateSubFramebuffer: - case GLChunk::glInvalidateTexImage: - case GLChunk::glInvalidateTexSubImage: case GLChunk::glDebugMessageCallback: case GLChunk::glDebugMessageCallbackARB: case GLChunk::glDebugMessageCallbackKHR: diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index ddfcb2ab9..947ea5e9b 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -251,6 +251,13 @@ public: bool IsReplayContext(void *ctx) { return m_ReplayCtx.ctx == NULL || ctx == m_ReplayCtx.ctx; } bool HasDebugContext() { return m_DebugCtx != NULL; } + void FillWithDiscardPattern(DiscardType type, GLuint framebuffer, GLsizei numAttachments, + const GLenum *attachments, GLint x, GLint y, GLsizei width, + GLsizei height); + void FillWithDiscardPattern(DiscardType type, ResourceId id, GLuint mip, GLint xoffset = 0, + GLint yoffset = 0, GLint zoffset = 0, GLsizei width = 65536, + GLsizei height = 65536, GLsizei depth = 65536); + private: void OpenGLFillCBufferVariables(ResourceId shader, GLuint prog, bool bufferBacked, rdcstr prefix, const rdcarray &variables, @@ -384,6 +391,9 @@ private: GLuint quadoverdrawFragShaderSPIRV; GLuint quadoverdrawResolveProg; + GLuint discardProg[4]; + GLuint discardPatternBuffer; + ResourceId overlayTexId; GLuint overlayTex; GLuint overlayFBO; @@ -400,6 +410,8 @@ private: HighlightCache m_HighlightCache; + std::map m_DiscardPatterns; + // eventId -> data std::map m_PostVSData; diff --git a/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp index 771061c7d..8e47eb2ef 100644 --- a/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_buffer_funcs.cpp @@ -1855,18 +1855,161 @@ void WrappedOpenGL::glBindBuffersRange(GLenum target, GLuint first, GLsizei coun } } +template +bool WrappedOpenGL::Serialise_glInvalidateBufferData(SerialiserType &ser, GLuint bufferHandle) +{ + SERIALISE_ELEMENT_LOCAL(buffer, BufferRes(GetCtx(), bufferHandle)); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + ResourceId id = GetResourceManager()->GetID(buffer); + + if(IsLoading(m_State)) + m_ResourceUses[id].push_back(EventUsage(m_CurEventID, ResourceUsage::Discard)); + + GL.glInvalidateBufferData(buffer.name); + + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + GLsizeiptr size = m_Buffers[id].size; + + bytebuf pattern; + pattern.resize(AlignUp4(size)); + + uint32_t value = 0xD15CAD3D; + + for(size_t i = 0; i < pattern.size(); i += 4) + memcpy(&pattern[i], &value, sizeof(uint32_t)); + + GL.glNamedBufferSubDataEXT(buffer.name, 0, size, pattern.data()); + } + + if(IsLoading(m_State)) + { + AddEvent(); + + DrawcallDescription draw; + draw.name = StringFormat::Fmt("%s(%s)", ToStr(gl_CurChunk).c_str(), + ToStr(GetResourceManager()->GetOriginalID(id)).c_str()); + draw.flags |= DrawFlags::Clear; + + draw.copyDestination = GetResourceManager()->GetOriginalID(id); + + AddDrawcall(draw, true); + + m_ResourceUses[id].push_back(EventUsage(m_CurEventID, ResourceUsage::Discard)); + } + } + + return true; +} + void WrappedOpenGL::glInvalidateBufferData(GLuint buffer) { - GL.glInvalidateBufferData(buffer); + if(buffer && IsBackgroundCapturing(m_State)) + { + GetResourceManager()->MarkResourceFrameReferenced(BufferRes(GetCtx(), buffer), + eFrameRef_ReadBeforeWrite); + } - GetResourceManager()->MarkDirtyResource(BufferRes(GetCtx(), buffer)); + SERIALISE_TIME_CALL(GL.glInvalidateBufferData(buffer)); + + if(IsCaptureMode(m_State)) + { + if(IsActiveCapturing(m_State)) + { + USE_SCRATCH_SERIALISER(); + SCOPED_SERIALISE_CHUNK(gl_CurChunk); + Serialise_glInvalidateBufferData(ser, buffer); + + GetContextRecord()->AddChunk(scope.Get()); + } + else + { + GetResourceManager()->MarkDirtyResource(BufferRes(GetCtx(), buffer)); + } + } +} + +template +bool WrappedOpenGL::Serialise_glInvalidateBufferSubData(SerialiserType &ser, GLuint bufferHandle, + GLintptr offsetPtr, GLsizeiptr lengthPtr) +{ + SERIALISE_ELEMENT_LOCAL(buffer, BufferRes(GetCtx(), bufferHandle)); + SERIALISE_ELEMENT_LOCAL(offset, (uint64_t)offsetPtr); + SERIALISE_ELEMENT_LOCAL(length, (uint64_t)lengthPtr); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + ResourceId id = GetResourceManager()->GetID(buffer); + + if(IsLoading(m_State)) + m_ResourceUses[id].push_back(EventUsage(m_CurEventID, ResourceUsage::Discard)); + + GL.glInvalidateBufferData(buffer.name); + + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + bytebuf pattern; + pattern.resize(AlignUp4(length)); + + uint32_t value = 0xD15CAD3D; + + for(size_t i = 0; i < pattern.size(); i += 4) + memcpy(&pattern[i], &value, sizeof(uint32_t)); + + GL.glNamedBufferSubDataEXT(buffer.name, offset, length, pattern.data()); + } + + if(IsLoading(m_State)) + { + AddEvent(); + + DrawcallDescription draw; + draw.name = StringFormat::Fmt("%s(%s)", ToStr(gl_CurChunk).c_str(), + ToStr(GetResourceManager()->GetOriginalID(id)).c_str()); + draw.flags |= DrawFlags::Clear; + + draw.copyDestination = GetResourceManager()->GetOriginalID(id); + + AddDrawcall(draw, true); + + m_ResourceUses[id].push_back(EventUsage(m_CurEventID, ResourceUsage::Discard)); + } + } + + return true; } void WrappedOpenGL::glInvalidateBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr length) { - GL.glInvalidateBufferSubData(buffer, offset, length); + if(buffer && IsBackgroundCapturing(m_State)) + { + GetResourceManager()->MarkResourceFrameReferenced(BufferRes(GetCtx(), buffer), + eFrameRef_ReadBeforeWrite); + } - GetResourceManager()->MarkDirtyResource(BufferRes(GetCtx(), buffer)); + SERIALISE_TIME_CALL(GL.glInvalidateBufferSubData(buffer, offset, length)); + + if(IsCaptureMode(m_State)) + { + if(IsActiveCapturing(m_State)) + { + USE_SCRATCH_SERIALISER(); + SCOPED_SERIALISE_CHUNK(gl_CurChunk); + Serialise_glInvalidateBufferSubData(ser, buffer, offset, length); + + GetContextRecord()->AddChunk(scope.Get()); + } + else + { + GetResourceManager()->MarkDirtyResource(BufferRes(GetCtx(), buffer)); + } + } } #pragma endregion @@ -5321,3 +5464,6 @@ INSTANTIATE_FUNCTION_SERIALISED(void, glVertexArrayVertexBindingDivisorEXT, GLui GLuint bindingindex, GLuint divisor); INSTANTIATE_FUNCTION_SERIALISED(void, glVertexAttrib, GLuint index, int count, GLenum type, GLboolean normalized, const void *value, AttribType attribtype); +INSTANTIATE_FUNCTION_SERIALISED(void, glInvalidateBufferData, GLuint buffer); +INSTANTIATE_FUNCTION_SERIALISED(void, glInvalidateBufferSubData, GLuint buffer, GLintptr offset, + GLsizeiptr length); diff --git a/renderdoc/driver/gl/wrappers/gl_emulated.cpp b/renderdoc/driver/gl/wrappers/gl_emulated.cpp index b22696b33..6c132451a 100644 --- a/renderdoc/driver/gl/wrappers/gl_emulated.cpp +++ b/renderdoc/driver/gl/wrappers/gl_emulated.cpp @@ -365,6 +365,22 @@ void APIENTRY _glInvalidateNamedFramebufferData(GLuint framebuffer, GLsizei numA } } +void APIENTRY _glInvalidateNamedFramebufferSubData(GLuint framebuffer, GLsizei numAttachments, + const GLenum *attachments, GLint x, GLint y, + GLsizei width, GLsizei height) +{ + if(HasExt[ARB_invalidate_subdata]) + { + PushPopFramebuffer(eGL_DRAW_FRAMEBUFFER, framebuffer); + GL.glInvalidateSubFramebuffer(eGL_DRAW_FRAMEBUFFER, numAttachments, attachments, x, y, width, + height); + } + else + { + RDCERR("No support for framebuffer invalidate on GL %d", GLCoreVersion); + } +} + #pragma endregion #pragma region Renderbuffers @@ -3714,6 +3730,7 @@ void GLDispatchTable::EmulateRequiredExtensions() EMULATE_FUNC(glVertexArrayElementBuffer); EMULATE_FUNC(glVertexArrayVertexBuffers) EMULATE_FUNC(glInvalidateNamedFramebufferData); + EMULATE_FUNC(glInvalidateNamedFramebufferSubData); } } diff --git a/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp index c2574cf35..b923dfc13 100644 --- a/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp @@ -24,6 +24,7 @@ ******************************************************************************/ #include "../gl_driver.h" +#include "../gl_replay.h" #include "common/common.h" #include "strings/string_utils.h" @@ -1767,6 +1768,12 @@ bool WrappedOpenGL::Serialise_glInvalidateNamedFramebufferData(SerialiserType &s GL.glInvalidateNamedFramebufferData(framebuffer.name, numAttachments, attachments); + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, framebuffer.name, + numAttachments, attachments, 0, 0, 65536, 65536); + } + if(IsLoading(m_State)) { AddEvent(); @@ -1922,13 +1929,99 @@ void WrappedOpenGL::glInvalidateNamedFramebufferData(GLuint framebuffer, GLsizei } } +template +bool WrappedOpenGL::Serialise_glInvalidateNamedFramebufferSubData( + SerialiserType &ser, GLuint framebufferHandle, GLsizei numAttachments, + const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) +{ + SERIALISE_ELEMENT_LOCAL(framebuffer, FramebufferRes(GetCtx(), framebufferHandle)); + SERIALISE_ELEMENT(numAttachments); + SERIALISE_ELEMENT_ARRAY(attachments, numAttachments); + SERIALISE_ELEMENT(x); + SERIALISE_ELEMENT(y); + SERIALISE_ELEMENT(width); + SERIALISE_ELEMENT(height); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + if(framebuffer.name == 0) + framebuffer.name = m_CurrentDefaultFBO; + + GLenum *att = (GLenum *)attachments; + for(GLsizei i = 0; i < numAttachments; i++) + { + // since we are faking the default framebuffer with our own + // to see the results, replace back/front/left/right with color attachment 0 + if(att[i] == eGL_BACK_LEFT || att[i] == eGL_BACK_RIGHT || att[i] == eGL_BACK || + att[i] == eGL_FRONT_LEFT || att[i] == eGL_FRONT_RIGHT || att[i] == eGL_FRONT) + att[i] = eGL_COLOR_ATTACHMENT0; + if(att[i] == eGL_COLOR) + att[i] = eGL_COLOR_ATTACHMENT0; + if(att[i] == eGL_DEPTH) + att[i] = eGL_DEPTH_ATTACHMENT; + if(att[i] == eGL_STENCIL) + att[i] = eGL_STENCIL_ATTACHMENT; + } + + GL.glInvalidateNamedFramebufferSubData(framebuffer.name, numAttachments, attachments, x, y, + width, height); + + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, framebuffer.name, + numAttachments, attachments, x, y, width, height); + } + + if(IsLoading(m_State)) + { + AddEvent(); + + ResourceId fbid = GetResourceManager()->GetID(framebuffer); + + DrawcallDescription draw; + draw.name = StringFormat::Fmt("%s(%s)", ToStr(gl_CurChunk).c_str(), + ToStr(GetResourceManager()->GetOriginalID(fbid)).c_str()); + draw.flags |= DrawFlags::Clear; + + for(GLsizei i = 0; i < numAttachments; i++) + { + GLuint obj = 0; + GLenum objtype = eGL_TEXTURE; + + GL.glGetNamedFramebufferAttachmentParameterivEXT( + framebuffer.name, att[i], eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&obj); + GL.glGetNamedFramebufferAttachmentParameterivEXT( + framebuffer.name, att[i], eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&objtype); + + ResourceId id; + + if(objtype == eGL_TEXTURE) + id = GetResourceManager()->GetID(TextureRes(GetCtx(), obj)); + else + id = GetResourceManager()->GetID(RenderbufferRes(GetCtx(), obj)); + + if(draw.copyDestination == ResourceId()) + draw.copyDestination = GetResourceManager()->GetOriginalID(id); + + m_ResourceUses[id].push_back(EventUsage(m_CurEventID, ResourceUsage::Discard)); + } + + AddDrawcall(draw, true); + } + } + + return true; +} + void WrappedOpenGL::glInvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) { GL.glInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height); - if(IsBackgroundCapturing(m_State)) + if(IsCaptureMode(m_State)) { GLResourceRecord *record = NULL; @@ -1943,7 +2036,22 @@ void WrappedOpenGL::glInvalidateSubFramebuffer(GLenum target, GLsizei numAttachm record = GetCtxData().m_ReadFramebufferRecord; } - if(record) + if(IsActiveCapturing(m_State)) + { + USE_SCRATCH_SERIALISER(); + SCOPED_SERIALISE_CHUNK(gl_CurChunk); + if(record) + Serialise_glInvalidateNamedFramebufferSubData(ser, record->Resource.name, numAttachments, + attachments, x, y, width, height); + else + Serialise_glInvalidateNamedFramebufferSubData(ser, 0, numAttachments, attachments, x, y, + width, height); + + GetContextRecord()->AddChunk(scope.Get()); + if(record) + GetResourceManager()->MarkFBOReferenced(record->Resource, eFrameRef_ReadBeforeWrite); + } + else if(record) { record->MarkParentsDirty(GetResourceManager()); } @@ -1957,13 +2065,30 @@ void WrappedOpenGL::glInvalidateNamedFramebufferSubData(GLuint framebuffer, GLsi GL.glInvalidateNamedFramebufferSubData(framebuffer, numAttachments, attachments, x, y, width, height); - if(IsBackgroundCapturing(m_State)) + if(IsCaptureMode(m_State)) { GLResourceRecord *record = GetResourceManager()->GetResourceRecord(FramebufferRes(GetCtx(), framebuffer)); - if(record) + if(IsActiveCapturing(m_State)) + { + USE_SCRATCH_SERIALISER(); + SCOPED_SERIALISE_CHUNK(gl_CurChunk); + if(record) + Serialise_glInvalidateNamedFramebufferSubData(ser, record->Resource.name, numAttachments, + attachments, x, y, width, height); + else + Serialise_glInvalidateNamedFramebufferSubData(ser, 0, numAttachments, attachments, x, y, + width, height); + + GetContextRecord()->AddChunk(scope.Get()); + if(record) + GetResourceManager()->MarkFBOReferenced(record->Resource, eFrameRef_ReadBeforeWrite); + } + else if(record) + { record->MarkParentsDirty(GetResourceManager()); + } } } @@ -2942,6 +3067,9 @@ INSTANTIATE_FUNCTION_SERIALISED(void, glNamedFramebufferParameteriEXT, GLuint fr INSTANTIATE_FUNCTION_SERIALISED(void, glFramebufferReadBufferEXT, GLuint framebufferHandle, GLenum mode); INSTANTIATE_FUNCTION_SERIALISED(void, glBindFramebuffer, GLenum target, GLuint framebufferHandle); +INSTANTIATE_FUNCTION_SERIALISED(void, glInvalidateNamedFramebufferSubData, GLuint framebufferHandle, + GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, + GLsizei width, GLsizei height); INSTANTIATE_FUNCTION_SERIALISED(void, glInvalidateNamedFramebufferData, GLuint framebufferHandle, GLsizei numAttachments, const GLenum *attachments); INSTANTIATE_FUNCTION_SERIALISED(void, glFramebufferDrawBufferEXT, GLuint framebufferHandle, diff --git a/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp index 21cfa0f10..d5c8604cc 100644 --- a/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp @@ -24,6 +24,7 @@ ******************************************************************************/ #include "../gl_driver.h" +#include "../gl_replay.h" #include "common/common.h" #include "strings/string_utils.h" @@ -904,11 +905,277 @@ void WrappedOpenGL::glGenerateMultiTexMipmapEXT(GLenum texunit, GLenum target) Common_glGenerateTextureMipmapEXT(GetCtxData().GetTexUnitRecord(target, texunit), target); } +template +bool WrappedOpenGL::Serialise_glInvalidateTexImage(SerialiserType &ser, GLuint textureHandle, + GLint level) +{ + SERIALISE_ELEMENT_LOCAL(texture, TextureRes(GetCtx(), textureHandle)); + SERIALISE_ELEMENT(level); + + Serialise_DebugMessages(ser); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + GL.glInvalidateTexImage(texture.name, level); + + ResourceId liveId = GetResourceManager()->GetID(texture); + + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + GLenum attach = eGL_COLOR_ATTACHMENT0; + + ResourceFormat fmt = + MakeResourceFormat(m_Textures[liveId].curType, m_Textures[liveId].internalFormat); + + if(fmt.type != ResourceFormatType::Regular && fmt.type != ResourceFormatType::D16S8 && + fmt.type != ResourceFormatType::D24S8 && fmt.type != ResourceFormatType::D32S8 && + fmt.type != ResourceFormatType::S8 && fmt.type != ResourceFormatType::R10G10B10A2 && + fmt.type != ResourceFormatType::R11G11B10) + { + // we don't expect to be able to render to this format, so fill it manually + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, liveId, level); + } + else + { + GLenum base = GetBaseFormat(m_Textures[liveId].internalFormat); + if(base == eGL_DEPTH_STENCIL) + attach = eGL_DEPTH_STENCIL_ATTACHMENT; + else if(base == eGL_DEPTH_COMPONENT) + attach = eGL_DEPTH_ATTACHMENT; + else if(base == eGL_STENCIL_INDEX) + attach = eGL_STENCIL_ATTACHMENT; + + GLuint oldFB = 0; + GL.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint *)&oldFB); + + GLuint fb = 0; + GL.glGenFramebuffers(1, &fb); + GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, fb); + + GLenum texTarget = m_Textures[liveId].curType; + + if(texTarget == eGL_TEXTURE_3D) + { + for(GLsizei z = 0; z < RDCMAX(1, m_Textures[liveId].depth >> level); z++) + { + GL.glFramebufferTextureLayer(eGL_DRAW_FRAMEBUFFER, attach, texture.name, level, z); + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, fb, 1, &attach, 0, 0, + 65536, 65536); + } + } + else if(texTarget == eGL_TEXTURE_2D_ARRAY || texTarget == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY || + texTarget == eGL_TEXTURE_CUBE_MAP || texTarget == eGL_TEXTURE_CUBE_MAP_ARRAY) + { + GLsizei depth = m_Textures[liveId].depth; + if(texTarget == eGL_TEXTURE_CUBE_MAP) + depth *= 6; + for(GLsizei z = 0; z < depth; z++) + { + GL.glFramebufferTextureLayer(eGL_DRAW_FRAMEBUFFER, attach, texture.name, level, z); + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, fb, 1, &attach, 0, 0, + 65536, 65536); + } + } + else if(texTarget == eGL_TEXTURE_2D || texTarget == eGL_TEXTURE_2D_MULTISAMPLE || + texTarget == eGL_TEXTURE_RECTANGLE) + { + GL.glFramebufferTexture2D(eGL_DRAW_FRAMEBUFFER, attach, texTarget, texture.name, level); + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, fb, 1, &attach, 0, 0, + 65536, 65536); + } + else if(texTarget == eGL_TEXTURE_1D_ARRAY) + { + for(GLsizei z = 0; z < m_Textures[liveId].height; z++) + { + GL.glFramebufferTextureLayer(eGL_DRAW_FRAMEBUFFER, attach, texture.name, level, z); + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, fb, 1, &attach, 0, 0, + 65536, 1); + } + } + else if(texTarget == eGL_TEXTURE_1D) + { + GL.glFramebufferTexture1D(eGL_DRAW_FRAMEBUFFER, attach, texTarget, texture.name, level); + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, fb, 1, &attach, 0, 0, + 65536, 1); + } + + GL.glDeleteFramebuffers(1, &fb); + + GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, oldFB); + } + } + + if(IsLoading(m_State)) + { + AddEvent(); + + DrawcallDescription draw; + draw.name = StringFormat::Fmt("%s(%s)", ToStr(gl_CurChunk).c_str(), + ToStr(GetResourceManager()->GetOriginalID(liveId)).c_str()); + draw.flags |= DrawFlags::Clear; + + draw.copyDestination = GetResourceManager()->GetOriginalID(liveId); + + AddDrawcall(draw, true); + + m_ResourceUses[GetResourceManager()->GetID(texture)].push_back( + EventUsage(m_CurEventID, ResourceUsage::Discard)); + } + } + + return true; +} + void WrappedOpenGL::glInvalidateTexImage(GLuint texture, GLint level) { SERIALISE_TIME_CALL(GL.glInvalidateTexImage(texture, level)); - GetResourceManager()->MarkDirtyResource(TextureRes(GetCtx(), texture)); + if(IsCaptureMode(m_State)) + { + GLResourceRecord *record = GetResourceManager()->GetResourceRecord(TextureRes(GetCtx(), texture)); + + if(IsActiveCapturing(m_State)) + { + USE_SCRATCH_SERIALISER(); + ser.SetDrawChunk(); + SCOPED_SERIALISE_CHUNK(gl_CurChunk); + Serialise_glInvalidateTexImage(ser, texture, level); + + GetContextRecord()->AddChunk(scope.Get()); + GetResourceManager()->MarkDirtyResource(record->GetResourceID()); + GetResourceManager()->MarkResourceFrameReferenced(record->GetResourceID(), + eFrameRef_ReadBeforeWrite); + } + else if(IsBackgroundCapturing(m_State)) + { + GetResourceManager()->MarkDirtyResource(record->Resource); + } + } +} + +template +bool WrappedOpenGL::Serialise_glInvalidateTexSubImage(SerialiserType &ser, GLuint textureHandle, + GLint level, GLint xoffset, GLint yoffset, + GLint zoffset, GLsizei width, GLsizei height, + GLsizei depth) +{ + SERIALISE_ELEMENT_LOCAL(texture, TextureRes(GetCtx(), textureHandle)); + SERIALISE_ELEMENT(level); + SERIALISE_ELEMENT(xoffset); + SERIALISE_ELEMENT(yoffset); + SERIALISE_ELEMENT(zoffset); + SERIALISE_ELEMENT(width); + SERIALISE_ELEMENT(height); + SERIALISE_ELEMENT(depth); + + Serialise_DebugMessages(ser); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + GL.glInvalidateTexSubImage(texture.name, level, xoffset, yoffset, zoffset, width, height, depth); + + ResourceId liveId = GetResourceManager()->GetID(texture); + + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + GLenum attach = eGL_COLOR_ATTACHMENT0; + + ResourceFormat fmt = + MakeResourceFormat(m_Textures[liveId].curType, m_Textures[liveId].internalFormat); + + if(fmt.type != ResourceFormatType::Regular && fmt.type != ResourceFormatType::D16S8 && + fmt.type != ResourceFormatType::D24S8 && fmt.type != ResourceFormatType::D32S8 && + fmt.type != ResourceFormatType::S8 && fmt.type != ResourceFormatType::R10G10B10A2 && + fmt.type != ResourceFormatType::R11G11B10) + { + // we don't expect to be able to render to this format, so fill it manually + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, liveId, level, xoffset, + yoffset, zoffset, width, height, depth); + } + else + { + GLenum base = GetBaseFormat(m_Textures[liveId].internalFormat); + if(base == eGL_DEPTH_STENCIL) + attach = eGL_DEPTH_STENCIL_ATTACHMENT; + else if(base == eGL_DEPTH_COMPONENT) + attach = eGL_DEPTH_ATTACHMENT; + else if(base == eGL_STENCIL_INDEX) + attach = eGL_STENCIL_ATTACHMENT; + + GLuint oldFB = 0; + GL.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint *)&oldFB); + + GLuint fb = 0; + GL.glGenFramebuffers(1, &fb); + GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, fb); + + GLenum texTarget = m_Textures[liveId].curType; + + if(texTarget == eGL_TEXTURE_3D || texTarget == eGL_TEXTURE_2D_ARRAY || + texTarget == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY || texTarget == eGL_TEXTURE_CUBE_MAP || + texTarget == eGL_TEXTURE_CUBE_MAP_ARRAY) + { + for(GLsizei z = 0; z < depth; z++) + { + GL.glFramebufferTextureLayer(eGL_DRAW_FRAMEBUFFER, attach, texture.name, level, + zoffset + z); + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, fb, 1, &attach, + xoffset, yoffset, width, height); + } + } + else if(texTarget == eGL_TEXTURE_2D || texTarget == eGL_TEXTURE_2D_MULTISAMPLE || + texTarget == eGL_TEXTURE_RECTANGLE) + { + GL.glFramebufferTexture2D(eGL_DRAW_FRAMEBUFFER, attach, texTarget, texture.name, level); + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, fb, 1, &attach, xoffset, + yoffset, width, height); + } + else if(texTarget == eGL_TEXTURE_1D_ARRAY) + { + for(GLsizei z = 0; z < height; z++) + { + GL.glFramebufferTextureLayer(eGL_DRAW_FRAMEBUFFER, attach, texture.name, level, + z + yoffset); + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, fb, 1, &attach, + xoffset, 0, width, 1); + } + } + else if(texTarget == eGL_TEXTURE_1D) + { + GL.glFramebufferTexture1D(eGL_DRAW_FRAMEBUFFER, attach, texTarget, texture.name, level); + GetReplay()->FillWithDiscardPattern(DiscardType::InvalidateCall, fb, 1, &attach, xoffset, + 0, width, 1); + } + + GL.glDeleteFramebuffers(1, &fb); + + GL.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, oldFB); + } + } + + if(IsLoading(m_State)) + { + AddEvent(); + + DrawcallDescription draw; + draw.name = StringFormat::Fmt("%s(%s)", ToStr(gl_CurChunk).c_str(), + ToStr(GetResourceManager()->GetOriginalID(liveId)).c_str()); + draw.flags |= DrawFlags::Clear; + + draw.copyDestination = GetResourceManager()->GetOriginalID(liveId); + + AddDrawcall(draw, true); + + m_ResourceUses[GetResourceManager()->GetID(texture)].push_back( + EventUsage(m_CurEventID, ResourceUsage::Discard)); + } + } + + return true; } void WrappedOpenGL::glInvalidateTexSubImage(GLuint texture, GLint level, GLint xoffset, @@ -918,7 +1185,28 @@ void WrappedOpenGL::glInvalidateTexSubImage(GLuint texture, GLint level, GLint x SERIALISE_TIME_CALL( GL.glInvalidateTexSubImage(texture, level, xoffset, yoffset, zoffset, width, height, depth)); - GetResourceManager()->MarkDirtyResource(TextureRes(GetCtx(), texture)); + if(IsCaptureMode(m_State)) + { + GLResourceRecord *record = GetResourceManager()->GetResourceRecord(TextureRes(GetCtx(), texture)); + + if(IsActiveCapturing(m_State)) + { + USE_SCRATCH_SERIALISER(); + ser.SetDrawChunk(); + SCOPED_SERIALISE_CHUNK(gl_CurChunk); + Serialise_glInvalidateTexSubImage(ser, texture, level, xoffset, yoffset, zoffset, width, + height, depth); + + GetContextRecord()->AddChunk(scope.Get()); + GetResourceManager()->MarkDirtyResource(record->GetResourceID()); + GetResourceManager()->MarkResourceFrameReferenced(record->GetResourceID(), + eFrameRef_ReadBeforeWrite); + } + else if(IsBackgroundCapturing(m_State)) + { + GetResourceManager()->MarkDirtyResource(record->Resource); + } + } } template @@ -6815,3 +7103,7 @@ INSTANTIATE_FUNCTION_SERIALISED(void, glTextureBufferEXT, GLuint texture, GLenum INSTANTIATE_FUNCTION_SERIALISED(void, glTextureFoveationParametersQCOM, GLuint texture, GLuint layer, GLuint focalPoint, GLfloat focalX, GLfloat focalY, GLfloat gainX, GLfloat gainY, GLfloat foveaArea); +INSTANTIATE_FUNCTION_SERIALISED(void, glInvalidateTexImage, GLuint texture, GLint level); +INSTANTIATE_FUNCTION_SERIALISED(void, glInvalidateTexSubImage, GLuint texture, GLint level, + GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, + GLsizei height, GLsizei depth); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 178bf76b3..7c0be18b6 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -3533,6 +3533,11 @@ VkBool32 WrappedVulkan::DebugCallback(MessageSeverity severity, MessageCategory if(category == MessageCategory::Performance) return false; + // "fragment shader writes to output location X with no matching attachment" + // Not an error, this is defined as with all APIs to drop the output. + if(strstr(pMessageId, "UNASSIGNED-CoreValidation-Shader-OutputNotConsumed")) + return false; + // "Non-linear image is aliased with linear buffer" // Not an error, the validation layers complain at our whole-mem bufs if(strstr(pMessageId, "InvalidAliasing") || strstr(pMessage, "InvalidAliasing")) diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index 124cceac1..29c44db51 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -737,6 +737,63 @@ VulkanDebugManager::VulkanDebugManager(WrappedVulkan *driver) } } + if(RenderDoc::Inst().IsReplayApp()) + { + VkDescriptorPoolSize descPoolTypes[] = { + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, ARRAY_COUNT(m_DiscardSet)}, + }; + + VkDescriptorPoolCreateInfo descPoolInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + NULL, + 0, + ARRAY_COUNT(m_DiscardSet), + ARRAY_COUNT(descPoolTypes), + &descPoolTypes[0], + }; + + // create descriptor pool + vkr = driver->vkCreateDescriptorPool(driver->GetDev(), &descPoolInfo, NULL, &m_DiscardPool); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + CREATE_OBJECT(m_DiscardSetLayout, + { + {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, NULL}, + }); + + CREATE_OBJECT(m_DiscardLayout, m_DiscardSetLayout, 4); + + ResourceFormat fmt; + fmt.type = ResourceFormatType::Regular; + fmt.compType = CompType::Float; + fmt.compByteWidth = 4; + fmt.compCount = 1; + + for(size_t i = 0; i < ARRAY_COUNT(m_DiscardSet); i++) + { + CREATE_OBJECT(m_DiscardSet[i], m_DiscardPool, m_DiscardSetLayout); + + bytebuf pattern = GetDiscardPattern(DiscardType(i), fmt); + + m_DiscardCB[i].Create(m_pDriver, m_Device, pattern.size(), 1, 0); + + memcpy(m_DiscardCB[i].Map(), pattern.data(), pattern.size()); + m_DiscardCB[i].Unmap(); + + VkDescriptorBufferInfo bufInfo = {}; + m_DiscardCB[i].FillDescriptor(bufInfo); + + VkWriteDescriptorSet writes[] = { + { + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, NULL, Unwrap(m_DiscardSet[i]), 0, 0, 1, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, NULL, &bufInfo, NULL, + }, + }; + + ObjDisp(dev)->UpdateDescriptorSets(Unwrap(dev), ARRAY_COUNT(writes), writes, 0, NULL); + } + } + // we only need this during replay, so don't create otherwise. if(RenderDoc::Inst().IsReplayApp()) m_ReadbackWindow.Create(driver, dev, STAGE_BUFFER_BYTE_SIZE, 1, GPUBuffer::eGPUBufferReadback); @@ -768,6 +825,29 @@ VulkanDebugManager::~VulkanDebugManager() m_pDriver->vkDestroyPipeline(dev, m_Array2MSPipe, NULL); m_pDriver->vkDestroyPipeline(dev, m_MS2ArrayPipe, NULL); + m_pDriver->vkDestroyDescriptorPool(dev, m_DiscardPool, NULL); + m_pDriver->vkDestroyPipelineLayout(dev, m_DiscardLayout, NULL); + m_pDriver->vkDestroyDescriptorSetLayout(dev, m_DiscardSetLayout, NULL); + for(size_t i = 0; i < ARRAY_COUNT(m_DiscardCB); i++) + m_DiscardCB[i].Destroy(); + + for(auto it = m_DiscardImages.begin(); it != m_DiscardImages.end(); it++) + { + for(VkImageView view : it->second.views) + m_pDriver->vkDestroyImageView(dev, view, NULL); + for(VkFramebuffer fb : it->second.fbs) + m_pDriver->vkDestroyFramebuffer(dev, fb, NULL); + } + + for(auto it = m_DiscardPipes.begin(); it != m_DiscardPipes.end(); it++) + { + m_pDriver->vkDestroyPipeline(dev, it->second.pso, NULL); + m_pDriver->vkDestroyRenderPass(dev, it->second.rp, NULL); + } + + for(auto it = m_DiscardPatterns.begin(); it != m_DiscardPatterns.end(); it++) + m_pDriver->vkDestroyBuffer(dev, it->second, NULL); + for(size_t i = 0; i < ARRAY_COUNT(m_DepthMS2ArrayPipe); i++) m_pDriver->vkDestroyPipeline(dev, m_DepthMS2ArrayPipe[i], NULL); @@ -1678,6 +1758,428 @@ void VulkanDebugManager::GetBufferData(ResourceId buff, uint64_t offset, uint64_ vt->DeviceWaitIdle(Unwrap(dev)); } +void VulkanDebugManager::FillWithDiscardPattern(VkCommandBuffer cmd, DiscardType type, + VkImage image, VkImageLayout curLayout, + VkImageSubresourceRange discardRange, + VkRect2D discardRect) +{ + VkDevice dev = m_Device; + const VkDevDispatchTable *vt = ObjDisp(dev); + const VulkanCreationInfo::Image &imInfo = GetImageInfo(GetResID(image)); + + VkMarkerRegion marker( + cmd, StringFormat::Fmt("FillWithDiscardPattern %s", ToStr(GetResID(image)).c_str())); + + if(imInfo.samples > 1) + { + WrappedVulkan *driver = m_pDriver; + + bool depth = false; + if(IsDepthOrStencilFormat(imInfo.format)) + depth = true; + + VkImageAspectFlags imAspects = FormatImageAspects(imInfo.format); + + rdcpair key = {imInfo.format, imInfo.samples}; + + DiscardPassData &passdata = m_DiscardPipes[key]; + + // create and cache a pipeline and RP that writes to this format and sample count + if(passdata.pso == VK_NULL_HANDLE) + { + VkAttachmentReference attRef = { + 0, depth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL + : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }; + + VkAttachmentDescription attDesc = { + 0, + imInfo.format, + imInfo.samples, + VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_STORE_OP_STORE, + attRef.layout, + attRef.layout, + }; + + VkSubpassDescription sub = { + 0, VK_PIPELINE_BIND_POINT_GRAPHICS, + }; + + if(depth) + { + sub.pDepthStencilAttachment = &attRef; + } + else + { + sub.pColorAttachments = &attRef; + sub.colorAttachmentCount = 1; + } + + VkRenderPassCreateInfo rpinfo = { + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 1, &attDesc, 1, &sub, 0, NULL, + }; + + VkResult vkr = m_pDriver->vkCreateRenderPass(m_pDriver->GetDev(), &rpinfo, NULL, &passdata.rp); + if(vkr != VK_SUCCESS) + RDCERR("Failed to create shader debug render pass: %s", ToStr(vkr).c_str()); + + ConciseGraphicsPipeline pipeInfo = { + passdata.rp, + m_DiscardLayout, + m_pDriver->GetShaderCache()->GetBuiltinModule(BuiltinShader::BlitVS), + m_pDriver->GetShaderCache()->GetBuiltinModule(BuiltinShader::DiscardFS), + {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}, + imInfo.samples, + false, // sampleRateShading + true, // depthEnable + true, // stencilEnable + VK_STENCIL_OP_REPLACE, + true, // colourOutput + false, // blendEnable + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_ZERO, + 0xf, // writeMask + }; + + CREATE_OBJECT(passdata.pso, pipeInfo); + } + + if(passdata.pso == VK_NULL_HANDLE) + return; + + DiscardImgData &imgdata = m_DiscardImages[GetResID(image)]; + + // create and cache views and framebuffers for every slice in this image + if(imgdata.fbs.empty()) + { + VkImageAspectFlags aspectMask = imAspects; + + for(int pass = 0; pass < 3; pass++) + { + // only depth/stencil images need multiple sets of views to mask out one aspect or the other + if(pass > 0) + { + if(imAspects != (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) + break; + + if(pass == 1) + aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + else if(pass == 2) + aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + } + + for(uint32_t a = 0; a < imInfo.arrayLayers; a++) + { + VkImageViewCreateInfo viewInfo = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + NULL, + 0, + image, + VK_IMAGE_VIEW_TYPE_2D, + imInfo.format, + {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}, + { + aspectMask, 0, 1, a, 1, + }, + }; + + VkImageView view; + VkResult vkr = driver->vkCreateImageView(driver->GetDev(), &viewInfo, NULL, &view); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + imgdata.views.push_back(view); + + // create framebuffer + VkFramebufferCreateInfo fbinfo = { + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + NULL, + 0, + passdata.rp, + 1, + &view, + imInfo.extent.width, + imInfo.extent.height, + 1, + }; + + VkFramebuffer fb; + vkr = driver->vkCreateFramebuffer(driver->GetDev(), &fbinfo, NULL, &fb); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + imgdata.fbs.push_back(fb); + } + } + } + + if(imgdata.fbs.empty()) + return; + + ObjDisp(cmd)->CmdBindPipeline(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(passdata.pso)); + ObjDisp(cmd)->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, + Unwrap(m_DiscardLayout), 0, 1, + UnwrapPtr(m_DiscardSet[(size_t)type]), 0, NULL); + VkViewport viewport = {0.0f, 0.0f, (float)imInfo.extent.width, (float)imInfo.extent.height, 1.0f}; + ObjDisp(cmd)->CmdSetViewport(Unwrap(cmd), 0, 1U, &viewport); + ObjDisp(cmd)->CmdSetScissor(Unwrap(cmd), 0, 1U, &discardRect); + + discardRect.extent.width = + RDCMIN(discardRect.extent.width, imInfo.extent.width - discardRect.offset.x); + discardRect.extent.height = + RDCMIN(discardRect.extent.height, imInfo.extent.height - discardRect.offset.y); + + discardRange.layerCount = + RDCMIN(discardRange.layerCount, imInfo.arrayLayers - discardRange.baseArrayLayer); + + VkRenderPassBeginInfo rpbegin = { + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + NULL, + Unwrap(passdata.rp), + VK_NULL_HANDLE, + discardRect, + }; + + uint32_t pass = 0; + + VkImageMemoryBarrier dstimBarrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, + VK_ACCESS_ALL_READ_BITS | VK_ACCESS_ALL_WRITE_BITS, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + curLayout, depth ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL + : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, Unwrap(image), discardRange, + }; + + DoPipelineBarrier(cmd, 1, &dstimBarrier); + + ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_DiscardLayout), VK_SHADER_STAGE_ALL, 0, 4, + &pass); + + uint32_t offset = 0; + if(imAspects != discardRange.aspectMask) + { + // if we're only discarding one of depth or stencil in a depth/stencil image, pick a + // framebuffer that only targets that aspect. + if(discardRange.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) + offset = imInfo.arrayLayers; + else + offset = imInfo.arrayLayers * 2; + } + + for(uint32_t slice = discardRange.baseArrayLayer; + slice < discardRange.baseArrayLayer + discardRange.layerCount; slice++) + { + rpbegin.framebuffer = Unwrap(imgdata.fbs[slice + offset]); + ObjDisp(cmd)->CmdBeginRenderPass(Unwrap(cmd), &rpbegin, VK_SUBPASS_CONTENTS_INLINE); + + if(depth && discardRange.aspectMask != VK_IMAGE_ASPECT_DEPTH_BIT) + { + pass = 1; + ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_DiscardLayout), VK_SHADER_STAGE_ALL, 0, + 4, &pass); + ObjDisp(cmd)->CmdSetStencilReference( + Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, 0x00); + ObjDisp(cmd)->CmdDraw(Unwrap(cmd), 4, 1, 0, 0); + + pass = 2; + ObjDisp(cmd)->CmdPushConstants(Unwrap(cmd), Unwrap(m_DiscardLayout), VK_SHADER_STAGE_ALL, 0, + 4, &pass); + ObjDisp(cmd)->CmdSetStencilReference( + Unwrap(cmd), VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, 0xff); + ObjDisp(cmd)->CmdDraw(Unwrap(cmd), 4, 1, 0, 0); + } + else + { + ObjDisp(cmd)->CmdDraw(Unwrap(cmd), 4, 1, 0, 0); + } + + ObjDisp(cmd)->CmdEndRenderPass(Unwrap(cmd)); + } + + dstimBarrier.oldLayout = dstimBarrier.newLayout; + dstimBarrier.newLayout = curLayout; + dstimBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dstimBarrier.dstAccessMask = VK_ACCESS_ALL_WRITE_BITS | VK_ACCESS_ALL_READ_BITS; + + DoPipelineBarrier(cmd, 1, &dstimBarrier); + + /* + for(UINT sub = 0; sub < region->NumSubresources; sub++) + { + UINT subresource = region->FirstSubresource + sub; + if(depth) + { + dsvDesc.Texture2DMSArray.FirstArraySlice = GetSliceForSubresource(res, subresource); + m_pDevice->CreateDepthStencilView(res, &dsvDesc, dsv); + cmd->OMSetRenderTargets(0, NULL, FALSE, &dsv); + } + else + { + rtvDesc.Texture2DMSArray.FirstArraySlice = GetSliceForSubresource(res, subresource); + m_pDevice->CreateRenderTargetView(res, &rtvDesc, rtv); + cmd->OMSetRenderTargets(1, &rtv, FALSE, NULL); + } + + UINT mip = GetMipForSubresource(res, subresource); + UINT plane = GetPlaneForSubresource(res, subresource); + + for(D3D12_RECT r : rects) + { + r.right = RDCMIN(LONG(RDCMAX(1U, (UINT)desc.Width >> mip)), r.right); + r.bottom = RDCMIN(LONG(RDCMAX(1U, (UINT)desc.Height >> mip)), r.bottom); + + cmd->RSSetScissorRects(1, &r); + + } + } + */ + + m_pDriver->GetCmdRenderState().BindPipeline(m_pDriver, cmd, VulkanRenderState::BindGraphics, + false); + + return; + } + + rdcpair key = {imInfo.format, type}; + + if(key.first == VK_FORMAT_S8_UINT) + key.first = VK_FORMAT_D32_SFLOAT_S8_UINT; + + VkBuffer buf = m_DiscardPatterns[key]; + VkResult vkr = VK_SUCCESS; + + if(buf == VK_NULL_HANDLE) + { + bytebuf pattern = GetDiscardPattern(key.second, MakeResourceFormat(key.first)); + + VkBufferCreateInfo bufInfo = { + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + NULL, + 0, + pattern.size(), + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + }; + + vkr = m_pDriver->vkCreateBuffer(dev, &bufInfo, NULL, &buf); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + MemoryAllocation alloc = m_pDriver->AllocateMemoryForResource( + buf, MemoryScope::ImmutableReplayDebug, MemoryType::GPULocal); + + vkr = vt->BindBufferMemory(Unwrap(dev), Unwrap(buf), Unwrap(alloc.mem), alloc.offs); + RDCASSERTEQUAL(vkr, VK_SUCCESS); + + vt->CmdUpdateBuffer(Unwrap(cmd), Unwrap(buf), 0, pattern.size(), pattern.data()); + + m_DiscardPatterns[key] = buf; + } + + VkImageAspectFlags aspectFlags = discardRange.aspectMask & FormatImageAspects(imInfo.format); + + rdcarray mainCopies, stencilCopies; + + VkExtent3D extent; + + // copy each slice/mip individually + for(uint32_t a = 0; a < imInfo.arrayLayers; a++) + { + if(a < discardRange.baseArrayLayer || a >= discardRange.baseArrayLayer + discardRange.layerCount) + continue; + + extent = imInfo.extent; + extent.width = RDCMIN(extent.width, discardRect.offset.x + discardRect.extent.width); + extent.height = RDCMIN(extent.height, discardRect.offset.y + discardRect.extent.height); + + for(uint32_t m = 0; m < imInfo.mipLevels; m++) + { + if(m >= discardRange.baseMipLevel && m < discardRange.baseMipLevel + discardRange.levelCount) + { + for(uint32_t z = 0; z < extent.depth; z++) + { + for(uint32_t y = discardRect.offset.y; y < extent.height; y += DiscardPatternHeight) + { + for(uint32_t x = discardRect.offset.x; x < extent.width; x += DiscardPatternWidth) + { + VkBufferImageCopy region = { + 0, + 0, + 0, + {aspectFlags, m, a, 1}, + { + (int)x, (int)y, (int)z, + }, + }; + + region.imageExtent.width = RDCMIN(DiscardPatternWidth, extent.width - x); + region.imageExtent.height = RDCMIN(DiscardPatternHeight, extent.height - y); + region.imageExtent.depth = 1; + + region.bufferRowLength = DiscardPatternWidth; + + // for depth/stencil copies, write depth first + if(aspectFlags == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + + if(aspectFlags != VK_IMAGE_ASPECT_STENCIL_BIT) + mainCopies.push_back(region); + + if(aspectFlags & VK_IMAGE_ASPECT_STENCIL_BIT) + { + uint32_t depthStride = (imInfo.format == VK_FORMAT_D16_UNORM_S8_UINT ? 2 : 4); + // if it's a depth/stencil format, write stencil separately + region.bufferOffset = DiscardPatternWidth * DiscardPatternHeight * depthStride; + region.bufferRowLength = DiscardPatternWidth * depthStride; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + + stencilCopies.push_back(region); + } + } + } + } + } + + // update the extent for the next mip + extent.width = RDCMAX(extent.width >> 1, 1U); + extent.height = RDCMAX(extent.height >> 1, 1U); + extent.depth = RDCMAX(extent.depth >> 1, 1U); + } + } + + VkImageMemoryBarrier dstimBarrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + NULL, + VK_ACCESS_ALL_READ_BITS | VK_ACCESS_ALL_WRITE_BITS, + VK_ACCESS_TRANSFER_WRITE_BIT, + curLayout, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + Unwrap(image), + discardRange, + }; + + DoPipelineBarrier(cmd, 1, &dstimBarrier); + + if(!mainCopies.empty()) + ObjDisp(cmd)->CmdCopyBufferToImage(Unwrap(cmd), Unwrap(buf), Unwrap(image), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + (uint32_t)mainCopies.size(), mainCopies.data()); + + if(!stencilCopies.empty()) + ObjDisp(cmd)->CmdCopyBufferToImage(Unwrap(cmd), Unwrap(buf), Unwrap(image), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + (uint32_t)stencilCopies.size(), stencilCopies.data()); + + dstimBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + dstimBarrier.newLayout = curLayout; + dstimBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + dstimBarrier.dstAccessMask = VK_ACCESS_ALL_WRITE_BITS | VK_ACCESS_ALL_READ_BITS; + + DoPipelineBarrier(cmd, 1, &dstimBarrier); +} + void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe, VkDescriptorPool &descpool, rdcarray &setLayouts, diff --git a/renderdoc/driver/vulkan/vk_debug.h b/renderdoc/driver/vulkan/vk_debug.h index 5212c3817..02695b51f 100644 --- a/renderdoc/driver/vulkan/vk_debug.h +++ b/renderdoc/driver/vulkan/vk_debug.h @@ -25,6 +25,7 @@ #pragma once #include "core/core.h" +#include "replay/replay_driver.h" #include "vk_common.h" #include "vk_core.h" #include "vk_shader_cache.h" @@ -67,6 +68,10 @@ public: void CopyArrayToTex2DMS(VkImage destMS, VkImage srcArray, VkExtent3D extent, uint32_t layers, uint32_t samples, VkFormat fmt); + void FillWithDiscardPattern(VkCommandBuffer cmd, DiscardType type, VkImage image, + VkImageLayout curLayout, VkImageSubresourceRange discardRange, + VkRect2D discardRect); + VkPipelineCache GetPipelineCache() { return m_PipelineCache; } VkPipeline GetCustomPipeline() { return m_Custom.TexPipeline; } VkImage GetCustomTexture() { return m_Custom.TexImg; } @@ -156,4 +161,25 @@ private: WrappedVulkan *m_pDriver = NULL; VkDevice m_Device = VK_NULL_HANDLE; + + struct DiscardPassData + { + VkPipeline pso = VK_NULL_HANDLE; + VkRenderPass rp = VK_NULL_HANDLE; + }; + + struct DiscardImgData + { + rdcarray views; + rdcarray fbs; + }; + + std::map, DiscardPassData> m_DiscardPipes; + std::map m_DiscardImages; + VkDescriptorPool m_DiscardPool = VK_NULL_HANDLE; + VkPipelineLayout m_DiscardLayout = VK_NULL_HANDLE; + VkDescriptorSetLayout m_DiscardSetLayout = VK_NULL_HANDLE; + VkDescriptorSet m_DiscardSet[(size_t)DiscardType::Count] = {}; + GPUBuffer m_DiscardCB[(size_t)DiscardType::Count]; + std::map, VkBuffer> m_DiscardPatterns; }; diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index ab4c8d9a3..4f73d75cf 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -730,6 +730,7 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) { Attachment &dst = attachments[i]; + dst.used = false; dst.flags = pCreateInfo->pAttachments[i].flags; dst.format = pCreateInfo->pAttachments[i].format; dst.samples = pCreateInfo->pAttachments[i].samples; @@ -775,6 +776,11 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, src.pResolveAttachments ? src.pResolveAttachments[i].attachment : ~0U; dst.colorAttachments[i] = src.pColorAttachments[i].attachment; dst.colorLayouts[i] = src.pColorAttachments[i].layout; + + if(dst.resolveAttachments[i] != VK_ATTACHMENT_UNUSED) + attachments[dst.resolveAttachments[i]].used = true; + if(dst.colorAttachments[i] != VK_ATTACHMENT_UNUSED) + attachments[dst.colorAttachments[i]].used = true; } dst.depthstencilAttachment = @@ -788,6 +794,9 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, ? src.pDepthStencilAttachment->layout : VK_IMAGE_LAYOUT_UNDEFINED); + if(dst.depthstencilAttachment >= 0) + attachments[dst.depthstencilAttachment].used = true; + dst.fragmentDensityAttachment = (fragmentDensity && fragmentDensity->fragmentDensityMapAttachment.attachment != VK_ATTACHMENT_UNUSED @@ -820,6 +829,7 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) { Attachment &dst = attachments[i]; + dst.used = false; dst.flags = pCreateInfo->pAttachments[i].flags; dst.format = pCreateInfo->pAttachments[i].format; dst.samples = pCreateInfo->pAttachments[i].samples; @@ -877,6 +887,11 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, src.pResolveAttachments ? src.pResolveAttachments[i].attachment : ~0U; dst.colorAttachments[i] = src.pColorAttachments[i].attachment; dst.colorLayouts[i] = src.pColorAttachments[i].layout; + + if(dst.resolveAttachments[i] != VK_ATTACHMENT_UNUSED) + attachments[dst.resolveAttachments[i]].used = true; + if(dst.colorAttachments[i] != VK_ATTACHMENT_UNUSED) + attachments[dst.colorAttachments[i]].used = true; } dst.depthstencilAttachment = @@ -890,6 +905,9 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, ? src.pDepthStencilAttachment->layout : VK_IMAGE_LAYOUT_UNDEFINED); + if(dst.depthstencilAttachment >= 0) + attachments[dst.depthstencilAttachment].used = true; + // VK_KHR_separate_depth_stencil_layouts const VkAttachmentReferenceStencilLayoutKHR *separateStencil = (const VkAttachmentReferenceStencilLayoutKHR *)FindNextStruct( diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index de9ad9ebd..2ad9659c7 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -375,6 +375,7 @@ struct VulkanCreationInfo struct Attachment { + bool used; VkAttachmentDescriptionFlags flags; VkFormat format; VkSampleCountFlagBits samples; @@ -480,7 +481,7 @@ struct VulkanCreationInfo VkImageType type; VkFormat format; VkExtent3D extent; - int arrayLayers, mipLevels; + uint32_t arrayLayers, mipLevels; VkSampleCountFlagBits samples; bool cube; diff --git a/renderdoc/driver/vulkan/vk_initstate.cpp b/renderdoc/driver/vulkan/vk_initstate.cpp index 80f6951e8..8d51013d6 100644 --- a/renderdoc/driver/vulkan/vk_initstate.cpp +++ b/renderdoc/driver/vulkan/vk_initstate.cpp @@ -1100,7 +1100,7 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, { // MSAA textures we upload into an array image, then the apply does an array-to-MSAA copy // instead of the usual buffer-to-image copies. - int numLayers = c.arrayLayers * (int)c.samples; + uint32_t numLayers = c.arrayLayers * (uint32_t)c.samples; VkImageCreateInfo arrayInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, @@ -1109,8 +1109,8 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, VK_IMAGE_TYPE_2D, c.format, c.extent, - (uint32_t)c.mipLevels, - (uint32_t)numLayers, + c.mipLevels, + numLayers, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, @@ -1169,17 +1169,17 @@ bool WrappedVulkan::Serialise_InitialState(SerialiserType &ser, ResourceId id, rdcarray mainCopies, stencilCopies; // copy each slice/mip individually - for(int a = 0; a < numLayers; a++) + for(uint32_t a = 0; a < numLayers; a++) { extent = c.extent; - for(int m = 0; m < c.mipLevels; m++) + for(uint32_t m = 0; m < c.mipLevels; m++) { VkBufferImageCopy region = { 0, 0, 0, - {aspectFlags, (uint32_t)m, (uint32_t)a, 1}, + {aspectFlags, m, a, 1}, { 0, 0, 0, }, @@ -1612,8 +1612,7 @@ void WrappedVulkan::Apply_InitialState(WrappedVkRes *live, const VkInitialConten RDCASSERTEQUAL(vkr, VK_SUCCESS); GetDebugManager()->CopyArrayToTex2DMS(ToUnwrappedHandle(live), Unwrap(arrayIm), - c.extent, (uint32_t)c.arrayLayers, (uint32_t)c.samples, - fmt); + c.extent, c.arrayLayers, (uint32_t)c.samples, fmt); cmd = GetNextCmd(); @@ -1687,17 +1686,17 @@ void WrappedVulkan::Apply_InitialState(WrappedVkRes *live, const VkInitialConten rdcarray clearRegions; // copy each slice/mip individually - for(int a = 0; a < m_CreationInfo.m_Image[id].arrayLayers; a++) + for(uint32_t a = 0; a < m_CreationInfo.m_Image[id].arrayLayers; a++) { extent = m_CreationInfo.m_Image[id].extent; - for(int m = 0; m < m_CreationInfo.m_Image[id].mipLevels; m++) + for(uint32_t m = 0; m < m_CreationInfo.m_Image[id].mipLevels; m++) { VkBufferImageCopy region = { 0, 0, 0, - {aspectFlags, (uint32_t)m, (uint32_t)a, 1}, + {aspectFlags, m, a, 1}, { 0, 0, 0, }, diff --git a/renderdoc/driver/vulkan/vk_rendertexture.cpp b/renderdoc/driver/vulkan/vk_rendertexture.cpp index f93d3854f..000638203 100644 --- a/renderdoc/driver/vulkan/vk_rendertexture.cpp +++ b/renderdoc/driver/vulkan/vk_rendertexture.cpp @@ -64,8 +64,8 @@ void VulkanReplay::CreateTexImageView(VkImage liveIm, const VulkanCreationInfo:: {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY}, { - VK_IMAGE_ASPECT_COLOR_BIT, 0, RDCMAX(1U, (uint32_t)iminfo.mipLevels), 0, - RDCMAX(1U, (uint32_t)iminfo.arrayLayers), + VK_IMAGE_ASPECT_COLOR_BIT, 0, RDCMAX(1U, iminfo.mipLevels), 0, + RDCMAX(1U, iminfo.arrayLayers), }, }; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index a7364cf42..c46e4fdca 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -2420,7 +2420,7 @@ bool VulkanReplay::GetMinMax(ResourceId texid, const Subresource &sub, CompType data->HistogramSlice = (float)RDCCLAMP(sub.slice, 0U, uint32_t(iminfo.extent.depth >> sub.mip) - 1) + 0.001f; else - data->HistogramSlice = (float)RDCCLAMP(sub.slice, 0U, (uint32_t)iminfo.arrayLayers - 1) + 0.001f; + data->HistogramSlice = (float)RDCCLAMP(sub.slice, 0U, iminfo.arrayLayers - 1) + 0.001f; data->HistogramMip = (int)sub.mip; data->HistogramNumSamples = iminfo.samples; data->HistogramSample = (int)RDCCLAMP(sub.sample, 0U, uint32_t(iminfo.samples) - 1); @@ -2718,7 +2718,7 @@ bool VulkanReplay::GetHistogram(ResourceId texid, const Subresource &sub, CompTy data->HistogramSlice = (float)RDCCLAMP(sub.slice, 0U, uint32_t(iminfo.extent.depth >> sub.mip) - 1) + 0.001f; else - data->HistogramSlice = (float)RDCCLAMP(sub.slice, 0U, (uint32_t)iminfo.arrayLayers - 1) + 0.001f; + data->HistogramSlice = (float)RDCCLAMP(sub.slice, 0U, iminfo.arrayLayers - 1) + 0.001f; data->HistogramMip = (int)sub.mip; data->HistogramNumSamples = iminfo.samples; data->HistogramSample = (int)RDCCLAMP(sub.sample, 0U, uint32_t(iminfo.samples) - 1); @@ -2913,8 +2913,8 @@ void VulkanReplay::GetTextureData(ResourceId tex, const Subresource &sub, imInfo.type, imInfo.format, imInfo.extent, - (uint32_t)imInfo.mipLevels, - (uint32_t)imInfo.arrayLayers, + imInfo.mipLevels, + imInfo.arrayLayers, imInfo.samples, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 45151f04e..35917755a 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -597,7 +597,7 @@ private: VkDeviceSize ImageMemSize = 0; VkImage Image = VK_NULL_HANDLE; VkExtent2D ImageDim = {0, 0}; - int32_t MipLevels = 0, ArrayLayers = 0; + uint32_t MipLevels = 0, ArrayLayers = 0; uint32_t MultiViewMask = 0; VkSampleCountFlagBits Samples = VK_SAMPLE_COUNT_1_BIT; VkRenderPass NoDepthRP = VK_NULL_HANDLE; diff --git a/renderdoc/driver/vulkan/vk_shader_cache.cpp b/renderdoc/driver/vulkan/vk_shader_cache.cpp index d332351b3..4f896e6a4 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.cpp +++ b/renderdoc/driver/vulkan/vk_shader_cache.cpp @@ -100,6 +100,8 @@ static const BuiltinShaderConfig builtinShaders[] = { rdcspv::ShaderStage::Fragment, FeatureCheck::NoCheck, true}, {BuiltinShader::ShaderDebugSampleVS, EmbeddedResource(glsl_shaderdebug_sample_vert), rdcspv::ShaderStage::Vertex, FeatureCheck::NoCheck, true}, + {BuiltinShader::DiscardFS, EmbeddedResource(glsl_discard_frag), rdcspv::ShaderStage::Fragment, + FeatureCheck::NoCheck, true}, }; RDCCOMPILE_ASSERT(ARRAY_COUNT(builtinShaders) == arraydim(), diff --git a/renderdoc/driver/vulkan/vk_shader_cache.h b/renderdoc/driver/vulkan/vk_shader_cache.h index be78d0bce..bf9ddd40c 100644 --- a/renderdoc/driver/vulkan/vk_shader_cache.h +++ b/renderdoc/driver/vulkan/vk_shader_cache.h @@ -58,6 +58,7 @@ enum class BuiltinShader PixelHistoryMSCopyDepthCS, PixelHistoryPrimIDFS, ShaderDebugSampleVS, + DiscardFS, Count, }; diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index e11c09121..7a85d0645 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include "../vk_core.h" +#include "../vk_debug.h" static rdcstr ToHumanStr(const VkAttachmentLoadOp &el) { @@ -1332,6 +1333,54 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(SerialiserType &ser, VkComman renderstate.SetFramebuffer(GetResID(RenderPassBegin.framebuffer), attachments); } + const VulkanCreationInfo::RenderPass &rpinfo = + m_CreationInfo.m_RenderPass[GetCmdRenderState().renderPass]; + + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + const rdcarray &attachments = GetCmdRenderState().GetFramebufferAttachments(); + + for(size_t i = 0; i < attachments.size(); i++) + { + const VulkanCreationInfo::ImageView &viewInfo = + m_CreationInfo.m_ImageView[attachments[i]]; + VkImage image = GetResourceManager()->GetCurrentHandle(viewInfo.image); + const VulkanCreationInfo::Image &imInfo = + GetDebugManager()->GetImageInfo(GetResID(image)); + + if(rpinfo.attachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE && + rpinfo.attachments[i].used) + { + VkImageLayout initialLayout = rpinfo.attachments[i].initialLayout; + // if originally it was UNDEFINED (which is fine with DONT_CARE) and we promoted to + // load so we could preserve the discard pattern, transition to general. + if(initialLayout == VK_IMAGE_LAYOUT_UNDEFINED) + { + VkImageMemoryBarrier dstimBarrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + NULL, + 0, + 0, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + Unwrap(image), + {FormatImageAspects(imInfo.format), 0, VK_REMAINING_MIP_LEVELS, 0, + VK_REMAINING_ARRAY_LAYERS}}; + + DoPipelineBarrier(commandBuffer, 1, &dstimBarrier); + + initialLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + GetDebugManager()->FillWithDiscardPattern(commandBuffer, DiscardType::RenderPassLoad, + image, initialLayout, viewInfo.range, + RenderPassBegin.renderArea); + } + } + } + rdcarray imgBarriers = GetImplicitRenderPassBarriers(); // if we're just replaying the vkCmdBeginRenderPass on its own, we use the first loadRP @@ -1341,8 +1390,6 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(SerialiserType &ser, VkComman // we also need to manually do any clears, since the loadRP will load all attachments if(m_FirstEventID == m_LastEventID) { - VulkanCreationInfo::RenderPass rpinfo = - m_CreationInfo.m_RenderPass[GetCmdRenderState().renderPass]; unwrappedInfo.renderPass = Unwrap(rpinfo.loadRPs[0]); unwrappedInfo.framebuffer = Unwrap(fbinfo.loadFBs[0]); @@ -1356,8 +1403,6 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(SerialiserType &ser, VkComman if(m_FirstEventID == m_LastEventID) { - VulkanCreationInfo::RenderPass rpinfo = - m_CreationInfo.m_RenderPass[GetCmdRenderState().renderPass]; const rdcarray &fbattachments = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.GetFramebufferAttachments(); @@ -1394,9 +1439,11 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(SerialiserType &ser, VkComman } } - ObjDisp(commandBuffer) - ->CmdClearAttachments(Unwrap(commandBuffer), (uint32_t)clearatts.size(), - clearatts.data(), (uint32_t)clearrects.size(), clearrects.data()); + if(!clearatts.empty()) + ObjDisp(commandBuffer) + ->CmdClearAttachments(Unwrap(commandBuffer), (uint32_t)clearatts.size(), + clearatts.data(), (uint32_t)clearrects.size(), + clearrects.data()); } if(eventId && m_DrawcallCallback->PostMisc(eventId, drawFlags, commandBuffer)) @@ -1710,8 +1757,17 @@ bool WrappedVulkan::Serialise_vkCmdEndRenderPass(SerialiserType &ser, VkCommandB m_Partial[Primary].renderPassActive = false; } + rdcarray attachments; + VkRect2D renderArea; + const VulkanCreationInfo::RenderPass &rpinfo = + m_CreationInfo.m_RenderPass[GetCmdRenderState().renderPass]; + { VulkanRenderState &renderstate = GetCmdRenderState(); + + attachments = GetCmdRenderState().GetFramebufferAttachments(); + renderArea = GetCmdRenderState().renderArea; + renderstate.renderPass = ResourceId(); renderstate.SetFramebuffer(ResourceId(), rdcarray()); renderstate.subpassContents = VK_SUBPASS_CONTENTS_MAX_ENUM; @@ -1728,6 +1784,24 @@ bool WrappedVulkan::Serialise_vkCmdEndRenderPass(SerialiserType &ser, VkCommandB m_DrawcallCallback->PostRemisc(eventId, drawFlags, commandBuffer); } + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + for(size_t i = 0; i < attachments.size(); i++) + { + const VulkanCreationInfo::ImageView &viewInfo = + m_CreationInfo.m_ImageView[attachments[i]]; + VkImage image = GetResourceManager()->GetCurrentHandle(viewInfo.image); + + if(rpinfo.attachments[i].storeOp == VK_ATTACHMENT_STORE_OP_DONT_CARE && + rpinfo.attachments[i].used) + { + GetDebugManager()->FillWithDiscardPattern(commandBuffer, DiscardType::RenderPassStore, + image, rpinfo.attachments[i].finalLayout, + viewInfo.range, renderArea); + } + } + } + ResourceId cmd = GetResID(commandBuffer); GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imageStates, FindCommandQueueFamily(m_LastCmdBufferID), @@ -1869,17 +1943,63 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass2(SerialiserType &ser, renderstate.SetFramebuffer(GetResID(RenderPassBegin.framebuffer), attachments); } + const VulkanCreationInfo::RenderPass &rpinfo = + m_CreationInfo.m_RenderPass[GetCmdRenderState().renderPass]; + + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + const rdcarray &attachments = GetCmdRenderState().GetFramebufferAttachments(); + + for(size_t i = 0; i < attachments.size(); i++) + { + const VulkanCreationInfo::ImageView &viewInfo = + m_CreationInfo.m_ImageView[attachments[i]]; + VkImage image = GetResourceManager()->GetCurrentHandle(viewInfo.image); + const VulkanCreationInfo::Image &imInfo = + GetDebugManager()->GetImageInfo(GetResID(image)); + + if(rpinfo.attachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE && + rpinfo.attachments[i].used) + { + VkImageLayout initialLayout = rpinfo.attachments[i].initialLayout; + // if originally it was UNDEFINED (which is fine with DONT_CARE) and we promoted to + // load so we could preserve the discard pattern, transition to general. + if(initialLayout == VK_IMAGE_LAYOUT_UNDEFINED) + { + VkImageMemoryBarrier dstimBarrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + NULL, + 0, + 0, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + Unwrap(image), + {FormatImageAspects(imInfo.format), 0, VK_REMAINING_MIP_LEVELS, 0, + VK_REMAINING_ARRAY_LAYERS}}; + + DoPipelineBarrier(commandBuffer, 1, &dstimBarrier); + + initialLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + GetDebugManager()->FillWithDiscardPattern(commandBuffer, DiscardType::RenderPassLoad, + image, initialLayout, viewInfo.range, + RenderPassBegin.renderArea); + } + } + } + rdcarray imgBarriers = GetImplicitRenderPassBarriers(); // if we're just replaying the vkCmdBeginRenderPass on its own, we use the first loadRP - // instead of the real thing. This still does the clears that we want but then doesn't - // require us to finish off any subpasses etc. + // instead of the real thing. This then doesn't require us to finish off any subpasses etc. // we need to manually do the subpass 0 barriers, since loadRP expects the image to already // be in subpass 0's layout + // we also need to manually do any clears, since the loadRP will load all attachments if(m_FirstEventID == m_LastEventID) { - VulkanCreationInfo::RenderPass rpinfo = - m_CreationInfo.m_RenderPass[GetCmdRenderState().renderPass]; unwrappedInfo.renderPass = Unwrap(rpinfo.loadRPs[0]); unwrappedInfo.framebuffer = Unwrap(fbinfo.loadFBs[0]); @@ -1892,6 +2012,51 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass2(SerialiserType &ser, ObjDisp(commandBuffer) ->CmdBeginRenderPass2(Unwrap(commandBuffer), &unwrappedInfo, &unwrappedBeginInfo); + if(m_FirstEventID == m_LastEventID) + { + const rdcarray &fbattachments = + m_BakedCmdBufferInfo[m_LastCmdBufferID].state.GetFramebufferAttachments(); + + rdcarray clearatts; + rdcarray clearrects; + RDCASSERT(unwrappedInfo.clearValueCount <= (uint32_t)rpinfo.attachments.size(), + unwrappedInfo.clearValueCount, rpinfo.attachments.size()); + for(int32_t c = 0; c < rpinfo.subpasses[0].colorAttachments.count() + 1; c++) + { + uint32_t att = ~0U; + + if(c < rpinfo.subpasses[0].colorAttachments.count()) + att = rpinfo.subpasses[0].colorAttachments[c]; + else if(rpinfo.subpasses[0].depthstencilAttachment >= 0) + att = (uint32_t)rpinfo.subpasses[0].depthstencilAttachment; + + if(att >= rpinfo.attachments.size()) + continue; + + if(rpinfo.attachments[att].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR || + rpinfo.attachments[att].stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) + { + VulkanCreationInfo::ImageView viewinfo = m_CreationInfo.m_ImageView[fbattachments[att]]; + + VkClearRect rect = {unwrappedInfo.renderArea, viewinfo.range.baseArrayLayer, + viewinfo.range.layerCount}; + VkClearAttachment clear = {}; + clear.aspectMask = FormatImageAspects(rpinfo.attachments[att].format); + clear.colorAttachment = c; + if(att < unwrappedInfo.clearValueCount) + clear.clearValue = unwrappedInfo.pClearValues[att]; + clearrects.push_back(rect); + clearatts.push_back(clear); + } + } + + if(!clearatts.empty()) + ObjDisp(commandBuffer) + ->CmdClearAttachments(Unwrap(commandBuffer), (uint32_t)clearatts.size(), + clearatts.data(), (uint32_t)clearrects.size(), + clearrects.data()); + } + if(eventId && m_DrawcallCallback->PostMisc(eventId, drawFlags, commandBuffer)) { // Do not call vkCmdBeginRenderPass2 again. @@ -2243,8 +2408,17 @@ bool WrappedVulkan::Serialise_vkCmdEndRenderPass2(SerialiserType &ser, VkCommand m_Partial[Primary].renderPassActive = false; } + rdcarray attachments; + VkRect2D renderArea; + const VulkanCreationInfo::RenderPass &rpinfo = + m_CreationInfo.m_RenderPass[GetCmdRenderState().renderPass]; + { VulkanRenderState &renderstate = GetCmdRenderState(); + + attachments = GetCmdRenderState().GetFramebufferAttachments(); + renderArea = GetCmdRenderState().renderArea; + renderstate.renderPass = ResourceId(); renderstate.SetFramebuffer(ResourceId(), rdcarray()); renderstate.subpassContents = VK_SUBPASS_CONTENTS_MAX_ENUM; @@ -2260,6 +2434,24 @@ bool WrappedVulkan::Serialise_vkCmdEndRenderPass2(SerialiserType &ser, VkCommand m_DrawcallCallback->PostRemisc(eventId, drawFlags, commandBuffer); } + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + for(size_t i = 0; i < attachments.size(); i++) + { + const VulkanCreationInfo::ImageView &viewInfo = + m_CreationInfo.m_ImageView[attachments[i]]; + VkImage image = GetResourceManager()->GetCurrentHandle(viewInfo.image); + + if(rpinfo.attachments[i].storeOp == VK_ATTACHMENT_STORE_OP_DONT_CARE && + rpinfo.attachments[i].used) + { + GetDebugManager()->FillWithDiscardPattern(commandBuffer, DiscardType::RenderPassStore, + image, rpinfo.attachments[i].finalLayout, + viewInfo.range, renderArea); + } + } + } + ResourceId cmd = GetResID(commandBuffer); GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imageStates, FindCommandQueueFamily(m_LastCmdBufferID), @@ -3245,6 +3437,21 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier( ->CmdPipelineBarrier(Unwrap(commandBuffer), srcStageMask, destStageMask, dependencyFlags, memoryBarrierCount, pMemoryBarriers, (uint32_t)bufBarriers.size(), bufBarriers.data(), (uint32_t)imgBarriers.size(), imgBarriers.data()); + + if(IsActiveReplaying(m_State) && + m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + for(uint32_t i = 0; i < imageMemoryBarrierCount; i++) + { + const VkImageMemoryBarrier &b = pImageMemoryBarriers[i]; + if(b.image != VK_NULL_HANDLE && b.oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) + { + GetDebugManager()->FillWithDiscardPattern( + commandBuffer, DiscardType::UndefinedTransition, b.image, b.newLayout, + b.subresourceRange, {{0, 0}, {65536, 65536}}); + } + } + } } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp index 9174a09c6..b5f841df9 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp @@ -2229,10 +2229,11 @@ bool WrappedVulkan::Serialise_vkCmdClearColorImage(SerialiserType &ser, VkComman AddEvent(); DrawcallDescription draw; - draw.name = StringFormat::Fmt("vkCmdClearColorImage(%f, %f, %f, %f)", Color.float32[0], - Color.float32[1], Color.float32[2], Color.float32[3]); draw.flags |= DrawFlags::Clear | DrawFlags::ClearColor; draw.copyDestination = GetResourceManager()->GetOriginalID(GetResID(image)); + draw.name = StringFormat::Fmt("vkCmdClearColorImage(%s, %f, %f, %f, %f)", + ToStr(draw.copyDestination).c_str(), Color.float32[0], + Color.float32[1], Color.float32[2], Color.float32[3]); draw.copyDestinationSubresource = Subresource(); if(rangeCount > 0) draw.copyDestinationSubresource = diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index 1fc8c625e..ded841927 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -892,13 +892,24 @@ bool WrappedVulkan::Serialise_vkCreateRenderPass(SerialiserType &ser, VkDevice d VkAttachmentDescription *att = (VkAttachmentDescription *)CreateInfo.pAttachments; for(uint32_t i = 0; i < CreateInfo.attachmentCount; i++) { - att[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - att[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + att[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + att[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - if(att[i].loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) - att[i].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - if(att[i].stencilLoadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) - att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + if(att[i].loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) + { + att[i].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + if(att[i].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED) + att[i].initialLayout = VK_IMAGE_LAYOUT_GENERAL; + } + if(att[i].stencilLoadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) + { + att[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + if(att[i].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED) + att[i].initialLayout = VK_IMAGE_LAYOUT_GENERAL; + } + } // sanitise the actual layouts used to create the renderpass SanitiseOldImageLayout(att[i].initialLayout); diff --git a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp index 0287378fe..c97b97586 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include "../vk_core.h" +#include "../vk_debug.h" /* * Events and fences need careful handling. @@ -870,6 +871,20 @@ bool WrappedVulkan::Serialise_vkCmdWaitEvents( ->CmdWaitEvents(Unwrap(commandBuffer), 1, &ev, srcStageMask, dstStageMask, memoryBarrierCount, pMemoryBarriers, (uint32_t)bufBarriers.size(), bufBarriers.data(), (uint32_t)imgBarriers.size(), imgBarriers.data()); + + if(m_ReplayOptions.optimisation != ReplayOptimisationLevel::Fastest) + { + for(uint32_t i = 0; i < imageMemoryBarrierCount; i++) + { + const VkImageMemoryBarrier &b = pImageMemoryBarriers[i]; + if(b.image != VK_NULL_HANDLE && b.oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) + { + GetDebugManager()->FillWithDiscardPattern( + commandBuffer, DiscardType::UndefinedTransition, b.image, b.newLayout, + b.subresourceRange, {{0, 0}, {~0U, ~0U}}); + } + } + } } } diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index 27d81967c..053d77528 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -589,6 +589,7 @@ + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index 2879291ce..6ace404af 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -1082,6 +1082,7 @@ Resources\glsl + diff --git a/renderdoc/replay/replay_driver.cpp b/renderdoc/replay/replay_driver.cpp index 7ccb9113c..44cd78907 100644 --- a/renderdoc/replay/replay_driver.cpp +++ b/renderdoc/replay/replay_driver.cpp @@ -23,7 +23,9 @@ ******************************************************************************/ #include "replay_driver.h" +#include "compressonator/CMP_Core.h" #include "maths/formatpacking.h" +#include "maths/half_convert.h" #include "serialise/serialiser.h" template <> @@ -1124,3 +1126,457 @@ const Vec4f colorRamp[22] = { Vec4f(1.000000f, 0.376471f, 0.752941f, 1.0f), Vec4f(1.000000f, 0.627451f, 1.000000f, 1.0f), Vec4f(1.000000f, 0.878431f, 1.000000f, 1.0f), Vec4f(1.000000f, 1.000000f, 1.000000f, 1.0f), }; + +bytebuf GetDiscardPattern(DiscardType type, const ResourceFormat &fmt, uint32_t rowPitch, bool invert) +{ + static const rdcliteral patterns[] = { + // DiscardType::RenderPassLoad + "..#.....##...##..##....##....##..#..#.####..###..##..###..####.." + "..#....#..#.#..#.#.#...#.#..#..#.##.#..#...#....#..#.#..#.#....." + "..#....#..#.#..#.#..#..#..#.#..#.##.#..#...#....#..#.#..#.###..." + "..#....#..#.####.#..#..#..#.#..#.#.##..#...#....####.###..#....." + "..#....#..#.#..#.#.#...#.#..#..#.#.##..#...#....#..#.#..#.#....." + "..####..##..#..#.##....##....##..#..#..#....###.#..#.#..#.####.." + "................................................................" + "................................................................"_lit, + + // DiscardType::RenderPassStore + "...###.####..##..###...##....##..#..#.####..###..##..###..####.." + "..#.....#...#..#.#..#..#.#..#..#.##.#..#...#....#..#.#..#.#....." + "...#....#...#..#.#..#..#..#.#..#.##.#..#...#....#..#.#..#.###..." + "....#...#...#..#.###...#..#.#..#.#.##..#...#....####.###..#....." + ".....#..#...#..#.#..#..#.#..#..#.#.##..#...#....#..#.#..#.#....." + "..###...#....##..#..#..##....##..#..#..#....###.#..#.#..#.####.." + "................................................................" + "................................................................"_lit, + + // DiscardType::UndefinedTransition + "..#..#.#..#.##...####.####.####.#..#.####.##....####.#..#..###.." + "..#..#.##.#.#.#..#....#.....#...##.#.#....#.#....#...####.#....." + "..#..#.##.#.#..#.###..###...#...##.#.###..#..#...#...##.#.#....." + "..#..#.#.##.#..#.#....#.....#...#.##.#....#..#...#...#..#.#.##.." + "..#..#.#.##.#.#..#....#.....#...#.##.#....#.#....#...#..#.#..#.." + "...##..#..#.##...####.#....####.#..#.####.##....####.#..#..##..." + "................................................................" + "................................................................"_lit, + + // DiscardType::DiscardCall + "..##...####..###..###...#...###..##...####.##...####.#..#..###.." + "..#.#...#...#....#.....#.#..#..#.#.#..#....#.#...#...####.#....." + "..#..#..#....#...#.....#.#..#..#.#..#.###..#..#..#...##.#.#....." + "..#..#..#.....#..#....#####.###..#..#.#....#..#..#...#..#.#.##.." + "..#.#...#......#.#....#...#.#..#.#.#..#....#.#...#...#..#.#..#.." + "..##...####.###...###.#...#.#..#.##...####.##...####.#..#..##..." + "................................................................" + "................................................................"_lit, + // DiscardType::InvalidateCall + "...####.#..#.#...#...#...#....####.##.....#...#####.####.##....." + "....#...##.#.#...#..#.#..#.....#...#.#...#.#....#...#....#.#...." + "....#...##.#.#...#..#.#..#.....#...#..#..#.#....#...###..#..#..." + "....#...#.##..#.#..#####.#.....#...#..#.#####...#...#....#..#..." + "....#...#.##..#.#..#...#.#.....#...#.#..#...#...#...#....#.#...." + "...####.#..#...#...#...#.####.####.##...#...#...#...####.##....." + "................................................................" + "................................................................"_lit, + }; + + const rdcliteral &pattern = patterns[(int)type]; + + RDCASSERT(pattern.length() == DiscardPatternWidth * DiscardPatternHeight); + + bytebuf ret; + + if(fmt.type == ResourceFormatType::Regular || fmt.type == ResourceFormatType::A8 || + fmt.type == ResourceFormatType::S8) + { + byte black[8] = {}; + byte white[8] = {}; + + if(fmt.compType == CompType::Float) + { + if(fmt.compByteWidth == 8) + { + double b = 0.0; + double w = 1000.0; + memcpy(black, &b, sizeof(b)); + memcpy(white, &w, sizeof(w)); + } + else if(fmt.compByteWidth == 4) + { + float b = 0.0f; + float w = 1000.0f; + memcpy(black, &b, sizeof(b)); + memcpy(white, &w, sizeof(w)); + } + else + { + uint16_t b = ConvertToHalf(0.0f); + uint16_t w = ConvertToHalf(1000.0f); + memcpy(black, &b, sizeof(b)); + memcpy(white, &w, sizeof(w)); + } + } + else if(fmt.compType == CompType::Depth) + { + if(fmt.compByteWidth == 4) + { + float b = 0.0f; + float w = 1.0f; + memcpy(black, &b, sizeof(b)); + memcpy(white, &w, sizeof(w)); + } + else + { + // other depth formats are normalised + memset(black, 0, sizeof(black)); + memset(white, 0xff, sizeof(white)); + } + } + else if(fmt.compType == CompType::UInt || fmt.compType == CompType::SInt) + { + // ints we use 0 and 127 so it's the same for every signed type and byte width + white[0] = 127; + } + else + { + // all other types are normalised, so we just set white to 0xff + memset(black, 0, sizeof(black)); + memset(white, 0xff, sizeof(white)); + } + + uint32_t tightPitch = DiscardPatternWidth * fmt.compByteWidth * fmt.compCount; + rowPitch = RDCMAX(rowPitch, tightPitch); + + ret.resize(rowPitch * DiscardPatternHeight); + byte *out = ret.data(); + + for(int yi = 0; yi < DiscardPatternHeight; yi++) + { + int y = invert ? DiscardPatternHeight - 1 - yi : yi; + for(int x = 0; x < DiscardPatternWidth; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + for(uint8_t i = 0; i < fmt.compCount; i++) + { + if(c == '#') + memcpy(out, white, fmt.compByteWidth); + else + memcpy(out, black, fmt.compByteWidth); + out += fmt.compByteWidth; + } + } + out += (rowPitch - tightPitch); + } + } + else if(fmt.type == ResourceFormatType::R10G10B10A2) + { + uint32_t tightPitch = DiscardPatternWidth * sizeof(uint32_t); + rowPitch = RDCMAX(rowPitch, tightPitch); + + ret.resize(rowPitch * DiscardPatternHeight); + uint32_t *out = (uint32_t *)ret.data(); + + for(int yi = 0; yi < DiscardPatternHeight; yi++) + { + int y = invert ? DiscardPatternHeight - 1 - yi : yi; + for(int x = 0; x < DiscardPatternWidth; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + *(out++) = (c == '#') ? 0xffffffff : 0x00000000; + } + out += (rowPitch - tightPitch); + } + } + else if(fmt.type == ResourceFormatType::R5G6B5 || fmt.type == ResourceFormatType::R5G5B5A1 || + fmt.type == ResourceFormatType::R4G4B4A4) + { + uint32_t tightPitch = DiscardPatternWidth * sizeof(uint16_t); + rowPitch = RDCMAX(rowPitch, tightPitch); + + ret.resize(rowPitch * DiscardPatternHeight); + uint16_t *out = (uint16_t *)ret.data(); + + for(int yi = 0; yi < DiscardPatternHeight; yi++) + { + int y = invert ? DiscardPatternHeight - 1 - yi : yi; + for(int x = 0; x < DiscardPatternWidth; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + *(out++) = (c == '#') ? 0xffff : 0x0000; + } + out += (rowPitch - tightPitch); + } + } + else if(fmt.type == ResourceFormatType::R4G4) + { + uint32_t tightPitch = DiscardPatternWidth * sizeof(uint8_t); + rowPitch = RDCMAX(rowPitch, tightPitch); + + ret.resize(rowPitch * DiscardPatternHeight); + byte *out = ret.data(); + + for(int yi = 0; yi < DiscardPatternHeight; yi++) + { + int y = invert ? DiscardPatternHeight - 1 - yi : yi; + for(int x = 0; x < DiscardPatternWidth; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + *(out++) = (c == '#') ? 0xff : 0x00; + } + out += (rowPitch - tightPitch); + } + } + else if(fmt.type == ResourceFormatType::R11G11B10) + { + const uint32_t black = ConvertToR11G11B10(Vec3f(0.0f, 0.0f, 0.0f)); + const uint32_t white = ConvertToR11G11B10(Vec3f(1000.0f, 1000.0f, 1000.0f)); + + uint32_t tightPitch = DiscardPatternWidth * sizeof(uint32_t); + rowPitch = RDCMAX(rowPitch, tightPitch); + + ret.resize(rowPitch * DiscardPatternHeight); + uint32_t *out = (uint32_t *)ret.data(); + + for(int yi = 0; yi < DiscardPatternHeight; yi++) + { + int y = invert ? DiscardPatternHeight - 1 - yi : yi; + for(int x = 0; x < DiscardPatternWidth; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + *(out++) = (c == '#') ? white : black; + } + out += (rowPitch - tightPitch); + } + } + else if(fmt.type == ResourceFormatType::R9G9B9E5) + { + const uint32_t black = ConvertToR9G9B9E5(Vec3f(0.0f, 0.0f, 0.0f)); + const uint32_t white = ConvertToR9G9B9E5(Vec3f(1000.0f, 1000.0f, 1000.0f)); + + uint32_t tightPitch = DiscardPatternWidth * sizeof(uint32_t); + rowPitch = RDCMAX(rowPitch, tightPitch); + + ret.resize(rowPitch * DiscardPatternHeight); + uint32_t *out = (uint32_t *)ret.data(); + + for(int yi = 0; yi < DiscardPatternHeight; yi++) + { + int y = invert ? DiscardPatternHeight - 1 - yi : yi; + for(int x = 0; x < DiscardPatternWidth; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + *(out++) = (c == '#') ? white : black; + } + out += (rowPitch - tightPitch); + } + } + else if(fmt.type == ResourceFormatType::D16S8 || fmt.type == ResourceFormatType::D24S8 || + fmt.type == ResourceFormatType::D32S8) + { + uint32_t white = 0xffffffff; + uint32_t black = 0; + + uint32_t depthStride = 0; + + if(fmt.type == ResourceFormatType::D16S8) + { + depthStride = 2; + } + else if(fmt.type == ResourceFormatType::D24S8) + { + depthStride = 4; + } + else if(fmt.type == ResourceFormatType::D32S8) + { + depthStride = 4; + float maxDepth = 1.0f; + memcpy(&white, &maxDepth, sizeof(float)); + } + + uint32_t tightPitch = DiscardPatternWidth * depthStride; + uint32_t tightStencilPitch = DiscardPatternWidth * sizeof(byte); + rowPitch = RDCMAX(rowPitch, RDCMAX(tightPitch, tightStencilPitch)); + + ret.resize(rowPitch * DiscardPatternHeight * 2); + byte *depthOut = ret.data(); + byte *stencilOut = depthOut + rowPitch * DiscardPatternHeight; + + for(int yi = 0; yi < DiscardPatternHeight; yi++) + { + int y = invert ? DiscardPatternHeight - 1 - yi : yi; + for(int x = 0; x < DiscardPatternWidth; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + if(c == '#') + { + memcpy(depthOut, &white, depthStride); + *(stencilOut++) = 0xff; + } + else + { + memcpy(depthOut, &black, depthStride); + *(stencilOut++) = 0x00; + } + + depthOut += depthStride; + } + + depthOut += (rowPitch - tightPitch); + stencilOut += (rowPitch - tightStencilPitch); + } + } + else if(fmt.type == ResourceFormatType::BC1 || fmt.type == ResourceFormatType::BC2 || + fmt.type == ResourceFormatType::BC3 || fmt.type == ResourceFormatType::BC4 || + fmt.type == ResourceFormatType::BC5 || fmt.type == ResourceFormatType::BC6 || + fmt.type == ResourceFormatType::BC7) + { +#if ENABLED(RDOC_ANDROID) + RDCERR("Format %s not supported on android", fmt.Name().c_str()); +#else + const uint16_t whalf = ConvertToHalf(1000.0f); + + byte block[16]; + + uint32_t blockSize = + fmt.type == ResourceFormatType::BC1 || fmt.type == ResourceFormatType::BC4 ? 8 : 16; + uint32_t tightPitch = (DiscardPatternWidth / 4) * blockSize; + rowPitch = RDCMAX(rowPitch, tightPitch); + + ret.reserve(rowPitch * (DiscardPatternHeight / 4)); + + bytebuf inblock; + + void *bc6opts = NULL; + + CreateOptionsBC6(&bc6opts); + + SetQualityBC6(bc6opts, 0.1f); + + for(uint32_t yi = 0; yi < DiscardPatternHeight; yi += 4) + { + uint32_t baseY = invert ? DiscardPatternHeight - 1 - yi : yi; + + for(uint32_t baseX = 0; baseX < DiscardPatternWidth; baseX += 4) + { + inblock.clear(); + + // inblock is 4x4 RGBA8_UNORM + if(fmt.type == ResourceFormatType::BC1 || fmt.type == ResourceFormatType::BC2 || + fmt.type == ResourceFormatType::BC3 || fmt.type == ResourceFormatType::BC7) + { + for(uint32_t y = baseY; y < baseY + 4; invert ? y-- : y++) + { + for(uint32_t x = baseX; x < baseX + 4; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + + inblock.push_back(c == '#' ? 0xff : 0x00); + inblock.push_back(c == '#' ? 0xff : 0x00); + inblock.push_back(c == '#' ? 0xff : 0x00); + inblock.push_back(c == '#' ? 0xff : 0x00); + } + } + } + // inblock is 4x4 R8_UNORM + else if(fmt.type == ResourceFormatType::BC4 || fmt.type == ResourceFormatType::BC5) + { + for(uint32_t y = baseY; y < baseY + 4; invert ? y-- : y++) + { + for(uint32_t x = baseX; x < baseX + 4; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + + inblock.push_back(c == '#' ? 0xff : 0x00); + } + } + } + // inblock is 4x4 RGB16_FLOAT + else if(fmt.type == ResourceFormatType::BC6) + { + for(uint32_t y = baseY; y < baseY + 4; invert ? y-- : y++) + { + for(uint32_t x = baseX; x < baseX + 4; x++) + { + char c = pattern.c_str()[y * DiscardPatternWidth + x]; + + inblock.push_back(c == '#' ? (whalf & 0xff) : 0x00); + inblock.push_back(c == '#' ? ((whalf >> 8) & 0xff) : 0x00); + + inblock.push_back(c == '#' ? (whalf & 0xff) : 0x00); + inblock.push_back(c == '#' ? ((whalf >> 8) & 0xff) : 0x00); + + inblock.push_back(c == '#' ? (whalf & 0xff) : 0x00); + inblock.push_back(c == '#' ? ((whalf >> 8) & 0xff) : 0x00); + } + } + } + + if(fmt.type == ResourceFormatType::BC1) + CompressBlockBC1(inblock.data(), 4 * sizeof(uint32_t), block, NULL); + else if(fmt.type == ResourceFormatType::BC2) + CompressBlockBC2(inblock.data(), 4 * sizeof(uint32_t), block, NULL); + else if(fmt.type == ResourceFormatType::BC3) + CompressBlockBC3(inblock.data(), 4 * sizeof(uint32_t), block, NULL); + else if(fmt.type == ResourceFormatType::BC4) + CompressBlockBC4(inblock.data(), 4, block, NULL); + else if(fmt.type == ResourceFormatType::BC5) + CompressBlockBC5(inblock.data(), 4, inblock.data(), 4, block, NULL); + else if(fmt.type == ResourceFormatType::BC6) + CompressBlockBC6((uint16_t *)inblock.data(), 4 * 3, block, bc6opts); + else if(fmt.type == ResourceFormatType::BC7) + CompressBlockBC7(inblock.data(), 4 * sizeof(uint32_t), block, NULL); + + ret.append(block, blockSize); + } + + ret.resize(ret.size() + (rowPitch - tightPitch)); + } + + DestroyOptionsBC6(bc6opts); +#endif + } + else if(fmt.type == ResourceFormatType::ETC2 || fmt.type == ResourceFormatType::EAC || + fmt.type == ResourceFormatType::ASTC || fmt.type == ResourceFormatType::PVRTC || + fmt.type == ResourceFormatType::YUV8 || fmt.type == ResourceFormatType::YUV10 || + fmt.type == ResourceFormatType::YUV12 || fmt.type == ResourceFormatType::YUV16) + { + RDCERR("Format %s not supported for proper discard pattern", fmt.Name().c_str()); + } + else + { + RDCERR("Unhandled format %s needing discard pattern", fmt.Name().c_str()); + } + + // if we didn't get a proper pattern, try at least to do some kind of checkerboard (not knowing if + // this will align with the format or not) + if(ret.empty()) + { + uint32_t tightPitch = DiscardPatternWidth * 16; + rowPitch = RDCMAX(rowPitch, tightPitch); + + ret.resize(rowPitch * DiscardPatternHeight); + + byte *out = ret.data(); + int val = 0; + for(uint32_t y = 0; y < DiscardPatternHeight; y++) + { + byte *rowout = out; + + for(uint32_t i = 0; i < DiscardPatternWidth * DiscardPatternHeight; i++) + { + memset(rowout, val, 16); + rowout += 16; + + // toggle between memset(0) and memset(0xff) + if(val) + val = 0; + else + val = 0xff; + } + + out += rowPitch; + } + } + + return ret; +} diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index e44ce6b83..887bd190a 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -344,3 +344,20 @@ struct HighlightCache }; extern const Vec4f colorRamp[22]; + +enum class DiscardType : int +{ + RenderPassLoad, // discarded on renderpass load + RenderPassStore, // discarded after renderpass store + UndefinedTransition, // transition from undefined layout + DiscardCall, // explicit Discard() type API call + InvalidateCall, // explicit Invalidate() type API call + Count, +}; + +static constexpr uint32_t DiscardPatternWidth = 64; +static constexpr uint32_t DiscardPatternHeight = 8; + +// returns a pattern to fill the texture with +bytebuf GetDiscardPattern(DiscardType type, const ResourceFormat &fmt, uint32_t rowPitch = 1, + bool invert = false); diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt index d0059f80c..1f1078f36 100644 --- a/util/test/demos/CMakeLists.txt +++ b/util/test/demos/CMakeLists.txt @@ -13,6 +13,7 @@ set(VULKAN_SRC vk/vk_custom_border_color.cpp vk/vk_descriptor_index.cpp vk/vk_discard_rects.cpp + vk/vk_discard_zoo.cpp vk/vk_draw_zoo.cpp vk/vk_empty_capture.cpp vk/vk_ext_buffer_address.cpp @@ -60,6 +61,7 @@ set(OPENGL_SRC gl/gl_callstacks.cpp gl/gl_cbuffer_zoo.cpp gl/gl_depthstencil_fbo.cpp + gl/gl_discard_zoo.cpp gl/gl_empty_capture.cpp gl/gl_entry_points.cpp gl/gl_large_bcn_arrays.cpp diff --git a/util/test/demos/d3d11/d3d11_discard_zoo.cpp b/util/test/demos/d3d11/d3d11_discard_zoo.cpp new file mode 100644 index 000000000..2edf59f38 --- /dev/null +++ b/util/test/demos/d3d11/d3d11_discard_zoo.cpp @@ -0,0 +1,282 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "d3d11_test.h" + +RD_TEST(D3D11_Discard_Zoo, D3D11GraphicsTest) +{ + static constexpr const char *Description = "Tests texture discarding resources in D3D11."; + + byte empty[16 * 1024 * 1024] = {}; + + void Clear(ID3D11Texture2DPtr t) + { + if(!t) + return; + + D3D11_TEXTURE2D_DESC desc = {}; + t->GetDesc(&desc); + + if(desc.BindFlags & D3D11_BIND_RENDER_TARGET) + { + ID3D11RenderTargetViewPtr rt; + + for(UINT m = 0; m < desc.MipLevels; m++) + { + rt = MakeRTV(t).FirstMip(m); + ClearRenderTargetView(rt, {0.0f, 1.0f, 0.0f, 1.0f}); + } + } + else if(desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) + { + ID3D11DepthStencilViewPtr dsv; + + for(UINT m = 0; m < desc.MipLevels; m++) + { + dsv = MakeDSV(t).FirstMip(m); + ctx->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0.4f, 0x40); + } + } + else + { + for(UINT i = 0; i < desc.ArraySize * desc.MipLevels; i++) + ctx->UpdateSubresource(t, i, NULL, empty, 32, 32); + } + } + + template + void DiscardView1(T view, UINT x, UINT y, UINT width, UINT height) + { + D3D11_RECT rect = {(LONG)x, (LONG)y, LONG(x + width), LONG(y + height)}; + ctx1->DiscardView1(view, &rect, 1); + } + + template + void DiscardView(T view) + { + ctx1->DiscardView(view); + } + + int main() + { + // initialise, create window, create device, etc + if(!Init()) + return 3; + + memset(empty, 0x88, sizeof(empty)); + + std::vector texs; + +#define TEX_TEST(name, x) \ + if(first) \ + { \ + texs.push_back(x); \ + Clear(texs.back()); \ + SetDebugName(texs.back(), "Tex" + std::to_string(texs.size()) + ": " + +name); \ + } \ + tex = texs[t++]; + + ID3D11BufferPtr rtvbuf = MakeBuffer().Size(1024).RTV(); + ID3D11BufferPtr srvbuf = MakeBuffer().Size(1024).SRV(); + ID3D11BufferPtr buf = MakeBuffer().Size(1024).Vertex(); + + SetDebugName(buf, "Buffer"); + SetDebugName(srvbuf, "BufferSRV"); + SetDebugName(rtvbuf, "BufferRTV"); + + ID3D11Texture1DPtr tex1d = MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300).Array(5).Mips(3); + ID3D11Texture3DPtr tex3d = MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300, 15).Mips(3); + ID3D11Texture1DPtr tex1drtv = + MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300).Array(5).Mips(3).RTV(); + ID3D11Texture3DPtr tex3drtv = + MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300, 15).Mips(3).RTV(); + + SetDebugName(tex1d, "Tex1D: DiscardAll"); + SetDebugName(tex3d, "Tex3D: DiscardAll"); + SetDebugName(tex1drtv, "Tex1D: DiscardRect Mip1 Slice1,2"); + SetDebugName(tex3drtv, "Tex3D: DiscardRect Mip1 Slice1,2"); + + bool first = true; + + while(Running()) + { + if(!first) + { + pushMarker("Clears"); + for(ID3D11Texture2DPtr t : texs) + Clear(t); + ctx->UpdateSubresource(rtvbuf, 0, NULL, empty, 1024, 1024); + ctx->UpdateSubresource(srvbuf, 0, NULL, empty, 1024, 1024); + ctx->UpdateSubresource(buf, 0, NULL, empty, 1024, 1024); + + ID3D11RenderTargetViewPtr rt; + + for(UINT m = 0; m < 3; m++) + { + rt = MakeRTV(tex1drtv).FirstMip(m); + ClearRenderTargetView(rt, {0.0f, 1.0f, 0.0f, 1.0f}); + + rt = MakeRTV(tex3drtv).FirstMip(m); + ClearRenderTargetView(rt, {0.0f, 1.0f, 0.0f, 1.0f}); + + ctx->UpdateSubresource(tex3d, m, NULL, empty, 32, 64); + for(UINT s = 0; s < 5; s++) + ctx->UpdateSubresource(tex1d, s * 3 + m, NULL, empty, 32, 64); + } + popMarker(); + } + + // this is an anchor point for us to jump to and observe textures with all cleared contents + // and no discard patterns + setMarker("TestStart"); + ClearRenderTargetView(bbRTV, {0.2f, 0.2f, 0.2f, 1.0f}); + + int t = 0; + ID3D11Texture2DPtr tex; + + // test a few different formats + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_R10G10B10A2_UNORM, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_R10G10B10A2_UINT, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_R9G9B9E5_SHAREDEXP, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_R8G8B8A8_UNORM, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_BC1_UNORM, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_BC2_UNORM, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_BC3_UNORM, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_BC4_UNORM, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_BC5_UNORM, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_BC6H_UF16, 300, 300)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_BC7_UNORM, 300, 300)); + ctx1->DiscardResource(tex); + + // test with different mips/array sizes + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Mips(5)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Array(4)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Array(4).Mips(5)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 30, 5)); + ctx1->DiscardResource(tex); + + // test MSAA textures + TEX_TEST("DiscardAll", + MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Multisampled(4).RTV()); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", + MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Multisampled(4).Array(5).RTV()); + ctx1->DiscardResource(tex); + + // test depth textures + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT, 300, 300).DSV()); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300).DSV()); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_D24_UNORM_S8_UINT, 300, 300).DSV()); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT, 300, 300).DSV().Mips(5)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT, 300, 300).DSV().Array(4)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT, 300, 300).DSV().Array(4).Mips(5)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300).DSV().Mips(5)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300).DSV().Array(4)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", + MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300).DSV().Array(4).Mips(5)); + ctx1->DiscardResource(tex); + TEX_TEST("DiscardAll", + MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300).Multisampled(4).DSV()); + ctx1->DiscardResource(tex); + TEX_TEST( + "DiscardAll", + MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300).Multisampled(4).Array(5).DSV()); + ctx1->DiscardResource(tex); + + // test discarding rects within a texture using DiscardView1. Only supported on RTVs and DSVs + TEX_TEST("DiscardRect Mip0", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).RTV()); + DiscardView1(MakeRTV(tex), 50, 50, 75, 75); + TEX_TEST("DiscardRect Mip1", + MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Mips(2).RTV()); + DiscardView1(MakeRTV(tex).FirstMip(1), 50, 50, 75, 75); + + TEX_TEST("DiscardRect Mip0", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300).DSV()); + DiscardView1(MakeDSV(tex), 50, 50, 75, 75); + TEX_TEST("DiscardRect Mip1", + MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300).Mips(2).DSV()); + DiscardView1(MakeDSV(tex).FirstMip(1), 50, 50, 75, 75); + + TEX_TEST("DiscardAll Slice2", + MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Multisampled(4).Array(5).RTV()); + DiscardView(MakeRTV(tex).FirstSlice(2).NumSlices(1)); + + // test 1D/3D textures + ctx1->DiscardResource(tex1d); + ctx1->DiscardResource(tex3d); + + DiscardView1( + MakeRTV(tex1drtv).FirstMip(1).FirstSlice(1).NumSlices(2), 50, 0, 75, 1); + DiscardView1( + MakeRTV(tex3drtv).FirstMip(1).FirstSlice(1).NumSlices(2), 50, 50, 75, 75); + + /////////////////////////// + // buffers + + // discard the buffer + ctx1->DiscardResource(buf); + + // discard the whole SRV buffer (can't discard a rect) + DiscardView( + MakeSRV(srvbuf).Format(DXGI_FORMAT_R32G32B32A32_FLOAT).NumElements(16)); + + // discard part of the RTV buffer with a rect + DiscardView1( + MakeRTV(rtvbuf).Format(DXGI_FORMAT_R32G32B32A32_FLOAT).NumElements(16), 50, 0, 75, 1); + + setMarker("TestEnd"); + ClearRenderTargetView(bbRTV, {0.2f, 0.2f, 0.2f, 1.0f}); + + first = false; + + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/d3d11/d3d11_helpers.cpp b/util/test/demos/d3d11/d3d11_helpers.cpp index fc14e8be2..dca03d8ae 100644 --- a/util/test/demos/d3d11/d3d11_helpers.cpp +++ b/util/test/demos/d3d11/d3d11_helpers.cpp @@ -184,6 +184,12 @@ D3D11BufferCreator &D3D11BufferCreator::SRV() return *this; } +D3D11BufferCreator &D3D11BufferCreator::RTV() +{ + m_BufDesc.BindFlags |= D3D11_BIND_RENDER_TARGET; + return *this; +} + D3D11BufferCreator &D3D11BufferCreator::UAV() { m_BufDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; @@ -213,6 +219,13 @@ D3D11BufferCreator &D3D11BufferCreator::Mappable() return *this; } +D3D11BufferCreator &D3D11BufferCreator::Immutable() +{ + m_BufDesc.CPUAccessFlags = 0; + m_BufDesc.Usage = D3D11_USAGE_IMMUTABLE; + return *this; +} + D3D11BufferCreator &D3D11BufferCreator::Staging() { m_BufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; @@ -260,7 +273,7 @@ D3D11TextureCreator::D3D11TextureCreator(D3D11GraphicsTest *test, DXGI_FORMAT fo Format = format; Width = width; Height = height; - depth = depth; + Depth = depth; } D3D11TextureCreator &D3D11TextureCreator::Mips(UINT mips) @@ -371,7 +384,7 @@ D3D11TextureCreator::operator ID3D11Texture3DPtr() const texdesc.Width = Width; texdesc.Height = Height; - texdesc.Depth = Height; + texdesc.Depth = Depth; texdesc.MipLevels = MipLevels; texdesc.MiscFlags = MiscFlags; texdesc.CPUAccessFlags = CPUAccessFlags; diff --git a/util/test/demos/d3d11/d3d11_helpers.h b/util/test/demos/d3d11/d3d11_helpers.h index 73aef4037..681871aa1 100644 --- a/util/test/demos/d3d11/d3d11_helpers.h +++ b/util/test/demos/d3d11/d3d11_helpers.h @@ -106,6 +106,7 @@ public: D3D11BufferCreator &Constant(); D3D11BufferCreator &StreamOut(); D3D11BufferCreator &SRV(); + D3D11BufferCreator &RTV(); D3D11BufferCreator &UAV(); D3D11BufferCreator &Structured(UINT structStride); @@ -113,6 +114,7 @@ public: D3D11BufferCreator &Mappable(); D3D11BufferCreator &Staging(); D3D11BufferCreator &Shared(); + D3D11BufferCreator &Immutable(); D3D11BufferCreator &Data(const void *data); D3D11BufferCreator &Size(UINT size); diff --git a/util/test/demos/d3d12/d3d12_discard_zoo.cpp b/util/test/demos/d3d12/d3d12_discard_zoo.cpp new file mode 100644 index 000000000..e01b07215 --- /dev/null +++ b/util/test/demos/d3d12/d3d12_discard_zoo.cpp @@ -0,0 +1,581 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "d3d12_test.h" + +RD_TEST(D3D12_Discard_Zoo, D3D12GraphicsTest) +{ + static constexpr const char *Description = "Tests texture discarding resources in D3D12."; + + std::string pixel = R"EOSHADER( + +struct v2f +{ + float4 pos : SV_POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +Texture2D smiley : register(t0); +SamplerState smileysamp : register(s0); + +cbuffer consts : register(b0) +{ + float4 tint; +}; + +float4 main(v2f IN) : SV_Target0 +{ + return smiley.Sample(smileysamp, IN.uv) * tint; +} + +)EOSHADER"; + + ID3D12ResourcePtr emptyRes; + + void Clear(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr tex) + { + if(!tex) + return; + + D3D12_RESOURCE_DESC desc = tex->GetDesc(); + + if(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) + { + for(UINT mip = 0; mip < desc.MipLevels; mip++) + { + UINT slices = desc.DepthOrArraySize; + if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) + slices = std::max(1U, slices >> mip); + for(UINT slice = 0; slice < slices; slice++) + { + D3D12_CPU_DESCRIPTOR_HANDLE rtv = MakeRTV(tex) + .Format(desc.Format) + .FirstSlice(slice) + .NumSlices(1) + .FirstMip(mip) + .NumMips(1) + .CreateCPU(1); + + ClearRenderTargetView(cmd, rtv, {0.0f, 1.0f, 0.0f, 1.0f}); + } + } + } + else if(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) + { + for(UINT mip = 0; mip < desc.MipLevels; mip++) + { + for(UINT slice = 0; slice < desc.DepthOrArraySize; slice++) + { + D3D12_CPU_DESCRIPTOR_HANDLE dsv = + MakeDSV(tex).FirstSlice(slice).NumSlices(1).FirstMip(mip).NumMips(1).CreateCPU(0); + + ClearDepthStencilView(cmd, dsv, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 0.4f, + 0x40); + } + } + } + else + { + for(UINT slice = 0; slice < desc.DepthOrArraySize; slice++) + { + for(UINT mip = 0; mip < desc.MipLevels; mip++) + { + D3D12_TEXTURE_COPY_LOCATION dst = {}; + dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dst.pResource = tex; + dst.SubresourceIndex = slice * desc.MipLevels + mip; + + D3D12_TEXTURE_COPY_LOCATION src = {}; + src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + src.pResource = emptyRes; + dev->GetCopyableFootprints(&desc, dst.SubresourceIndex, 1, 0, &src.PlacedFootprint, NULL, + NULL, NULL); + + cmd->CopyTextureRegion(&dst, 0, 0, 0, &src, NULL); + } + + if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) + break; + } + } + } + + void DiscardResource(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr res, UINT firstSub = 0, + UINT numSub = ~0U, LONG x = 0, LONG y = 0, LONG width = 0, LONG height = 0) + { + D3D12_DISCARD_REGION reg = {}; + reg.FirstSubresource = firstSub; + reg.NumSubresources = numSub; + + D3D12_RESOURCE_DESC desc = res->GetDesc(); + + UINT arraySlices = desc.DepthOrArraySize; + if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D) + arraySlices = 1; + + UINT planes = 1; + if(desc.Format == DXGI_FORMAT_D32_FLOAT_S8X24_UINT || + desc.Format == DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS || + desc.Format == DXGI_FORMAT_X32_TYPELESS_G8X24_UINT || + desc.Format == DXGI_FORMAT_R24G8_TYPELESS || desc.Format == DXGI_FORMAT_D24_UNORM_S8_UINT || + desc.Format == DXGI_FORMAT_R24_UNORM_X8_TYPELESS || + desc.Format == DXGI_FORMAT_X24_TYPELESS_G8_UINT) + planes = 2; + + reg.NumSubresources = std::min(reg.NumSubresources, + (planes * desc.MipLevels * arraySlices) - reg.FirstSubresource); + + D3D12_RECT rect = {x, y, x + width, x + height}; + + if(width > 0) + { + reg.NumRects = 1; + reg.pRects = ▭ + } + + cmd->DiscardResource(res, ®); + } + + int main() + { + // initialise, create window, create device, etc + if(!Init()) + return 3; + + std::vector empty; + empty.resize(16 * 1024 * 1024); + { + memset(empty.data(), 0x88, empty.size()); + + emptyRes = MakeBuffer().Data(empty.data()).Size((UINT)empty.size()); + } + + ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_4_0"); + ID3DBlobPtr psblob = Compile(pixel, "main", "ps_4_0"); + + ID3D12ResourcePtr vb = MakeBuffer().Data(DefaultTri); + + D3D12_STATIC_SAMPLER_DESC samp = { + D3D12_FILTER_MIN_MAG_MIP_POINT, + D3D12_TEXTURE_ADDRESS_MODE_CLAMP, + D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE_WRAP, + 0.0f, + 0, + D3D12_COMPARISON_FUNC_ALWAYS, + D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + 0.0f, + 0.0f, + 0, + 0, + D3D12_SHADER_VISIBILITY_PIXEL, + }; + + ID3D12RootSignaturePtr sig = MakeSig( + { + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 0, 1, 0), + constParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 0, 16), + }, + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, 1, &samp); + + ID3D12PipelineStatePtr pso = MakePSO().RootSig(sig).InputLayout().VS(vsblob).PS(psblob); + + ResourceBarrier(vb, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); + + ID3D12ResourcePtr uploadBuf = MakeBuffer().Size(1024 * 1024).Upload(); + + Texture rgba8; + LoadXPM(SmileyTexture, rgba8); + + ID3D12ResourcePtr smiley = MakeTexture(DXGI_FORMAT_R8G8B8A8_UNORM, 48, 48) + .Mips(1) + .InitialState(D3D12_RESOURCE_STATE_COPY_DEST); + + MakeSRV(smiley).CreateCPU(0); + + { + D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout = {}; + + D3D12_RESOURCE_DESC desc = smiley->GetDesc(); + + dev->GetCopyableFootprints(&desc, 0, 1, 0, &layout, NULL, NULL, NULL); + + byte *srcptr = (byte *)rgba8.data.data(); + byte *mapptr = NULL; + uploadBuf->Map(0, NULL, (void **)&mapptr); + + ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer(); + + Reset(cmd); + + { + D3D12_TEXTURE_COPY_LOCATION dst, src; + + dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dst.pResource = smiley; + dst.SubresourceIndex = 0; + + byte *dstptr = mapptr + layout.Offset; + + for(UINT row = 0; row < rgba8.height; row++) + { + memcpy(dstptr, srcptr, rgba8.width * sizeof(uint32_t)); + srcptr += rgba8.width * sizeof(uint32_t); + dstptr += layout.Footprint.RowPitch; + } + + src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + src.pResource = uploadBuf; + src.PlacedFootprint = layout; + + // copy buffer into this array slice + cmd->CopyTextureRegion(&dst, 0, 0, 0, &src, NULL); + + // this slice now needs to be in shader-read to copy to the MSAA texture + D3D12_RESOURCE_BARRIER b = {}; + b.Transition.pResource = smiley; + b.Transition.Subresource = 0; + b.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + b.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + cmd->ResourceBarrier(1, &b); + } + + cmd->Close(); + + uploadBuf->Unmap(0, NULL); + + Submit({cmd}); + GPUSync(); + } + + ID3D12ResourcePtr buf = MakeBuffer().Data(empty).Size(1024); + + buf->SetName(L"Buffer"); + + std::vector texs; + +#define TEX_TEST(name, x) \ + if(first) \ + { \ + texs.push_back(x); \ + Clear(cmd, texs.back()); \ + texs.back()->SetName((L"Tex" + std::to_wstring(texs.size()) + L": " + +name).c_str()); \ + } \ + tex = texs[t++]; + + ID3D12ResourcePtr tex1d = MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300) + .Array(5) + .Mips(3) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET); + ID3D12ResourcePtr tex3d = MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300, 15) + .Mips(3) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET); + ID3D12ResourcePtr tex1drtv = MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300) + .Array(5) + .Mips(3) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET); + ID3D12ResourcePtr tex3drtv = MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300, 15) + .Mips(3) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET); + + tex1d->SetName(L"Tex1D: DiscardAll"); + tex3d->SetName(L"Tex3D: DiscardAll"); + tex1drtv->SetName(L"Tex1D: DiscardAll Mip1 Slice1,2"); + tex3drtv->SetName(L"Tex3D: DiscardAll Mip1"); + + bool first = true; + + while(Running()) + { + if(!first) + { + ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer(); + + Reset(cmd); + + pushMarker(cmd, "Clears"); + for(ID3D12ResourcePtr t : texs) + Clear(cmd, t); + + Clear(cmd, tex1d); + Clear(cmd, tex3d); + Clear(cmd, tex1drtv); + Clear(cmd, tex3drtv); + popMarker(cmd); + + cmd->Close(); + + Submit({cmd}); + + SetBufferData(buf, D3D12_RESOURCE_STATE_COMMON, empty.data(), 1024); + } + + ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer(); + + Reset(cmd); + + ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + + D3D12_CPU_DESCRIPTOR_HANDLE rtv = + MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(0); + + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0); + cmd->SetPipelineState(pso); + cmd->SetGraphicsRootSignature(sig); + + RSSetViewport(cmd, {128.0f, 0.0f, 128.0f, 128.0f, 0.0f, 1.0f}); + RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); + + OMSetRenderTargets(cmd, {rtv}, {}); + + ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f}); + + OMSetRenderTargets(cmd, {rtv}, {}); + + Vec4f tint = {0.2f, 0.4f, 0.6f, 1.0f}; + cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); + cmd->SetGraphicsRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRoot32BitConstants(1, 4, &tint, 0); + + // this is an anchor point for us to jump to and observe textures with all cleared contents + // and no discard patterns + setMarker(cmd, "TestStart"); + ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f}); + + // discard the buffer first, we can't mess with rectangles or subresources for it + DiscardResource(cmd, buf); + + int t = 0; + ID3D12ResourcePtr tex; + + // test a few different formats + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R10G10B10A2_UNORM, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R10G10B10A2_UINT, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R9G9B9E5_SHAREDEXP, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R8G8B8A8_UNORM, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_BC1_UNORM, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_BC2_UNORM, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_BC3_UNORM, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_BC4_UNORM, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_BC5_UNORM, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_BC6H_UF16, 300, 300)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_BC7_UNORM, 300, 300)); + DiscardResource(cmd, tex); + + // test with different mips/array sizes + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Mips(5)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Array(4)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300).Array(4).Mips(5)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 30, 5)); + DiscardResource(cmd, tex); + + // test MSAA textures + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300) + .Multisampled(4) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300) + .Multisampled(4) + .Array(5) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET)); + DiscardResource(cmd, tex); + + // test depth textures + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT, 300, 300) + .DSV() + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .DSV() + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D24_UNORM_S8_UINT, 300, 300) + .DSV() + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT, 300, 300) + .DSV() + .Mips(5) + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT, 300, 300) + .DSV() + .Array(4) + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT, 300, 300) + .DSV() + .Array(4) + .Mips(5) + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .DSV() + .Mips(5) + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .DSV() + .Array(4) + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .DSV() + .Array(4) + .Mips(5) + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .Multisampled(4) + .DSV() + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + TEX_TEST(L"DiscardAll", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .Multisampled(4) + .Array(5) + .DSV() + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex); + + // test discarding rects within a texture using DiscardView1. Only supported on RTVs and DSVs + TEX_TEST(L"DiscardRect Mip0", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET)); + DiscardResource(cmd, tex, 0, 1, 50, 50, 75, 75); + TEX_TEST(L"DiscardRect Mip1", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300) + .Mips(2) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET)); + DiscardResource(cmd, tex, 1, 1, 50, 50, 75, 75); + + TEX_TEST(L"DiscardRect Mip0", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .Mips(2) + .DSV() + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex, 0, 1, 50, 50, 75, 75); // depth mip0 + DiscardResource(cmd, tex, 2, 1, 50, 50, 75, 75); // stencil mip1 + TEX_TEST(L"DiscardRect Mip1", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .Mips(2) + .DSV() + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex, 1, 1, 50, 50, 75, 75); // depth mip1 + DiscardResource(cmd, tex, 3, 1, 50, 50, 75, 75); // stencil mip1 + + TEX_TEST(L"DiscardAll Slice2", MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 300, 300) + .Multisampled(4) + .Array(5) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET)); + DiscardResource(cmd, tex, 2, 1); + + // test discarding only depth or only stencil + TEX_TEST(L"DiscardRect DepthOnly", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .DSV() + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex, 0, 1, 50, 50, 75, 75); + TEX_TEST(L"DiscardRect StencilOnly", MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 300, 300) + .DSV() + .InitialState(D3D12_RESOURCE_STATE_DEPTH_WRITE)); + DiscardResource(cmd, tex, 1, 1, 50, 50, 75, 75); + + // test 1D/3D textures + DiscardResource(cmd, tex1d); + DiscardResource(cmd, tex3d); + + DiscardResource(cmd, tex1drtv, 4, 1); // mip 1, slice 1 + DiscardResource(cmd, tex1drtv, 7, 1); // mip 1, slice 2 + DiscardResource(cmd, tex3drtv, 1, 1); // mip 1 + + setMarker(cmd, "TestEnd"); + ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f}); + + cmd->DrawInstanced(3, 1, 0, 0); + + cmd->Close(); + + Submit({cmd}); + + cmd = GetCommandBuffer(); + + Reset(cmd); + + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0); + cmd->SetPipelineState(pso); + cmd->SetGraphicsRootSignature(sig); + + RSSetViewport(cmd, {0.0f, 128.0f, 128.0f, 128.0f, 0.0f, 1.0f}); + RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); + + OMSetRenderTargets(cmd, {rtv}, {}); + + cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); + cmd->SetGraphicsRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRoot32BitConstants(1, 4, &tint, 0); + + cmd->DrawInstanced(3, 1, 0, 0); + + FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + + cmd->Close(); + + Submit({cmd}); + + Present(); + + first = false; + } + + emptyRes = NULL; + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/d3d12/d3d12_test.cpp b/util/test/demos/d3d12/d3d12_test.cpp index 96635b2b8..42d66918b 100644 --- a/util/test/demos/d3d12/d3d12_test.cpp +++ b/util/test/demos/d3d12/d3d12_test.cpp @@ -928,6 +928,13 @@ void D3D12GraphicsTest::ClearDepthStencilView(ID3D12GraphicsCommandListPtr cmd, NULL); } +void D3D12GraphicsTest::ClearDepthStencilView(ID3D12GraphicsCommandListPtr cmd, + D3D12_CPU_DESCRIPTOR_HANDLE dsv, + D3D12_CLEAR_FLAGS flags, float depth, UINT8 stencil) +{ + cmd->ClearDepthStencilView(dsv, flags, depth, stencil, 0, NULL); +} + void D3D12GraphicsTest::RSSetViewport(ID3D12GraphicsCommandListPtr cmd, D3D12_VIEWPORT view) { cmd->RSSetViewports(1, &view); diff --git a/util/test/demos/d3d12/d3d12_test.h b/util/test/demos/d3d12/d3d12_test.h index 6f98f003d..e0ce24920 100644 --- a/util/test/demos/d3d12/d3d12_test.h +++ b/util/test/demos/d3d12/d3d12_test.h @@ -161,6 +161,8 @@ struct D3D12GraphicsTest : public GraphicsTest void ClearRenderTargetView(ID3D12GraphicsCommandListPtr cmd, D3D12_CPU_DESCRIPTOR_HANDLE rt, Vec4f col); void ClearRenderTargetView(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr rt, Vec4f col); + void ClearDepthStencilView(ID3D12GraphicsCommandListPtr cmd, D3D12_CPU_DESCRIPTOR_HANDLE dsv, + D3D12_CLEAR_FLAGS flags, float depth, UINT8 stencil); void ClearDepthStencilView(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr dsv, D3D12_CLEAR_FLAGS flags, float depth, UINT8 stencil); diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 282f80ec8..c25f3b25a 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -130,6 +130,7 @@ + @@ -171,6 +172,7 @@ + @@ -213,6 +215,7 @@ + @@ -258,6 +261,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index ba0c2bb72..149a872f3 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -517,6 +517,18 @@ Vulkan\demos + + Vulkan\demos + + + OpenGL\demos + + + D3D12\demos + + + D3D11\demos + diff --git a/util/test/demos/gl/gl_discard_zoo.cpp b/util/test/demos/gl/gl_discard_zoo.cpp new file mode 100644 index 000000000..7809a9785 --- /dev/null +++ b/util/test/demos/gl/gl_discard_zoo.cpp @@ -0,0 +1,454 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "gl_test.h" + +RD_TEST(GL_Discard_Zoo, OpenGLGraphicsTest) +{ + static constexpr const char *Description = "Tests texture discard methods in GL."; + + byte empty[16 * 1024 * 1024] = {}; + + uint32_t greens10_2[300 * 300]; + + void SetDebugName(GLuint t, std::string name) { glObjectLabel(GL_TEXTURE, t, -1, name.c_str()); } + void Clear(GLuint t) + { + GLenum fmt = GL_NONE; + GLint mips = 0; + GLint width = 0; + GLint height = 0; + + glGetTextureLevelParameteriv(t, 0, GL_TEXTURE_INTERNAL_FORMAT, (GLint *)&fmt); + glGetTextureParameteriv(t, GL_TEXTURE_IMMUTABLE_LEVELS, &mips); + glGetTextureLevelParameteriv(t, 0, GL_TEXTURE_WIDTH, &width); + glGetTextureLevelParameteriv(t, 0, GL_TEXTURE_HEIGHT, &height); + + if(fmt == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || fmt == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT || + fmt == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT || fmt == GL_COMPRESSED_RED_RGTC1 || + fmt == GL_COMPRESSED_RG_RGTC2 || fmt == GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB || + fmt == GL_COMPRESSED_RGBA_BPTC_UNORM_ARB) + { + GLsizei size = 16; + + if(fmt == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || fmt == GL_COMPRESSED_RED_RGTC1) + size = 8; + + size *= (width / 4) * (height / 4); + + // can't clear compressed tex image + for(GLint m = 0; m < mips; m++) + glCompressedTextureSubImage2D(t, m, 0, 0, width, height, fmt, size, empty); + + return; + } + + if(fmt == GL_RGB10_A2UI) + { + for(GLint m = 0; m < mips; m++) + glTextureSubImage2D(t, m, 0, 0, width, height, GL_RGBA_INTEGER, + GL_UNSIGNED_INT_2_10_10_10_REV, greens10_2); + } + else if(fmt == GL_DEPTH_COMPONENT32F) + { + float depth = 0.4f; + for(GLint m = 0; m < mips; m++) + glClearTexImage(t, m, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); + } + else if(fmt == GL_DEPTH32F_STENCIL8) + { + struct + { + float depth; + uint32_t stencil; + } ds; + ds.depth = 0.4f; + ds.stencil = 0x40; + for(GLint m = 0; m < mips; m++) + glClearTexImage(t, m, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, &ds); + } + else if(fmt == GL_DEPTH24_STENCIL8) + { + uint32_t depth_stencil = 0x40666666; + for(GLint m = 0; m < mips; m++) + glClearTexImage(t, m, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, &depth_stencil); + } + else if(fmt == GL_STENCIL_INDEX8) + { + uint32_t stencil = 0x40; + for(GLint m = 0; m < mips; m++) + glClearTexImage(t, m, GL_STENCIL_INDEX, GL_UNSIGNED_INT, &stencil); + } + else + { + Vec4f green(0.0f, 1.0f, 0.0f, 1.0f); + for(GLint m = 0; m < mips; m++) + glClearTexImage(t, m, GL_RGBA, GL_FLOAT, &green); + } + } + + void Invalidate(GLuint t) + { + GLint mips = 1; + glGetTextureParameteriv(t, GL_TEXTURE_IMMUTABLE_LEVELS, &mips); + + for(GLint m = 0; m < mips; m++) + glInvalidateTexImage(t, m); + } + + void InvalidateFBO(const std::vector &atts, int x = 0, int y = 0, int width = 0, + int height = 0) + { + if(width == 0) + glInvalidateFramebuffer(GL_FRAMEBUFFER, (GLsizei)atts.size(), atts.data()); + else + glInvalidateSubFramebuffer(GL_FRAMEBUFFER, (GLsizei)atts.size(), atts.data(), x, y, width, + height); + } + + GLuint MakeTex2D(GLenum fmt, GLuint width, GLuint height, GLsizei mips = 1) + { + GLuint ret = MakeTexture(); + glBindTexture(GL_TEXTURE_2D, ret); + glTexStorage2D(GL_TEXTURE_2D, mips, fmt, width, height); + return ret; + } + + GLuint MakeTex2DArray(GLenum fmt, GLuint width, GLuint height, GLuint slices, GLsizei mips = 1) + { + GLuint ret = MakeTexture(); + glBindTexture(GL_TEXTURE_2D_ARRAY, ret); + glTexStorage3D(GL_TEXTURE_2D_ARRAY, mips, fmt, width, height, slices); + return ret; + } + + GLuint MakeTex2DMS(GLenum fmt, GLuint width, GLuint height, GLsizei samples, GLuint slices = 1) + { + GLuint ret = MakeTexture(); + if(slices == 1) + { + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, ret); + glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, fmt, width, height, TRUE); + } + else + { + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, ret); + glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, samples, fmt, width, height, + slices, TRUE); + } + return ret; + } + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + for(size_t i = 0; i < 300 * 300; i++) + greens10_2[i] = 0xC00FFC00; + + std::vector texs, fbos; + +#define TEX_TEST(name, x) \ + if(first) \ + { \ + texs.push_back(x); \ + Clear(texs.back()); \ + SetDebugName(texs.back(), "Tex" + std::to_string(texs.size()) + ": " + +name); \ + } \ + tex = texs[t++]; + +#define FBO_TEST() \ + if(first) \ + { \ + fbos.push_back(MakeFBO()); \ + } \ + fbo = fbos[f++]; \ + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + memset(empty, 0x88, sizeof(empty)); + + GLuint buf = MakeBuffer(); + glBindBuffer(GL_UNIFORM_BUFFER, buf); + glBufferStorage(GL_UNIFORM_BUFFER, 1024, NULL, GL_DYNAMIC_STORAGE_BIT); + glObjectLabel(GL_BUFFER, buf, -1, "Buffer"); + + GLuint subbuf = MakeBuffer(); + glBindBuffer(GL_UNIFORM_BUFFER, subbuf); + glBufferStorage(GL_UNIFORM_BUFFER, 1024, NULL, GL_DYNAMIC_STORAGE_BIT); + glObjectLabel(GL_BUFFER, subbuf, -1, "BufferSub"); + + GLuint tex1d = MakeTexture(); + glBindTexture(GL_TEXTURE_1D_ARRAY, tex1d); + glTexStorage2D(GL_TEXTURE_1D_ARRAY, 3, GL_RGBA16F, 300, 5); + GLuint tex3d = MakeTexture(); + glBindTexture(GL_TEXTURE_3D, tex3d); + glTexStorage3D(GL_TEXTURE_3D, 3, GL_RGBA16F, 300, 300, 15); + GLuint tex1dsub = MakeTexture(); + glBindTexture(GL_TEXTURE_1D_ARRAY, tex1dsub); + glTexStorage2D(GL_TEXTURE_1D_ARRAY, 3, GL_RGBA16F, 300, 5); + GLuint tex3dsub = MakeTexture(); + glBindTexture(GL_TEXTURE_3D, tex3dsub); + glTexStorage3D(GL_TEXTURE_3D, 3, GL_RGBA16F, 300, 300, 15); + GLuint tex3dsub2 = MakeTexture(); + glBindTexture(GL_TEXTURE_3D, tex3dsub2); + glTexStorage3D(GL_TEXTURE_3D, 1, GL_RGBA16F, 300, 300, 15); + + GLuint texcube = MakeTexture(); + glBindTexture(GL_TEXTURE_CUBE_MAP, texcube); + glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA16F, 300, 300); + GLuint texcubesub = MakeTexture(); + glBindTexture(GL_TEXTURE_CUBE_MAP, texcubesub); + glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA16F, 300, 300); + + GLuint rb = 0; + glGenRenderbuffers(1, &rb); + glBindRenderbuffer(GL_RENDERBUFFER, rb); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA16F, 300, 300); + + glObjectLabel(GL_TEXTURE, tex1d, -1, "Tex1D: DiscardAll"); + glObjectLabel(GL_TEXTURE, tex3d, -1, "Tex3D: DiscardAll"); + glObjectLabel(GL_TEXTURE, tex1dsub, -1, "Tex1D: DiscardRect Mip1 Slice1,2"); + glObjectLabel(GL_TEXTURE, tex3dsub, -1, "Tex3D: DiscardRect Mip1 Slice1,2"); + glObjectLabel(GL_TEXTURE, texcube, -1, "TexCube: DiscardAll"); + glObjectLabel(GL_TEXTURE, texcubesub, -1, "TexCube: DiscardAll Slice2"); + glObjectLabel(GL_TEXTURE, tex3dsub2, -1, "Tex3D: DiscardRect Slice7"); + glObjectLabel(GL_RENDERBUFFER, rb, -1, "RB: DiscardAll"); + + GLuint tmpfbo = MakeFBO(); + glBindFramebuffer(GL_FRAMEBUFFER, tmpfbo); + + bool first = true; + + while(Running()) + { + if(!first) + { + pushMarker("Clears"); + for(GLuint t : texs) + Clear(t); + + Vec4f green(0.0f, 1.0f, 0.0f, 1.0f); + + glBindFramebuffer(GL_FRAMEBUFFER, tmpfbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); + glClearBufferfv(GL_COLOR, 0, &green.x); + + for(int m = 0; m < 3; m++) + { + glClearTexImage(tex1d, m, GL_RGBA, GL_FLOAT, &green.x); + glClearTexImage(tex3d, m, GL_RGBA, GL_FLOAT, &green.x); + glClearTexImage(tex1dsub, m, GL_RGBA, GL_FLOAT, &green.x); + glClearTexImage(tex3dsub, m, GL_RGBA, GL_FLOAT, &green.x); + } + + glClearTexImage(texcube, 0, GL_RGBA, GL_FLOAT, &green.x); + glClearTexImage(texcubesub, 0, GL_RGBA, GL_FLOAT, &green.x); + glClearTexImage(tex3dsub2, 0, GL_RGBA, GL_FLOAT, &green.x); + + glNamedBufferSubDataEXT(buf, 0, 1024, empty); + glNamedBufferSubDataEXT(subbuf, 0, 1024, empty); + popMarker(); + } + + float col[] = {0.2f, 0.2f, 0.2f, 1.0f}; + + setMarker("TestStart"); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearBufferfv(GL_COLOR, 0, col); + + glInvalidateBufferData(buf); + glInvalidateBufferSubData(subbuf, 50, 75); + + int t = 0, f = 0; + GLuint tex, fbo; + + // test a few different formats + TEX_TEST("DiscardAll", MakeTex2D(GL_RGBA16F, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_RGB10_A2, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_RGB10_A2UI, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_RGB9_E5, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_RGBA8, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_COMPRESSED_RED_RGTC1, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_COMPRESSED_RG_RGTC2, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, 300, 300)); + Invalidate(tex); + + // test with different mips/array sizes + TEX_TEST("DiscardAll", MakeTex2D(GL_RGBA16F, 300, 300, 5)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2DArray(GL_RGBA16F, 300, 300, 4)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2DArray(GL_RGBA16F, 300, 300, 4, 5)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_RGBA16F, 30, 5)); + Invalidate(tex); + + // test MSAA textures + TEX_TEST("DiscardAll", MakeTex2DMS(GL_RGBA16F, 300, 300, 4)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2DMS(GL_RGBA16F, 300, 300, 4, 5)); + Invalidate(tex); + + // test depth textures + TEX_TEST("DiscardAll", MakeTex2D(GL_DEPTH_COMPONENT32F, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_DEPTH32F_STENCIL8, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_DEPTH24_STENCIL8, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_STENCIL_INDEX8, 300, 300)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_DEPTH_COMPONENT32F, 300, 300, 5)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2DArray(GL_DEPTH_COMPONENT32F, 300, 300, 4)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2DArray(GL_DEPTH_COMPONENT32F, 300, 300, 4, 5)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2D(GL_DEPTH32F_STENCIL8, 300, 300, 5)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2DArray(GL_DEPTH32F_STENCIL8, 300, 300, 4)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2DArray(GL_DEPTH32F_STENCIL8, 300, 300, 4, 5)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2DMS(GL_DEPTH32F_STENCIL8, 300, 300, 4)); + Invalidate(tex); + TEX_TEST("DiscardAll", MakeTex2DMS(GL_DEPTH32F_STENCIL8, 300, 300, 4, 5)); + Invalidate(tex); + + // test discarding rects within a texture + TEX_TEST("DiscardRect Mip0", MakeTex2D(GL_RGBA16F, 300, 300)); + glInvalidateTexSubImage(tex, 0, 50, 50, 0, 75, 75, 1); + TEX_TEST("DiscardRect Mip1", MakeTex2D(GL_RGBA16F, 300, 300, 2)); + glInvalidateTexSubImage(tex, 1, 50, 50, 0, 75, 75, 1); + + TEX_TEST("DiscardRect Mip0", MakeTex2D(GL_DEPTH32F_STENCIL8, 300, 300)); + glInvalidateTexSubImage(tex, 0, 50, 50, 0, 75, 75, 1); + TEX_TEST("DiscardRect Mip1", MakeTex2D(GL_DEPTH32F_STENCIL8, 300, 300, 2)); + glInvalidateTexSubImage(tex, 1, 50, 50, 0, 75, 75, 1); + + TEX_TEST("DiscardAll Slice2", MakeTex2DMS(GL_RGBA16F, 300, 300, 4, 5)); + glInvalidateTexSubImage(tex, 0, 0, 0, 2, 300, 300, 1); + + // test 1D/3D/Cube textures + Invalidate(tex1d); + Invalidate(tex3d); + Invalidate(texcube); + + glInvalidateTexSubImage(tex1dsub, 1, 50, 1, 0, 75, 2, 1); + glInvalidateTexSubImage(tex3dsub, 1, 50, 50, 1, 75, 75, 2); + + // test invalidating framebuffer attachments + TEX_TEST("DiscardAll", MakeTex2D(GL_RGBA16F, 300, 300)); + FBO_TEST(); + glBindTexture(GL_TEXTURE_2D, tex); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); + InvalidateFBO({GL_COLOR_ATTACHMENT0}); + + TEX_TEST("DiscardRect", MakeTex2D(GL_RGBA16F, 300, 300)); + FBO_TEST(); + glBindTexture(GL_TEXTURE_2D, tex); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); + InvalidateFBO({GL_COLOR_ATTACHMENT0}, 50, 50, 75, 75); + + // test invalidating depth and stencil components in different combinations + TEX_TEST("DiscardAll", MakeTex2D(GL_DEPTH_COMPONENT32F, 300, 300)); + FBO_TEST(); + glBindTexture(GL_TEXTURE_2D, tex); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex, 0); + InvalidateFBO({GL_DEPTH_ATTACHMENT}); + + TEX_TEST("DiscardAll", MakeTex2D(GL_STENCIL_INDEX8, 300, 300)); + FBO_TEST(); + glBindTexture(GL_TEXTURE_2D, tex); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex, 0); + InvalidateFBO({GL_STENCIL_ATTACHMENT}); + + TEX_TEST("DiscardAll", MakeTex2D(GL_DEPTH32F_STENCIL8, 300, 300)); + FBO_TEST(); + glBindTexture(GL_TEXTURE_2D, tex); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex, 0); + InvalidateFBO({GL_DEPTH_STENCIL_ATTACHMENT}); + + TEX_TEST("DiscardAll DepthOnly", MakeTex2D(GL_DEPTH32F_STENCIL8, 300, 300)); + FBO_TEST(); + glBindTexture(GL_TEXTURE_2D, tex); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex, 0); + InvalidateFBO({GL_DEPTH_ATTACHMENT}); + + TEX_TEST("DiscardAll StencilOnly", MakeTex2D(GL_DEPTH32F_STENCIL8, 300, 300)); + FBO_TEST(); + glBindTexture(GL_TEXTURE_2D, tex); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex, 0); + InvalidateFBO({GL_STENCIL_ATTACHMENT}); + + FBO_TEST(); + glBindTexture(GL_TEXTURE_CUBE_MAP, texcubesub); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + texcubesub, 0); + InvalidateFBO({GL_COLOR_ATTACHMENT0}); + + FBO_TEST(); + glBindTexture(GL_TEXTURE_3D, tex3dsub2); + glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, tex3dsub2, 0, 7); + InvalidateFBO({GL_COLOR_ATTACHMENT0}); + + FBO_TEST(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); + InvalidateFBO({GL_COLOR_ATTACHMENT0}); + + glFlush(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + setMarker("TestEnd"); + glClearBufferfv(GL_COLOR, 0, col); + + Present(); + + first = false; + } + + glDeleteRenderbuffers(1, &rb); + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/vk/vk_discard_zoo.cpp b/util/test/demos/vk/vk_discard_zoo.cpp new file mode 100644 index 000000000..5e6671e2a --- /dev/null +++ b/util/test/demos/vk/vk_discard_zoo.cpp @@ -0,0 +1,520 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2020 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "vk_test.h" + +RD_TEST(VK_Discard_Zoo, VulkanGraphicsTest) +{ + static constexpr const char *Description = + "Tests the different discard patterns possible on replay."; + + AllocatedBuffer emptyBuf; + + void Clear(VkCommandBuffer cmd, const AllocatedImage &img) + { + if(img.image == VK_NULL_HANDLE) + return; + + vkh::ImageSubresourceRange range; + + if(img.createInfo.format == VK_FORMAT_D32_SFLOAT_S8_UINT || + img.createInfo.format == VK_FORMAT_D24_UNORM_S8_UINT) + range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + else if(img.createInfo.format == VK_FORMAT_D32_SFLOAT || + img.createInfo.format == VK_FORMAT_S8_UINT) + range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + else if(img.createInfo.format == VK_FORMAT_S8_UINT) + range.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + + vkh::cmdPipelineBarrier( + cmd, { + vkh::ImageMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + img.image, range), + }); + + VkClearDepthStencilValue val = {0.4f, 0x40}; + + if(img.createInfo.format == VK_FORMAT_BC1_RGBA_UNORM_BLOCK || + img.createInfo.format == VK_FORMAT_BC2_UNORM_BLOCK || + img.createInfo.format == VK_FORMAT_BC3_UNORM_BLOCK || + img.createInfo.format == VK_FORMAT_BC4_UNORM_BLOCK || + img.createInfo.format == VK_FORMAT_BC5_UNORM_BLOCK || + img.createInfo.format == VK_FORMAT_BC6H_UFLOAT_BLOCK || + img.createInfo.format == VK_FORMAT_BC7_UNORM_BLOCK) + { + // can't clear compressed formats with vkCmdClearColorImage + VkBufferImageCopy region = {}; + std::vector regions; + + region.imageSubresource.aspectMask = range.aspectMask; + region.imageSubresource.layerCount = img.createInfo.arrayLayers; + + for(uint32_t m = 0; m < img.createInfo.mipLevels; m++) + { + region.imageExtent.width = std::max(1U, img.createInfo.extent.width >> m); + region.imageExtent.height = std::max(1U, img.createInfo.extent.height >> m); + region.imageExtent.depth = std::max(1U, img.createInfo.extent.depth >> m); + region.imageSubresource.mipLevel = m; + + regions.push_back(region); + } + + vkCmdCopyBufferToImage(cmd, emptyBuf.buffer, img.image, VK_IMAGE_LAYOUT_GENERAL, + (uint32_t)regions.size(), regions.data()); + return; + } + + if(range.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) + { + if(img.createInfo.format == VK_FORMAT_A2B10G10R10_UINT_PACK32) + vkCmdClearColorImage(cmd, img.image, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0u, 1023u, 0u, 1u), 1, range); + else + vkCmdClearColorImage(cmd, img.image, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0.0f, 1.0f, 0.0f, 1.0f), 1, range); + } + else + { + vkCmdClearDepthStencilImage(cmd, img.image, VK_IMAGE_LAYOUT_GENERAL, &val, 1, range); + } + } + + void DiscardImage(VkCommandBuffer cmd, const AllocatedImage &img, + vkh::ImageSubresourceRange range = {}) + { + if(img.createInfo.format == VK_FORMAT_D32_SFLOAT_S8_UINT || + img.createInfo.format == VK_FORMAT_D24_UNORM_S8_UINT) + range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + else if(img.createInfo.format == VK_FORMAT_D32_SFLOAT) + range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + else if(img.createInfo.format == VK_FORMAT_S8_UINT) + range.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + + vkh::cmdPipelineBarrier( + cmd, { + vkh::ImageMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + img.image, range), + }); + } + + AllocatedImage MakeTex2D(VkFormat fmt, uint32_t width, uint32_t height, uint32_t mips = 1, + uint32_t arraySlices = 1) + { + return AllocatedImage(this, + vkh::ImageCreateInfo(width, height, 0, fmt, + VK_IMAGE_USAGE_TRANSFER_DST_BIT, mips, arraySlices), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + } + + AllocatedImage MakeTex2DMS(VkFormat fmt, uint32_t width, uint32_t height, uint32_t samples, + uint32_t arraySlices = 1) + { + bool depth = (fmt == VK_FORMAT_D32_SFLOAT_S8_UINT || fmt == VK_FORMAT_D24_UNORM_S8_UINT || + fmt == VK_FORMAT_D32_SFLOAT || fmt == VK_FORMAT_S8_UINT); + + return AllocatedImage( + this, vkh::ImageCreateInfo(width, height, 0, fmt, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + (depth ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT), + 1, arraySlices, (VkSampleCountFlagBits)samples), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + } + + void Prepare(int argc, char **argv) + { + optDevExts.push_back(VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME); + + VulkanGraphicsTest::Prepare(argc, argv); + + if(!Avail.empty()) + return; + + static VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR separateDepthStencilFeatures = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES_KHR, + }; + + if(std::find(devExts.begin(), devExts.end(), + VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME) != devExts.end()) + { + getPhysFeatures2(&separateDepthStencilFeatures); + + if(!separateDepthStencilFeatures.separateDepthStencilLayouts) + Avail = "'separateDepthStencilLayouts' not available"; + + devInfoNext = &separateDepthStencilFeatures; + } + } + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + bool d32s8 = true; + VkFormat depthStencilFormat = VK_FORMAT_D32_SFLOAT_S8_UINT; + + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(phys, depthStencilFormat, &props); + if((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0) + { + depthStencilFormat = VK_FORMAT_D24_UNORM_S8_UINT; + d32s8 = false; + } + + bool d24s8 = true; + vkGetPhysicalDeviceFormatProperties(phys, VK_FORMAT_D24_UNORM_S8_UINT, &props); + if((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0) + d24s8 = false; + + bool d32 = true; + VkFormat depthFormat = VK_FORMAT_D32_SFLOAT; + + vkGetPhysicalDeviceFormatProperties(phys, depthFormat, &props); + if((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0) + { + depthFormat = VK_FORMAT_X8_D24_UNORM_PACK32; + d32 = false; + } + + bool KHR_separate_stencil = + std::find(devExts.begin(), devExts.end(), + VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME) != devExts.end(); + + { + byte *empty = new byte[16 * 1024 * 1024]; + memset(empty, 0x88, 16 * 1024 * 1024); + + emptyBuf = AllocatedBuffer( + this, vkh::BufferCreateInfo(16 * 1024 * 1024, VK_BUFFER_USAGE_TRANSFER_SRC_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + emptyBuf.upload(empty, 16 * 1024 * 1024); + delete[] empty; + } + + const VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + const VmaAllocationCreateInfo gpu = VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}); + + AllocatedImage ignoreimg( + this, + vkh::ImageCreateInfo(screenWidth, screenHeight, 0, VK_FORMAT_R16G16B16A16_SFLOAT, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + VkImageView ignoreview = createImageView(vkh::ImageViewCreateInfo( + ignoreimg.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R16G16B16A16_SFLOAT)); + + setName(ignoreimg.image, "NoDiscard"); + + // create RP color image + AllocatedImage colimg( + this, + vkh::ImageCreateInfo(mainWindow->scissor.extent.width, mainWindow->scissor.extent.height, 0, + VK_FORMAT_R16G16B16A16_SFLOAT, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + VkImageView colview = createImageView(vkh::ImageViewCreateInfo( + colimg.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R16G16B16A16_SFLOAT)); + + setName(colimg.image, "RPColor"); + + // create depth-stencil image + AllocatedImage depthimg( + this, + vkh::ImageCreateInfo(mainWindow->scissor.extent.width, mainWindow->scissor.extent.height, 0, + depthStencilFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + VkImageView depthview = createImageView(vkh::ImageViewCreateInfo( + depthimg.image, VK_IMAGE_VIEW_TYPE_2D, depthStencilFormat, {}, + vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))); + + setName(depthimg.image, "RPDepth"); + + vkh::RenderPassCreator renderPassCreateInfo; + + renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription( + VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE)); + renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription( + depthStencilFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_SAMPLE_COUNT_1_BIT, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE)); + renderPassCreateInfo.attachments.push_back(vkh::AttachmentDescription( + VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, + VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE)); + + renderPassCreateInfo.addSubpass({VkAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL})}, 1, + VK_IMAGE_LAYOUT_GENERAL); + + VkRenderPass renderPass = createRenderPass(renderPassCreateInfo); + + VkFramebuffer fb = createFramebuffer(vkh::FramebufferCreateInfo( + renderPass, {colview, depthview, ignoreview}, mainWindow->scissor.extent)); + + AllocatedImage tex1d(this, vkh::ImageCreateInfo(300, 0, 0, VK_FORMAT_R16G16B16A16_SFLOAT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT, 3, 5), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + AllocatedImage tex3d(this, vkh::ImageCreateInfo(300, 300, 15, VK_FORMAT_R16G16B16A16_SFLOAT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT, 3), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + AllocatedImage tex1dsub(this, vkh::ImageCreateInfo(300, 0, 0, VK_FORMAT_R16G16B16A16_SFLOAT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT, 3, 5), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + AllocatedImage tex3dsub(this, vkh::ImageCreateInfo(300, 300, 15, VK_FORMAT_R16G16B16A16_SFLOAT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT, 3), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + setName(tex1d.image, "Tex1D: DiscardAll"); + setName(tex3d.image, "Tex3D: DiscardAll"); + setName(tex1dsub.image, "Tex1D: DiscardAll Mip1 Slice1,2"); + setName(tex3dsub.image, "Tex3D: DiscardAll Mip1"); + + std::vector texs; + +#define TEX_TEST(name, x) \ + if(first) \ + { \ + texs.push_back(x); \ + Clear(cmd, texs.back()); \ + setName(texs.back().image, "Tex" + std::to_string(texs.size()) + ": " + name); \ + } \ + tex = texs[t++]; + + bool first = true; + + while(Running()) + { + if(!first) + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + pushMarker(cmd, "Clears"); + + for(AllocatedImage t : texs) + Clear(cmd, t); + + Clear(cmd, tex1d); + Clear(cmd, tex3d); + Clear(cmd, tex1dsub); + Clear(cmd, tex3dsub); + + popMarker(cmd); + + vkEndCommandBuffer(cmd); + + Submit(999, 999, {cmd}); + } + + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + Clear(cmd, ignoreimg); + Clear(cmd, colimg); + Clear(cmd, depthimg); + + VkImage swapimg = + StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkh::cmdPipelineBarrier( + cmd, { + vkh::ImageMemoryBarrier( + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL, swapimg), + }); + + // this is an anchor point for us to jump to and observe textures with all cleared contents + // and no discard patterns + setMarker(cmd, "TestStart"); + vkCmdClearColorImage(cmd, swapimg, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f), 1, + vkh::ImageSubresourceRange()); + + int t = 0; + AllocatedImage tex; + + // test a few different formats + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_R16G16B16A16_SFLOAT, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_A2B10G10R10_UNORM_PACK32, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_A2B10G10R10_UINT_PACK32, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_R8G8B8A8_UNORM, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_BC1_RGBA_UNORM_BLOCK, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_BC2_UNORM_BLOCK, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_BC3_UNORM_BLOCK, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_BC4_UNORM_BLOCK, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_BC5_UNORM_BLOCK, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_BC6H_UFLOAT_BLOCK, 300, 300)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_BC7_UNORM_BLOCK, 300, 300)); + DiscardImage(cmd, tex); + + // test with different mips/array sizes + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_R16G16B16A16_SFLOAT, 300, 300, 5)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_R16G16B16A16_SFLOAT, 300, 300, 1, 4)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_R16G16B16A16_SFLOAT, 300, 300, 5, 4)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_R16G16B16A16_SFLOAT, 30, 5)); + DiscardImage(cmd, tex); + + // test MSAA textures + TEX_TEST("DiscardAll", MakeTex2DMS(VK_FORMAT_R16G16B16A16_SFLOAT, 300, 300, 4)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2DMS(VK_FORMAT_R16G16B16A16_SFLOAT, 300, 300, 4, 5)); + DiscardImage(cmd, tex); + + // test depth textures + if(d32) + { + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_D32_SFLOAT, 300, 300)); + DiscardImage(cmd, tex); + } + + if(d32s8) + { + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_D32_SFLOAT_S8_UINT, 300, 300)); + DiscardImage(cmd, tex); + } + + if(d24s8) + { + TEX_TEST("DiscardAll", MakeTex2D(VK_FORMAT_D24_UNORM_S8_UINT, 300, 300)); + DiscardImage(cmd, tex); + } + + TEX_TEST("DiscardAll", MakeTex2D(depthFormat, 300, 300, 5)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(depthFormat, 300, 300, 1, 4)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(depthFormat, 300, 300, 5, 4)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(depthStencilFormat, 300, 300, 5)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(depthStencilFormat, 300, 300, 1, 4)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2D(depthStencilFormat, 300, 300, 5, 4)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2DMS(depthStencilFormat, 300, 300, 4)); + DiscardImage(cmd, tex); + TEX_TEST("DiscardAll", MakeTex2DMS(depthStencilFormat, 300, 300, 4, 5)); + DiscardImage(cmd, tex); + + // if supported, test invalidating depth and stencil alone + if(KHR_separate_stencil) + { + TEX_TEST("DiscardAll DepthOnly", MakeTex2D(depthStencilFormat, 300, 300)); + + vkh::cmdPipelineBarrier( + cmd, { + vkh::ImageMemoryBarrier( + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR, + tex.image, vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT)), + }); + + TEX_TEST("DiscardAll StencilOnly", MakeTex2D(depthStencilFormat, 300, 300)); + + vkh::cmdPipelineBarrier( + cmd, { + vkh::ImageMemoryBarrier( + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR, + tex.image, vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_STENCIL_BIT)), + }); + } + + // test 1D/3D textures + DiscardImage(cmd, tex1d); + DiscardImage(cmd, tex3d); + + DiscardImage(cmd, tex1dsub, vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 1, 1, 1, 2)); + DiscardImage(cmd, tex3dsub, vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 1, 1)); + + // test a renderpass. This tests rects via renderArea, as well as vulkan-specific load-op and + // store-op and ensures that unused attachments are not discarded + + VkRect2D sc = {{50, 50}, {75, 75}}; + + vkCmdBeginRenderPass(cmd, vkh::RenderPassBeginInfo(renderPass, fb, sc), + VK_SUBPASS_CONTENTS_INLINE); + + // add an anchor for us to check mid-render pass. This clear only sets one pixel to black + // which won't affect our tests + setMarker(cmd, "TestMiddle"); + VkClearAttachment att = {VK_IMAGE_ASPECT_COLOR_BIT, 0, vkh::ClearValue(0.0f, 0.0f, 0.0f, 0.0f)}; + VkClearRect rect = {vkh::Rect2D({50, 50}, {1, 1}), 0, 1}; + vkCmdClearAttachments(cmd, 1, &att, 1, &rect); + + vkCmdEndRenderPass(cmd); + + vkh::cmdPipelineBarrier( + cmd, + { + vkh::ImageMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, swapimg), + }); + + setMarker(cmd, "TestEnd"); + vkCmdClearColorImage(cmd, swapimg, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f), 1, + vkh::ImageSubresourceRange()); + + FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + + vkEndCommandBuffer(cmd); + + Submit(0, 1, {cmd}); + + Present(); + + first = false; + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/vk/vk_test.cpp b/util/test/demos/vk/vk_test.cpp index d470ff676..6ffdfe169 100644 --- a/util/test/demos/vk/vk_test.cpp +++ b/util/test/demos/vk/vk_test.cpp @@ -1301,6 +1301,7 @@ VkFormat vkh::_FormatFromObj() AllocatedImage::AllocatedImage(VulkanGraphicsTest *test, const VkImageCreateInfo &imgInfo, const VmaAllocationCreateInfo &allocInfo) { + createInfo = imgInfo; this->test = test; allocator = test->allocator; vmaCreateImage(allocator, &imgInfo, &allocInfo, &image, &alloc, NULL); diff --git a/util/test/demos/vk/vk_test.h b/util/test/demos/vk/vk_test.h index d096c9dbe..95a42fd86 100644 --- a/util/test/demos/vk/vk_test.h +++ b/util/test/demos/vk/vk_test.h @@ -82,6 +82,7 @@ struct AllocatedImage VmaAllocator allocator = NULL; VkImage image = VK_NULL_HANDLE; VmaAllocation alloc = {}; + VkImageCreateInfo createInfo; AllocatedImage() {} AllocatedImage(VulkanGraphicsTest *test, const VkImageCreateInfo &imgInfo, diff --git a/util/test/rdtest/__init__.py b/util/test/rdtest/__init__.py index 236612c04..d4625b6fe 100644 --- a/util/test/rdtest/__init__.py +++ b/util/test/rdtest/__init__.py @@ -8,3 +8,4 @@ from .shared.Mesh_Zoo import * from .shared.Draw_Zoo import * from .shared.Overlay_Test import * from .shared.Buffer_Truncation import * +from .shared.Discard_Zoo import * diff --git a/util/test/rdtest/shared/Discard_Zoo.py b/util/test/rdtest/shared/Discard_Zoo.py new file mode 100644 index 000000000..d6d6d2985 --- /dev/null +++ b/util/test/rdtest/shared/Discard_Zoo.py @@ -0,0 +1,210 @@ +import renderdoc as rd +import rdtest + +# Not a real test, re-used by API-specific tests +class Discard_Zoo(rdtest.TestCase): + internal = True + + def check_val(self, picked, val, fmt): + if type(val) != list: + val = [val, val, val, val] + + if fmt.compType == rd.CompType.UInt or fmt.compType == rd.CompType.SInt: + val = [int(a) for a in val] + return rdtest.value_compare(picked.intValue[0:fmt.compCount], val[0:fmt.compCount]) + else: + comp_val = picked.floatValue[0:fmt.compCount] + if fmt.compType == rd.CompType.Depth or fmt.type in [rd.ResourceFormatType.D16S8, rd.ResourceFormatType.D24S8, rd.ResourceFormatType.D32S8]: + comp_val = [min(1.0, a) for a in comp_val] + + return rdtest.value_compare(comp_val, val[0:fmt.compCount]) + + def check_texture(self, id, discarded: bool): + tex: rd.TextureDescription = self.get_texture(id) + res: rd.ResourceDescription = self.get_resource(id) + + fmt: rd.ResourceFormat = tex.format + + props: rd.APIProperties = self.controller.GetAPIProperties() + gl = (props.pipelineType == rd.GraphicsAPI.OpenGL) + + name = '{} - {}x{} {} mip {} slice {}x MSAA {} format texture'.format(res.name, tex.width, tex.height, + tex.mips, tex.arraysize, + tex.msSamp, tex.format.Name()) + + minval = 0.0 + maxval = 1000.0 + + if fmt.type == rd.ResourceFormatType.R9G9B9E5: + maxval = 998.0 + elif fmt.type == rd.ResourceFormatType.BC6: + maxval = 996.0 + elif fmt.type == rd.ResourceFormatType.R10G10B10A2 and fmt.compType == rd.CompType.UInt: + maxval = [1023.0, 1023.0, 1023.0, 3.0] + elif fmt.type in [rd.ResourceFormatType.D16S8, rd.ResourceFormatType.D24S8, rd.ResourceFormatType.D32S8]: + maxval = 1.0 + fmt.compCount = 2 + + if "DepthOnly" in res.name: + minval = [minval, float(0x40)/float(255)] + maxval = [maxval, float(0x40)/float(255)] + elif "StencilOnly" in res.name: + minval = [0.4, minval] + maxval = [0.4, maxval] + elif fmt.type == rd.ResourceFormatType.S8: + minval = [0.0, 0.0] + maxval = [0.0, 1.0] + fmt.compCount = 2 + elif fmt.compType == rd.CompType.UNorm or fmt.compType == rd.CompType.Depth: + maxval = 1.0 + + # ignore alpha in BC1 and BC6 + if fmt.type == rd.ResourceFormatType.BC1 or fmt.type == rd.ResourceFormatType.BC6: + fmt.compCount = 3 + + for mip in range(tex.mips): + for slice in range(tex.arraysize): + for samp in range(tex.msSamp): + sub = rd.Subresource(mip, slice, samp) + + sub_discarded = discarded + + if "Mip" in res.name: + idx = res.name.index('Mip')+3 + try: + idx2 = res.name.index(' ', idx) + except ValueError: + idx2 = len(res.name) + if mip not in [int(m) for m in res.name[idx:idx2].split(',')]: + sub_discarded = False + + if "Slice" in res.name: + idx = res.name.index('Slice') + 5 + try: + idx2 = res.name.index(' ', idx) + except ValueError: + idx2 = len(res.name) + if slice not in [int(s) for s in res.name[idx:idx2].split(',')]: + sub_discarded = False + + if "NoDiscard" in res.name: + sub_discarded = False + + w = max(1, tex.width >> mip) + h = max(1, tex.height >> mip) + + if not sub_discarded or "DiscardRect" in res.name: + # if not discarded, or we only discarded a rect, check a few locations in the corners and ensure + # that we don't see our pattern colours. + for (x, y) in [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (1, 1), (2, 2), (3, 3), + (4, 4), (w - 1, h - 1), (w - 2, h - 2), (w - 1, 0), (w - 1, 1), (w - 1, 2), + (0, h - 1), (1, h - 1), (2, h - 1), (3, h - 2)]: + # underflow can happen with 1D textures or tiny mips + if x < 0: + x = 0 + if y < 0: + y = 0 + + if gl and h > 1: + y = h - 1 - y + + picked: rd.PixelValue = self.controller.PickPixel(id, x, y, sub, rd.CompType.Typeless) + + if self.check_val(picked, minval, fmt) or self.check_val(picked, maxval, fmt): + raise rdtest.TestFailureException( + '{} has unexpected value at {},{}: {}'.format(name, x, y, + picked.floatValue)) + + if sub_discarded: + seen = [False, False] + # Check that pixels inside the rect only show our pattern colours (we don't check the actual + # pattern) + for (x, y) in [(50, 50), (51, 50), (52, 50), (53, 50), (54, 50), (55, 50), (51, 52), (120, 123), + (124, 124), (119, 122), (119, 124), (122, 124), (70, 70), (80, 85), (73, 124), + (123, 60)]: + # overflow can happen with 1D textures or tiny mips + if x >= w: + x = w-1 + if y >= h: + y = h-1 + + if gl and h > 1: + y = h - 1 - y + + picked: rd.PixelValue = self.controller.PickPixel(id, x, y, sub, rd.CompType.Typeless) + + is_min = self.check_val(picked, minval, fmt) + is_max = self.check_val(picked, maxval, fmt) + + if not is_min and not is_max: + raise rdtest.TestFailureException( + '{} has unexpected value at {},{}: {}'.format(name, x, y, + picked.floatValue)) + + if is_min: + seen[0] = True + if is_max: + seen[1] = True + + # Do an additional checks outside the rect + if "DiscardRect" not in res.name: + for (x, y) in [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (1, 1), (2, 2), (3, 3), + (4, 4), (w - 1, h - 1), (w - 2, h - 2), (w - 1, 0), (w - 1, 1), (w - 1, 2), + (0, h - 1), (1, h - 1), (2, h - 1), (3, h - 2)]: + # underflow can happen with 1D textures or tiny mips + if x < 0: + x = 0 + if y < 0: + y = 0 + + if gl and h > 1: + y = h - 1 - y + + picked: rd.PixelValue = self.controller.PickPixel(id, x, y, sub, rd.CompType.Typeless) + + is_min = self.check_val(picked, minval, fmt) + is_max = self.check_val(picked, maxval, fmt) + + if not is_min and not is_max: + raise rdtest.TestFailureException( + '{} has unexpected value at {},{}: {}'.format(name, x, y, + picked.floatValue)) + + if is_min: + seen[0] = True + if is_max: + seen[1] = True + + # We also expect to have seen both colours. That means if we only saw black for example then we + # fail + if not seen[0] or not seen[1]: + raise rdtest.TestFailureException('{} doesn\'t contain expected pattern'.format(name)) + + rdtest.log.success('{} is OK {} discarding'.format(name, "after" if discarded else "before")) + + def check_textures(self): + draw = self.find_draw("TestStart") + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, True) + + for tex in self.controller.GetTextures(): + tex: rd.TextureDescription + res: rd.ResourceDescription = self.get_resource(tex.resourceId) + + if "Discard" in res.name: + self.check_texture(tex.resourceId, False) + + draw = self.find_draw("TestEnd") + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, True) + + for tex in self.controller.GetTextures(): + tex: rd.TextureDescription + res: rd.ResourceDescription = self.get_resource(tex.resourceId) + + if "Discard" in res.name: + self.check_texture(tex.resourceId, True) \ No newline at end of file diff --git a/util/test/tests/D3D11/D3D11_Discard_Zoo.py b/util/test/tests/D3D11/D3D11_Discard_Zoo.py new file mode 100644 index 000000000..d73794461 --- /dev/null +++ b/util/test/tests/D3D11/D3D11_Discard_Zoo.py @@ -0,0 +1,55 @@ +import rdtest +import struct +import renderdoc as rd + + +class D3D11_Discard_Zoo(rdtest.Discard_Zoo): + demos_test_name = 'D3D11_Discard_Zoo' + internal = False + + def __init__(self): + rdtest.Discard_Zoo.__init__(self) + + def check_capture(self): + self.check_textures() + + draw = self.find_draw("TestStart") + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, True) + + # Check the buffer + for res in self.controller.GetResources(): + if res.name == "Buffer" or res.name == "BufferSRV" or res.name == "BufferRTV": + data: bytes = self.controller.GetBufferData(res.resourceId, 0, 0) + + self.check(all([b == 0x88 for b in data])) + + draw = self.find_draw("TestEnd") + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, True) + + # Check the buffers + for res in self.controller.GetResources(): + if res.name == "Buffer" or res.name == "BufferSRV": + data: bytes = self.controller.GetBufferData(res.resourceId, 0, 0) + + data_u32 = struct.unpack_from('=256L', data, 0) + + self.check(all([u == 0xD15CAD3D for u in data_u32])) + elif res.name == "BufferRTV": + data: bytes = self.controller.GetBufferData(res.resourceId, 0, 0) + + data_u32 = struct.unpack_from('=18L', data, 50) + + self.check(all([u == 0xD15CAD3D for u in data_u32])) + + data_u16 = struct.unpack_from('=H', data, 50+72) + + self.check(data_u16[0] == 0xAD3D) + + self.check(all([b == 0x88 for b in data[0:50]])) + self.check(all([b == 0x88 for b in data[50+75:-1]])) \ No newline at end of file diff --git a/util/test/tests/D3D12/D3D12_Discard_Zoo.py b/util/test/tests/D3D12/D3D12_Discard_Zoo.py new file mode 100644 index 000000000..507ba6f5b --- /dev/null +++ b/util/test/tests/D3D12/D3D12_Discard_Zoo.py @@ -0,0 +1,42 @@ +import rdtest +import struct +import renderdoc as rd + + +class D3D12_Discard_Zoo(rdtest.Discard_Zoo): + demos_test_name = 'D3D12_Discard_Zoo' + internal = False + + def __init__(self): + rdtest.Discard_Zoo.__init__(self) + + def check_capture(self): + self.check_textures() + + draw = self.find_draw("TestStart") + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, True) + + # Check the buffer + for res in self.controller.GetResources(): + if res.name == "Buffer": + data: bytes = self.controller.GetBufferData(res.resourceId, 0, 0) + + self.check(all([b == 0x88 for b in data])) + + draw = self.find_draw("TestEnd") + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, True) + + # Check the buffer + for res in self.controller.GetResources(): + if res.name == "Buffer": + data: bytes = self.controller.GetBufferData(res.resourceId, 0, 0) + + data_u32 = struct.unpack_from('=256L', data, 0) + + self.check(all([u == 0xD15CAD3D for u in data_u32])) \ No newline at end of file diff --git a/util/test/tests/GL/GL_Discard_Zoo.py b/util/test/tests/GL/GL_Discard_Zoo.py new file mode 100644 index 000000000..ee874c365 --- /dev/null +++ b/util/test/tests/GL/GL_Discard_Zoo.py @@ -0,0 +1,55 @@ +import rdtest +import struct +import renderdoc as rd + + +class GL_Discard_Zoo(rdtest.Discard_Zoo): + demos_test_name = 'GL_Discard_Zoo' + internal = False + + def __init__(self): + rdtest.Discard_Zoo.__init__(self) + + def check_capture(self): + self.check_textures() + + draw = self.find_draw("TestStart") + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, True) + + # Check the buffer + for res in self.controller.GetResources(): + if res.name == "Buffer" or res.name == "BufferSub": + data: bytes = self.controller.GetBufferData(res.resourceId, 0, 0) + + self.check(all([b == 0x88 for b in data])) + + draw = self.find_draw("TestEnd") + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.eventId, True) + + # Check the buffer + for res in self.controller.GetResources(): + if res.name == "Buffer": + data: bytes = self.controller.GetBufferData(res.resourceId, 0, 0) + + data_u32 = struct.unpack_from('=256L', data, 0) + + self.check(all([u == 0xD15CAD3D for u in data_u32])) + elif res.name == "BufferSub": + data: bytes = self.controller.GetBufferData(res.resourceId, 0, 0) + + data_u32 = struct.unpack_from('=18L', data, 50) + + self.check(all([u == 0xD15CAD3D for u in data_u32])) + + data_u16 = struct.unpack_from('=H', data, 50+72) + + self.check(data_u16[0] == 0xAD3D) + + self.check(all([b == 0x88 for b in data[0:50]])) + self.check(all([b == 0x88 for b in data[50+75:-1]])) \ No newline at end of file diff --git a/util/test/tests/Vulkan/VK_Discard_Zoo.py b/util/test/tests/Vulkan/VK_Discard_Zoo.py new file mode 100644 index 000000000..00119872b --- /dev/null +++ b/util/test/tests/Vulkan/VK_Discard_Zoo.py @@ -0,0 +1,96 @@ +import rdtest +import struct +import renderdoc as rd + + +class VK_Discard_Zoo(rdtest.Discard_Zoo): + demos_test_name = 'VK_Discard_Zoo' + internal = False + + def __init__(self): + rdtest.Discard_Zoo.__init__(self) + + def check_capture(self): + self.check_textures() + + # Test render pass attachments + draw = self.find_draw("TestStart") + + rpcol: rd.TextureDescription = self.get_texture( + [res for res in self.controller.GetResources() if "RPCol" in res.name][0].resourceId) + rpdepth: rd.TextureDescription = self.get_texture( + [res for res in self.controller.GetResources() if "RPDepth" in res.name][0].resourceId) + + self.check(draw is not None) + + self.controller.SetFrameEvent(draw.next.eventId, True) + + # At the start they should be cleared + + for y in range(0, rpcol.height-1, 17): + for x in range(0, rpcol.width-1, 17): + self.check_pixel_value(rpcol.resourceId, x, y, [0.0, 1.0, 0.0, 1.0]) + self.check_pixel_value(rpdepth.resourceId, x, y, [0.4, float(0x40)/float(255), 0.0, 1.0]) + + draw = self.find_draw("TestMiddle") + + self.controller.SetFrameEvent(draw.next.eventId, True) + + for y in range(0, rpcol.height-1, 17): + for x in range(0, rpcol.width-1, 17): + + # if we're in the rect, check for pattern colors + if 50 <= x < 125 and 50 <= y < 125: + c: rd.PixelValue = self.controller.PickPixel(rpcol.resourceId, x, y, rd.Subresource(), + rd.CompType.Typeless) + d: rd.PixelValue = self.controller.PickPixel(rpdepth.resourceId, x, y, rd.Subresource(), + rd.CompType.Typeless) + + if not rdtest.value_compare(c.floatValue, [0.0] * 4) and not rdtest.value_compare(c.floatValue, + [1000.0] * 4): + raise rdtest.TestFailureException( + 'middle color has unexpected value at {},{}: {}'.format(x, y, c.floatValue)) + + if not rdtest.value_compare(d.floatValue[0:2], [0.0] * 2) and not rdtest.value_compare( + d.floatValue[0:2], [1.0] * 2): + raise rdtest.TestFailureException( + 'middle depth has unexpected value at {},{}: {}'.format(x, y, d.floatValue)) + else: + self.check_pixel_value(rpcol.resourceId, x, y, [0.0, 1.0, 0.0, 1.0]) + self.check_pixel_value(rpdepth.resourceId, x, y, [0.4, float(0x40)/float(255), 0.0, 1.0]) + + middle_col_bytes = self.controller.GetTextureData(rpcol.resourceId, rd.Subresource()) + middle_depth_bytes = self.controller.GetTextureData(rpdepth.resourceId, rd.Subresource()) + + draw = self.find_draw("TestEnd") + + self.controller.SetFrameEvent(draw.next.eventId, True) + + for y in range(0, rpcol.height-1, 17): + for x in range(0, rpcol.width-1, 17): + + # if we're in the rect, check for pattern colors + if 50 <= x < 125 and 50 <= y < 125: + c: rd.PixelValue = self.controller.PickPixel(rpcol.resourceId, x, y, rd.Subresource(), + rd.CompType.Typeless) + d: rd.PixelValue = self.controller.PickPixel(rpdepth.resourceId, x, y, rd.Subresource(), + rd.CompType.Typeless) + + if not rdtest.value_compare(c.floatValue, [0.0] * 4) and not rdtest.value_compare(c.floatValue, + [1000.0] * 4): + raise rdtest.TestFailureException( + 'middle color has unexpected value at {},{}: {}'.format(x, y, c.floatValue)) + + if not rdtest.value_compare(d.floatValue[0:2], [0.0] * 2) and not rdtest.value_compare( + d.floatValue[0:2], [1.0] * 2): + raise rdtest.TestFailureException( + 'middle depth has unexpected value at {},{}: {}'.format(x, y, d.floatValue)) + else: + self.check_pixel_value(rpcol.resourceId, x, y, [0.0, 1.0, 0.0, 1.0]) + self.check_pixel_value(rpdepth.resourceId, x, y, [0.4, float(0x40)/float(255), 0.0, 1.0]) + + end_col_bytes = self.controller.GetTextureData(rpcol.resourceId, rd.Subresource()) + end_depth_bytes = self.controller.GetTextureData(rpdepth.resourceId, rd.Subresource()) + + self.check(middle_col_bytes != end_col_bytes) + self.check(middle_depth_bytes != end_depth_bytes) \ No newline at end of file