From 965d1c956862bc60bb2dd4493f98f676be90bee7 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 23 Jan 2026 15:43:19 +0000 Subject: [PATCH] Add demo tests for annotations --- util/test/demos/CMakeLists.txt | 2 + util/test/demos/d3d11/d3d11_annotations.cpp | 191 +++++++++++++++ util/test/demos/d3d12/d3d12_annotations.cpp | 205 ++++++++++++++++ util/test/demos/demos.vcxproj | 4 + util/test/demos/demos.vcxproj.filters | 12 + util/test/demos/gl/gl_annotations.cpp | 189 +++++++++++++++ util/test/demos/main.cpp | 2 + util/test/demos/vk/vk_annotations.cpp | 239 +++++++++++++++++++ util/test/demos/vk/vk_simple_triangle.cpp | 94 ++++++++ util/test/rdtest/__init__.py | 1 + util/test/rdtest/shared/Annotations.py | 252 ++++++++++++++++++++ util/test/tests/D3D11/D3D11_Annotations.py | 11 + util/test/tests/D3D12/D3D12_Annotations.py | 11 + util/test/tests/GL/GL_Annotations.py | 11 + util/test/tests/Vulkan/VK_Annotations.py | 11 + 15 files changed, 1235 insertions(+) create mode 100644 util/test/demos/d3d11/d3d11_annotations.cpp create mode 100644 util/test/demos/d3d12/d3d12_annotations.cpp create mode 100644 util/test/demos/gl/gl_annotations.cpp create mode 100644 util/test/demos/vk/vk_annotations.cpp create mode 100644 util/test/rdtest/shared/Annotations.py create mode 100644 util/test/tests/D3D11/D3D11_Annotations.py create mode 100644 util/test/tests/D3D12/D3D12_Annotations.py create mode 100644 util/test/tests/GL/GL_Annotations.py create mode 100644 util/test/tests/Vulkan/VK_Annotations.py diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt index 87af43c06..3f24d7116 100644 --- a/util/test/demos/CMakeLists.txt +++ b/util/test/demos/CMakeLists.txt @@ -101,6 +101,7 @@ set(VULKAN_SRC vk/vk_test.cpp vk/vk_test.h vk/vk_adv_cbuffer_zoo.cpp + vk/vk_annotations.cpp vk/vk_blend.cpp vk/vk_buffer_truncation.cpp vk/vk_cbuffer_zoo.cpp @@ -174,6 +175,7 @@ set(OPENGL_SRC 3rdparty/glad/glad.c gl/gl_test.cpp gl/gl_test.h + gl/gl_annotations.cpp gl/gl_buffer_resizing.cpp gl/gl_buffer_spam.cpp gl/gl_buffer_truncation.cpp diff --git a/util/test/demos/d3d11/d3d11_annotations.cpp b/util/test/demos/d3d11/d3d11_annotations.cpp new file mode 100644 index 000000000..32bfbdf31 --- /dev/null +++ b/util/test/demos/d3d11/d3d11_annotations.cpp @@ -0,0 +1,191 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2023-2026 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_Annotations, D3D11GraphicsTest) +{ + static constexpr const char *Description = "Test annotations via the D3D11 API."; + + int main() + { + // initialise, create window, create device, etc + if(!Init()) + return 3; + + ID3D11Texture2DPtr img = MakeTexture(DXGI_FORMAT_R32G32B32A32_FLOAT, 4, 4).RTV(); + ID3D11RenderTargetViewPtr imgRTV = MakeRTV(img); + + SetDebugName(img, "Annotated Image"); + SetDebugName(DefaultTriVB, "Vertex Buffer"); + + // cache the device pointer we pass in + void *d = dev; + + if(rdoc) + { + rdoc->SetObjectAnnotation(d, img, "basic.bool", eRENDERDOC_Bool, 0, RDAnnotationHelper(true)); + rdoc->SetObjectAnnotation(d, img, "basic.int32", eRENDERDOC_Int32, 0, RDAnnotationHelper(-3)); + rdoc->SetObjectAnnotation(d, img, "basic.int64", eRENDERDOC_Int64, 0, + RDAnnotationHelper(-3000000000000LL)); + rdoc->SetObjectAnnotation(d, img, "basic.uint32", eRENDERDOC_UInt32, 0, RDAnnotationHelper(3)); + rdoc->SetObjectAnnotation(d, img, "basic.uint64", eRENDERDOC_UInt64, 0, + RDAnnotationHelper(3000000000000LL)); + rdoc->SetObjectAnnotation(d, img, "basic.float", eRENDERDOC_Float, 0, + RDAnnotationHelper(3.25f)); + rdoc->SetObjectAnnotation(d, img, "basic.double", eRENDERDOC_Double, 0, + RDAnnotationHelper(3.25000000001)); + rdoc->SetObjectAnnotation(d, img, "basic.string", eRENDERDOC_String, 0, + RDAnnotationHelper("Hello, World!")); + + RENDERDOC_AnnotationValue val; + val.apiObject = (void *)DefaultTriVB; + rdoc->SetObjectAnnotation(d, img, "basic.object", eRENDERDOC_APIObject, 0, &val); + + rdoc->SetObjectAnnotation(d, img, "basic.object.__offset", eRENDERDOC_UInt32, 0, + RDAnnotationHelper(64)); + rdoc->SetObjectAnnotation(d, img, "basic.object.__size", eRENDERDOC_UInt32, 0, + RDAnnotationHelper(32)); + rdoc->SetObjectAnnotation(d, img, "basic.object.__rd_format", eRENDERDOC_String, 0, + RDAnnotationHelper("float4 vertex_data;")); + + rdoc->SetObjectAnnotation(d, DefaultTriVB, "__rd_format", eRENDERDOC_String, 0, + RDAnnotationHelper("float3 pos;\n" + "float4 col;\n" + "float2 uv;\n")); + + val = {}; + val.vector.float32[0] = 1.1f; + val.vector.float32[1] = 2.2f; + val.vector.float32[2] = 3.3f; + val.vector.float32[3] = 4.4f; // should be ignored + rdoc->SetObjectAnnotation(d, img, "basic.vec3", eRENDERDOC_Float, 3, &val); + + rdoc->SetObjectAnnotation(d, img, "deep.nested.path.to.annotation", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-4)); + rdoc->SetObjectAnnotation(d, img, "deep.nested.path.to.annotation2", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-5)); + rdoc->SetObjectAnnotation(d, img, "deep.alternate.path.to.annotation", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-6)); + + // deleted paths should not stay around + rdoc->SetObjectAnnotation(d, img, "deleteme", eRENDERDOC_Int32, 0, RDAnnotationHelper(-7)); + rdoc->SetObjectAnnotation(d, img, "deleteme", eRENDERDOC_Empty, 0, NULL); + + rdoc->SetObjectAnnotation(d, img, "path.deleted.by.parent", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-8)); + rdoc->SetObjectAnnotation(d, img, "path.deleted.by.parent2", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-9)); + + // this will delete all children. `path` will still exist, but will be empty + rdoc->SetObjectAnnotation(d, img, "path.deleted", eRENDERDOC_Empty, 0, NULL); + } + + while(Running()) + { + if(rdoc) + { + // queue annotations don't exist in D3D11, but we set these to share the same test code. + // we specify the immediate context here specifically. Later we'll use NULL (which is equivalent on D3D11) + if(curFrame == 2) + rdoc->SetCommandAnnotation(d, ctx, "queue.too_old", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1000)); + + rdoc->SetCommandAnnotation(d, ctx, "queue.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1000)); + + rdoc->SetCommandAnnotation(d, ctx, "command.overwritten", eRENDERDOC_Int32, 0, + RDAnnotationHelper(9999)); + + rdoc->SetCommandAnnotation(d, ctx, "command.inherited", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1234)); + + rdoc->SetCommandAnnotation(d, ctx, "command.deleted", eRENDERDOC_Int32, 0, + RDAnnotationHelper(50)); + } + + setMarker("Start"); + + if(rdoc) + { + rdoc->SetCommandAnnotation(d, NULL, "new.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(2000)); + + rdoc->SetCommandAnnotation(d, NULL, "command.overwritten", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-3333)); + + rdoc->SetCommandAnnotation(d, NULL, "command.new", eRENDERDOC_Int32, 0, + RDAnnotationHelper(3333)); + + rdoc->SetCommandAnnotation(d, NULL, "command.deleted", eRENDERDOC_Empty, 0, NULL); + } + + setMarker("Initial"); + + ClearRenderTargetView(bbRTV, {0.2f, 0.2f, 0.2f, 1.0f}); + ClearRenderTargetView(imgRTV, {0.2f, 0.2f, 0.2f, 1.0f}); + + if(rdoc) + rdoc->SetCommandAnnotation(d, NULL, "command.new", eRENDERDOC_Float, 0, + RDAnnotationHelper(1.75f)); + + IASetVertexBuffer(DefaultTriVB, sizeof(DefaultA2V), 0); + ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + ctx->IASetInputLayout(defaultLayout); + + ctx->VSSetShader(DefaultTriVS, NULL, 0); + ctx->PSSetShader(DefaultTriPS, NULL, 0); + + RSSetViewport({0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + + ctx->OMSetRenderTargets(1, &bbRTV.GetInterfacePtr(), NULL); + + setMarker("Pre-Draw"); + + // deleting a value is fine if it's re-added before the next event + if(rdoc) + { + rdoc->SetCommandAnnotation(d, NULL, "new.value", eRENDERDOC_Empty, 0, NULL); + rdoc->SetCommandAnnotation(d, NULL, "new.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(4000)); + } + + setMarker("Draw 1"); + + ctx->Draw(3, 0); + + RSSetViewport({0.0f, 0.0f, (float)screenWidth / 2.0f, (float)screenHeight / 2.0f, 0.0f, 1.0f}); + + setMarker("Draw 2"); + + ctx->Draw(3, 0); + + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/d3d12/d3d12_annotations.cpp b/util/test/demos/d3d12/d3d12_annotations.cpp new file mode 100644 index 000000000..909eb59d1 --- /dev/null +++ b/util/test/demos/d3d12/d3d12_annotations.cpp @@ -0,0 +1,205 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2023-2026 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_Annotations, D3D12GraphicsTest) +{ + static constexpr const char *Description = "Test annotations via the D3D12 API."; + + int main() + { + // initialise, create window, create device, etc + if(!Init()) + return 3; + + ID3D12ResourcePtr img = MakeTexture(DXGI_FORMAT_R32G32B32A32_FLOAT, 4, 4) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET); + + img->SetName(L"Annotated Image"); + DefaultTriVB->SetName(L"Vertex Buffer"); + + // cache the device pointer we pass in + void *d = dev; + + if(rdoc) + { + rdoc->SetObjectAnnotation(d, img, "basic.bool", eRENDERDOC_Bool, 0, RDAnnotationHelper(true)); + rdoc->SetObjectAnnotation(d, img, "basic.int32", eRENDERDOC_Int32, 0, RDAnnotationHelper(-3)); + rdoc->SetObjectAnnotation(d, img, "basic.int64", eRENDERDOC_Int64, 0, + RDAnnotationHelper(-3000000000000LL)); + rdoc->SetObjectAnnotation(d, img, "basic.uint32", eRENDERDOC_UInt32, 0, RDAnnotationHelper(3)); + rdoc->SetObjectAnnotation(d, img, "basic.uint64", eRENDERDOC_UInt64, 0, + RDAnnotationHelper(3000000000000LL)); + rdoc->SetObjectAnnotation(d, img, "basic.float", eRENDERDOC_Float, 0, + RDAnnotationHelper(3.25f)); + rdoc->SetObjectAnnotation(d, img, "basic.double", eRENDERDOC_Double, 0, + RDAnnotationHelper(3.25000000001)); + rdoc->SetObjectAnnotation(d, img, "basic.string", eRENDERDOC_String, 0, + RDAnnotationHelper("Hello, World!")); + + RENDERDOC_AnnotationValue val; + val.apiObject = (void *)DefaultTriVB; + rdoc->SetObjectAnnotation(d, img, "basic.object", eRENDERDOC_APIObject, 0, &val); + + rdoc->SetObjectAnnotation(d, img, "basic.object.__offset", eRENDERDOC_UInt32, 0, + RDAnnotationHelper(64)); + rdoc->SetObjectAnnotation(d, img, "basic.object.__size", eRENDERDOC_UInt32, 0, + RDAnnotationHelper(32)); + rdoc->SetObjectAnnotation(d, img, "basic.object.__rd_format", eRENDERDOC_String, 0, + RDAnnotationHelper("float4 vertex_data;")); + + rdoc->SetObjectAnnotation(d, DefaultTriVB, "__rd_format", eRENDERDOC_String, 0, + RDAnnotationHelper("float3 pos;\n" + "float4 col;\n" + "float2 uv;\n")); + + val = {}; + val.vector.float32[0] = 1.1f; + val.vector.float32[1] = 2.2f; + val.vector.float32[2] = 3.3f; + val.vector.float32[3] = 4.4f; // should be ignored + rdoc->SetObjectAnnotation(d, img, "basic.vec3", eRENDERDOC_Float, 3, &val); + + rdoc->SetObjectAnnotation(d, img, "deep.nested.path.to.annotation", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-4)); + rdoc->SetObjectAnnotation(d, img, "deep.nested.path.to.annotation2", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-5)); + rdoc->SetObjectAnnotation(d, img, "deep.alternate.path.to.annotation", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-6)); + + // deleted paths should not stay around + rdoc->SetObjectAnnotation(d, img, "deleteme", eRENDERDOC_Int32, 0, RDAnnotationHelper(-7)); + rdoc->SetObjectAnnotation(d, img, "deleteme", eRENDERDOC_Empty, 0, NULL); + + rdoc->SetObjectAnnotation(d, img, "path.deleted.by.parent", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-8)); + rdoc->SetObjectAnnotation(d, img, "path.deleted.by.parent2", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-9)); + + // this will delete all children. `path` will still exist, but will be empty + rdoc->SetObjectAnnotation(d, img, "path.deleted", eRENDERDOC_Empty, 0, NULL); + } + + while(Running()) + { + if(rdoc) + { + // queue annotations are only included when in the captured frame + if(curFrame == 2) + rdoc->SetCommandAnnotation(d, queue, "queue.too_old", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1000)); + + rdoc->SetCommandAnnotation(d, queue, "queue.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1000)); + + rdoc->SetCommandAnnotation(d, queue, "command.overwritten", eRENDERDOC_Int32, 0, + RDAnnotationHelper(9999)); + + rdoc->SetCommandAnnotation(d, queue, "command.inherited", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1234)); + + rdoc->SetCommandAnnotation(d, queue, "command.deleted", eRENDERDOC_Int32, 0, + RDAnnotationHelper(50)); + } + + ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer(); + + Reset(cmd); + + setMarker(cmd, "Start"); + + if(rdoc) + { + rdoc->SetCommandAnnotation(d, cmd, "new.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(2000)); + + rdoc->SetCommandAnnotation(d, cmd, "command.overwritten", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-3333)); + + rdoc->SetCommandAnnotation(d, cmd, "command.new", eRENDERDOC_Int32, 0, + RDAnnotationHelper(3333)); + + rdoc->SetCommandAnnotation(d, cmd, "command.deleted", eRENDERDOC_Empty, 0, NULL); + } + + setMarker(cmd, "Initial"); + + D3D12_CPU_DESCRIPTOR_HANDLE rtv = + MakeRTV(img).Format(DXGI_FORMAT_R32G32B32A32_FLOAT).CreateCPU(0); + + ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f}); + + ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + + ClearRenderTargetView(cmd, BBRTV, {0.2f, 0.2f, 0.2f, 1.0f}); + + if(rdoc) + rdoc->SetCommandAnnotation(d, cmd, "command.new", eRENDERDOC_Float, 0, + RDAnnotationHelper(1.75f)); + + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + IASetVertexBuffer(cmd, DefaultTriVB, sizeof(DefaultA2V), 0); + cmd->SetPipelineState(DefaultTriPSO); + cmd->SetGraphicsRootSignature(DefaultTriSig); + + SetMainWindowViewScissor(cmd); + + OMSetRenderTargets(cmd, {BBRTV}, {}); + + setMarker(cmd, "Pre-Draw"); + + // deleting a value is fine if it's re-added before the next event + if(rdoc) + { + rdoc->SetCommandAnnotation(d, cmd, "new.value", eRENDERDOC_Empty, 0, NULL); + rdoc->SetCommandAnnotation(d, cmd, "new.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(4000)); + } + + setMarker(cmd, "Draw 1"); + + cmd->DrawInstanced(3, 1, 0, 0); + + RSSetViewport( + cmd, {0.0f, 0.0f, (float)screenWidth / 2.0f, (float)screenHeight / 2.0f, 0.0f, 1.0f}); + + setMarker(cmd, "Draw 2"); + + cmd->DrawInstanced(3, 1, 0, 0); + + FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + + cmd->Close(); + + SubmitAndPresent({cmd}); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index b39934009..b75e59c5e 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -135,6 +135,7 @@ + @@ -192,6 +193,7 @@ + @@ -252,6 +254,7 @@ true + @@ -315,6 +318,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 8d1a84458..cc6cd559c 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -742,6 +742,18 @@ OpenGL\demos + + Vulkan\demos + + + OpenGL\demos + + + D3D12\demos + + + D3D11\demos + diff --git a/util/test/demos/gl/gl_annotations.cpp b/util/test/demos/gl/gl_annotations.cpp new file mode 100644 index 000000000..26d30f960 --- /dev/null +++ b/util/test/demos/gl/gl_annotations.cpp @@ -0,0 +1,189 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2023-2026 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_Annotations, OpenGLGraphicsTest) +{ + static constexpr const char *Description = "Test annotations via the OpenGL API."; + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + GLuint tex = MakeTexture(); + glBindTexture(GL_TEXTURE_2D, tex); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 4, 4); + + glObjectLabel(GL_TEXTURE, tex, -1, "Annotated Image"); + glObjectLabel(GL_BUFFER, DefaultTriVB, -1, "Vertex Buffer"); + + // cache the device pointer we pass in + void *d = mainContext; + + if(rdoc) + { + // GL needs a helper struct to specify the type and handle together + RDGLObjectHelper img(GL_TEXTURE, tex); + RDGLObjectHelper buf(GL_BUFFER, DefaultTriVB); + + rdoc->SetObjectAnnotation(d, img, "basic.bool", eRENDERDOC_Bool, 0, RDAnnotationHelper(true)); + rdoc->SetObjectAnnotation(d, img, "basic.int32", eRENDERDOC_Int32, 0, RDAnnotationHelper(-3)); + rdoc->SetObjectAnnotation(d, img, "basic.int64", eRENDERDOC_Int64, 0, + RDAnnotationHelper(-3000000000000LL)); + rdoc->SetObjectAnnotation(d, img, "basic.uint32", eRENDERDOC_UInt32, 0, RDAnnotationHelper(3)); + rdoc->SetObjectAnnotation(d, img, "basic.uint64", eRENDERDOC_UInt64, 0, + RDAnnotationHelper(3000000000000LL)); + rdoc->SetObjectAnnotation(d, img, "basic.float", eRENDERDOC_Float, 0, + RDAnnotationHelper(3.25f)); + rdoc->SetObjectAnnotation(d, img, "basic.double", eRENDERDOC_Double, 0, + RDAnnotationHelper(3.25000000001)); + rdoc->SetObjectAnnotation(d, img, "basic.string", eRENDERDOC_String, 0, + RDAnnotationHelper("Hello, World!")); + + RENDERDOC_AnnotationValue val; + val.apiObject = (void *)(RENDERDOC_GLResourceReference *)buf; + rdoc->SetObjectAnnotation(d, img, "basic.object", eRENDERDOC_APIObject, 0, &val); + + rdoc->SetObjectAnnotation(d, img, "basic.object.__offset", eRENDERDOC_UInt32, 0, + RDAnnotationHelper(64)); + rdoc->SetObjectAnnotation(d, img, "basic.object.__size", eRENDERDOC_UInt32, 0, + RDAnnotationHelper(32)); + rdoc->SetObjectAnnotation(d, img, "basic.object.__rd_format", eRENDERDOC_String, 0, + RDAnnotationHelper("float4 vertex_data;")); + + rdoc->SetObjectAnnotation(d, buf, "__rd_format", eRENDERDOC_String, 0, + RDAnnotationHelper("float3 pos;\n" + "float4 col;\n" + "float2 uv;\n")); + + val = {}; + val.vector.float32[0] = 1.1f; + val.vector.float32[1] = 2.2f; + val.vector.float32[2] = 3.3f; + val.vector.float32[3] = 4.4f; // should be ignored + rdoc->SetObjectAnnotation(d, img, "basic.vec3", eRENDERDOC_Float, 3, &val); + + rdoc->SetObjectAnnotation(d, img, "deep.nested.path.to.annotation", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-4)); + rdoc->SetObjectAnnotation(d, img, "deep.nested.path.to.annotation2", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-5)); + rdoc->SetObjectAnnotation(d, img, "deep.alternate.path.to.annotation", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-6)); + + // deleted paths should not stay around + rdoc->SetObjectAnnotation(d, img, "deleteme", eRENDERDOC_Int32, 0, RDAnnotationHelper(-7)); + rdoc->SetObjectAnnotation(d, img, "deleteme", eRENDERDOC_Empty, 0, NULL); + + rdoc->SetObjectAnnotation(d, img, "path.deleted.by.parent", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-8)); + rdoc->SetObjectAnnotation(d, img, "path.deleted.by.parent2", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-9)); + + // this will delete all children. `path` will still exist, but will be empty + rdoc->SetObjectAnnotation(d, img, "path.deleted", eRENDERDOC_Empty, 0, NULL); + } + + while(Running()) + { + if(rdoc) + { + // queue annotations don't exist in GL, but we set these to share the same test code. + if(curFrame == 2) + rdoc->SetCommandAnnotation(d, NULL, "queue.too_old", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1000)); + + rdoc->SetCommandAnnotation(d, NULL, "queue.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1000)); + + rdoc->SetCommandAnnotation(d, NULL, "command.overwritten", eRENDERDOC_Int32, 0, + RDAnnotationHelper(9999)); + + rdoc->SetCommandAnnotation(d, NULL, "command.inherited", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1234)); + + rdoc->SetCommandAnnotation(d, NULL, "command.deleted", eRENDERDOC_Int32, 0, + RDAnnotationHelper(50)); + } + + setMarker("Start"); + + if(rdoc) + { + rdoc->SetCommandAnnotation(d, NULL, "new.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(2000)); + + rdoc->SetCommandAnnotation(d, NULL, "command.overwritten", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-3333)); + + rdoc->SetCommandAnnotation(d, NULL, "command.new", eRENDERDOC_Int32, 0, + RDAnnotationHelper(3333)); + + rdoc->SetCommandAnnotation(d, NULL, "command.deleted", eRENDERDOC_Empty, 0, NULL); + } + + setMarker("Initial"); + + glClearBufferfv(GL_COLOR, 0, DefaultClearCol); + + glBindTexture(GL_TEXTURE_2D, tex); + + if(rdoc) + rdoc->SetCommandAnnotation(d, NULL, "command.new", eRENDERDOC_Float, 0, + RDAnnotationHelper(1.75f)); + + glBindVertexArray(DefaultTriVAO); + glUseProgram(DefaultTriProgram); + glViewport(0, 0, GLsizei(screenWidth), GLsizei(screenHeight)); + + setMarker("Pre-Draw"); + + // deleting a value is fine if it's re-added before the next event + if(rdoc) + { + rdoc->SetCommandAnnotation(d, NULL, "new.value", eRENDERDOC_Empty, 0, NULL); + rdoc->SetCommandAnnotation(d, NULL, "new.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(4000)); + } + + setMarker("Draw 1"); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glViewport(0, 0, GLsizei(screenWidth) / 2, GLsizei(screenHeight) / 2); + + setMarker("Draw 2"); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/main.cpp b/util/test/demos/main.cpp index 60389a1a0..c15209464 100644 --- a/util/test/demos/main.cpp +++ b/util/test/demos/main.cpp @@ -390,6 +390,8 @@ int main(int argc, char **argv) { std::vector &tests = test_list(); + LoadLibraryA("P:/renderdoc/x64/development/renderdoc.dll"); + std::sort(tests.begin(), tests.end()); if(argc >= 2 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h") || !strcmp(argv[1], "-?") || diff --git a/util/test/demos/vk/vk_annotations.cpp b/util/test/demos/vk/vk_annotations.cpp new file mode 100644 index 000000000..dd5e638f2 --- /dev/null +++ b/util/test/demos/vk/vk_annotations.cpp @@ -0,0 +1,239 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2023-2026 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_Annotations, VulkanGraphicsTest) +{ + static constexpr const char *Description = "Test annotations via the vulkan API."; + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + // these tags provide the layer a way (for dispatchable objects at least) to identify + // dispatchable objects that may be wrapped by the loader + VkDebugUtilsObjectTagInfoEXT tag = { + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT, + NULL, + VK_OBJECT_TYPE_INSTANCE, + (uint64_t)instance, + RENDERDOC_APIObjectAnnotationHelper, + sizeof(instance), + instance, + }; + + vkSetDebugUtilsObjectTagEXT(device, &tag); + + tag.objectHandle = (uint64_t)phys; + tag.objectType = VK_OBJECT_TYPE_PHYSICAL_DEVICE; + tag.pTag = phys; + vkSetDebugUtilsObjectTagEXT(device, &tag); + + tag.objectHandle = (uint64_t)device; + tag.objectType = VK_OBJECT_TYPE_DEVICE; + tag.pTag = device; + vkSetDebugUtilsObjectTagEXT(device, &tag); + + AllocatedImage img(this, + vkh::ImageCreateInfo(4, 4, 0, VK_FORMAT_R32G32B32A32_SFLOAT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + setName(img.image, "Annotated Image"); + setName(DefaultTriVB.buffer, "Vertex Buffer"); + + // cache the device pointer we pass in + void *d = RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance); + + if(rdoc) + { + rdoc->SetObjectAnnotation(d, img.image, "basic.bool", eRENDERDOC_Bool, 0, + RDAnnotationHelper(true)); + rdoc->SetObjectAnnotation(d, img.image, "basic.int32", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-3)); + rdoc->SetObjectAnnotation(d, img.image, "basic.int64", eRENDERDOC_Int64, 0, + RDAnnotationHelper(-3000000000000LL)); + rdoc->SetObjectAnnotation(d, img.image, "basic.uint32", eRENDERDOC_UInt32, 0, + RDAnnotationHelper(3)); + rdoc->SetObjectAnnotation(d, img.image, "basic.uint64", eRENDERDOC_UInt64, 0, + RDAnnotationHelper(3000000000000LL)); + rdoc->SetObjectAnnotation(d, img.image, "basic.float", eRENDERDOC_Float, 0, + RDAnnotationHelper(3.25f)); + rdoc->SetObjectAnnotation(d, img.image, "basic.double", eRENDERDOC_Double, 0, + RDAnnotationHelper(3.25000000001)); + rdoc->SetObjectAnnotation(d, img.image, "basic.string", eRENDERDOC_String, 0, + RDAnnotationHelper("Hello, World!")); + + RENDERDOC_AnnotationValue val; + val.apiObject = (void *)DefaultTriVB.buffer; + rdoc->SetObjectAnnotation(d, img.image, "basic.object", eRENDERDOC_APIObject, 0, &val); + + rdoc->SetObjectAnnotation(d, img.image, "basic.object.__offset", eRENDERDOC_UInt32, 0, + RDAnnotationHelper(64)); + rdoc->SetObjectAnnotation(d, img.image, "basic.object.__size", eRENDERDOC_UInt32, 0, + RDAnnotationHelper(32)); + rdoc->SetObjectAnnotation(d, img.image, "basic.object.__rd_format", eRENDERDOC_String, 0, + RDAnnotationHelper("float4 vertex_data;")); + + rdoc->SetObjectAnnotation(d, DefaultTriVB.buffer, "__rd_format", eRENDERDOC_String, 0, + RDAnnotationHelper("float3 pos;\n" + "float4 col;\n" + "float2 uv;\n")); + + val = {}; + val.vector.float32[0] = 1.1f; + val.vector.float32[1] = 2.2f; + val.vector.float32[2] = 3.3f; + val.vector.float32[3] = 4.4f; // should be ignored + rdoc->SetObjectAnnotation(d, img.image, "basic.vec3", eRENDERDOC_Float, 3, &val); + + rdoc->SetObjectAnnotation(d, img.image, "deep.nested.path.to.annotation", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-4)); + rdoc->SetObjectAnnotation(d, img.image, "deep.nested.path.to.annotation2", eRENDERDOC_Int32, + 0, RDAnnotationHelper(-5)); + rdoc->SetObjectAnnotation(d, img.image, "deep.alternate.path.to.annotation", eRENDERDOC_Int32, + 0, RDAnnotationHelper(-6)); + + // deleted paths should not stay around + rdoc->SetObjectAnnotation(d, img.image, "deleteme", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-7)); + rdoc->SetObjectAnnotation(d, img.image, "deleteme", eRENDERDOC_Empty, 0, NULL); + + rdoc->SetObjectAnnotation(d, img.image, "path.deleted.by.parent", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-8)); + rdoc->SetObjectAnnotation(d, img.image, "path.deleted.by.parent2", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-9)); + + // this will delete all children. `path` will still exist, but will be empty + rdoc->SetObjectAnnotation(d, img.image, "path.deleted", eRENDERDOC_Empty, 0, NULL); + } + + while(Running()) + { + if(rdoc) + { + // queue annotations are only included when in the captured frame + if(curFrame == 2) + rdoc->SetCommandAnnotation(d, queue, "queue.too_old", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1000)); + + rdoc->SetCommandAnnotation(d, queue, "queue.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1000)); + + rdoc->SetCommandAnnotation(d, queue, "command.overwritten", eRENDERDOC_Int32, 0, + RDAnnotationHelper(9999)); + + rdoc->SetCommandAnnotation(d, queue, "command.inherited", eRENDERDOC_Int32, 0, + RDAnnotationHelper(1234)); + + rdoc->SetCommandAnnotation(d, queue, "command.deleted", eRENDERDOC_Int32, 0, + RDAnnotationHelper(50)); + } + + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + setMarker(cmd, "Start"); + + if(rdoc) + { + rdoc->SetCommandAnnotation(d, cmd, "new.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(2000)); + + rdoc->SetCommandAnnotation(d, cmd, "command.overwritten", eRENDERDOC_Int32, 0, + RDAnnotationHelper(-3333)); + + rdoc->SetCommandAnnotation(d, cmd, "command.new", eRENDERDOC_Int32, 0, + RDAnnotationHelper(3333)); + + rdoc->SetCommandAnnotation(d, cmd, "command.deleted", eRENDERDOC_Empty, 0, NULL); + } + + setMarker(cmd, "Initial"); + + VkImage swapimg = StartUsingBackbuffer(cmd); + + vkh::cmdClearImage(cmd, swapimg, vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f)); + + vkh::cmdPipelineBarrier(cmd, { + vkh::ImageMemoryBarrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL, img.image), + }); + + vkCmdClearColorImage(cmd, img.image, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f), 1, + vkh::ImageSubresourceRange()); + + vkCmdBeginRenderPass(cmd, mainWindow->beginRP(), VK_SUBPASS_CONTENTS_INLINE); + + if(rdoc) + rdoc->SetCommandAnnotation(d, cmd, "command.new", eRENDERDOC_Float, 0, + RDAnnotationHelper(1.75f)); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, DefaultTriPipe); + mainWindow->setViewScissor(cmd); + vkh::cmdBindVertexBuffers(cmd, {DefaultTriVB.buffer}); + + setMarker(cmd, "Pre-Draw"); + + // deleting a value is fine if it's re-added before the next event + if(rdoc) + { + rdoc->SetCommandAnnotation(d, cmd, "new.value", eRENDERDOC_Empty, 0, NULL); + rdoc->SetCommandAnnotation(d, cmd, "new.value", eRENDERDOC_Int32, 0, + RDAnnotationHelper(4000)); + } + + setMarker(cmd, "Draw 1"); + + vkCmdDraw(cmd, 3, 1, 0, 0); + + VkViewport view = mainWindow->viewport; + view.width /= 2; + view.height /= 2; + vkCmdSetViewport(cmd, 0, 1, &view); + + setMarker(cmd, "Draw 2"); + + vkCmdDraw(cmd, 3, 1, 0, 0); + + vkCmdEndRenderPass(cmd); + + FinishUsingBackbuffer(cmd); + + vkEndCommandBuffer(cmd); + + SubmitAndPresent({cmd}); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/vk/vk_simple_triangle.cpp b/util/test/demos/vk/vk_simple_triangle.cpp index ea885404a..7ebfb7781 100644 --- a/util/test/demos/vk/vk_simple_triangle.cpp +++ b/util/test/demos/vk/vk_simple_triangle.cpp @@ -76,8 +76,45 @@ RD_TEST(VK_Simple_Triangle, VulkanGraphicsTest) VK_IMAGE_USAGE_TRANSFER_DST_BIT, 1, 1, VK_SAMPLE_COUNT_4_BIT), VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + VkDebugUtilsObjectTagInfoEXT tag = { + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT, + NULL, + VK_OBJECT_TYPE_INSTANCE, + (uint64_t)instance, + RENDERDOC_APIObjectAnnotationHelper, + sizeof(instance), + instance, + }; + + vkSetDebugUtilsObjectTagEXT(device, &tag); + tag.objectHandle = (uint64_t)phys; + tag.objectType = VK_OBJECT_TYPE_PHYSICAL_DEVICE; + tag.pTag = phys; + vkSetDebugUtilsObjectTagEXT(device, &tag); + + RENDERDOC_AnnotationValue val; + + rdoc->SetObjectAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), instance, + "someInstData", eRENDERDOC_Bool, 0, RDAnnotationHelper(true)); + rdoc->SetObjectAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), phys, "physData", + eRENDERDOC_String, 0, RDAnnotationHelper("my physical device ha ha")); + + rdoc->SetObjectAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), offimg.image, + "cool.secretInfo", eRENDERDOC_Float, 0, RDAnnotationHelper(3.14f)); + val.apiObject = offimgMS.image; + rdoc->SetObjectAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), offimg.image, + "cool.otherimage", eRENDERDOC_APIObject, 0, &val); + while(Running()) { + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), queue, + "global.coolQueue", eRENDERDOC_Bool, 0, RDAnnotationHelper(true)); + + int counter = 1; + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), queue, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); + VkCommandBuffer cmd = GetCommandBuffer(); vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); @@ -85,20 +122,45 @@ RD_TEST(VK_Simple_Triangle, VulkanGraphicsTest) VkImage swapimg = StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); + + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.coolCommand", eRENDERDOC_Bool, 0, RDAnnotationHelper(true)); + + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "draw.isClear", eRENDERDOC_Bool, 0, RDAnnotationHelper(true)); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "draw.clearingswap", eRENDERDOC_Float, 0, RDAnnotationHelper(1.1f)); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "draw.notclearing", eRENDERDOC_APIObject, 0, &val); + vkCmdClearColorImage(cmd, swapimg, VK_IMAGE_LAYOUT_GENERAL, vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f), 1, vkh::ImageSubresourceRange()); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, "draw", + eRENDERDOC_Empty, 0, NULL); + vkh::cmdPipelineBarrier( cmd, { vkh::ImageMemoryBarrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, offimg.image), }); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); + vkCmdClearColorImage(cmd, offimg.image, VK_IMAGE_LAYOUT_GENERAL, vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f), 1, vkh::ImageSubresourceRange()); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); + vkh::cmdPipelineBarrier( cmd, { vkh::ImageMemoryBarrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, @@ -109,20 +171,52 @@ RD_TEST(VK_Simple_Triangle, VulkanGraphicsTest) vkh::ClearColorValue(0.2f, 0.2f, 0.2f, 1.0f), 1, vkh::ImageSubresourceRange()); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); + + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "renderpass.type", eRENDERDOC_String, 0, + RDAnnotationHelper("boring")); + vkCmdBeginRenderPass( cmd, vkh::RenderPassBeginInfo(mainWindow->rp, mainWindow->GetFB(), mainWindow->scissor), VK_SUBPASS_CONTENTS_INLINE); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); vkCmdSetViewport(cmd, 0, 1, &mainWindow->viewport); + + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor); vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0}); + + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); vkCmdDraw(cmd, 3, 1, 0, 0); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); + vkCmdEndRenderPass(cmd); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); + FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); + rdoc->SetCommandAnnotation(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), cmd, + "global.counter", eRENDERDOC_Int32, 0, + RDAnnotationHelper(counter++)); + vkEndCommandBuffer(cmd); Submit(0, 1, {cmd}); diff --git a/util/test/rdtest/__init__.py b/util/test/rdtest/__init__.py index d02604561..ef1d26134 100644 --- a/util/test/rdtest/__init__.py +++ b/util/test/rdtest/__init__.py @@ -13,3 +13,4 @@ from .shared.Subgroup_Zoo import * from .shared.Pixel_History import * from .shared.Workgroup_Zoo import * from .shared.Groupshared import * +from .shared.Annotations import * diff --git a/util/test/rdtest/shared/Annotations.py b/util/test/rdtest/shared/Annotations.py new file mode 100644 index 000000000..c1f95d4d6 --- /dev/null +++ b/util/test/rdtest/shared/Annotations.py @@ -0,0 +1,252 @@ +import renderdoc as rd +import rdtest + + +# Not a direct test, re-used by API-specific tests +class Annotations(rdtest.TestCase): + internal = True + + def check_resource_annotations(self): + annot = lambda x: annots.FindChildByKeyPath(x) + + with rdtest.log.auto_section('Resource annotations'): + res = self.get_resource_by_name('Annotated Image') + + annots = res.annotations + self.check(annots is not None) + + self.check_eq(annot("basic.bool").type.basetype, rd.SDBasic.Boolean) + self.check_eq(annot("basic.bool").AsBool(), True) + + self.check_eq(annot("basic.int32").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("basic.int32").type.byteSize, 4) + self.check_eq(annot("basic.int32").AsInt(), -3) + + self.check_eq(annot("basic.int64").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("basic.int64").type.byteSize, 8) + self.check_eq(annot("basic.int64").AsInt(), -3000000000000) + + self.check_eq(annot("basic.uint32").type.basetype, rd.SDBasic.UnsignedInteger) + self.check_eq(annot("basic.uint32").type.byteSize, 4) + self.check_eq(annot("basic.uint32").AsInt(), 3) + + self.check_eq(annot("basic.uint64").type.basetype, rd.SDBasic.UnsignedInteger) + self.check_eq(annot("basic.uint64").type.byteSize, 8) + self.check_eq(annot("basic.uint64").AsInt(), 3000000000000) + + self.check_eq(annot("basic.float").type.basetype, rd.SDBasic.Float) + self.check_eq(annot("basic.float").type.byteSize, 4) + self.check_eq(annot("basic.float").AsFloat(), 3.25) + + self.check_eq(annot("basic.double").type.basetype, rd.SDBasic.Float) + self.check_eq(annot("basic.double").type.byteSize, 8) + self.check(rdtest.value_compare(annot("basic.double").AsFloat(), 3.25000000001)) + + self.check_eq(annot("basic.string").type.basetype, rd.SDBasic.String) + self.check_eq(annot("basic.string").AsString(), "Hello, World!") + + self.check_eq(annot("basic.object").type.basetype, rd.SDBasic.Resource) + obj = self.get_resource(annot("basic.object").AsResourceId()) + self.check_eq(obj.name, "Vertex Buffer") + + self.check_eq(annot("basic.object.__offset").AsInt(), 64) + self.check_eq(annot("basic.object.__size").AsInt(), 32) + + self.check(rdtest.value_compare(annot("basic.vec3.x").AsFloat(), 1.1)) + self.check(rdtest.value_compare(annot("basic.vec3.y").AsFloat(), 2.2)) + self.check(rdtest.value_compare(annot("basic.vec3.z").AsFloat(), 3.3)) + + self.check_eq(annot("deep.nested.path.to.annotation").AsInt(), -4) + self.check_eq(annot("deep.nested.path.to.annotation2").AsInt(), -5) + self.check_eq(annot("deep.alternate.path.to.annotation").AsInt(), -6) + + self.check(annot("deleteme") is None) + + self.check(annot("path.deleted.by.parent") is None) + self.check(annot("path.deleted.by.parent2") is None) + self.check(annot("path.deleted") is None) + + def check_command_annotations(self, cmd_buffers: bool): + annot = lambda x: annots.FindChildByKeyPath(x) + + with rdtest.log.auto_section('Event annotations'): + action = self.find_action("Start") + rdtest.log.print(f"Checking {action.customName}") + + annots = action.events[-1].annotations + self.check(annots is not None) + + # Should not have this annotation, it happened prior to the capture + self.check(annot("queue.too_old") is None) + + # normal value set on the queue + self.check_eq(annot("queue.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("queue.value").AsInt(), 1000) + + # this will later be overwritten by the commands, but for now has the queue value + self.check_eq(annot("command.overwritten").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.overwritten").AsInt(), 9999) + + # this is inherited by the commands even as other siblings are modified + self.check_eq(annot("command.inherited").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.inherited").AsInt(), 1234) + + # this will be deleted + self.check_eq(annot("command.deleted").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.deleted").AsInt(), 50) + + action = self.find_action("Initial") + rdtest.log.print(f"Checking {action.customName}") + + annots = action.events[-1].annotations + self.check(annots is not None) + + # normal value set on the queue + self.check_eq(annot("queue.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("queue.value").AsInt(), 1000) + + # this has the overwritten value now + self.check_eq(annot("command.overwritten").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.overwritten").AsInt(), -3333) + + # this is inherited by the commands even as other siblings are modified + self.check_eq(annot("command.inherited").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.inherited").AsInt(), 1234) + + # this has now been deleted + self.check(annot("command.deleted") is None) + + # this is a new command-local value + self.check_eq(annot("command.new").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.new").AsInt(), 3333) + + # this is a new command-local value + self.check_eq(annot("new.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("new.value").AsInt(), 2000) + + action = self.find_action("Pre-Draw") + rdtest.log.print(f"Checking {action.customName}") + + annots = action.events[-1].annotations + self.check(annots is not None) + + # normal value set on the queue + self.check_eq(annot("queue.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("queue.value").AsInt(), 1000) + + # this has the overwritten value now + self.check_eq(annot("command.overwritten").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.overwritten").AsInt(), -3333) + + # this is inherited by the commands even as other siblings are modified + self.check_eq(annot("command.inherited").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.inherited").AsInt(), 1234) + + # this has now been deleted + self.check(annot("command.deleted") is None) + + # this is a command-local value, which has changed type + self.check_eq(annot("command.new").type.basetype, rd.SDBasic.Float) + self.check_eq(annot("command.new").AsFloat(), 1.75) + + # this value has't changed + self.check_eq(annot("new.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("new.value").AsInt(), 2000) + + predraw_id = action.eventId + + action = self.find_action("Draw 1") + rdtest.log.print(f"Checking {action.customName}") + + annots = action.events[-1].annotations + self.check(annots is not None) + + # should be no events between these + self.check_eq(predraw_id + 1, action.eventId) + + # this value was deleted and re-added, so it should be present as the new value + self.check_eq(annot("new.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("new.value").AsInt(), 4000) + + # normal value set on the queue + self.check_eq(annot("queue.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("queue.value").AsInt(), 1000) + + # this has the overwritten value now + self.check_eq(annot("command.overwritten").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.overwritten").AsInt(), -3333) + + # this is inherited by the commands even as other siblings are modified + self.check_eq(annot("command.inherited").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.inherited").AsInt(), 1234) + + # this has now been deleted + self.check(annot("command.deleted") is None) + + # this is a command-local value, which has changed type + self.check_eq(annot("command.new").type.basetype, rd.SDBasic.Float) + self.check_eq(annot("command.new").AsFloat(), 1.75) + + self.check_eq(annot("new.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("new.value").AsInt(), 4000) + + action = self.find_action("Draw 2") + rdtest.log.print(f"Checking {action.customName}") + + annots = action.events[-1].annotations + self.check(annots is not None) + + # all of the values should still be present + + self.check_eq(annot("queue.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("queue.value").AsInt(), 1000) + + self.check_eq(annot("command.overwritten").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.overwritten").AsInt(), -3333) + + self.check_eq(annot("command.inherited").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.inherited").AsInt(), 1234) + + self.check(annot("command.deleted") is None) + + self.check_eq(annot("command.new").type.basetype, rd.SDBasic.Float) + self.check_eq(annot("command.new").AsFloat(), 1.75) + + self.check_eq(annot("new.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("new.value").AsInt(), 4000) + + action = self.get_last_action() + rdtest.log.print(f"Checking {action.customName}") + + annots = action.events[-1].annotations + self.check(annots is not None) + + # normal value set on the queue + self.check_eq(annot("queue.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("queue.value").AsInt(), 1000) + + self.check_eq(annot("command.inherited").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.inherited").AsInt(), 1234) + + # if we have real command buffers, the annotations revert back to where they were on queues + if cmd_buffers: + self.check_eq(annot("command.overwritten").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.overwritten").AsInt(), 9999) + + self.check_eq(annot("command.deleted").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.deleted").AsInt(), 50) + + self.check(annot("command.new") is None) + self.check(annot("new.value") is None) + # otherwise they will be the same as on the command buffer + else: + self.check_eq(annot("command.overwritten").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("command.overwritten").AsInt(), -3333) + + self.check(annot("command.deleted") is None) + + self.check_eq(annot("command.new").type.basetype, rd.SDBasic.Float) + self.check_eq(annot("command.new").AsFloat(), 1.75) + + self.check_eq(annot("new.value").type.basetype, rd.SDBasic.SignedInteger) + self.check_eq(annot("new.value").AsInt(), 4000) diff --git a/util/test/tests/D3D11/D3D11_Annotations.py b/util/test/tests/D3D11/D3D11_Annotations.py new file mode 100644 index 000000000..cfde35b98 --- /dev/null +++ b/util/test/tests/D3D11/D3D11_Annotations.py @@ -0,0 +1,11 @@ +import renderdoc as rd +import rdtest + + +class D3D11_Annotations(rdtest.Annotations): + demos_test_name = 'D3D11_Annotations' + internal = False + + def check_capture(self): + super().check_resource_annotations() + super().check_command_annotations(False) \ No newline at end of file diff --git a/util/test/tests/D3D12/D3D12_Annotations.py b/util/test/tests/D3D12/D3D12_Annotations.py new file mode 100644 index 000000000..5ec7003ba --- /dev/null +++ b/util/test/tests/D3D12/D3D12_Annotations.py @@ -0,0 +1,11 @@ +import renderdoc as rd +import rdtest + + +class D3D12_Annotations(rdtest.Annotations): + demos_test_name = 'D3D12_Annotations' + internal = False + + def check_capture(self): + super().check_resource_annotations() + super().check_command_annotations(True) \ No newline at end of file diff --git a/util/test/tests/GL/GL_Annotations.py b/util/test/tests/GL/GL_Annotations.py new file mode 100644 index 000000000..267fb01c9 --- /dev/null +++ b/util/test/tests/GL/GL_Annotations.py @@ -0,0 +1,11 @@ +import renderdoc as rd +import rdtest + + +class GL_Annotations(rdtest.Annotations): + demos_test_name = 'GL_Annotations' + internal = False + + def check_capture(self): + super().check_resource_annotations() + super().check_command_annotations(False) \ No newline at end of file diff --git a/util/test/tests/Vulkan/VK_Annotations.py b/util/test/tests/Vulkan/VK_Annotations.py new file mode 100644 index 000000000..fe5d816a2 --- /dev/null +++ b/util/test/tests/Vulkan/VK_Annotations.py @@ -0,0 +1,11 @@ +import renderdoc as rd +import rdtest + + +class VK_Annotations(rdtest.Annotations): + demos_test_name = 'VK_Annotations' + internal = False + + def check_capture(self): + super().check_resource_annotations() + super().check_command_annotations(True) \ No newline at end of file