diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index 021b4cacc..48c73cb61 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -2252,6 +2252,19 @@ void WrappedID3D12Device::StartFrameCapture(DeviceOwnedWindow devWnd) RDCLOG("Starting capture"); + if(m_Queue == NULL) + { + RDCLOG("Creating direct queue as none was found in the application"); + + // pretend this is the application's call and just release - everything else will be handled the + // same (the queue will be kept alive internally) + ID3D12CommandQueue *q = NULL; + D3D12_COMMAND_QUEUE_DESC desc = {}; + desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + CreateCommandQueue(&desc, __uuidof(ID3D12CommandQueue), (void **)&q); + q->Release(); + } + WrappedID3D12CommandAllocator::PauseResets(); m_CaptureTimer.Restart(); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index ee96409bf..6dc6aade0 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -1796,6 +1796,14 @@ void WrappedVulkan::StartFrameCapture(DeviceOwnedWindow devWnd) RDCLOG("Starting capture"); + if(m_Queue == VK_NULL_HANDLE && m_QueueFamilyIdx != ~0U) + { + RDCLOG("Creating desired queue as none was obtained by the application"); + + VkQueue q = VK_NULL_HANDLE; + vkGetDeviceQueue(m_Device, m_QueueFamilyIdx, 0, &q); + } + Atomic::Dec32(&m_ReuseEnabled); m_CaptureTimer.Restart(); diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt index ec2faf436..fa27cc7b7 100644 --- a/util/test/demos/CMakeLists.txt +++ b/util/test/demos/CMakeLists.txt @@ -12,6 +12,7 @@ set(VULKAN_SRC vk/vk_adv_cbuffer_zoo.cpp vk/vk_buffer_truncation.cpp vk/vk_cbuffer_zoo.cpp + vk/vk_compute_only.cpp vk/vk_custom_border_color.cpp vk/vk_dedicated_allocation.cpp vk/vk_descriptor_index.cpp diff --git a/util/test/demos/d3d12/d3d12_compute_only.cpp b/util/test/demos/d3d12/d3d12_compute_only.cpp new file mode 100644 index 000000000..46076cd32 --- /dev/null +++ b/util/test/demos/d3d12/d3d12_compute_only.cpp @@ -0,0 +1,149 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2020-2022 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_Compute_Only, D3D12GraphicsTest) +{ + static constexpr const char *Description = + "Test that uses a compute only queue with no graphics queue"; + + std::string compute = R"EOSHADER( + +cbuffer blah : register(b0) +{ + uint4 mult; +}; + +RWStructuredBuffer bufin : register(u0); +RWStructuredBuffer bufout : register(u1); + +[numthreads(1,1,1)] +void main() +{ + bufout[0].x += bufin[0].x * mult.x; + bufout[0].y += bufin[0].y * mult.y; + bufout[0].z += bufin[0].z * mult.z; + bufout[0].w += bufin[0].w * mult.w; +} + +)EOSHADER"; + + int main() + { + headless = true; + queueType = D3D12_COMMAND_LIST_TYPE_COMPUTE; + + // initialise, create window, create device, etc + if(!Init()) + return 3; + + ID3DBlobPtr csblob = Compile(compute, "main", "cs_5_0"); + + ID3D12RootSignaturePtr sig = MakeSig({ + uavParam(D3D12_SHADER_VISIBILITY_ALL, 0, 0), uavParam(D3D12_SHADER_VISIBILITY_ALL, 0, 1), + constParam(D3D12_SHADER_VISIBILITY_ALL, 0, 0, 4), + tableParam(D3D12_SHADER_VISIBILITY_ALL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 2, 1, 3), + }); + ID3D12PipelineStatePtr pso = MakePSO().RootSig(sig).CS(csblob); + + ID3D12ResourcePtr bufin = MakeBuffer().Size(1024).UAV(); + ID3D12ResourcePtr bufout = MakeBuffer().Size(1024).UAV(); + + bufin->SetName(L"bufin"); + bufout->SetName(L"bufout"); + + ID3D12ResourcePtr tex = MakeTexture(DXGI_FORMAT_R32G32B32A32_FLOAT, 8, 8) + .InitialState(D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE) + .UAV(); + + tex->SetName(L"tex"); + + { + ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer(); + Reset(cmd); + + float col[] = {0.25f, 0.5f, 0.75f, 1.0f}; + + D3D12_RECT rect = {}; + rect.right = rect.bottom = 8; + cmd->ClearUnorderedAccessViewFloat( + MakeUAV(tex).Format(DXGI_FORMAT_R32G32B32A32_FLOAT).CreateGPU(3), + MakeUAV(tex).Format(DXGI_FORMAT_R32G32B32A32_FLOAT).CreateClearCPU(3), tex, col, 1, &rect); + + cmd->Close(); + Submit({cmd}); + } + + if(rdoc) + rdoc->StartFrameCapture(NULL, NULL); + + { + ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer(); + Reset(cmd); + + uint32_t a[4] = {111, 111, 111, 111}; + uint32_t b[4] = {222, 222, 222, 222}; + + D3D12_RECT rect = {}; + rect.right = 1024; + rect.bottom = 1; + cmd->ClearUnorderedAccessViewUint( + MakeUAV(bufin).Format(DXGI_FORMAT_R32G32B32A32_UINT).CreateGPU(0), + MakeUAV(bufin).Format(DXGI_FORMAT_R32G32B32A32_UINT).CreateClearCPU(0), bufin, a, 1, &rect); + cmd->ClearUnorderedAccessViewUint( + MakeUAV(bufout).Format(DXGI_FORMAT_R32G32B32A32_UINT).CreateGPU(1), + MakeUAV(bufout).Format(DXGI_FORMAT_R32G32B32A32_UINT).CreateClearCPU(1), bufin, b, 1, + &rect); + + setMarker(cmd, "Pre-Dispatch"); + + cmd->SetComputeRootSignature(sig); + cmd->SetPipelineState(pso); + cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); + cmd->SetComputeRootUnorderedAccessView(0, bufin->GetGPUVirtualAddress()); + cmd->SetComputeRootUnorderedAccessView(1, bufout->GetGPUVirtualAddress()); + cmd->SetComputeRoot32BitConstant(2, 5, 0); + cmd->SetComputeRoot32BitConstant(2, 6, 1); + cmd->SetComputeRoot32BitConstant(2, 7, 2); + cmd->SetComputeRoot32BitConstant(2, 8, 3); + cmd->SetComputeRootDescriptorTable(3, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->Dispatch(1, 1, 1); + + setMarker(cmd, "Post-Dispatch"); + + cmd->Close(); + Submit({cmd}); + } + + if(rdoc) + rdoc->EndFrameCapture(NULL, NULL); + + GPUSync(); + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/d3d12/d3d12_helpers.cpp b/util/test/demos/d3d12/d3d12_helpers.cpp index 2d23e1cc5..9fc4820ab 100644 --- a/util/test/demos/d3d12/d3d12_helpers.cpp +++ b/util/test/demos/d3d12/d3d12_helpers.cpp @@ -156,6 +156,19 @@ D3D12_ROOT_PARAMETER1 cbvParam(D3D12_SHADER_VISIBILITY vis, UINT space, UINT reg return ret; } +D3D12_ROOT_PARAMETER1 uavParam(D3D12_SHADER_VISIBILITY vis, UINT space, UINT reg) +{ + D3D12_ROOT_PARAMETER1 ret; + + ret.ShaderVisibility = vis; + ret.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; + ret.Descriptor.RegisterSpace = space; + ret.Descriptor.ShaderRegister = reg; + ret.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE; + + return ret; +} + D3D12_ROOT_PARAMETER1 constParam(D3D12_SHADER_VISIBILITY vis, UINT space, UINT reg, UINT num) { D3D12_ROOT_PARAMETER1 ret; diff --git a/util/test/demos/d3d12/d3d12_helpers.h b/util/test/demos/d3d12/d3d12_helpers.h index e8ed96886..330576320 100644 --- a/util/test/demos/d3d12/d3d12_helpers.h +++ b/util/test/demos/d3d12/d3d12_helpers.h @@ -265,6 +265,7 @@ private: }; D3D12_ROOT_PARAMETER1 cbvParam(D3D12_SHADER_VISIBILITY vis, UINT space, UINT reg); +D3D12_ROOT_PARAMETER1 uavParam(D3D12_SHADER_VISIBILITY vis, UINT space, UINT reg); D3D12_ROOT_PARAMETER1 constParam(D3D12_SHADER_VISIBILITY vis, UINT space, UINT reg, UINT num); diff --git a/util/test/demos/d3d12/d3d12_test.cpp b/util/test/demos/d3d12/d3d12_test.cpp index 3cd85914e..e6f9570b8 100644 --- a/util/test/demos/d3d12/d3d12_test.cpp +++ b/util/test/demos/d3d12/d3d12_test.cpp @@ -316,7 +316,7 @@ void D3D12GraphicsTest::PostDeviceCreate() { { D3D12_COMMAND_QUEUE_DESC desc = {}; - desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + desc.Type = queueType; dev->CreateCommandQueue(&desc, __uuidof(ID3D12CommandQueue), (void **)&queue); } @@ -325,13 +325,13 @@ void D3D12GraphicsTest::PostDeviceCreate() m_GPUSyncFence->SetName(L"GPUSync fence"); - CHECK_HR(dev->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, - __uuidof(ID3D12CommandAllocator), (void **)&m_Alloc)); + CHECK_HR( + dev->CreateCommandAllocator(queueType, __uuidof(ID3D12CommandAllocator), (void **)&m_Alloc)); m_Alloc->SetName(L"Command allocator"); - CHECK_HR(dev->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_Alloc, NULL, - __uuidof(ID3D12GraphicsCommandList), (void **)&m_DebugList)); + CHECK_HR(dev->CreateCommandList(0, queueType, m_Alloc, NULL, __uuidof(ID3D12GraphicsCommandList), + (void **)&m_DebugList)); // command buffers are allocated opened, close it immediately. m_DebugList->Close(); @@ -1349,7 +1349,7 @@ ID3D12GraphicsCommandListPtr D3D12GraphicsTest::GetCommandBuffer() if(freeCommandBuffers.empty()) { ID3D12GraphicsCommandListPtr list = NULL; - CHECK_HR(dev->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_Alloc, NULL, + CHECK_HR(dev->CreateCommandList(0, queueType, m_Alloc, NULL, __uuidof(ID3D12GraphicsCommandList), (void **)&list)); // list starts opened, close it list->Close(); diff --git a/util/test/demos/d3d12/d3d12_test.h b/util/test/demos/d3d12/d3d12_test.h index 7f1f4cd89..615f7d94a 100644 --- a/util/test/demos/d3d12/d3d12_test.h +++ b/util/test/demos/d3d12/d3d12_test.h @@ -226,6 +226,7 @@ struct D3D12GraphicsTest : public GraphicsTest ID3D12CommandAllocatorPtr m_Alloc; ID3D12GraphicsCommandListPtr m_DebugList; + D3D12_COMMAND_LIST_TYPE queueType = D3D12_COMMAND_LIST_TYPE_DIRECT; ID3D12CommandQueuePtr queue; D3D12_FEATURE_DATA_D3D12_OPTIONS opts = {}; diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 1b86dec10..71313f07e 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -179,6 +179,7 @@ + @@ -280,6 +281,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index d5682026c..94321fe36 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -628,6 +628,12 @@ OpenGL\demos + + Vulkan\demos + + + D3D12\demos + diff --git a/util/test/demos/vk/vk_compute_only.cpp b/util/test/demos/vk/vk_compute_only.cpp new file mode 100644 index 000000000..1544624dc --- /dev/null +++ b/util/test/demos/vk/vk_compute_only.cpp @@ -0,0 +1,182 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2022 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_Compute_Only, VulkanGraphicsTest) +{ + static constexpr const char *Description = + "Test that uses a compute only queue with no graphics queue."; + + const std::string comp = R"EOSHADER( + +#version 450 core + +layout(push_constant) uniform PushData +{ + uvec4 data; +} push; + +layout(binding = 0, std430) buffer inbuftype { + uvec4 data[]; +} inbuf; + +layout(binding = 1, std430) buffer outbuftype { + uvec4 data[]; +} outbuf; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + outbuf.data[0].x += inbuf.data[0].x * push.data.x; + outbuf.data[0].y += inbuf.data[0].y * push.data.y; + outbuf.data[0].z += inbuf.data[0].z * push.data.z; + outbuf.data[0].w += inbuf.data[0].w * push.data.w; +} + +)EOSHADER"; + + int main() + { + headless = true; + queueFlagsRequired = VK_QUEUE_COMPUTE_BIT; + queueFlagsBanned = VK_QUEUE_GRAPHICS_BIT; + + // initialise, create window, create context, etc + if(!Init()) + return 3; + + VkDescriptorSetLayout setLayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT}, + {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT}, + {2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT}, + })); + + VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo( + {setLayout}, + { + vkh::PushConstantRange(VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(Vec4i)), + })); + + VkPipeline pipe = createComputePipeline(vkh::ComputePipelineCreateInfo( + layout, CompileShaderModule(comp, ShaderLang::glsl, ShaderStage::comp, "main"))); + + AllocatedImage tex( + this, vkh::ImageCreateInfo(4, 4, 0, VK_FORMAT_R32G32B32A32_SFLOAT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY})); + + setName(tex.image, "tex"); + + VkImageView view = createImageView( + vkh::ImageViewCreateInfo(tex.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32B32A32_SFLOAT)); + + AllocatedBuffer bufin(this, vkh::BufferCreateInfo(1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + AllocatedBuffer bufout(this, vkh::BufferCreateInfo(1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + + setName(bufin.buffer, "bufin"); + setName(bufout.buffer, "bufout"); + + VkDescriptorSet set = allocateDescriptorSet(setLayout); + + vkh::updateDescriptorSets( + device, { + vkh::WriteDescriptorSet(set, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(bufin.buffer)}), + vkh::WriteDescriptorSet(set, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(bufout.buffer)}), + vkh::WriteDescriptorSet( + set, 2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + {vkh::DescriptorImageInfo(view, VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE)}), + }); + + // clear the buffers + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + vkCmdFillBuffer(cmd, bufin.buffer, 0, 1024, 111); + vkCmdFillBuffer(cmd, bufout.buffer, 0, 1024, 222); + + vkh::cmdPipelineBarrier( + cmd, + { + vkh::ImageMemoryBarrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL, tex.image), + }, + { + vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + bufin.buffer, 0, 1024), + vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_WRITE_BIT, + bufout.buffer, 0, 1024), + }); + + vkCmdClearColorImage(cmd, tex.image, VK_IMAGE_LAYOUT_GENERAL, + vkh::ClearColorValue(0.25f, 0.5f, 0.75f, 1.0f), 1, + vkh::ImageSubresourceRange()); + + vkEndCommandBuffer(cmd); + + Submit(0, 1, {cmd}); + } + + if(rdoc) + rdoc->StartFrameCapture(NULL, NULL); + + { + VkCommandBuffer cmd = GetCommandBuffer(); + + vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipe); + vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, {set}, {}); + + setMarker(cmd, "Pre-Dispatch"); + + Vec4i push = {5, 6, 7, 8}; + vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(Vec4i), &push); + vkCmdDispatch(cmd, 1, 1, 1); + + setMarker(cmd, "Post-Dispatch"); + + vkEndCommandBuffer(cmd); + + Submit(0, 1, {cmd}); + } + + if(rdoc) + rdoc->EndFrameCapture(NULL, NULL); + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/vk/vk_multi_present.cpp b/util/test/demos/vk/vk_multi_present.cpp index 41fee3d29..f3b84a948 100644 --- a/util/test/demos/vk/vk_multi_present.cpp +++ b/util/test/demos/vk/vk_multi_present.cpp @@ -143,7 +143,7 @@ RD_TEST(VK_Multi_Present, VulkanGraphicsTest) vkEndCommandBuffer(cmd); - Submit(0, 1, {cmd}, {}, win); + win->Submit(0, 1, {cmd}, {}, queue); } VulkanWindow::MultiPresent(queue, presentWindows); diff --git a/util/test/demos/vk/vk_multi_thread_windows.cpp b/util/test/demos/vk/vk_multi_thread_windows.cpp index dafe402b4..95abbf151 100644 --- a/util/test/demos/vk/vk_multi_thread_windows.cpp +++ b/util/test/demos/vk/vk_multi_thread_windows.cpp @@ -129,9 +129,9 @@ RD_TEST(VK_Multi_Thread_Windows, VulkanGraphicsTest) vkEndCommandBuffer(cmd); - Submit(0, 1, {cmd}, {}, win, q); + win->Submit(0, 1, {cmd}, {}, q); - Present(win, q); + win->Present(q); } while(win); }; diff --git a/util/test/demos/vk/vk_sync2.cpp b/util/test/demos/vk/vk_sync2.cpp index dddf67b11..e3efeffeb 100644 --- a/util/test/demos/vk/vk_sync2.cpp +++ b/util/test/demos/vk/vk_sync2.cpp @@ -278,7 +278,7 @@ RD_TEST(VK_Synchronization_2, VulkanGraphicsTest) vkEndCommandBuffer(cmd); - Submit(0, 1, {cmd}, {}, NULL, VK_NULL_HANDLE, true); + Submit(0, 1, {cmd}); Present(); diff --git a/util/test/demos/vk/vk_test.cpp b/util/test/demos/vk/vk_test.cpp index 04084a804..4a830603b 100644 --- a/util/test/demos/vk/vk_test.cpp +++ b/util/test/demos/vk/vk_test.cpp @@ -586,15 +586,15 @@ bool VulkanGraphicsTest::Init() std::vector queueProps; vkh::getQueueFamilyProperties(queueProps, phys); - VkQueueFlags required = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; - // if no queue has been selected, find it now if(queueFamilyIndex == ~0U) { + // try to find an exact match first for(uint32_t q = 0; q < queueProps.size(); q++) { VkQueueFlags flags = queueProps[q].queueFlags; - if((flags & required) == required) + + if(flags == queueFlagsRequired) { queueFamilyIndex = q; queueCount = 1; @@ -605,7 +605,23 @@ bool VulkanGraphicsTest::Init() if(queueFamilyIndex == ~0U) { - TEST_ERROR("No graphics/compute queues available"); + // if we didn't find an exact match, look for any that does satisfy what we want + for(uint32_t q = 0; q < queueProps.size(); q++) + { + VkQueueFlags flags = queueProps[q].queueFlags; + + if(((flags & queueFlagsRequired) == queueFlagsRequired) && ((flags & queueFlagsBanned) == 0)) + { + queueFamilyIndex = q; + queueCount = 1; + break; + } + } + } + + if(queueFamilyIndex == ~0U) + { + TEST_ERROR("No satisfactory queue family available"); return false; } @@ -638,13 +654,17 @@ bool VulkanGraphicsTest::Init() volkLoadDevice(device); vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue); - mainWindow = MakeWindow(screenWidth, screenHeight, "Autotesting"); - if(!mainWindow->Initialised()) + if(!headless) { - TEST_ERROR("Error creating surface"); - return false; - }; + mainWindow = MakeWindow(screenWidth, screenHeight, "Autotesting"); + + if(!mainWindow->Initialised()) + { + TEST_ERROR("Error creating surface"); + return false; + } + } VmaVulkanFunctions funcs = { vkGetPhysicalDeviceProperties, @@ -680,6 +700,8 @@ bool VulkanGraphicsTest::Init() TEST_LOG("Running Vulkan test on %s (version %d.%d)", physProperties.deviceName, VK_VERSION_MAJOR(physProperties.apiVersion), VK_VERSION_MINOR(physProperties.apiVersion)); + headlessCmds = new VulkanCommands(this); + return true; } @@ -738,6 +760,9 @@ void VulkanGraphicsTest::Shutdown() vmaDestroyAllocator(allocator); + if(headlessCmds) + delete headlessCmds; + delete mainWindow; vkDestroyDevice(device, NULL); @@ -789,27 +814,17 @@ void VulkanGraphicsTest::FinishUsingBackbuffer(VkCommandBuffer cmd, VkAccessFlag } void VulkanGraphicsTest::Submit(int index, int totalSubmits, const std::vector &cmds, - const std::vector &seccmds, VulkanWindow *window, - VkQueue q, bool sync2) + const std::vector &seccmds) { - if(window == NULL) - window = mainWindow; - - if(q == VK_NULL_HANDLE) - q = queue; - - window->Submit(index, totalSubmits, cmds, seccmds, q, sync2); + if(mainWindow) + mainWindow->Submit(index, totalSubmits, cmds, seccmds, queue); + else + headlessCmds->Submit(cmds, seccmds, queue, VK_NULL_HANDLE, VK_NULL_HANDLE); } -void VulkanGraphicsTest::Present(VulkanWindow *window, VkQueue q) +void VulkanGraphicsTest::Present() { - if(!window) - window = mainWindow; - - if(q == VK_NULL_HANDLE) - q = queue; - - window->Present(q); + mainWindow->Present(queue); } VkPipelineShaderStageCreateInfo VulkanGraphicsTest::CompileShaderModule( @@ -845,25 +860,10 @@ VkCommandBuffer VulkanGraphicsTest::GetCommandBuffer(VkCommandBufferLevel level, if(window == NULL) window = mainWindow; - return window->GetCommandBuffer(level); -} + if(window) + return window->GetCommandBuffer(level); -VkCommandBuffer VulkanWindow::GetCommandBuffer(VkCommandBufferLevel level) -{ - std::vector &buflist = freeCommandBuffers[level]; - - if(buflist.empty()) - { - buflist.resize(4); - - CHECK_VKR(vkAllocateCommandBuffers( - m_Test->device, vkh::CommandBufferAllocateInfo(cmdPool, 4, level), &buflist[0])); - } - - VkCommandBuffer ret = buflist.back(); - buflist.pop_back(); - - return ret; + return headlessCmds->GetCommandBuffer(level); } template <> @@ -1136,8 +1136,117 @@ VkSampler VulkanGraphicsTest::createSampler(const VkSamplerCreateInfo *info) return ret; } +VulkanCommands::VulkanCommands(VulkanGraphicsTest *test) +{ + m_Test = test; + + CHECK_VKR(vkCreateCommandPool( + m_Test->device, vkh::CommandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + m_Test->queueFamilyIndex), + NULL, &cmdPool)); +} + +VulkanCommands::~VulkanCommands() +{ + vkDestroyCommandPool(m_Test->device, cmdPool, NULL); + + for(VkFence fence : fences) + vkDestroyFence(m_Test->device, fence, NULL); +} + +VkCommandBuffer VulkanCommands::GetCommandBuffer(VkCommandBufferLevel level) +{ + std::vector &buflist = freeCommandBuffers[level]; + + if(buflist.empty()) + { + buflist.resize(4); + + CHECK_VKR(vkAllocateCommandBuffers( + m_Test->device, vkh::CommandBufferAllocateInfo(cmdPool, 4, level), &buflist[0])); + } + + VkCommandBuffer ret = buflist.back(); + buflist.pop_back(); + + return ret; +} + +void VulkanCommands::Submit(const std::vector &cmds, + const std::vector &seccmds, VkQueue q, + VkSemaphore wait, VkSemaphore signal) +{ + VkFence fence; + CHECK_VKR(vkCreateFence(m_Test->device, vkh::FenceCreateInfo(), NULL, &fence)); + + fences.insert(fence); + + if(m_Test->hasExt(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME)) + { + VkSubmitInfo2KHR submit = {VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR}; + + std::vector cmdSubmits; + for(VkCommandBuffer cmd : cmds) + cmdSubmits.push_back({VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR, NULL, cmd, 0}); + + submit.commandBufferInfoCount = (uint32_t)cmdSubmits.size(); + submit.pCommandBufferInfos = cmdSubmits.data(); + + VkSemaphoreSubmitInfoKHR waitInfo = {}, signalInfo = {}; + + if(wait != VK_NULL_HANDLE) + { + waitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR; + waitInfo.semaphore = wait; + waitInfo.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR; + + submit.waitSemaphoreInfoCount = 1; + submit.pWaitSemaphoreInfos = &waitInfo; + } + + if(signal != VK_NULL_HANDLE) + { + signalInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR; + signalInfo.semaphore = signal; + signalInfo.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR; + + submit.signalSemaphoreInfoCount = 1; + submit.pSignalSemaphoreInfos = &signalInfo; + } + + CHECK_VKR(vkQueueSubmit2KHR(q, 1, &submit, fence)); + } + else + { + VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + + VkSubmitInfo submit = vkh::SubmitInfo(cmds); + + if(wait != VK_NULL_HANDLE) + { + submit.waitSemaphoreCount = 1; + submit.pWaitDstStageMask = &waitStage; + submit.pWaitSemaphores = &wait; + } + + if(signal != VK_NULL_HANDLE) + { + submit.signalSemaphoreCount = 1; + submit.pSignalSemaphores = &signal; + } + + CHECK_VKR(vkQueueSubmit(q, 1, &submit, fence)); + } + + for(const VkCommandBuffer &cmd : cmds) + pendingCommandBuffers[0].push_back(std::make_pair(cmd, fence)); + + for(const VkCommandBuffer &cmd : seccmds) + pendingCommandBuffers[1].push_back(std::make_pair(cmd, fence)); +} + VulkanWindow::VulkanWindow(VulkanGraphicsTest *test, GraphicsWindow *win) - : GraphicsWindow(win->title) + : GraphicsWindow(win->title), VulkanCommands(test) { m_Test = test; m_Win = win; @@ -1145,10 +1254,6 @@ VulkanWindow::VulkanWindow(VulkanGraphicsTest *test, GraphicsWindow *win) { std::lock_guard lock(m_Test->mutex); - CHECK_VKR(vkCreateCommandPool( - m_Test->device, vkh::CommandPoolCreateInfo(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT), - NULL, &cmdPool)); - CHECK_VKR( vkCreateSemaphore(m_Test->device, vkh::SemaphoreCreateInfo(), NULL, &renderStartSemaphore)); CHECK_VKR( @@ -1193,11 +1298,6 @@ VulkanWindow::~VulkanWindow() vkDestroySemaphore(m_Test->device, renderStartSemaphore, NULL); vkDestroySemaphore(m_Test->device, renderEndSemaphore, NULL); - vkDestroyCommandPool(m_Test->device, cmdPool, NULL); - - for(VkFence fence : fences) - vkDestroyFence(m_Test->device, fence, NULL); - if(surface) vkDestroySurfaceKHR(m_Test->instance, surface, NULL); } @@ -1328,75 +1428,16 @@ void VulkanWindow::Acquire() } void VulkanWindow::Submit(int index, int totalSubmits, const std::vector &cmds, - const std::vector &seccmds, VkQueue q, bool sync2) + const std::vector &seccmds, VkQueue q) { - VkFence fence; - CHECK_VKR(vkCreateFence(m_Test->device, vkh::FenceCreateInfo(), NULL, &fence)); + VkSemaphore signal = VK_NULL_HANDLE, wait = VK_NULL_HANDLE; - fences.insert(fence); + if(index == 0) + wait = renderStartSemaphore; + if(index == totalSubmits - 1) + signal = renderEndSemaphore; - if(sync2) - { - VkSubmitInfo2KHR submit = {VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR}; - - std::vector cmdSubmits; - for(VkCommandBuffer cmd : cmds) - cmdSubmits.push_back({VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR, NULL, cmd, 0}); - - submit.commandBufferInfoCount = (uint32_t)cmdSubmits.size(); - submit.pCommandBufferInfos = cmdSubmits.data(); - - VkSemaphoreSubmitInfoKHR renderStart = {}, renderEnd = {}; - - if(index == 0) - { - renderStart.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR; - renderStart.semaphore = renderStartSemaphore; - renderStart.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR; - - submit.waitSemaphoreInfoCount = 1; - submit.pWaitSemaphoreInfos = &renderStart; - } - - if(index == totalSubmits - 1) - { - renderEnd.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR; - renderEnd.semaphore = renderEndSemaphore; - renderEnd.stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR; - - submit.signalSemaphoreInfoCount = 1; - submit.pSignalSemaphoreInfos = &renderEnd; - } - - CHECK_VKR(vkQueueSubmit2KHR(q, 1, &submit, fence)); - } - else - { - VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - - VkSubmitInfo submit = vkh::SubmitInfo(cmds); - - if(index == 0) - { - submit.waitSemaphoreCount = 1; - submit.pWaitDstStageMask = &waitStage; - submit.pWaitSemaphores = &renderStartSemaphore; - } - - if(index == totalSubmits - 1) - { - submit.signalSemaphoreCount = 1; - submit.pSignalSemaphores = &renderEndSemaphore; - } - - CHECK_VKR(vkQueueSubmit(q, 1, &submit, fence)); - } - - for(const VkCommandBuffer &cmd : cmds) - pendingCommandBuffers[0].push_back(std::make_pair(cmd, fence)); - - for(const VkCommandBuffer &cmd : seccmds) - pendingCommandBuffers[1].push_back(std::make_pair(cmd, fence)); + VulkanCommands::Submit(cmds, seccmds, q, wait, signal); } void VulkanWindow::MultiPresent(VkQueue queue, std::vector windows) @@ -1463,6 +1504,13 @@ void VulkanWindow::PostPresent(VkResult vkr) CHECK_VKR(queuePresentError); } + VulkanCommands::ProcessCompletions(); + + Acquire(); +} + +void VulkanCommands::ProcessCompletions() +{ std::set doneFences; std::map fenceStatus; @@ -1494,8 +1542,6 @@ void VulkanWindow::PostPresent(VkResult vkr) vkDestroyFence(m_Test->device, *it, NULL); fences.erase(*it); } - - Acquire(); } void VulkanWindow::DestroySwapchain() diff --git a/util/test/demos/vk/vk_test.h b/util/test/demos/vk/vk_test.h index a18aa0c1d..fc5b172ed 100644 --- a/util/test/demos/vk/vk_test.h +++ b/util/test/demos/vk/vk_test.h @@ -107,7 +107,27 @@ struct AllocatedImage struct VulkanGraphicsTest; -struct VulkanWindow : public GraphicsWindow +struct VulkanCommands +{ +public: + VulkanCommands(VulkanGraphicsTest *test); + ~VulkanCommands(); + VkCommandBuffer GetCommandBuffer(VkCommandBufferLevel level); + void Submit(const std::vector &cmds, const std::vector &seccmds, + VkQueue q, VkSemaphore wait, VkSemaphore signal); + void ProcessCompletions(); + +private: + VulkanGraphicsTest *m_Test; + + VkCommandPool cmdPool; + std::set fences; + + std::vector freeCommandBuffers[2]; + std::vector> pendingCommandBuffers[2]; +}; + +struct VulkanWindow : public GraphicsWindow, public VulkanCommands { VkFormat format; uint32_t imgIndex = 0; @@ -139,9 +159,8 @@ struct VulkanWindow : public GraphicsWindow return fbs[idx]; } bool Initialised() { return swap != VK_NULL_HANDLE; } - VkCommandBuffer GetCommandBuffer(VkCommandBufferLevel level); void Submit(int index, int totalSubmits, const std::vector &cmds, - const std::vector &seccmds, VkQueue q, bool sync2); + const std::vector &seccmds, VkQueue q); static void MultiPresent(VkQueue queue, std::vector windows); void Present(VkQueue q); @@ -162,12 +181,6 @@ private: VkSemaphore renderStartSemaphore = VK_NULL_HANDLE, renderEndSemaphore = VK_NULL_HANDLE; std::vector fbs; - VkCommandPool cmdPool; - std::set fences; - - std::vector freeCommandBuffers[2]; - std::vector> pendingCommandBuffers[2]; - GraphicsWindow *m_Win; VulkanGraphicsTest *m_Test; }; @@ -189,9 +202,8 @@ struct VulkanGraphicsTest : public GraphicsTest void FinishUsingBackbuffer(VkCommandBuffer cmd, VkAccessFlags prevUse, VkImageLayout layout, VulkanWindow *window = NULL); void Submit(int index, int totalSubmits, const std::vector &cmds, - const std::vector &seccmds = {}, VulkanWindow *window = NULL, - VkQueue q = VK_NULL_HANDLE, bool sync2 = false); - void Present(VulkanWindow *window = NULL, VkQueue q = VK_NULL_HANDLE); + const std::vector &seccmds = {}); + void Present(); VkPipelineShaderStageCreateInfo CompileShaderModule( const std::string &source_text, ShaderLang lang, ShaderStage stage, @@ -256,6 +268,9 @@ struct VulkanGraphicsTest : public GraphicsTest // optional extensions, will be added to devExts if supported (allows fallback paths) std::vector optDevExts; + VkQueueFlags queueFlagsRequired = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; + VkQueueFlags queueFlagsBanned = 0; + bool hasExt(const char *ext); // a custom struct to pass to vkDeviceCreateInfo::pNext @@ -290,6 +305,8 @@ struct VulkanGraphicsTest : public GraphicsTest VulkanWindow *mainWindow = NULL; + VulkanCommands *headlessCmds = NULL; + // VMA bool vmaDedicated = false; VmaAllocator allocator = VK_NULL_HANDLE; diff --git a/util/test/tests/D3D12/D3D12_Compute_Only.py b/util/test/tests/D3D12/D3D12_Compute_Only.py new file mode 100644 index 000000000..c151387ea --- /dev/null +++ b/util/test/tests/D3D12/D3D12_Compute_Only.py @@ -0,0 +1,36 @@ +import renderdoc as rd +import rdtest +import struct + + +class D3D12_Compute_Only(rdtest.TestCase): + demos_test_name = 'D3D12_Compute_Only' + + def check_capture(self): + tex = self.get_resource_by_name("tex").resourceId + bufin = self.get_resource_by_name("bufin").resourceId + bufout = self.get_resource_by_name("bufout").resourceId + + self.check_pixel_value(tex, 0, 0, [0.25, 0.5, 0.75, 1.0]) + + self.controller.SetFrameEvent(self.find_action("Pre-Dispatch").eventId, True) + + uints = struct.unpack_from('=4L', self.controller.GetBufferData(bufin, 0, 0), 0) + + if not rdtest.value_compare(uints, [111, 111, 111, 111]): + raise rdtest.TestFailureException( + 'bufin data is incorrect before dispatch: {}'.format(uints)) + + uints = struct.unpack_from('=4L', self.controller.GetBufferData(bufout, 0, 0), 0) + + if not rdtest.value_compare(uints, [222, 222, 222, 222]): + raise rdtest.TestFailureException( + 'bufin data is incorrect before dispatch: {}'.format(uints)) + + self.controller.SetFrameEvent(self.find_action("Post-Dispatch").eventId, True) + + uints = struct.unpack_from('=4L', self.controller.GetBufferData(bufout, 0, 0), 0) + + if not rdtest.value_compare(uints, [777, 888, 999, 1110]): + raise rdtest.TestFailureException( + 'bufin data is incorrect after dispatch: {}'.format(uints)) diff --git a/util/test/tests/Vulkan/VK_Compute_Only.py b/util/test/tests/Vulkan/VK_Compute_Only.py new file mode 100644 index 000000000..c76f05532 --- /dev/null +++ b/util/test/tests/Vulkan/VK_Compute_Only.py @@ -0,0 +1,36 @@ +import renderdoc as rd +import rdtest +import struct + + +class VK_Compute_Only(rdtest.TestCase): + demos_test_name = 'VK_Compute_Only' + + def check_capture(self): + tex = self.get_resource_by_name("tex").resourceId + bufin = self.get_resource_by_name("bufin").resourceId + bufout = self.get_resource_by_name("bufout").resourceId + + self.check_pixel_value(tex, 0, 0, [0.25, 0.5, 0.75, 1.0]) + + self.controller.SetFrameEvent(self.find_action("Pre-Dispatch").eventId, True) + + uints = struct.unpack_from('=4L', self.controller.GetBufferData(bufin, 0, 0), 0) + + if not rdtest.value_compare(uints, [111, 111, 111, 111]): + raise rdtest.TestFailureException( + 'bufin data is incorrect before dispatch: {}'.format(uints)) + + uints = struct.unpack_from('=4L', self.controller.GetBufferData(bufout, 0, 0), 0) + + if not rdtest.value_compare(uints, [222, 222, 222, 222]): + raise rdtest.TestFailureException( + 'bufout data is incorrect before dispatch: {}'.format(uints)) + + self.controller.SetFrameEvent(self.find_action("Post-Dispatch").eventId, True) + + uints = struct.unpack_from('=4L', self.controller.GetBufferData(bufout, 0, 0), 0) + + if not rdtest.value_compare(uints, [777, 888, 999, 1110]): + raise rdtest.TestFailureException( + 'bufout data is incorrect after dispatch: {}'.format(uints))