From c2e5d2468ad6a38cdb8a067da0678302f6cc6066 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Fri, 13 Sep 2019 15:59:15 -0400 Subject: Refactor render-test to make cross platform (#1053) * First pass of render-test refactor. * Make window construction a function that can choose an implementation. * Remove OpenGL as currently has windows dependency. * Disable Vulkan as Renderer impl has dependency on windows. * Pass Window in as parameter of 'update'. * Add win-window.cpp as was missing. * Fix warning on windows about signs during comparison. --- examples/hello-world/main.cpp | 2 +- examples/model-viewer/main.cpp | 2 +- premake5.lua | 64 +- slang.sln | 11 - tools/cpu-render-test/cpu-render-test-tool.vcxproj | 213 -- .../cpu-render-test-tool.vcxproj.filters | 60 - tools/gfx/circular-resource-heap-d3d12.cpp | 222 -- tools/gfx/circular-resource-heap-d3d12.h | 206 -- tools/gfx/d3d-util.cpp | 454 --- tools/gfx/d3d-util.h | 79 - tools/gfx/d3d/d3d-util.cpp | 454 +++ tools/gfx/d3d/d3d-util.h | 79 + tools/gfx/d3d11/render-d3d11.cpp | 2371 +++++++++++++ tools/gfx/d3d11/render-d3d11.h | 10 + tools/gfx/d3d12/circular-resource-heap-d3d12.cpp | 222 ++ tools/gfx/d3d12/circular-resource-heap-d3d12.h | 206 ++ tools/gfx/d3d12/descriptor-heap-d3d12.cpp | 47 + tools/gfx/d3d12/descriptor-heap-d3d12.h | 198 ++ tools/gfx/d3d12/render-d3d12.cpp | 3722 ++++++++++++++++++++ tools/gfx/d3d12/render-d3d12.h | 10 + tools/gfx/d3d12/resource-d3d12.cpp | 214 ++ tools/gfx/d3d12/resource-d3d12.h | 178 + tools/gfx/descriptor-heap-d3d12.cpp | 47 - tools/gfx/descriptor-heap-d3d12.h | 198 -- tools/gfx/gfx.vcxproj | 54 +- tools/gfx/gfx.vcxproj.filters | 72 +- tools/gfx/open-gl/render-gl.cpp | 1504 ++++++++ tools/gfx/open-gl/render-gl.h | 10 + tools/gfx/render-d3d11.cpp | 2371 ------------- tools/gfx/render-d3d11.h | 10 - tools/gfx/render-d3d12.cpp | 3722 -------------------- tools/gfx/render-d3d12.h | 10 - tools/gfx/render-gl.cpp | 1504 -------- tools/gfx/render-gl.h | 10 - tools/gfx/render-vk.cpp | 2793 --------------- tools/gfx/render-vk.h | 10 - tools/gfx/render.cpp | 33 + tools/gfx/render.h | 5 + tools/gfx/resource-d3d12.cpp | 214 -- tools/gfx/resource-d3d12.h | 178 - tools/gfx/vk-api.cpp | 142 - tools/gfx/vk-api.h | 203 -- tools/gfx/vk-device-queue.cpp | 199 -- tools/gfx/vk-device-queue.h | 94 - tools/gfx/vk-module.cpp | 76 - tools/gfx/vk-module.h | 40 - tools/gfx/vk-swap-chain.cpp | 421 --- tools/gfx/vk-swap-chain.h | 141 - tools/gfx/vk-util.cpp | 59 - tools/gfx/vk-util.h | 41 - tools/gfx/vulkan/render-vk.cpp | 2793 +++++++++++++++ tools/gfx/vulkan/render-vk.h | 10 + tools/gfx/vulkan/vk-api.cpp | 142 + tools/gfx/vulkan/vk-api.h | 203 ++ tools/gfx/vulkan/vk-device-queue.cpp | 199 ++ tools/gfx/vulkan/vk-device-queue.h | 94 + tools/gfx/vulkan/vk-module.cpp | 76 + tools/gfx/vulkan/vk-module.h | 40 + tools/gfx/vulkan/vk-swap-chain.cpp | 421 +++ tools/gfx/vulkan/vk-swap-chain.h | 141 + tools/gfx/vulkan/vk-util.cpp | 59 + tools/gfx/vulkan/vk-util.h | 41 + tools/gfx/window.cpp | 399 --- tools/gfx/windows/win-window.cpp | 398 +++ tools/render-test/cpu-memory-binding.cpp | 2 +- tools/render-test/render-test-main.cpp | 377 +- tools/render-test/render-test-tool.vcxproj | 3 + tools/render-test/render-test-tool.vcxproj.filters | 9 + tools/render-test/window.cpp | 21 + tools/render-test/window.h | 45 + tools/render-test/windows/win-window.cpp | 185 + tools/slang-test/slang-test-main.cpp | 23 +- 72 files changed, 14356 insertions(+), 14510 deletions(-) delete mode 100644 tools/cpu-render-test/cpu-render-test-tool.vcxproj delete mode 100644 tools/cpu-render-test/cpu-render-test-tool.vcxproj.filters delete mode 100644 tools/gfx/circular-resource-heap-d3d12.cpp delete mode 100644 tools/gfx/circular-resource-heap-d3d12.h delete mode 100644 tools/gfx/d3d-util.cpp delete mode 100644 tools/gfx/d3d-util.h create mode 100644 tools/gfx/d3d/d3d-util.cpp create mode 100644 tools/gfx/d3d/d3d-util.h create mode 100644 tools/gfx/d3d11/render-d3d11.cpp create mode 100644 tools/gfx/d3d11/render-d3d11.h create mode 100644 tools/gfx/d3d12/circular-resource-heap-d3d12.cpp create mode 100644 tools/gfx/d3d12/circular-resource-heap-d3d12.h create mode 100644 tools/gfx/d3d12/descriptor-heap-d3d12.cpp create mode 100644 tools/gfx/d3d12/descriptor-heap-d3d12.h create mode 100644 tools/gfx/d3d12/render-d3d12.cpp create mode 100644 tools/gfx/d3d12/render-d3d12.h create mode 100644 tools/gfx/d3d12/resource-d3d12.cpp create mode 100644 tools/gfx/d3d12/resource-d3d12.h delete mode 100644 tools/gfx/descriptor-heap-d3d12.cpp delete mode 100644 tools/gfx/descriptor-heap-d3d12.h create mode 100644 tools/gfx/open-gl/render-gl.cpp create mode 100644 tools/gfx/open-gl/render-gl.h delete mode 100644 tools/gfx/render-d3d11.cpp delete mode 100644 tools/gfx/render-d3d11.h delete mode 100644 tools/gfx/render-d3d12.cpp delete mode 100644 tools/gfx/render-d3d12.h delete mode 100644 tools/gfx/render-gl.cpp delete mode 100644 tools/gfx/render-gl.h delete mode 100644 tools/gfx/render-vk.cpp delete mode 100644 tools/gfx/render-vk.h delete mode 100644 tools/gfx/resource-d3d12.cpp delete mode 100644 tools/gfx/resource-d3d12.h delete mode 100644 tools/gfx/vk-api.cpp delete mode 100644 tools/gfx/vk-api.h delete mode 100644 tools/gfx/vk-device-queue.cpp delete mode 100644 tools/gfx/vk-device-queue.h delete mode 100644 tools/gfx/vk-module.cpp delete mode 100644 tools/gfx/vk-module.h delete mode 100644 tools/gfx/vk-swap-chain.cpp delete mode 100644 tools/gfx/vk-swap-chain.h delete mode 100644 tools/gfx/vk-util.cpp delete mode 100644 tools/gfx/vk-util.h create mode 100644 tools/gfx/vulkan/render-vk.cpp create mode 100644 tools/gfx/vulkan/render-vk.h create mode 100644 tools/gfx/vulkan/vk-api.cpp create mode 100644 tools/gfx/vulkan/vk-api.h create mode 100644 tools/gfx/vulkan/vk-device-queue.cpp create mode 100644 tools/gfx/vulkan/vk-device-queue.h create mode 100644 tools/gfx/vulkan/vk-module.cpp create mode 100644 tools/gfx/vulkan/vk-module.h create mode 100644 tools/gfx/vulkan/vk-swap-chain.cpp create mode 100644 tools/gfx/vulkan/vk-swap-chain.h create mode 100644 tools/gfx/vulkan/vk-util.cpp create mode 100644 tools/gfx/vulkan/vk-util.h delete mode 100644 tools/gfx/window.cpp create mode 100644 tools/gfx/windows/win-window.cpp create mode 100644 tools/render-test/window.cpp create mode 100644 tools/render-test/window.h create mode 100644 tools/render-test/windows/win-window.cpp diff --git a/examples/hello-world/main.cpp b/examples/hello-world/main.cpp index bac378d96..4a15bd4fa 100644 --- a/examples/hello-world/main.cpp +++ b/examples/hello-world/main.cpp @@ -33,7 +33,7 @@ // design choices in their abstraction layer. // #include "gfx/render.h" -#include "gfx/render-d3d11.h" +#include "gfx/d3d11/render-d3d11.h" #include "gfx/window.h" using namespace gfx; diff --git a/examples/model-viewer/main.cpp b/examples/model-viewer/main.cpp index 66a53c245..5dcc51257 100644 --- a/examples/model-viewer/main.cpp +++ b/examples/model-viewer/main.cpp @@ -18,7 +18,7 @@ // #include "gfx/model.h" #include "gfx/render.h" -#include "gfx/render-d3d11.h" +#include "gfx/d3d11/render-d3d11.h" #include "gfx/vector-math.h" #include "gfx/window.h" #include "gfx/gui.h" diff --git a/premake5.lua b/premake5.lua index 63f83b00e..a1e729113 100644 --- a/premake5.lua +++ b/premake5.lua @@ -501,42 +501,26 @@ toolSharedLibrary "slang-reflection-test" -- TODO: Fix that requirement. -- -if isTargetWindows then - toolSharedLibrary "render-test" - uuid "61F7EB00-7281-4BF3-9470-7C2EA92620C3" - - includedirs { ".", "external", "source", "tools/gfx" } - links { "core", "slang", "gfx" } - - systemversion "10.0.14393.0" - - removefiles { "tools/render-test/cpu-render-test-main.cpp" } - - -- For Windows targets, we want to copy - -- dxcompiler.dll, and dxil.dll from the Windows SDK redistributable - -- directory into the output directory. - -- d3dcompiler_47.dll is copied from the external/slang-binaries submodule. - postbuildcommands { '"$(SolutionDir)tools\\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/%{cfg.platform:lower()}/" "%{cfg.targetdir}/" "windows-%{cfg.platform:lower()}"'} - -end - -toolSharedLibrary "cpu-render-test" - uuid "5701695E-7324-4B4D-977A-8D56C2A041B1" +toolSharedLibrary "render-test" + uuid "61F7EB00-7281-4BF3-9470-7C2EA92620C3" includedirs { ".", "external", "source", "tools/gfx" } links { "core", "slang", "gfx" } + + removefiles { "tools/render-test/cpu-render-test-main.cpp" } - addSourceDir("tools/render-test") - - removefiles { "tools/render-test/render-test-main.cpp" } - - if isTargetWindows then + if isTargetWindows then + addSourceDir "tools/render-test/windows" + + systemversion "10.0.14393.0" + -- For Windows targets, we want to copy -- dxcompiler.dll, and dxil.dll from the Windows SDK redistributable -- directory into the output directory. -- d3dcompiler_47.dll is copied from the external/slang-binaries submodule. - postbuildcommands { '"$(SolutionDir)tools\\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/%{cfg.platform:lower()}/" "%{cfg.targetdir}/"'} - end + postbuildcommands { '"$(SolutionDir)tools\\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/%{cfg.platform:lower()}/" "%{cfg.targetdir}/" "windows-%{cfg.platform:lower()}"'} + end + -- -- `gfx` is a utility library for doing GPU rendering -- and compute, which is used by both our testing and exmaples. @@ -561,15 +545,25 @@ tool "gfx" -- directory into the output directory. -- d3dcompiler_47.dll is copied from the external/slang-binaries submodule. postbuildcommands { '"$(SolutionDir)tools\\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/%{cfg.platform:lower()}/" "%{cfg.targetdir}/"'} + + addSourceDir "tools/gfx/vulkan" + addSourceDir "tools/gfx/open-gl" + addSourceDir "tools/gfx/d3d" + addSourceDir "tools/gfx/d3d11" + addSourceDir "tools/gfx/d3d12" + + addSourceDir "tools/gfx/windows" + + elseif targetDetail == "mingw" or targetDetail == "cygwin" then + -- Don't support any render techs... + elseif os.target() == "macosx" then + --addSourceDir "tools/gfx/open-gl" else - removefiles { "tools/gfx/circular-resource-heap-d3d12.cpp", "tools/gfx/d3d-util.cpp", "tools/gfx/descriptor-heap-d3d12.cpp", "tools/gfx/render-d3d11.cpp", "tools/gfx/render-d3d12.cpp", "tools/gfx/render-gl.cpp", "tools/gfx/resource-d3d12.cpp", "tools/gfx/render-vk.cpp", "tools/gfx/vk-swap-chain.cpp", "tools/gfx/window.cpp" } + -- Linux like + --addSourceDir "tools/gfx/vulkan" + --addSourceDir "tools/gfx/open-gl" end - - -- Remove VK from OSX gfx build - if os.target() == "macosx" then - removefiles { "tools/gfx/render-vk.cpp", "tools/gfx/vk-device-queue.cpp", "tools/gfx/vk-api.cpp", "tools/gfx/vk-module.cpp", "tools/gfx/vk-swap-chain.cpp", "tools/gfx/vk-util.cpp" } - end - + filter { "system:linux" } -- might be able to do pic(true) buildoptions{"-fPIC"} diff --git a/slang.sln b/slang.sln index 95c5d3345..c3f6e9a5e 100644 --- a/slang.sln +++ b/slang.sln @@ -23,8 +23,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-reflection-test-tool" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "render-test-tool", "tools\render-test\render-test-tool.vcxproj", "{61F7EB00-7281-4BF3-9470-7C2EA92620C3}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpu-render-test-tool", "tools\cpu-render-test\cpu-render-test-tool.vcxproj", "{5701695E-7324-4B4D-977A-8D56C2A041B1}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slangc", "source\slangc\slangc.vcxproj", "{D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang", "source\slang\slang.vcxproj", "{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}" @@ -106,14 +104,6 @@ Global {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Release|Win32.Build.0 = Release|Win32 {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Release|x64.ActiveCfg = Release|x64 {61F7EB00-7281-4BF3-9470-7C2EA92620C3}.Release|x64.Build.0 = Release|x64 - {5701695E-7324-4B4D-977A-8D56C2A041B1}.Debug|Win32.ActiveCfg = Debug|Win32 - {5701695E-7324-4B4D-977A-8D56C2A041B1}.Debug|Win32.Build.0 = Debug|Win32 - {5701695E-7324-4B4D-977A-8D56C2A041B1}.Debug|x64.ActiveCfg = Debug|x64 - {5701695E-7324-4B4D-977A-8D56C2A041B1}.Debug|x64.Build.0 = Debug|x64 - {5701695E-7324-4B4D-977A-8D56C2A041B1}.Release|Win32.ActiveCfg = Release|Win32 - {5701695E-7324-4B4D-977A-8D56C2A041B1}.Release|Win32.Build.0 = Release|Win32 - {5701695E-7324-4B4D-977A-8D56C2A041B1}.Release|x64.ActiveCfg = Release|x64 - {5701695E-7324-4B4D-977A-8D56C2A041B1}.Release|x64.Build.0 = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.ActiveCfg = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.Build.0 = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|x64.ActiveCfg = Debug|x64 @@ -150,6 +140,5 @@ Global {222F7498-B40C-4F3F-A704-DDEB91A4484A} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13} {C5ACCA6E-C04D-4B36-8516-3752B3C13C2F} = {57B5AA5E-C340-1823-CC51-9B17385C7423} {61F7EB00-7281-4BF3-9470-7C2EA92620C3} = {57B5AA5E-C340-1823-CC51-9B17385C7423} - {5701695E-7324-4B4D-977A-8D56C2A041B1} = {57B5AA5E-C340-1823-CC51-9B17385C7423} EndGlobalSection EndGlobal diff --git a/tools/cpu-render-test/cpu-render-test-tool.vcxproj b/tools/cpu-render-test/cpu-render-test-tool.vcxproj deleted file mode 100644 index 975aa61b6..000000000 --- a/tools/cpu-render-test/cpu-render-test-tool.vcxproj +++ /dev/null @@ -1,213 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {5701695E-7324-4B4D-977A-8D56C2A041B1} - true - Win32Proj - cpu-render-test-tool - - - - DynamicLibrary - true - Unicode - v140 - - - DynamicLibrary - true - Unicode - v140 - - - DynamicLibrary - false - Unicode - v140 - - - DynamicLibrary - false - Unicode - v140 - - - - - - - - - - - - - - - - - - - true - ..\..\bin\windows-x86\debug\ - ..\..\intermediate\windows-x86\debug\cpu-render-test-tool\ - cpu-render-test-tool - .dll - - - true - ..\..\bin\windows-x64\debug\ - ..\..\intermediate\windows-x64\debug\cpu-render-test-tool\ - cpu-render-test-tool - .dll - - - false - ..\..\bin\windows-x86\release\ - ..\..\intermediate\windows-x86\release\cpu-render-test-tool\ - cpu-render-test-tool - .dll - - - false - ..\..\bin\windows-x64\release\ - ..\..\intermediate\windows-x64\release\cpu-render-test-tool\ - cpu-render-test-tool - .dll - - - - NotUsing - Level3 - _DEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) - EditAndContinue - Disabled - MultiThreadedDebug - - - Windows - true - ..\..\bin\windows-x86\debug\cpu-render-test-tool.lib - - - "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../bin/windows-x86/debug/" - - - - - NotUsing - Level3 - _DEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) - EditAndContinue - Disabled - MultiThreadedDebug - - - Windows - true - ..\..\bin\windows-x64\debug\cpu-render-test-tool.lib - - - "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../bin/windows-x64/debug/" - - - - - NotUsing - Level3 - NDEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - - - Windows - true - true - ..\..\bin\windows-x86\release\cpu-render-test-tool.lib - - - "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../bin/windows-x86/release/" - - - - - NotUsing - Level3 - NDEBUG;SLANG_SHARED_LIBRARY_TOOL;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - - - Windows - true - true - ..\..\bin\windows-x64\release\cpu-render-test-tool.lib - - - "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../bin/windows-x64/release/" - - - - - - - - - - - - - - - - - - - - - - - - {F9BE7957-8399-899E-0C49-E714FDDD4B65} - - - {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} - - - {222F7498-B40C-4F3F-A704-DDEB91A4484A} - - - - - - \ No newline at end of file diff --git a/tools/cpu-render-test/cpu-render-test-tool.vcxproj.filters b/tools/cpu-render-test/cpu-render-test-tool.vcxproj.filters deleted file mode 100644 index 2b822a5be..000000000 --- a/tools/cpu-render-test/cpu-render-test-tool.vcxproj.filters +++ /dev/null @@ -1,60 +0,0 @@ - - - - - {21EB8090-0D4E-1035-B6D3-48EBA215DCB7} - - - {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/tools/gfx/circular-resource-heap-d3d12.cpp b/tools/gfx/circular-resource-heap-d3d12.cpp deleted file mode 100644 index 685dd364f..000000000 --- a/tools/gfx/circular-resource-heap-d3d12.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "circular-resource-heap-d3d12.h" - -namespace gfx { -using namespace Slang; - -D3D12CircularResourceHeap::D3D12CircularResourceHeap(): - m_fence(nullptr), - m_device(nullptr), - m_blockFreeList(sizeof(Block), SLANG_ALIGN_OF(Block), 16), - m_blocks(nullptr) -{ - m_back.m_block = nullptr; - m_back.m_position = nullptr; - m_front.m_block = nullptr; - m_front.m_position = nullptr; -} - -D3D12CircularResourceHeap::~D3D12CircularResourceHeap() -{ - _freeBlockListResources(m_blocks); -} - -void D3D12CircularResourceHeap::_freeBlockListResources(const Block* start) -{ - if (start) - { - const Block* block = start; - do - { - ID3D12Resource* resource = block->m_resource; - - resource->Unmap(0, nullptr); - resource->Release(); - - // Next in list - block = block->m_next; - - } while (block != start); - } -} - -Result D3D12CircularResourceHeap::init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence) -{ - assert(m_blocks == nullptr); - assert(desc.m_blockSize > 0); - - m_fence = fence; - m_desc = desc; - m_device = device; - - return SLANG_OK; -} - -void D3D12CircularResourceHeap::addSync(uint64_t signalValue) -{ - assert(signalValue == m_fence->getCurrentValue()); - PendingEntry entry; - entry.m_completedValue = signalValue; - entry.m_cursor = m_front; - m_pendingQueue.add(entry); -} - -void D3D12CircularResourceHeap::updateCompleted() -{ - const uint64_t completedValue = m_fence->getCompletedValue(); - -#if 0 - while (m_pendingQueue.getCount() != 0) - { - const PendingEntry& entry = m_pendingQueue[0]; - if (entry.m_completedValue <= completedValue) - { - m_back = entry.m_cursor; - m_pendingQueue.removeAt(0); - } - else - { - break; - } - } -#else - // A more efficient implementation is m_pendingQueue is implemented as a vector like type - const Index size = m_pendingQueue.getCount(); - Index end = 0; - while (end < size && m_pendingQueue[end].m_completedValue <= completedValue) - { - end++; - } - - if (end > 0) - { - // Set the back position - m_back = m_pendingQueue[end - 1].m_cursor; - if (end == size) - { - m_pendingQueue.clear(); - } - else - { - m_pendingQueue.removeRange(0, size); - } - } -#endif -} - -D3D12CircularResourceHeap::Block* D3D12CircularResourceHeap::_newBlock() -{ - D3D12_RESOURCE_DESC desc; - - desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - desc.Alignment = 0; - desc.Width = m_desc.m_blockSize; - desc.Height = 1; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - desc.Flags = D3D12_RESOURCE_FLAG_NONE; - - ComPtr resource; - Result res = m_device->CreateCommittedResource(&m_desc.m_heapProperties, m_desc.m_heapFlags, &desc, m_desc.m_initialState, nullptr, IID_PPV_ARGS(resource.writeRef())); - if (SLANG_FAILED(res)) - { - assert(!"Resource allocation failed"); - return nullptr; - } - - uint8_t* data = nullptr; - if (m_desc.m_heapProperties.Type == D3D12_HEAP_TYPE_READBACK) - { - } - else - { - // Map it, and keep it mapped - resource->Map(0, nullptr, (void**)&data); - } - - // We have no blocks -> so lets allocate the first - Block* block = (Block*)m_blockFreeList.allocate(); - block->m_next = nullptr; - - block->m_resource = resource.detach(); - block->m_start = data; - return block; -} - -D3D12CircularResourceHeap::Cursor D3D12CircularResourceHeap::allocate(size_t size, size_t alignment) -{ - const size_t blockSize = getBlockSize(); - - assert(size <= blockSize); - - // If nothing is allocated add the first block - if (m_blocks == nullptr) - { - Block* block = _newBlock(); - if (!block) - { - Cursor cursor = {}; - return cursor; - } - m_blocks = block; - // Make circular - block->m_next = block; - - // Point front and back to same position, as currently it is all free - m_back = { block, block->m_start }; - m_front = m_back; - } - - // If front and back are in the same block then front MUST be ahead of back (as that defined as - // an invariant and is required for block insertion to be possible - Block* block = m_front.m_block; - - // Check the invariant - assert(block != m_back.m_block || m_front.m_position >= m_back.m_position); - - { - uint8_t* cur = (uint8_t*)((size_t(m_front.m_position) + alignment - 1) & ~(alignment - 1)); - // Does the the allocation fit? - if (cur + size <= block->m_start + blockSize) - { - // It fits - // Move the front forward - m_front.m_position = cur + size; - Cursor cursor = { block, cur }; - return cursor; - } - } - - // Okay I can't fit into current block... - - // If the next block contains front, we need to add a block, else we can use that block - if (block->m_next == m_back.m_block) - { - Block* newBlock = _newBlock(); - // Insert into the list - newBlock->m_next = block->m_next; - block->m_next = newBlock; - } - - // Use the block we are going to add to - block = block->m_next; - uint8_t* cur = (uint8_t*)((size_t(block->m_start) + alignment - 1) & ~(alignment - 1)); - // Does the the allocation fit? - if (cur + size > block->m_start + blockSize) - { - assert(!"Couldn't fit into a free block(!) Alignment breaks it?"); - Cursor cursor = {}; - return cursor; - } - // It fits - // Move the front forward - m_front.m_block = block; - m_front.m_position = cur + size; - Cursor cursor = { block, cur }; - return cursor; -} - -} // namespace gfx diff --git a/tools/gfx/circular-resource-heap-d3d12.h b/tools/gfx/circular-resource-heap-d3d12.h deleted file mode 100644 index bf9f412cf..000000000 --- a/tools/gfx/circular-resource-heap-d3d12.h +++ /dev/null @@ -1,206 +0,0 @@ -#pragma once - -#include "../../slang-com-ptr.h" -#include "../../source/core/slang-list.h" -#include "../../source/core/slang-free-list.h" - -#include "resource-d3d12.h" - -namespace gfx { - -/*! \brief The D3D12CircularResourceHeap is a heap that is suited for size constrained real-time resources allocation that -is transitory in nature. It is designed to allocate resources which are used and discarded, often used where in -previous versions of DirectX the 'DISCARD' flag was used. - -The idea is to have a heap which chunks of resource can be allocated, and used for GPU execution, -and that the heap is able through the addSync/updateCompleted idiom is able to track when the usage of the resources is -completed allowing them to be reused. The heap is arranged as circularly, with new allocations made from the front, and the back -being updated as the GPU updating the back when it is informed anything using prior parts of the heap have completed. In this -arrangement all the heap between the back and the front can be thought of as in use or potentially in use by the GPU. All the heap -from the front back around to the back, is free and can be allocated from. It is the responsibility of the user of the Heap to make -sure the invariant holds, but in most normal usage it does so simply. - -Another feature of the heap is that it does not require upfront knowledge of how big a heap is needed. The backing resources will be expanded -dynamically with requests as needed. The only requirement is that know single request can be larger than m_blockSize specified in the Desc -used to initialize the heap. This is because all the backing resources are allocated to a single size. This limitation means the D3D12CircularResourceHeap -may not be the best use for example for uploading a texture - because it's design is really around transitory uploads or write backs, and so more suited -to constant buffers, vertex buffer, index buffers and the like. - -To upload a texture at program startup it is most likely better to use a D3D12ResourceScopeManager. - -\code{.cpp} - -typedef D3D12CircularResourceHeap Heap; - -Heap::Cursor cursor = heap.allocateVertexBuffer(sizeof(Vertex) * numVerts); -Memory:copy(cursor.m_position, verts, sizeof(Vertex) * numVerts); - -// Do a command using the GPU handle -m_commandList->... -// Do another command using the GPU handle - -m_commandList->... - -// Execute the command list on the command queue -{ - ID3D12CommandList* lists[] = { m_commandList }; - m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(lists), lists); -} - -// Add a sync point -const uint64_t signalValue = m_fence.nextSignal(m_commandQueue); -heap.addSync(signalValue) - -// The cursors cannot be used anymore - -// At some later point call updateCompleted. This will see where the GPU is at, and make resources available that the GPU no longer accesses. -heap.updateCompleted(); - -\endcode - -### Implementation - -Front and back can be in the same block, but ONLY if back is behind front, because we have to always be able to insert -new blocks in front of front. So it must be possible to do an block insertion between the two of them. - -|--B---F-----| |----------| - -When B and F are on top of one another it means there is nothing in the list. NOTE this also means that a move of front can never place it -top of the back. - -https://msdn.microsoft.com/en-us/library/windows/desktop/dn899125%28v=vs.85%29.aspx -https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx -*/ - -class D3D12CircularResourceHeap -{ - protected: - struct Block; - public: - typedef D3D12CircularResourceHeap ThisType; - - /// The alignment used for VERTEX_BUFFER allocations - /// Strictly speaking it seems the hardware can handle 4 byte alignment, but since often in use - /// data will be copied from CPU memory to the allocation, using 16 byte alignment is superior as allows - /// significantly faster memcpy. - /// The sample that shows sizeof(float) - 4 bytes is appropriate is at the link below. - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx - enum - { - VERTEX_BUFFER_ALIGNMENT = 16, - }; - - struct Desc - { - void init() - { - { - D3D12_HEAP_PROPERTIES& props = m_heapProperties; - - props.Type = D3D12_HEAP_TYPE_UPLOAD; - props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - props.CreationNodeMask = 1; - props.VisibleNodeMask = 1; - } - m_heapFlags = D3D12_HEAP_FLAG_NONE; - m_initialState = D3D12_RESOURCE_STATE_GENERIC_READ; - m_blockSize = 0; - } - - D3D12_HEAP_PROPERTIES m_heapProperties; - D3D12_HEAP_FLAGS m_heapFlags; - D3D12_RESOURCE_STATES m_initialState; - size_t m_blockSize; - }; - - /// Cursor position - struct Cursor - { - /// Get GpuHandle - SLANG_FORCE_INLINE D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle() const { return m_block->m_resource->GetGPUVirtualAddress() + size_t(m_position - m_block->m_start); } - /// Must have a block and position - SLANG_FORCE_INLINE bool isValid() const { return m_block != nullptr; } - /// Calculate the offset into the underlying resource - SLANG_FORCE_INLINE size_t getOffset() const { return size_t(m_position - m_block->m_start); } - /// Get the underlying resource - SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_block->m_resource; } - - Block* m_block; ///< The block index - uint8_t* m_position; ///< The current position - }; - - /// Get the desc used to initialize the heap - SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } - - /// Must be called before used - /// Block size must be at least as large as the _largest_ thing allocated - /// Also note depending on alignment of a resource allocation, the block size might also need to take into account the - /// maximum alignment use. It is a REQUIREMENT that a newly allocated resource block is large enough to hold any - /// allocation taking into account the alignment used. - Slang::Result init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence); - - /// Get the block size - SLANG_FORCE_INLINE size_t getBlockSize() const { return m_desc.m_blockSize; } - - /// Allocate constant buffer of specified size - Cursor allocate(size_t size, size_t alignment); - - /// Allocate a constant buffer - SLANG_FORCE_INLINE Cursor allocateConstantBuffer(size_t size) { return allocate(size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); } - /// Allocate a vertex buffer - SLANG_FORCE_INLINE Cursor allocateVertexBuffer(size_t size) { return allocate(size, VERTEX_BUFFER_ALIGNMENT); } - - /// Create filled in constant buffer - SLANG_FORCE_INLINE Cursor newConstantBuffer(const void* data, size_t size) { Cursor cursor = allocateConstantBuffer(size); ::memcpy(cursor.m_position, data, size); return cursor; } - /// Create in filled in constant buffer - template - SLANG_FORCE_INLINE Cursor newConstantBuffer(const T& in) { return newConstantBuffer(&in, sizeof(T)); } - - /// Look where the GPU has got to and release anything not currently used - void updateCompleted(); - /// Add a sync point - meaning that when this point is hit in the queue - /// all of the resources up to this point will no longer be used. - void addSync(uint64_t signalValue); - - /// Get the gpu address of this cursor - D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle(const Cursor& cursor) const { return cursor.m_block->m_resource->GetGPUVirtualAddress() + size_t(cursor.m_position - cursor.m_block->m_start); } - - /// Ctor - D3D12CircularResourceHeap(); - /// Dtor - ~D3D12CircularResourceHeap(); - - protected: - - struct Block - { - ID3D12Resource* m_resource; ///< The mapped resource - uint8_t* m_start; ///< Once created the resource is mapped to here - Block* m_next; ///< Points to next block in the list - }; - struct PendingEntry - { - uint64_t m_completedValue; ///< The value when this is completed - Cursor m_cursor; ///< the cursor at that point - }; - void _freeBlockListResources(const Block* block); - /// Create a new block (with associated resource), do not add the block list - Block* _newBlock(); - - Block* m_blocks; ///< Circular singly linked list of block. nullptr initially - Slang::FreeList m_blockFreeList; ///< Free list of actual allocations of blocks - Slang::List m_pendingQueue; ///< Holds the list of pending positions. When the fence value is greater than the value on the queue entry, the entry is done. - - // Allocation is made from the front, and freed from the back. - Cursor m_back; ///< Current back position. - Cursor m_front; ///< Current front position. - - Desc m_desc; ///< Describes the heap - - D3D12CounterFence* m_fence; ///< The fence to use - ID3D12Device* m_device; ///< The device that resources will be constructed on -}; - -} // namespace gfx - diff --git a/tools/gfx/d3d-util.cpp b/tools/gfx/d3d-util.cpp deleted file mode 100644 index 3f25ff46c..000000000 --- a/tools/gfx/d3d-util.cpp +++ /dev/null @@ -1,454 +0,0 @@ -// d3d-util.cpp -#include "d3d-util.h" - -#include - -#include - -// We will use the C standard library just for printing error messages. -#include - -namespace gfx { -using namespace Slang; - -/* static */D3D_PRIMITIVE_TOPOLOGY D3DUtil::getPrimitiveTopology(PrimitiveTopology topology) -{ - switch (topology) - { - case PrimitiveTopology::TriangleList: - { - return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - } - default: break; - } - return D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; -} - -/* static */DXGI_FORMAT D3DUtil::getMapFormat(Format format) -{ - switch (format) - { - case Format::RGBA_Float32: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case Format::RGB_Float32: return DXGI_FORMAT_R32G32B32_FLOAT; - case Format::RG_Float32: return DXGI_FORMAT_R32G32_FLOAT; - case Format::R_Float32: return DXGI_FORMAT_R32_FLOAT; - case Format::RGBA_Unorm_UInt8: return DXGI_FORMAT_R8G8B8A8_UNORM; - case Format::R_UInt16: return DXGI_FORMAT_R16_UINT; - case Format::R_UInt32: return DXGI_FORMAT_R32_UINT; - - case Format::D_Float32: return DXGI_FORMAT_D32_FLOAT; - case Format::D_Unorm24_S8: return DXGI_FORMAT_D24_UNORM_S8_UINT; - - default: return DXGI_FORMAT_UNKNOWN; - } -} - -/* static */DXGI_FORMAT D3DUtil::calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format) -{ - SLANG_UNUSED(usage); - if (usageFlags) - { - switch (format) - { - case DXGI_FORMAT_R32_FLOAT: /* fallthru */ - case DXGI_FORMAT_R32_UINT: - case DXGI_FORMAT_D32_FLOAT: - { - return DXGI_FORMAT_R32_TYPELESS; - } - case DXGI_FORMAT_D24_UNORM_S8_UINT: return DXGI_FORMAT_R24G8_TYPELESS; - default: break; - } - return format; - } - return format; -} - -/* static */DXGI_FORMAT D3DUtil::calcFormat(UsageType usage, DXGI_FORMAT format) -{ - switch (usage) - { - case USAGE_COUNT_OF: - case USAGE_UNKNOWN: - { - return DXGI_FORMAT_UNKNOWN; - } - case USAGE_DEPTH_STENCIL: - { - switch (format) - { - case DXGI_FORMAT_D32_FLOAT: /* fallthru */ - case DXGI_FORMAT_R32_TYPELESS: - { - return DXGI_FORMAT_D32_FLOAT; - } - case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; - case DXGI_FORMAT_R24G8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; - default: break; - } - return format; - } - case USAGE_TARGET: - { - switch (format) - { - case DXGI_FORMAT_D32_FLOAT: /* fallthru */ - case DXGI_FORMAT_D24_UNORM_S8_UINT: - { - return DXGI_FORMAT_UNKNOWN; - } - case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT; - default: break; - } - return format; - } - case USAGE_SRV: - { - switch (format) - { - case DXGI_FORMAT_D32_FLOAT: /* fallthru */ - case DXGI_FORMAT_R32_TYPELESS: - { - return DXGI_FORMAT_R32_FLOAT; - } - case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; - default: break; - } - - return format; - } - } - - assert(!"Not reachable"); - return DXGI_FORMAT_UNKNOWN; -} - -bool D3DUtil::isTypeless(DXGI_FORMAT format) -{ - switch (format) - { - case DXGI_FORMAT_R32G32B32A32_TYPELESS: - case DXGI_FORMAT_R32G32B32_TYPELESS: - case DXGI_FORMAT_R16G16B16A16_TYPELESS: - case DXGI_FORMAT_R32G32_TYPELESS: - case DXGI_FORMAT_R32G8X24_TYPELESS: - case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: - case DXGI_FORMAT_R10G10B10A2_TYPELESS: - case DXGI_FORMAT_R8G8B8A8_TYPELESS: - case DXGI_FORMAT_R16G16_TYPELESS: - case DXGI_FORMAT_R32_TYPELESS: - case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: - case DXGI_FORMAT_R24G8_TYPELESS: - case DXGI_FORMAT_R8G8_TYPELESS: - case DXGI_FORMAT_R16_TYPELESS: - case DXGI_FORMAT_R8_TYPELESS: - case DXGI_FORMAT_BC1_TYPELESS: - case DXGI_FORMAT_BC2_TYPELESS: - case DXGI_FORMAT_BC3_TYPELESS: - case DXGI_FORMAT_BC4_TYPELESS: - case DXGI_FORMAT_BC5_TYPELESS: - case DXGI_FORMAT_B8G8R8A8_TYPELESS: - case DXGI_FORMAT_BC6H_TYPELESS: - case DXGI_FORMAT_BC7_TYPELESS: - { - return true; - } - default: break; - } - return false; -} - -/* static */Int D3DUtil::getNumColorChannelBits(DXGI_FORMAT fmt) -{ - switch (fmt) - { - case DXGI_FORMAT_R32G32B32A32_TYPELESS: - case DXGI_FORMAT_R32G32B32A32_FLOAT: - case DXGI_FORMAT_R32G32B32A32_UINT: - case DXGI_FORMAT_R32G32B32A32_SINT: - case DXGI_FORMAT_R32G32B32_TYPELESS: - case DXGI_FORMAT_R32G32B32_FLOAT: - case DXGI_FORMAT_R32G32B32_UINT: - case DXGI_FORMAT_R32G32B32_SINT: - { - return 32; - } - case DXGI_FORMAT_R16G16B16A16_TYPELESS: - case DXGI_FORMAT_R16G16B16A16_FLOAT: - case DXGI_FORMAT_R16G16B16A16_UNORM: - case DXGI_FORMAT_R16G16B16A16_UINT: - case DXGI_FORMAT_R16G16B16A16_SNORM: - case DXGI_FORMAT_R16G16B16A16_SINT: - { - return 16; - } - case DXGI_FORMAT_R10G10B10A2_TYPELESS: - case DXGI_FORMAT_R10G10B10A2_UNORM: - case DXGI_FORMAT_R10G10B10A2_UINT: - case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: - { - return 10; - } - case DXGI_FORMAT_R8G8B8A8_TYPELESS: - case DXGI_FORMAT_R8G8B8A8_UNORM: - case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: - case DXGI_FORMAT_R8G8B8A8_UINT: - case DXGI_FORMAT_R8G8B8A8_SNORM: - case DXGI_FORMAT_R8G8B8A8_SINT: - case DXGI_FORMAT_B8G8R8A8_UNORM: - case DXGI_FORMAT_B8G8R8X8_UNORM: - case DXGI_FORMAT_B8G8R8A8_TYPELESS: - case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: - case DXGI_FORMAT_B8G8R8X8_TYPELESS: - case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: - { - return 8; - } - case DXGI_FORMAT_B5G6R5_UNORM: - case DXGI_FORMAT_B5G5R5A1_UNORM: - { - return 5; - } - case DXGI_FORMAT_B4G4R4A4_UNORM: - return 4; - - default: - return 0; - } -} - -// Note: this subroutine is now only used by D3D11 for generating bytecode to go into input layouts. -// -// TODO: we can probably remove that code completely by switching to a PSO-like model across all APIs. -// -/* static */Result D3DUtil::compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, ComPtr& shaderBlobOut) -{ - // Rather than statically link against the `d3dcompile` library, we - // dynamically load it. - // - // Note: A more realistic application would compile from HLSL text to D3D - // shader bytecode as part of an offline process, rather than doing it - // on-the-fly like this - // - static pD3DCompile compileFunc = nullptr; - if (!compileFunc) - { - // TODO(tfoley): maybe want to search for one of a few versions of the DLL - HMODULE compilerModule = LoadLibraryA("d3dcompiler_47.dll"); - if (!compilerModule) - { - fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n"); - return SLANG_FAIL; - } - - compileFunc = (pD3DCompile)GetProcAddress(compilerModule, "D3DCompile"); - if (!compileFunc) - { - fprintf(stderr, "error: failed load symbol 'D3DCompile'\n"); - return SLANG_FAIL; - } - } - - // For this example, we turn on debug output, and turn off all - // optimization. A real application would only use these flags - // when shader debugging is needed. - UINT flags = 0; - flags |= D3DCOMPILE_DEBUG; - flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION; - - // We will always define `__HLSL__` when compiling here, so that - // input code can react differently to being compiled as pure HLSL. - D3D_SHADER_MACRO defines[] = { - { "__HLSL__", "1" }, - { nullptr, nullptr }, - }; - - // The `D3DCompile` entry point takes a bunch of parameters, but we - // don't really need most of them for Slang-generated code. - ComPtr shaderBlob; - ComPtr errorBlob; - - HRESULT hr = compileFunc(source, strlen(source), sourcePath, &defines[0], nullptr, entryPointName, dxProfileName, flags, 0, - shaderBlob.writeRef(), errorBlob.writeRef()); - - // If the HLSL-to-bytecode compilation produced any diagnostic messages - // then we will print them out (whether or not the compilation failed). - if (errorBlob) - { - ::fputs((const char*)errorBlob->GetBufferPointer(), stderr); - ::fflush(stderr); - ::OutputDebugStringA((const char*)errorBlob->GetBufferPointer()); - } - - SLANG_RETURN_ON_FAIL(hr); - shaderBlobOut.swap(shaderBlob); - return SLANG_OK; -} - -/* static */void D3DUtil::appendWideChars(const char* in, List& out) -{ - size_t len = ::strlen(in); - - const DWORD dwFlags = 0; - int outSize = ::MultiByteToWideChar(CP_UTF8, dwFlags, in, int(len), nullptr, 0); - - if (outSize > 0) - { - const Index prevSize = out.getCount(); - out.setCount(prevSize + len + 1); - - WCHAR* dst = out.getBuffer() + prevSize; - ::MultiByteToWideChar(CP_UTF8, dwFlags, in, int(len), dst, outSize); - // Make null terminated - dst[outSize] = 0; - // Remove terminating 0 from array - out.unsafeShrinkToCount(prevSize + outSize); - } -} - -/* static */HMODULE D3DUtil::getDxgiModule() -{ - static HMODULE s_dxgiModule = LoadLibraryA("dxgi.dll"); - if (!s_dxgiModule) - { - fprintf(stderr, "error: failed load 'dxgi.dll'\n"); - return nullptr; - } - - return s_dxgiModule; -} - -/* static */SlangResult D3DUtil::createFactory(DeviceCheckFlags flags, ComPtr& outFactory) -{ - auto dxgiModule = getDxgiModule(); - if (!dxgiModule) - { - return SLANG_FAIL; - } - - typedef HRESULT(WINAPI *PFN_DXGI_CREATE_FACTORY)(REFIID riid, void **ppFactory); - typedef HRESULT(WINAPI *PFN_DXGI_CREATE_FACTORY_2)(UINT Flags, REFIID riid, _COM_Outptr_ void **ppFactory); - - { - auto createFactory2 = (PFN_DXGI_CREATE_FACTORY_2)::GetProcAddress(dxgiModule, "CreateDXGIFactory2"); - if (createFactory2) - { - UINT dxgiFlags = 0; - - if (flags & DeviceCheckFlag::UseDebug) - { - dxgiFlags |= DXGI_CREATE_FACTORY_DEBUG; - } - - ComPtr factory; - SLANG_RETURN_ON_FAIL(createFactory2(dxgiFlags, IID_PPV_ARGS(factory.writeRef()))); - - outFactory = factory; - return SLANG_OK; - } - } - - { - auto createFactory = (PFN_DXGI_CREATE_FACTORY)::GetProcAddress(dxgiModule, "CreateDXGIFactory"); - if (!createFactory) - { - fprintf(stderr, "error: failed load symbol '%s'\n", "CreateDXGIFactory"); - return SLANG_FAIL; - } - return createFactory(IID_PPV_ARGS(outFactory.writeRef())); - } -} - -/* static */SlangResult D3DUtil::findAdapters(DeviceCheckFlags flags, const Slang::UnownedStringSlice& adapaterName, List>& outDxgiAdapters) -{ - ComPtr factory; - SLANG_RETURN_ON_FAIL(createFactory(flags, factory)); - return findAdapters(flags, adapaterName, factory, outDxgiAdapters); -} - -static bool _isMatch(IDXGIAdapter* adapter, const Slang::UnownedStringSlice& lowerAdapaterName) -{ - if (lowerAdapaterName.size() == 0) - { - return true; - } - - DXGI_ADAPTER_DESC desc; - adapter->GetDesc(&desc); - - String descName = String::fromWString(desc.Description).toLower(); - - return descName.indexOf(lowerAdapaterName) != Index(-1); -} - -/* static */bool D3DUtil::isWarp(IDXGIFactory* dxgiFactory, IDXGIAdapter* adapterIn) -{ - ComPtr dxgiFactory4; - if (SLANG_SUCCEEDED(dxgiFactory->QueryInterface(IID_PPV_ARGS(dxgiFactory4.writeRef())))) - { - ComPtr warpAdapter; - dxgiFactory4->EnumWarpAdapter(IID_PPV_ARGS(warpAdapter.writeRef())); - - return adapterIn == warpAdapter; - } - - return false; -} - -/* static */SlangResult D3DUtil::findAdapters(DeviceCheckFlags flags, const UnownedStringSlice& adapterName, IDXGIFactory* dxgiFactory, List>& outDxgiAdapters) -{ - String lowerAdapterName = String(adapterName).toLower(); - - outDxgiAdapters.clear(); - - ComPtr warpAdapter; - if ((flags & DeviceCheckFlag::UseHardwareDevice) == 0) - { - ComPtr dxgiFactory4; - if (SLANG_SUCCEEDED(dxgiFactory->QueryInterface(IID_PPV_ARGS(dxgiFactory4.writeRef())))) - { - dxgiFactory4->EnumWarpAdapter(IID_PPV_ARGS(warpAdapter.writeRef())); - if (_isMatch(warpAdapter, lowerAdapterName.getUnownedSlice())) - { - outDxgiAdapters.add(warpAdapter); - } - } - } - - for (UINT adapterIndex = 0; true; adapterIndex++) - { - ComPtr dxgiAdapter; - if (dxgiFactory->EnumAdapters(adapterIndex, dxgiAdapter.writeRef()) == DXGI_ERROR_NOT_FOUND) - break; - - // Skip if warp (as we will have already added it) - if (dxgiAdapter == warpAdapter) - { - continue; - } - if (!_isMatch(dxgiAdapter, lowerAdapterName.getUnownedSlice())) - { - continue; - } - - // Get if it's software - UINT deviceFlags = 0; - ComPtr dxgiAdapter1; - if (SLANG_SUCCEEDED(dxgiAdapter->QueryInterface(IID_PPV_ARGS(dxgiAdapter1.writeRef())))) - { - DXGI_ADAPTER_DESC1 desc; - dxgiAdapter1->GetDesc1(&desc); - deviceFlags = desc.Flags; - } - - // If the right type then add it - if ((deviceFlags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0 && (flags & DeviceCheckFlag::UseHardwareDevice) != 0) - { - outDxgiAdapters.add(dxgiAdapter); - } - } - - return SLANG_OK; -} - -} // renderer_test diff --git a/tools/gfx/d3d-util.h b/tools/gfx/d3d-util.h deleted file mode 100644 index 6bcee054c..000000000 --- a/tools/gfx/d3d-util.h +++ /dev/null @@ -1,79 +0,0 @@ -// d3d-util.h -#pragma once - -#include - -#include "../../slang-com-helper.h" - -#include "../../slang-com-ptr.h" -#include "../../source/core/slang-list.h" - -#include "flag-combiner.h" - -#include "render.h" - -#include -#include -#include - -namespace gfx { - -class D3DUtil -{ - public: - enum UsageType - { - USAGE_UNKNOWN, ///< Generally used to mark an error - USAGE_TARGET, ///< Format should be used when written as target - USAGE_DEPTH_STENCIL, ///< Format should be used when written as depth stencil - USAGE_SRV, ///< Format if being read as srv - USAGE_COUNT_OF, - }; - enum UsageFlag - { - USAGE_FLAG_MULTI_SAMPLE = 0x1, ///< If set will be used form multi sampling (such as MSAA) - USAGE_FLAG_SRV = 0x2, ///< If set means will be used as a shader resource view (SRV) - }; - - /// Get primitive topology as D3D primitive topology - static D3D_PRIMITIVE_TOPOLOGY getPrimitiveTopology(PrimitiveTopology prim); - - /// Calculate size taking into account alignment. Alignment must be a power of 2 - static UInt calcAligned(UInt size, UInt alignment) { return (size + alignment - 1) & ~(alignment - 1); } - - /// Compile HLSL code to DXBC - static Slang::Result compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, Slang::ComPtr& shaderBlobOut); - - /// Given a slang pixel format returns the equivalent DXGI_ pixel format. If the format is not known, will return DXGI_FORMAT_UNKNOWN - static DXGI_FORMAT getMapFormat(Format format); - - /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible - static DXGI_FORMAT calcFormat(UsageType usage, DXGI_FORMAT format); - /// Calculate appropriate format for creating a buffer for usage and flags - static DXGI_FORMAT calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format); - /// True if the type is 'typeless' - static bool isTypeless(DXGI_FORMAT format); - - /// Returns number of bits used for color channel for format (for channels with multiple sizes, returns smallest ie RGB565 -> 5) - static Int getNumColorChannelBits(DXGI_FORMAT fmt); - - /// Append text in in, into wide char array - static void appendWideChars(const char* in, Slang::List& out); - - - static SlangResult createFactory(DeviceCheckFlags flags, Slang::ComPtr& outFactory); - - /// Get the dxgiModule - static HMODULE getDxgiModule(); - - /// Find adapters - static SlangResult findAdapters(DeviceCheckFlags flags, const Slang::UnownedStringSlice& adapaterName, IDXGIFactory* dxgiFactory, Slang::List>& dxgiAdapters); - /// Find adapters - static SlangResult findAdapters(DeviceCheckFlags flags, const Slang::UnownedStringSlice& adapaterName, Slang::List>& dxgiAdapters); - - /// True if the adapter is warp - static bool isWarp(IDXGIFactory* dxgiFactory, IDXGIAdapter* adapter); - -}; - -} // renderer_test diff --git a/tools/gfx/d3d/d3d-util.cpp b/tools/gfx/d3d/d3d-util.cpp new file mode 100644 index 000000000..3f25ff46c --- /dev/null +++ b/tools/gfx/d3d/d3d-util.cpp @@ -0,0 +1,454 @@ +// d3d-util.cpp +#include "d3d-util.h" + +#include + +#include + +// We will use the C standard library just for printing error messages. +#include + +namespace gfx { +using namespace Slang; + +/* static */D3D_PRIMITIVE_TOPOLOGY D3DUtil::getPrimitiveTopology(PrimitiveTopology topology) +{ + switch (topology) + { + case PrimitiveTopology::TriangleList: + { + return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + } + default: break; + } + return D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; +} + +/* static */DXGI_FORMAT D3DUtil::getMapFormat(Format format) +{ + switch (format) + { + case Format::RGBA_Float32: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case Format::RGB_Float32: return DXGI_FORMAT_R32G32B32_FLOAT; + case Format::RG_Float32: return DXGI_FORMAT_R32G32_FLOAT; + case Format::R_Float32: return DXGI_FORMAT_R32_FLOAT; + case Format::RGBA_Unorm_UInt8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case Format::R_UInt16: return DXGI_FORMAT_R16_UINT; + case Format::R_UInt32: return DXGI_FORMAT_R32_UINT; + + case Format::D_Float32: return DXGI_FORMAT_D32_FLOAT; + case Format::D_Unorm24_S8: return DXGI_FORMAT_D24_UNORM_S8_UINT; + + default: return DXGI_FORMAT_UNKNOWN; + } +} + +/* static */DXGI_FORMAT D3DUtil::calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format) +{ + SLANG_UNUSED(usage); + if (usageFlags) + { + switch (format) + { + case DXGI_FORMAT_R32_FLOAT: /* fallthru */ + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_D32_FLOAT: + { + return DXGI_FORMAT_R32_TYPELESS; + } + case DXGI_FORMAT_D24_UNORM_S8_UINT: return DXGI_FORMAT_R24G8_TYPELESS; + default: break; + } + return format; + } + return format; +} + +/* static */DXGI_FORMAT D3DUtil::calcFormat(UsageType usage, DXGI_FORMAT format) +{ + switch (usage) + { + case USAGE_COUNT_OF: + case USAGE_UNKNOWN: + { + return DXGI_FORMAT_UNKNOWN; + } + case USAGE_DEPTH_STENCIL: + { + switch (format) + { + case DXGI_FORMAT_D32_FLOAT: /* fallthru */ + case DXGI_FORMAT_R32_TYPELESS: + { + return DXGI_FORMAT_D32_FLOAT; + } + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; + case DXGI_FORMAT_R24G8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; + default: break; + } + return format; + } + case USAGE_TARGET: + { + switch (format) + { + case DXGI_FORMAT_D32_FLOAT: /* fallthru */ + case DXGI_FORMAT_D24_UNORM_S8_UINT: + { + return DXGI_FORMAT_UNKNOWN; + } + case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT; + default: break; + } + return format; + } + case USAGE_SRV: + { + switch (format) + { + case DXGI_FORMAT_D32_FLOAT: /* fallthru */ + case DXGI_FORMAT_R32_TYPELESS: + { + return DXGI_FORMAT_R32_FLOAT; + } + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + default: break; + } + + return format; + } + } + + assert(!"Not reachable"); + return DXGI_FORMAT_UNKNOWN; +} + +bool D3DUtil::isTypeless(DXGI_FORMAT format) +{ + switch (format) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC7_TYPELESS: + { + return true; + } + default: break; + } + return false; +} + +/* static */Int D3DUtil::getNumColorChannelBits(DXGI_FORMAT fmt) +{ + switch (fmt) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + { + return 32; + } + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + { + return 16; + } + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + { + return 10; + } + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + { + return 8; + } + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + { + return 5; + } + case DXGI_FORMAT_B4G4R4A4_UNORM: + return 4; + + default: + return 0; + } +} + +// Note: this subroutine is now only used by D3D11 for generating bytecode to go into input layouts. +// +// TODO: we can probably remove that code completely by switching to a PSO-like model across all APIs. +// +/* static */Result D3DUtil::compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, ComPtr& shaderBlobOut) +{ + // Rather than statically link against the `d3dcompile` library, we + // dynamically load it. + // + // Note: A more realistic application would compile from HLSL text to D3D + // shader bytecode as part of an offline process, rather than doing it + // on-the-fly like this + // + static pD3DCompile compileFunc = nullptr; + if (!compileFunc) + { + // TODO(tfoley): maybe want to search for one of a few versions of the DLL + HMODULE compilerModule = LoadLibraryA("d3dcompiler_47.dll"); + if (!compilerModule) + { + fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n"); + return SLANG_FAIL; + } + + compileFunc = (pD3DCompile)GetProcAddress(compilerModule, "D3DCompile"); + if (!compileFunc) + { + fprintf(stderr, "error: failed load symbol 'D3DCompile'\n"); + return SLANG_FAIL; + } + } + + // For this example, we turn on debug output, and turn off all + // optimization. A real application would only use these flags + // when shader debugging is needed. + UINT flags = 0; + flags |= D3DCOMPILE_DEBUG; + flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION; + + // We will always define `__HLSL__` when compiling here, so that + // input code can react differently to being compiled as pure HLSL. + D3D_SHADER_MACRO defines[] = { + { "__HLSL__", "1" }, + { nullptr, nullptr }, + }; + + // The `D3DCompile` entry point takes a bunch of parameters, but we + // don't really need most of them for Slang-generated code. + ComPtr shaderBlob; + ComPtr errorBlob; + + HRESULT hr = compileFunc(source, strlen(source), sourcePath, &defines[0], nullptr, entryPointName, dxProfileName, flags, 0, + shaderBlob.writeRef(), errorBlob.writeRef()); + + // If the HLSL-to-bytecode compilation produced any diagnostic messages + // then we will print them out (whether or not the compilation failed). + if (errorBlob) + { + ::fputs((const char*)errorBlob->GetBufferPointer(), stderr); + ::fflush(stderr); + ::OutputDebugStringA((const char*)errorBlob->GetBufferPointer()); + } + + SLANG_RETURN_ON_FAIL(hr); + shaderBlobOut.swap(shaderBlob); + return SLANG_OK; +} + +/* static */void D3DUtil::appendWideChars(const char* in, List& out) +{ + size_t len = ::strlen(in); + + const DWORD dwFlags = 0; + int outSize = ::MultiByteToWideChar(CP_UTF8, dwFlags, in, int(len), nullptr, 0); + + if (outSize > 0) + { + const Index prevSize = out.getCount(); + out.setCount(prevSize + len + 1); + + WCHAR* dst = out.getBuffer() + prevSize; + ::MultiByteToWideChar(CP_UTF8, dwFlags, in, int(len), dst, outSize); + // Make null terminated + dst[outSize] = 0; + // Remove terminating 0 from array + out.unsafeShrinkToCount(prevSize + outSize); + } +} + +/* static */HMODULE D3DUtil::getDxgiModule() +{ + static HMODULE s_dxgiModule = LoadLibraryA("dxgi.dll"); + if (!s_dxgiModule) + { + fprintf(stderr, "error: failed load 'dxgi.dll'\n"); + return nullptr; + } + + return s_dxgiModule; +} + +/* static */SlangResult D3DUtil::createFactory(DeviceCheckFlags flags, ComPtr& outFactory) +{ + auto dxgiModule = getDxgiModule(); + if (!dxgiModule) + { + return SLANG_FAIL; + } + + typedef HRESULT(WINAPI *PFN_DXGI_CREATE_FACTORY)(REFIID riid, void **ppFactory); + typedef HRESULT(WINAPI *PFN_DXGI_CREATE_FACTORY_2)(UINT Flags, REFIID riid, _COM_Outptr_ void **ppFactory); + + { + auto createFactory2 = (PFN_DXGI_CREATE_FACTORY_2)::GetProcAddress(dxgiModule, "CreateDXGIFactory2"); + if (createFactory2) + { + UINT dxgiFlags = 0; + + if (flags & DeviceCheckFlag::UseDebug) + { + dxgiFlags |= DXGI_CREATE_FACTORY_DEBUG; + } + + ComPtr factory; + SLANG_RETURN_ON_FAIL(createFactory2(dxgiFlags, IID_PPV_ARGS(factory.writeRef()))); + + outFactory = factory; + return SLANG_OK; + } + } + + { + auto createFactory = (PFN_DXGI_CREATE_FACTORY)::GetProcAddress(dxgiModule, "CreateDXGIFactory"); + if (!createFactory) + { + fprintf(stderr, "error: failed load symbol '%s'\n", "CreateDXGIFactory"); + return SLANG_FAIL; + } + return createFactory(IID_PPV_ARGS(outFactory.writeRef())); + } +} + +/* static */SlangResult D3DUtil::findAdapters(DeviceCheckFlags flags, const Slang::UnownedStringSlice& adapaterName, List>& outDxgiAdapters) +{ + ComPtr factory; + SLANG_RETURN_ON_FAIL(createFactory(flags, factory)); + return findAdapters(flags, adapaterName, factory, outDxgiAdapters); +} + +static bool _isMatch(IDXGIAdapter* adapter, const Slang::UnownedStringSlice& lowerAdapaterName) +{ + if (lowerAdapaterName.size() == 0) + { + return true; + } + + DXGI_ADAPTER_DESC desc; + adapter->GetDesc(&desc); + + String descName = String::fromWString(desc.Description).toLower(); + + return descName.indexOf(lowerAdapaterName) != Index(-1); +} + +/* static */bool D3DUtil::isWarp(IDXGIFactory* dxgiFactory, IDXGIAdapter* adapterIn) +{ + ComPtr dxgiFactory4; + if (SLANG_SUCCEEDED(dxgiFactory->QueryInterface(IID_PPV_ARGS(dxgiFactory4.writeRef())))) + { + ComPtr warpAdapter; + dxgiFactory4->EnumWarpAdapter(IID_PPV_ARGS(warpAdapter.writeRef())); + + return adapterIn == warpAdapter; + } + + return false; +} + +/* static */SlangResult D3DUtil::findAdapters(DeviceCheckFlags flags, const UnownedStringSlice& adapterName, IDXGIFactory* dxgiFactory, List>& outDxgiAdapters) +{ + String lowerAdapterName = String(adapterName).toLower(); + + outDxgiAdapters.clear(); + + ComPtr warpAdapter; + if ((flags & DeviceCheckFlag::UseHardwareDevice) == 0) + { + ComPtr dxgiFactory4; + if (SLANG_SUCCEEDED(dxgiFactory->QueryInterface(IID_PPV_ARGS(dxgiFactory4.writeRef())))) + { + dxgiFactory4->EnumWarpAdapter(IID_PPV_ARGS(warpAdapter.writeRef())); + if (_isMatch(warpAdapter, lowerAdapterName.getUnownedSlice())) + { + outDxgiAdapters.add(warpAdapter); + } + } + } + + for (UINT adapterIndex = 0; true; adapterIndex++) + { + ComPtr dxgiAdapter; + if (dxgiFactory->EnumAdapters(adapterIndex, dxgiAdapter.writeRef()) == DXGI_ERROR_NOT_FOUND) + break; + + // Skip if warp (as we will have already added it) + if (dxgiAdapter == warpAdapter) + { + continue; + } + if (!_isMatch(dxgiAdapter, lowerAdapterName.getUnownedSlice())) + { + continue; + } + + // Get if it's software + UINT deviceFlags = 0; + ComPtr dxgiAdapter1; + if (SLANG_SUCCEEDED(dxgiAdapter->QueryInterface(IID_PPV_ARGS(dxgiAdapter1.writeRef())))) + { + DXGI_ADAPTER_DESC1 desc; + dxgiAdapter1->GetDesc1(&desc); + deviceFlags = desc.Flags; + } + + // If the right type then add it + if ((deviceFlags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0 && (flags & DeviceCheckFlag::UseHardwareDevice) != 0) + { + outDxgiAdapters.add(dxgiAdapter); + } + } + + return SLANG_OK; +} + +} // renderer_test diff --git a/tools/gfx/d3d/d3d-util.h b/tools/gfx/d3d/d3d-util.h new file mode 100644 index 000000000..fd7a31d3a --- /dev/null +++ b/tools/gfx/d3d/d3d-util.h @@ -0,0 +1,79 @@ +// d3d-util.h +#pragma once + +#include + +#include "../../slang-com-helper.h" + +#include "../../slang-com-ptr.h" +#include "../../source/core/slang-list.h" + +#include "../flag-combiner.h" + +#include "../render.h" + +#include +#include +#include + +namespace gfx { + +class D3DUtil +{ + public: + enum UsageType + { + USAGE_UNKNOWN, ///< Generally used to mark an error + USAGE_TARGET, ///< Format should be used when written as target + USAGE_DEPTH_STENCIL, ///< Format should be used when written as depth stencil + USAGE_SRV, ///< Format if being read as srv + USAGE_COUNT_OF, + }; + enum UsageFlag + { + USAGE_FLAG_MULTI_SAMPLE = 0x1, ///< If set will be used form multi sampling (such as MSAA) + USAGE_FLAG_SRV = 0x2, ///< If set means will be used as a shader resource view (SRV) + }; + + /// Get primitive topology as D3D primitive topology + static D3D_PRIMITIVE_TOPOLOGY getPrimitiveTopology(PrimitiveTopology prim); + + /// Calculate size taking into account alignment. Alignment must be a power of 2 + static UInt calcAligned(UInt size, UInt alignment) { return (size + alignment - 1) & ~(alignment - 1); } + + /// Compile HLSL code to DXBC + static Slang::Result compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, Slang::ComPtr& shaderBlobOut); + + /// Given a slang pixel format returns the equivalent DXGI_ pixel format. If the format is not known, will return DXGI_FORMAT_UNKNOWN + static DXGI_FORMAT getMapFormat(Format format); + + /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible + static DXGI_FORMAT calcFormat(UsageType usage, DXGI_FORMAT format); + /// Calculate appropriate format for creating a buffer for usage and flags + static DXGI_FORMAT calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format); + /// True if the type is 'typeless' + static bool isTypeless(DXGI_FORMAT format); + + /// Returns number of bits used for color channel for format (for channels with multiple sizes, returns smallest ie RGB565 -> 5) + static Int getNumColorChannelBits(DXGI_FORMAT fmt); + + /// Append text in in, into wide char array + static void appendWideChars(const char* in, Slang::List& out); + + + static SlangResult createFactory(DeviceCheckFlags flags, Slang::ComPtr& outFactory); + + /// Get the dxgiModule + static HMODULE getDxgiModule(); + + /// Find adapters + static SlangResult findAdapters(DeviceCheckFlags flags, const Slang::UnownedStringSlice& adapaterName, IDXGIFactory* dxgiFactory, Slang::List>& dxgiAdapters); + /// Find adapters + static SlangResult findAdapters(DeviceCheckFlags flags, const Slang::UnownedStringSlice& adapaterName, Slang::List>& dxgiAdapters); + + /// True if the adapter is warp + static bool isWarp(IDXGIFactory* dxgiFactory, IDXGIAdapter* adapter); + +}; + +} // renderer_test diff --git a/tools/gfx/d3d11/render-d3d11.cpp b/tools/gfx/d3d11/render-d3d11.cpp new file mode 100644 index 000000000..cf2ae75e2 --- /dev/null +++ b/tools/gfx/d3d11/render-d3d11.cpp @@ -0,0 +1,2371 @@ +// render-d3d11.cpp + +#define _CRT_SECURE_NO_WARNINGS + +#include "render-d3d11.h" + +//WORKING: #include "options.h" +#include "../render.h" +#include "../d3d/d3d-util.h" + +#include "../surface.h" + +// In order to use the Slang API, we need to include its header + +//#include + +#include "../../slang-com-ptr.h" +#include "../flag-combiner.h" + +// We will be rendering with Direct3D 11, so we need to include +// the Windows and D3D11 headers + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#include +#include + +// We will use the C standard library just for printing error messages. +#include + +#ifdef _MSC_VER +#include +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif +// +using namespace Slang; + +namespace gfx { + +class D3D11Renderer : public Renderer +{ +public: + enum + { + kMaxUAVs = 64, + kMaxRTVs = 8, + }; + + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual const List& getFeatures() override { return m_features; } + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + TextureResource::Desc getSwapChainTextureDesc() override; + + Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; + Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; + Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; + + Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; + Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; + + Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; + + Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; + Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; + Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; + + Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; + Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; + Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; + + virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; + virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + void setViewports(UInt count, Viewport const* viewports) override; + void setScissorRects(UInt count, ScissorRect const* rects) override; + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override {} + virtual void waitForGpu() override {} + virtual RendererType getRendererType() const override { return RendererType::DirectX11; } + + ~D3D11Renderer() {} + + protected: + +#if 0 + struct BindingDetail + { + ComPtr m_srv; + ComPtr m_uav; + ComPtr m_samplerState; + }; + + class BindingStateImpl: public BindingState + { + public: + typedef BindingState Parent; + + /// Ctor + BindingStateImpl(const Desc& desc): + Parent(desc) + {} + + List m_bindingDetails; + }; +#endif + + enum class D3D11DescriptorSlotType + { + ConstantBuffer, + ShaderResourceView, + UnorderedAccessView, + Sampler, + + CombinedTextureSampler, + + CountOf, + }; + + class DescriptorSetLayoutImpl : public DescriptorSetLayout + { + public: + struct RangeInfo + { + D3D11DescriptorSlotType type; + UInt arrayIndex; + UInt pairedSamplerArrayIndex; + }; + List m_ranges; + + UInt m_counts[int(D3D11DescriptorSlotType::CountOf)]; + }; + + class PipelineLayoutImpl : public PipelineLayout + { + public: + struct DescriptorSetInfo + { + RefPtr layout; + UInt baseIndices[int(D3D11DescriptorSlotType::CountOf)]; + }; + + List m_descriptorSets; + UINT m_uavCount; + }; + + class DescriptorSetImpl : public DescriptorSet + { + public: + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; + virtual void setResource(UInt range, UInt index, ResourceView* view) override; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) override; + + RefPtr m_layout; + + List> m_cbs; + List> m_srvs; + List> m_uavs; + List> m_samplers; + }; + + class ShaderProgramImpl: public ShaderProgram + { + public: + ComPtr m_vertexShader; + ComPtr m_pixelShader; + ComPtr m_computeShader; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + BufferResourceImpl(const Desc& desc, Usage initialUsage): + Parent(desc), + m_initialUsage(initialUsage) + { + } + + MapFlavor m_mapFlavor; + Usage m_initialUsage; + ComPtr m_buffer; + ComPtr m_staging; + }; + class TextureResourceImpl : public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(const Desc& desc, Usage initialUsage) : + Parent(desc), + m_initialUsage(initialUsage) + { + } + Usage m_initialUsage; + ComPtr m_resource; + + }; + + class SamplerStateImpl : public SamplerState + { + public: + ComPtr m_sampler; + }; + + + class ResourceViewImpl : public ResourceView + { + public: + enum class Type + { + SRV, + UAV, + DSV, + RTV, + }; + Type m_type; + }; + + class ShaderResourceViewImpl : public ResourceViewImpl + { + public: + ComPtr m_srv; + }; + + class UnorderedAccessViewImpl : public ResourceViewImpl + { + public: + ComPtr m_uav; + }; + + class DepthStencilViewImpl : public ResourceViewImpl + { + public: + ComPtr m_dsv; + }; + + class RenderTargetViewImpl : public ResourceViewImpl + { + public: + ComPtr m_rtv; + }; + + class InputLayoutImpl: public InputLayout + { + public: + ComPtr m_layout; + }; + + class PipelineStateImpl : public PipelineState + { + public: + RefPtr m_program; + RefPtr m_pipelineLayout; + }; + + + class GraphicsPipelineStateImpl : public PipelineStateImpl + { + public: + UINT m_rtvCount; + + RefPtr m_inputLayout; + ComPtr m_depthStencilState; + ComPtr m_rasterizerState; + ComPtr m_blendState; + + UINT m_stencilRef; + float m_blendColor[4]; + UINT m_sampleMask; + }; + + class ComputePipelineStateImpl : public PipelineStateImpl + { + public: + }; + + /// Capture a texture to a file + static HRESULT captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut); + + void _flushGraphicsState(); + void _flushComputeState(); + + ComPtr m_swapChain; + ComPtr m_device; + ComPtr m_immediateContext; + ComPtr m_backBufferTexture; + + RefPtr m_primaryRenderTargetTexture; + RefPtr m_primaryRenderTargetView; + +// List > m_renderTargetViews; +// List > m_renderTargetTextures; + + bool m_renderTargetBindingsDirty = false; + + RefPtr m_currentGraphicsState; + RefPtr m_currentComputeState; + + ComPtr m_rtvBindings[kMaxRTVs]; + ComPtr m_dsvBinding; + ComPtr m_uavBindings[int(PipelineType::CountOf)][kMaxUAVs]; + bool m_targetBindingsDirty[int(PipelineType::CountOf)]; + + Desc m_desc; + + float m_clearColor[4] = { 0, 0, 0, 0 }; + + List m_features; +}; + +Renderer* createD3D11Renderer() +{ + return new D3D11Renderer(); +} + +/* static */HRESULT D3D11Renderer::captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut) +{ + if (!context) return E_INVALIDARG; + if (!texture) return E_INVALIDARG; + + D3D11_TEXTURE2D_DESC textureDesc; + texture->GetDesc(&textureDesc); + + // Don't bother supporting MSAA for right now + if (textureDesc.SampleDesc.Count > 1) + { + fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + ComPtr stagingTexture; + + if (textureDesc.Usage == D3D11_USAGE_STAGING && (textureDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) + { + stagingTexture = texture; + } + else + { + // Modify the descriptor to give us a staging texture + textureDesc.BindFlags = 0; + textureDesc.MiscFlags &= ~D3D11_RESOURCE_MISC_TEXTURECUBE; + textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + textureDesc.Usage = D3D11_USAGE_STAGING; + + hr = device->CreateTexture2D(&textureDesc, 0, stagingTexture.writeRef()); + if (FAILED(hr)) + { + fprintf(stderr, "ERROR: failed to create staging texture\n"); + return hr; + } + + context->CopyResource(stagingTexture, texture); + } + + // Now just read back texels from the staging textures + { + D3D11_MAPPED_SUBRESOURCE mappedResource; + SLANG_RETURN_ON_FAIL(context->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource)); + + Result res = surfaceOut.set(textureDesc.Width, textureDesc.Height, Format::RGBA_Unorm_UInt8, mappedResource.RowPitch, mappedResource.pData, SurfaceAllocator::getMallocAllocator()); + + // Make sure to unmap + context->Unmap(stagingTexture, 0); + return res; + } +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult D3D11Renderer::initialize(const Desc& desc, void* inWindowHandle) +{ + auto windowHandle = (HWND)inWindowHandle; + m_desc = desc; + + // Rather than statically link against D3D, we load it dynamically. + HMODULE d3dModule = LoadLibraryA("d3d11.dll"); + if (!d3dModule) + { + fprintf(stderr, "error: failed load 'd3d11.dll'\n"); + return SLANG_FAIL; + } + + PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN D3D11CreateDeviceAndSwapChain_ = + (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress(d3dModule, "D3D11CreateDeviceAndSwapChain"); + if (!D3D11CreateDeviceAndSwapChain_) + { + fprintf(stderr, + "error: failed load symbol 'D3D11CreateDeviceAndSwapChain'\n"); + return SLANG_FAIL; + } + + // Our swap chain uses RGBA8 with sRGB, with double buffering. + DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + + // Note(tfoley): Disabling sRGB for DX back buffer for now, so that we + // can get consistent output with OpenGL, where setting up sRGB will + // probably be more involved. + // swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferCount = 2; + swapChainDesc.OutputWindow = windowHandle; + swapChainDesc.Windowed = TRUE; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapChainDesc.Flags = 0; + + // We will ask for the highest feature level that can be supported. + const D3D_FEATURE_LEVEL featureLevels[] = { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1, + }; + D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_9_1; + const int totalNumFeatureLevels = SLANG_COUNT_OF(featureLevels); + + { + // On a machine that does not have an up-to-date version of D3D installed, + // the `D3D11CreateDeviceAndSwapChain` call will fail with `E_INVALIDARG` + // if you ask for feature level 11_1 (DeviceCheckFlag::UseFullFeatureLevel). + // The workaround is to call `D3D11CreateDeviceAndSwapChain` the first time + // with 11_1 and then back off to 11_0 if that fails. + + FlagCombiner combiner; + // TODO: we should probably provide a command-line option + // to override UseDebug of default rather than leave it + // up to each back-end to specify. + +#if _DEBUG + combiner.add(DeviceCheckFlag::UseDebug, ChangeType::OnOff); ///< First try debug then non debug +#else + combiner.add(DeviceCheckFlag::UseDebug, ChangeType::Off); ///< Don't bother with debug +#endif + combiner.add(DeviceCheckFlag::UseHardwareDevice, ChangeType::OnOff); ///< First try hardware, then reference + combiner.add(DeviceCheckFlag::UseFullFeatureLevel, ChangeType::OnOff); ///< First try fully featured, then degrade features + + const int numCombinations = combiner.getNumCombinations(); + Result res = SLANG_FAIL; + for (int i = 0; i < numCombinations; ++i) + { + const auto deviceCheckFlags = combiner.getCombination(i); + + // If we have an adapter set on the desc, look it up. We only need to do so for hardware + ComPtr adapter; + if (desc.adapter.getLength() && (deviceCheckFlags & DeviceCheckFlag::UseHardwareDevice)) + { + List> dxgiAdapters; + D3DUtil::findAdapters(deviceCheckFlags, desc.adapter.getUnownedSlice(), dxgiAdapters); + if (dxgiAdapters.getCount() == 0) + { + continue; + } + adapter = dxgiAdapters[0]; + } + + // The adapter can be nullptr - that just means 'default', but when so we need to select the driver type + D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_UNKNOWN; + if (adapter == nullptr) + { + // If we don't have an adapter, select directly + driverType = (deviceCheckFlags & DeviceCheckFlag::UseHardwareDevice) ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_REFERENCE; + } + + const int startFeatureIndex = (deviceCheckFlags & DeviceCheckFlag::UseFullFeatureLevel) ? 0 : 1; + const UINT deviceFlags = (deviceCheckFlags & DeviceCheckFlag::UseDebug) ? D3D11_CREATE_DEVICE_DEBUG : 0; + + res = D3D11CreateDeviceAndSwapChain_( + adapter, + driverType, + nullptr, // software + deviceFlags, + &featureLevels[startFeatureIndex], + totalNumFeatureLevels - startFeatureIndex, + D3D11_SDK_VERSION, + &swapChainDesc, + m_swapChain.writeRef(), + m_device.writeRef(), + &featureLevel, + m_immediateContext.writeRef()); + + // Check if successfully constructed - if so we are done. + if (SLANG_SUCCEEDED(res)) + { + break; + } + } + // If res is failure, means all styles have have failed, and so initialization fails. + if (SLANG_FAILED(res)) + { + return res; + } + // Check we have a swap chain, context and device + SLANG_ASSERT(m_immediateContext && m_swapChain && m_device); + } + + // TODO: Add support for debugging to help detect leaks: + // + // ComPtr gDebug; + // m_device->QueryInterface(IID_PPV_ARGS(gDebug.writeRef())); + // + + // After we've created the swap chain, we can request a pointer to the + // back buffer as a D3D11 texture, and create a render-target view from it. + + static const IID kIID_ID3D11Texture2D = { + 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, + 0x95, 0x35, 0xd3, 0x4f, 0x9c }; + + SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, kIID_ID3D11Texture2D, (void**)m_backBufferTexture.writeRef())); + +// for (int i = 0; i < 8; i++) + { + ComPtr texture; + D3D11_TEXTURE2D_DESC textureDesc; + m_backBufferTexture->GetDesc(&textureDesc); + SLANG_RETURN_ON_FAIL(m_device->CreateTexture2D(&textureDesc, nullptr, texture.writeRef())); + + ComPtr rtv; + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + rtvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + rtvDesc.Texture2D.MipSlice = 0; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + SLANG_RETURN_ON_FAIL(m_device->CreateRenderTargetView(texture, &rtvDesc, rtv.writeRef())); + + TextureResource::Desc resourceDesc; + resourceDesc.init2D(Resource::Type::Texture2D, Format::RGBA_Unorm_UInt8, textureDesc.Width, textureDesc.Height, 1); + + RefPtr primaryRenderTargetTexture; + SLANG_RETURN_ON_FAIL(createTextureResource(Resource::Usage::RenderTarget, resourceDesc, nullptr, primaryRenderTargetTexture.writeRef())); + + ResourceView::Desc viewDesc; + viewDesc.format = resourceDesc.format; + viewDesc.type = ResourceView::Type::RenderTarget; + RefPtr primaryRenderTargetView; + SLANG_RETURN_ON_FAIL(createTextureView(primaryRenderTargetTexture, viewDesc, primaryRenderTargetView.writeRef())); + + m_primaryRenderTargetTexture = (TextureResourceImpl*) primaryRenderTargetTexture.Ptr(); + m_primaryRenderTargetView = (RenderTargetViewImpl*) primaryRenderTargetView.Ptr(); + } + +// m_immediateContext->OMSetRenderTargets(1, m_primaryRenderTargetView->m_rtv.readRef(), nullptr); + m_rtvBindings[0] = m_primaryRenderTargetView->m_rtv; + m_targetBindingsDirty[int(PipelineType::Graphics)] = true; + + // Similarly, we are going to set up a viewport once, and then never + // switch, since this is a simple test app. + D3D11_VIEWPORT viewport; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + viewport.Width = (float)desc.width; + viewport.Height = (float)desc.height; + viewport.MaxDepth = 1; // TODO(tfoley): use reversed depth + viewport.MinDepth = 0; + m_immediateContext->RSSetViewports(1, &viewport); + + return SLANG_OK; +} + +void D3D11Renderer::setClearColor(const float color[4]) +{ + memcpy(m_clearColor, color, sizeof(m_clearColor)); +} + +void D3D11Renderer::clearFrame() +{ + m_immediateContext->ClearRenderTargetView(m_primaryRenderTargetView->m_rtv, m_clearColor); + + if(m_dsvBinding) + { + m_immediateContext->ClearDepthStencilView(m_dsvBinding, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); + } +} + +void D3D11Renderer::presentFrame() +{ + m_immediateContext->CopyResource(m_backBufferTexture, m_primaryRenderTargetTexture->m_resource); + m_swapChain->Present(0, 0); +} + +TextureResource::Desc D3D11Renderer::getSwapChainTextureDesc() +{ + D3D11_TEXTURE2D_DESC dxDesc; + ((ID3D11Texture2D*)m_primaryRenderTargetTexture->m_resource.get())->GetDesc(&dxDesc); + + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::Unknown, dxDesc.Width, dxDesc.Height, 1); + + return desc; +} + +SlangResult D3D11Renderer::captureScreenSurface(Surface& surfaceOut) +{ + return captureTextureToSurface(m_device, m_immediateContext, (ID3D11Texture2D*) m_primaryRenderTargetTexture->m_resource.get(), surfaceOut); +} + +static D3D11_BIND_FLAG _calcResourceFlag(Resource::BindFlag::Enum bindFlag) +{ + typedef Resource::BindFlag BindFlag; + switch (bindFlag) + { + case BindFlag::VertexBuffer: return D3D11_BIND_VERTEX_BUFFER; + case BindFlag::IndexBuffer: return D3D11_BIND_INDEX_BUFFER; + case BindFlag::ConstantBuffer: return D3D11_BIND_CONSTANT_BUFFER; + case BindFlag::StreamOutput: return D3D11_BIND_STREAM_OUTPUT; + case BindFlag::RenderTarget: return D3D11_BIND_RENDER_TARGET; + case BindFlag::DepthStencil: return D3D11_BIND_DEPTH_STENCIL; + case BindFlag::UnorderedAccess: return D3D11_BIND_UNORDERED_ACCESS; + case BindFlag::PixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; + case BindFlag::NonPixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; + default: return D3D11_BIND_FLAG(0); + } +} + +static int _calcResourceBindFlags(int bindFlags) +{ + int dstFlags = 0; + while (bindFlags) + { + int lsb = bindFlags & -bindFlags; + + dstFlags |= _calcResourceFlag(Resource::BindFlag::Enum(lsb)); + bindFlags &= ~lsb; + } + return dstFlags; +} + +static int _calcResourceAccessFlags(int accessFlags) +{ + switch (accessFlags) + { + case 0: return 0; + case Resource::AccessFlag::Read: return D3D11_CPU_ACCESS_READ; + case Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_WRITE; + case Resource::AccessFlag::Read | + Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + default: assert(!"Invalid flags"); return 0; + } +} + +Result D3D11Renderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) +{ + TextureResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); + + if(initData) + { + assert(initData->numSubResources == srcDesc.numMipLevels * effectiveArraySize * srcDesc.size.depth); + } + + const DXGI_FORMAT format = D3DUtil::getMapFormat(srcDesc.format); + if (format == DXGI_FORMAT_UNKNOWN) + { + return SLANG_FAIL; + } + + const int bindFlags = _calcResourceBindFlags(srcDesc.bindFlags); + + // Set up the initialize data + List subRes; + D3D11_SUBRESOURCE_DATA* subResourcesPtr = nullptr; + if(initData) + { + subRes.setCount(srcDesc.numMipLevels * effectiveArraySize); + { + int subResourceIndex = 0; + for (int i = 0; i < effectiveArraySize; i++) + { + for (int j = 0; j < srcDesc.numMipLevels; j++) + { + const int mipHeight = TextureResource::calcMipSize(srcDesc.size.height, j); + + D3D11_SUBRESOURCE_DATA& data = subRes[subResourceIndex]; + + data.pSysMem = initData->subResources[subResourceIndex]; + + data.SysMemPitch = UINT(initData->mipRowStrides[j]); + data.SysMemSlicePitch = UINT(initData->mipRowStrides[j] * mipHeight); + + subResourceIndex++; + } + } + } + subResourcesPtr = subRes.getBuffer(); + } + + const int accessFlags = _calcResourceAccessFlags(srcDesc.cpuAccessFlags); + + RefPtr texture(new TextureResourceImpl(srcDesc, initialUsage)); + + switch (srcDesc.type) + { + case Resource::Type::Texture1D: + { + D3D11_TEXTURE1D_DESC desc = { 0 }; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = accessFlags; + desc.Format = format; + desc.MiscFlags = 0; + desc.MipLevels = srcDesc.numMipLevels; + desc.ArraySize = effectiveArraySize; + desc.Width = srcDesc.size.width; + desc.Usage = D3D11_USAGE_DEFAULT; + + ComPtr texture1D; + SLANG_RETURN_ON_FAIL(m_device->CreateTexture1D(&desc, subResourcesPtr, texture1D.writeRef())); + + texture->m_resource = texture1D; + break; + } + case Resource::Type::TextureCube: + case Resource::Type::Texture2D: + { + D3D11_TEXTURE2D_DESC desc = { 0 }; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = accessFlags; + desc.Format = format; + desc.MiscFlags = 0; + desc.MipLevels = srcDesc.numMipLevels; + desc.ArraySize = effectiveArraySize; + + desc.Width = srcDesc.size.width; + desc.Height = srcDesc.size.height; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.SampleDesc.Count = srcDesc.sampleDesc.numSamples; + desc.SampleDesc.Quality = srcDesc.sampleDesc.quality; + + if (srcDesc.type == Resource::Type::TextureCube) + { + desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE; + } + + ComPtr texture2D; + SLANG_RETURN_ON_FAIL(m_device->CreateTexture2D(&desc, subResourcesPtr, texture2D.writeRef())); + + texture->m_resource = texture2D; + break; + } + case Resource::Type::Texture3D: + { + D3D11_TEXTURE3D_DESC desc = { 0 }; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = accessFlags; + desc.Format = format; + desc.MiscFlags = 0; + desc.MipLevels = srcDesc.numMipLevels; + desc.Width = srcDesc.size.width; + desc.Height = srcDesc.size.height; + desc.Depth = srcDesc.size.depth; + desc.Usage = D3D11_USAGE_DEFAULT; + + ComPtr texture3D; + SLANG_RETURN_ON_FAIL(m_device->CreateTexture3D(&desc, subResourcesPtr, texture3D.writeRef())); + + texture->m_resource = texture3D; + break; + } + default: + return SLANG_FAIL; + } + + *outResource = texture.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) +{ + BufferResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + auto d3dBindFlags = _calcResourceBindFlags(srcDesc.bindFlags); + + size_t alignedSizeInBytes = srcDesc.sizeInBytes; + + if(d3dBindFlags & D3D11_BIND_CONSTANT_BUFFER) + { + // Make aligned to 256 bytes... not sure why, but if you remove this the tests do fail. + alignedSizeInBytes = D3DUtil::calcAligned(alignedSizeInBytes, 256); + } + + // Hack to make the initialization never read from out of bounds memory, by copying into a buffer + List initDataBuffer; + if (initData && alignedSizeInBytes > srcDesc.sizeInBytes) + { + initDataBuffer.setCount(alignedSizeInBytes); + ::memcpy(initDataBuffer.getBuffer(), initData, srcDesc.sizeInBytes); + initData = initDataBuffer.getBuffer(); + } + + D3D11_BUFFER_DESC bufferDesc = { 0 }; + bufferDesc.ByteWidth = UINT(alignedSizeInBytes); + bufferDesc.BindFlags = d3dBindFlags; + // For read we'll need to do some staging + bufferDesc.CPUAccessFlags = _calcResourceAccessFlags(descIn.cpuAccessFlags & Resource::AccessFlag::Write); + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + + // If written by CPU, make it dynamic + if (descIn.cpuAccessFlags & Resource::AccessFlag::Write) + { + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + } + + switch (initialUsage) + { + case Resource::Usage::ConstantBuffer: + { + // We'll just assume ConstantBuffers are dynamic for now + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + break; + } + default: break; + } + + if (bufferDesc.BindFlags & (D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE)) + { + //desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; + if (srcDesc.elementSize != 0) + { + bufferDesc.StructureByteStride = srcDesc.elementSize; + bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + } + else + { + bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + } + } + + D3D11_SUBRESOURCE_DATA subResourceData = { 0 }; + subResourceData.pSysMem = initData; + + RefPtr buffer(new BufferResourceImpl(srcDesc, initialUsage)); + + SLANG_RETURN_ON_FAIL(m_device->CreateBuffer(&bufferDesc, initData ? &subResourceData : nullptr, buffer->m_buffer.writeRef())); + + if (srcDesc.cpuAccessFlags & Resource::AccessFlag::Read) + { + D3D11_BUFFER_DESC bufDesc = {}; + bufDesc.BindFlags = 0; + bufDesc.ByteWidth = (UINT)alignedSizeInBytes; + bufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + bufDesc.Usage = D3D11_USAGE_STAGING; + + SLANG_RETURN_ON_FAIL(m_device->CreateBuffer(&bufDesc, nullptr, buffer->m_staging.writeRef())); + } + + *outResource = buffer.detach(); + return SLANG_OK; +} + +D3D11_FILTER_TYPE translateFilterMode(TextureFilteringMode mode) +{ + switch (mode) + { + default: + return D3D11_FILTER_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureFilteringMode::SRC: return D3D11_FILTER_TYPE_##DST + + CASE(Point, POINT); + CASE(Linear, LINEAR); + +#undef CASE + } +} + +D3D11_FILTER_REDUCTION_TYPE translateFilterReduction(TextureReductionOp op) +{ + switch (op) + { + default: + return D3D11_FILTER_REDUCTION_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureReductionOp::SRC: return D3D11_FILTER_REDUCTION_TYPE_##DST + + CASE(Average, STANDARD); + CASE(Comparison, COMPARISON); + CASE(Minimum, MINIMUM); + CASE(Maximum, MAXIMUM); + +#undef CASE + } +} + +D3D11_TEXTURE_ADDRESS_MODE translateAddressingMode(TextureAddressingMode mode) +{ + switch (mode) + { + default: + return D3D11_TEXTURE_ADDRESS_MODE(0); + +#define CASE(SRC, DST) \ + case TextureAddressingMode::SRC: return D3D11_TEXTURE_ADDRESS_##DST + + CASE(Wrap, WRAP); + CASE(ClampToEdge, CLAMP); + CASE(ClampToBorder, BORDER); + CASE(MirrorRepeat, MIRROR); + CASE(MirrorOnce, MIRROR_ONCE); + +#undef CASE + } +} + +static D3D11_COMPARISON_FUNC translateComparisonFunc(ComparisonFunc func) +{ + switch (func) + { + default: + // TODO: need to report failures + return D3D11_COMPARISON_ALWAYS; + +#define CASE(FROM, TO) \ + case ComparisonFunc::FROM: return D3D11_COMPARISON_##TO + + CASE(Never, NEVER); + CASE(Less, LESS); + CASE(Equal, EQUAL); + CASE(LessEqual, LESS_EQUAL); + CASE(Greater, GREATER); + CASE(NotEqual, NOT_EQUAL); + CASE(GreaterEqual, GREATER_EQUAL); + CASE(Always, ALWAYS); +#undef CASE + } +} + +Result D3D11Renderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) +{ + D3D11_FILTER_REDUCTION_TYPE dxReduction = translateFilterReduction(desc.reductionOp); + D3D11_FILTER dxFilter; + if (desc.maxAnisotropy > 1) + { + dxFilter = D3D11_ENCODE_ANISOTROPIC_FILTER(dxReduction); + } + else + { + D3D11_FILTER_TYPE dxMin = translateFilterMode(desc.minFilter); + D3D11_FILTER_TYPE dxMag = translateFilterMode(desc.magFilter); + D3D11_FILTER_TYPE dxMip = translateFilterMode(desc.mipFilter); + + dxFilter = D3D11_ENCODE_BASIC_FILTER(dxMin, dxMag, dxMip, dxReduction); + } + + D3D11_SAMPLER_DESC dxDesc = {}; + dxDesc.Filter = dxFilter; + dxDesc.AddressU = translateAddressingMode(desc.addressU); + dxDesc.AddressV = translateAddressingMode(desc.addressV); + dxDesc.AddressW = translateAddressingMode(desc.addressW); + dxDesc.MipLODBias = desc.mipLODBias; + dxDesc.MaxAnisotropy = desc.maxAnisotropy; + dxDesc.ComparisonFunc = translateComparisonFunc(desc.comparisonFunc); + for (int ii = 0; ii < 4; ++ii) + dxDesc.BorderColor[ii] = desc.borderColor[ii]; + dxDesc.MinLOD = desc.minLOD; + dxDesc.MaxLOD = desc.maxLOD; + + ComPtr sampler; + SLANG_RETURN_ON_FAIL(m_device->CreateSamplerState( + &dxDesc, + sampler.writeRef())); + + RefPtr samplerImpl = new SamplerStateImpl(); + samplerImpl->m_sampler = sampler; + *outSampler = samplerImpl.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (TextureResourceImpl*) texture; + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::RenderTarget: + { + ComPtr rtv; + SLANG_RETURN_ON_FAIL(m_device->CreateRenderTargetView(resourceImpl->m_resource, nullptr, rtv.writeRef())); + + RefPtr viewImpl = new RenderTargetViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::RTV; + viewImpl->m_rtv = rtv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::DepthStencil: + { + ComPtr dsv; + SLANG_RETURN_ON_FAIL(m_device->CreateDepthStencilView(resourceImpl->m_resource, nullptr, dsv.writeRef())); + + RefPtr viewImpl = new DepthStencilViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::DSV; + viewImpl->m_dsv = dsv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::UnorderedAccess: + { + ComPtr uav; + SLANG_RETURN_ON_FAIL(m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, uav.writeRef())); + + RefPtr viewImpl = new UnorderedAccessViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::UAV; + viewImpl->m_uav = uav; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::ShaderResource: + { + ComPtr srv; + SLANG_RETURN_ON_FAIL(m_device->CreateShaderResourceView(resourceImpl->m_resource, nullptr, srv.writeRef())); + + RefPtr viewImpl = new ShaderResourceViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::SRV; + viewImpl->m_srv = srv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + } +} + +Result D3D11Renderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + auto resourceDesc = resourceImpl->getDesc(); + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::UnorderedAccess: + { + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + uavDesc.Format = D3DUtil::getMapFormat(desc.format); + uavDesc.Buffer.FirstElement = 0; + + if(resourceDesc.elementSize) + { + uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / resourceDesc.elementSize); + } + else if(desc.format == Format::Unknown) + { + uavDesc.Buffer.Flags |= D3D11_BUFFER_UAV_FLAG_RAW; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / 4); + } + else + { + uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / RendererUtil::getFormatSize(desc.format)); + } + + ComPtr uav; + SLANG_RETURN_ON_FAIL(m_device->CreateUnorderedAccessView(resourceImpl->m_buffer, &uavDesc, uav.writeRef())); + + RefPtr viewImpl = new UnorderedAccessViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::UAV; + viewImpl->m_uav = uav; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::ShaderResource: + { + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; + srvDesc.Format = D3DUtil::getMapFormat(desc.format); + srvDesc.Buffer.FirstElement = 0; + + if(resourceDesc.elementSize) + { + srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / resourceDesc.elementSize); + } + else if(desc.format == Format::Unknown) + { + // We need to switch to a different member of the `union`, + // so that we can set the `BufferEx.Flags` member. + // + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; + + // Because we've switched, we need to re-set the `FirstElement` + // field to be valid, since we can't count on all compilers + // to respect that `Buffer.FirstElement` and `BufferEx.FirstElement` + // alias in memory. + // + srvDesc.BufferEx.FirstElement = 0; + + srvDesc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW; + srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; + srvDesc.BufferEx.NumElements = UINT(resourceDesc.sizeInBytes / 4); + } + else + { + srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / RendererUtil::getFormatSize(desc.format)); + } + + ComPtr srv; + SLANG_RETURN_ON_FAIL(m_device->CreateShaderResourceView(resourceImpl->m_buffer, &srvDesc, srv.writeRef())); + + RefPtr viewImpl = new ShaderResourceViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::SRV; + viewImpl->m_srv = srv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + } +} + +Result D3D11Renderer::createInputLayout(const InputElementDesc* inputElementsIn, UInt inputElementCount, InputLayout** outLayout) +{ + D3D11_INPUT_ELEMENT_DESC inputElements[16] = {}; + + char hlslBuffer[1024]; + char* hlslCursor = &hlslBuffer[0]; + + hlslCursor += sprintf(hlslCursor, "float4 main(\n"); + + for (UInt ii = 0; ii < inputElementCount; ++ii) + { + inputElements[ii].SemanticName = inputElementsIn[ii].semanticName; + inputElements[ii].SemanticIndex = (UINT)inputElementsIn[ii].semanticIndex; + inputElements[ii].Format = D3DUtil::getMapFormat(inputElementsIn[ii].format); + inputElements[ii].InputSlot = 0; + inputElements[ii].AlignedByteOffset = (UINT)inputElementsIn[ii].offset; + inputElements[ii].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + inputElements[ii].InstanceDataStepRate = 0; + + if (ii != 0) + { + hlslCursor += sprintf(hlslCursor, ",\n"); + } + + char const* typeName = "Unknown"; + switch (inputElementsIn[ii].format) + { + case Format::RGBA_Float32: + case Format::RGBA_Unorm_UInt8: + typeName = "float4"; + break; + case Format::RGB_Float32: + typeName = "float3"; + break; + case Format::RG_Float32: + typeName = "float2"; + break; + case Format::R_Float32: + typeName = "float"; + break; + default: + return SLANG_FAIL; + } + + hlslCursor += sprintf(hlslCursor, "%s a%d : %s%d", + typeName, + (int)ii, + inputElementsIn[ii].semanticName, + (int)inputElementsIn[ii].semanticIndex); + } + + hlslCursor += sprintf(hlslCursor, "\n) : SV_Position { return 0; }"); + + ComPtr vertexShaderBlob; + SLANG_RETURN_ON_FAIL(D3DUtil::compileHLSLShader("inputLayout", hlslBuffer, "main", "vs_5_0", vertexShaderBlob)); + + ComPtr inputLayout; + SLANG_RETURN_ON_FAIL(m_device->CreateInputLayout(&inputElements[0], (UINT)inputElementCount, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), + inputLayout.writeRef())); + + RefPtr impl = new InputLayoutImpl; + impl->m_layout.swap(inputLayout); + + *outLayout = impl.detach(); + return SLANG_OK; +} + +void* D3D11Renderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + BufferResourceImpl* bufferResource = static_cast(bufferIn); + + D3D11_MAP mapType; + ID3D11Buffer* buffer = bufferResource->m_buffer; + + switch (flavor) + { + case MapFlavor::WriteDiscard: + mapType = D3D11_MAP_WRITE_DISCARD; + break; + case MapFlavor::HostWrite: + mapType = D3D11_MAP_WRITE; + break; + case MapFlavor::HostRead: + mapType = D3D11_MAP_READ; + + buffer = bufferResource->m_staging; + if (!buffer) + { + return nullptr; + } + + // Okay copy the data over + m_immediateContext->CopyResource(buffer, bufferResource->m_buffer); + + break; + default: + return nullptr; + } + + // We update our constant buffer per-frame, just for the purposes + // of the example, but we don't actually load different data + // per-frame (we always use an identity projection). + D3D11_MAPPED_SUBRESOURCE mappedSub; + SLANG_RETURN_NULL_ON_FAIL(m_immediateContext->Map(buffer, 0, mapType, 0, &mappedSub)); + + bufferResource->m_mapFlavor = flavor; + + return mappedSub.pData; +} + +void D3D11Renderer::unmap(BufferResource* bufferIn) +{ + BufferResourceImpl* bufferResource = static_cast(bufferIn); + ID3D11Buffer* buffer = (bufferResource->m_mapFlavor == MapFlavor::HostRead) ? bufferResource->m_staging : bufferResource->m_buffer; + m_immediateContext->Unmap(buffer, 0); +} + +#if 0 +void D3D11Renderer::setInputLayout(InputLayout* inputLayoutIn) +{ + auto inputLayout = static_cast(inputLayoutIn); + m_immediateContext->IASetInputLayout(inputLayout->m_layout); +} +#endif + +void D3D11Renderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + m_immediateContext->IASetPrimitiveTopology(D3DUtil::getPrimitiveTopology(topology)); +} + +void D3D11Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffersIn, const UInt* stridesIn, const UInt* offsetsIn) +{ + static const int kMaxVertexBuffers = 16; + assert(slotCount <= kMaxVertexBuffers); + + UINT vertexStrides[kMaxVertexBuffers]; + UINT vertexOffsets[kMaxVertexBuffers]; + ID3D11Buffer* dxBuffers[kMaxVertexBuffers]; + + auto buffers = (BufferResourceImpl*const*)buffersIn; + + for (UInt ii = 0; ii < slotCount; ++ii) + { + vertexStrides[ii] = (UINT)stridesIn[ii]; + vertexOffsets[ii] = (UINT)offsetsIn[ii]; + dxBuffers[ii] = buffers[ii]->m_buffer; + } + + m_immediateContext->IASetVertexBuffers((UINT)startSlot, (UINT)slotCount, dxBuffers, &vertexStrides[0], &vertexOffsets[0]); +} + +void D3D11Renderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) +{ + DXGI_FORMAT dxFormat = D3DUtil::getMapFormat(indexFormat); + m_immediateContext->IASetIndexBuffer(((BufferResourceImpl*)buffer)->m_buffer, dxFormat, UINT(offset)); +} + +void D3D11Renderer::setDepthStencilTarget(ResourceView* depthStencilView) +{ + m_dsvBinding = ((DepthStencilViewImpl*) depthStencilView)->m_dsv; + m_targetBindingsDirty[int(PipelineType::Graphics)] = true; +} + +void D3D11Renderer::setViewports(UInt count, Viewport const* viewports) +{ + static const int kMaxViewports = D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX + 1; + assert(count <= kMaxViewports); + + D3D11_VIEWPORT dxViewports[kMaxViewports]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inViewport = viewports[ii]; + auto& dxViewport = dxViewports[ii]; + + dxViewport.TopLeftX = inViewport.originX; + dxViewport.TopLeftY = inViewport.originY; + dxViewport.Width = inViewport.extentX; + dxViewport.Height = inViewport.extentY; + dxViewport.MinDepth = inViewport.minZ; + dxViewport.MaxDepth = inViewport.maxZ; + } + + m_immediateContext->RSSetViewports(UINT(count), dxViewports); +} + +void D3D11Renderer::setScissorRects(UInt count, ScissorRect const* rects) +{ + static const int kMaxScissorRects = D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX + 1; + assert(count <= kMaxScissorRects); + + D3D11_RECT dxRects[kMaxScissorRects]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inRect = rects[ii]; + auto& dxRect = dxRects[ii]; + + dxRect.left = LONG(inRect.minX); + dxRect.top = LONG(inRect.minY); + dxRect.right = LONG(inRect.maxX); + dxRect.bottom = LONG(inRect.maxY); + } + + m_immediateContext->RSSetScissorRects(UINT(count), dxRects); +} + + +void D3D11Renderer::setPipelineState(PipelineType pipelineType, PipelineState* state) +{ + switch(pipelineType) + { + default: + break; + + case PipelineType::Graphics: + { + auto stateImpl = (GraphicsPipelineStateImpl*) state; + auto programImpl = stateImpl->m_program; + + // TODO: We could conceivably do some lightweight state + // differencing here (e.g., check if `programImpl` is the + // same as the program that is currently bound). + // + // It isn't clear how much that would pay off given that + // the D3D11 runtime seems to do its own state diffing. + + // IA + + m_immediateContext->IASetInputLayout(stateImpl->m_inputLayout->m_layout); + + // VS + + m_immediateContext->VSSetShader(programImpl->m_vertexShader, nullptr, 0); + + // HS + + // DS + + // GS + + // RS + + m_immediateContext->RSSetState(stateImpl->m_rasterizerState); + + // PS + + m_immediateContext->PSSetShader(programImpl->m_pixelShader, nullptr, 0); + + // OM + + m_immediateContext->OMSetBlendState(stateImpl->m_blendState, stateImpl->m_blendColor, stateImpl->m_sampleMask); + m_immediateContext->OMSetDepthStencilState(stateImpl->m_depthStencilState, stateImpl->m_stencilRef); + + m_currentGraphicsState = stateImpl; + } + break; + + case PipelineType::Compute: + { + auto stateImpl = (ComputePipelineStateImpl*) state; + auto programImpl = stateImpl->m_program; + + // CS + + m_immediateContext->CSSetShader(programImpl->m_computeShader, nullptr, 0); + + m_currentComputeState = stateImpl; + } + break; + } + + /// ... +} + +void D3D11Renderer::draw(UInt vertexCount, UInt startVertex) +{ + _flushGraphicsState(); + m_immediateContext->Draw((UINT)vertexCount, (UINT)startVertex); +} + +void D3D11Renderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) +{ + _flushGraphicsState(); + m_immediateContext->DrawIndexed((UINT)indexCount, (UINT)startIndex, (INT)baseVertex); +} + +Result D3D11Renderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) +{ + if (desc.pipelineType == PipelineType::Compute) + { + auto computeKernel = desc.findKernel(StageType::Compute); + + ComPtr computeShader; + SLANG_RETURN_ON_FAIL(m_device->CreateComputeShader(computeKernel->codeBegin, computeKernel->getCodeSize(), nullptr, computeShader.writeRef())); + + RefPtr shaderProgram = new ShaderProgramImpl(); + shaderProgram->m_computeShader.swap(computeShader); + + *outProgram = shaderProgram.detach(); + return SLANG_OK; + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + ComPtr vertexShader; + ComPtr pixelShader; + + SLANG_RETURN_ON_FAIL(m_device->CreateVertexShader(vertexKernel->codeBegin, vertexKernel->getCodeSize(), nullptr, vertexShader.writeRef())); + SLANG_RETURN_ON_FAIL(m_device->CreatePixelShader(fragmentKernel->codeBegin, fragmentKernel->getCodeSize(), nullptr, pixelShader.writeRef())); + + RefPtr shaderProgram = new ShaderProgramImpl(); + shaderProgram->m_vertexShader.swap(vertexShader); + shaderProgram->m_pixelShader.swap(pixelShader); + + *outProgram = shaderProgram.detach(); + return SLANG_OK; + } +} + +static D3D11_STENCIL_OP translateStencilOp(StencilOp op) +{ + switch(op) + { + default: + // TODO: need to report failures + return D3D11_STENCIL_OP_KEEP; + +#define CASE(FROM, TO) \ + case StencilOp::FROM: return D3D11_STENCIL_OP_##TO + + CASE(Keep, KEEP); + CASE(Zero, ZERO); + CASE(Replace, REPLACE); + CASE(IncrementSaturate, INCR_SAT); + CASE(DecrementSaturate, DECR_SAT); + CASE(Invert, INVERT); + CASE(IncrementWrap, INCR); + CASE(DecrementWrap, DECR); +#undef CASE + + } +} + +static D3D11_FILL_MODE translateFillMode(FillMode mode) +{ + switch(mode) + { + default: + // TODO: need to report failures + return D3D11_FILL_SOLID; + + case FillMode::Solid: return D3D11_FILL_SOLID; + case FillMode::Wireframe: return D3D11_FILL_WIREFRAME; + } +} + +static D3D11_CULL_MODE translateCullMode(CullMode mode) +{ + switch(mode) + { + default: + // TODO: need to report failures + return D3D11_CULL_NONE; + + case CullMode::None: return D3D11_CULL_NONE; + case CullMode::Back: return D3D11_CULL_BACK; + case CullMode::Front: return D3D11_CULL_FRONT; + } +} + +bool isBlendDisabled(AspectBlendDesc const& desc) +{ + return desc.op == BlendOp::Add + && desc.srcFactor == BlendFactor::One + && desc.dstFactor == BlendFactor::Zero; +} + + +bool isBlendDisabled(TargetBlendDesc const& desc) +{ + return isBlendDisabled(desc.color) + && isBlendDisabled(desc.alpha); +} + +D3D11_BLEND_OP translateBlendOp(BlendOp op) +{ + switch(op) + { + default: + assert(!"unimplemented"); + return (D3D11_BLEND_OP) -1; + +#define CASE(FROM, TO) case BlendOp::FROM: return D3D11_BLEND_OP_##TO + CASE(Add, ADD); + CASE(Subtract, SUBTRACT); + CASE(ReverseSubtract, REV_SUBTRACT); + CASE(Min, MIN); + CASE(Max, MAX); +#undef CASE + } +} + +D3D11_BLEND translateBlendFactor(BlendFactor factor) +{ + switch(factor) + { + default: + assert(!"unimplemented"); + return (D3D11_BLEND) -1; + +#define CASE(FROM, TO) case BlendFactor::FROM: return D3D11_BLEND_##TO + CASE(Zero, ZERO); + CASE(One, ONE); + CASE(SrcColor, SRC_COLOR); + CASE(InvSrcColor, INV_SRC_COLOR); + CASE(SrcAlpha, SRC_ALPHA); + CASE(InvSrcAlpha, INV_SRC_ALPHA); + CASE(DestAlpha, DEST_ALPHA); + CASE(InvDestAlpha, INV_DEST_ALPHA); + CASE(DestColor, DEST_COLOR); + CASE(InvDestColor, INV_DEST_ALPHA); + CASE(SrcAlphaSaturate, SRC_ALPHA_SAT); + CASE(BlendColor, BLEND_FACTOR); + CASE(InvBlendColor, INV_BLEND_FACTOR); + CASE(SecondarySrcColor, SRC1_COLOR); + CASE(InvSecondarySrcColor, INV_SRC1_COLOR); + CASE(SecondarySrcAlpha, SRC1_ALPHA); + CASE(InvSecondarySrcAlpha, INV_SRC1_ALPHA); +#undef CASE + } +} + +D3D11_COLOR_WRITE_ENABLE translateRenderTargetWriteMask(RenderTargetWriteMaskT mask) +{ + UINT result = 0; +#define CASE(FROM, TO) if(mask & RenderTargetWriteMask::Enable##FROM) result |= D3D11_COLOR_WRITE_ENABLE_##TO + + CASE(Red, RED); + CASE(Green, GREEN); + CASE(Blue, BLUE); + CASE(Alpha, ALPHA); + +#undef CASE + return D3D11_COLOR_WRITE_ENABLE(result); +} + +Result D3D11Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + + ComPtr depthStencilState; + { + D3D11_DEPTH_STENCIL_DESC dsDesc; + dsDesc.DepthEnable = desc.depthStencil.depthTestEnable; + dsDesc.DepthWriteMask = desc.depthStencil.depthWriteEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dsDesc.DepthFunc = translateComparisonFunc(desc.depthStencil.depthFunc); + dsDesc.StencilEnable = desc.depthStencil.stencilEnable; + dsDesc.StencilReadMask = desc.depthStencil.stencilReadMask; + dsDesc.StencilWriteMask = desc.depthStencil.stencilWriteMask; + + #define FACE(DST, SRC) \ + dsDesc.DST.StencilFailOp = translateStencilOp( desc.depthStencil.SRC.stencilFailOp); \ + dsDesc.DST.StencilDepthFailOp = translateStencilOp( desc.depthStencil.SRC.stencilDepthFailOp); \ + dsDesc.DST.StencilPassOp = translateStencilOp( desc.depthStencil.SRC.stencilPassOp); \ + dsDesc.DST.StencilFunc = translateComparisonFunc(desc.depthStencil.SRC.stencilFunc); \ + /* end */ + + FACE(FrontFace, frontFace); + FACE(BackFace, backFace); + + SLANG_RETURN_ON_FAIL(m_device->CreateDepthStencilState( + &dsDesc, + depthStencilState.writeRef())); + } + + ComPtr rasterizerState; + { + D3D11_RASTERIZER_DESC rsDesc; + rsDesc.FillMode = translateFillMode(desc.rasterizer.fillMode); + rsDesc.CullMode = translateCullMode(desc.rasterizer.cullMode); + rsDesc.FrontCounterClockwise = desc.rasterizer.frontFace == FrontFaceMode::Clockwise; + rsDesc.DepthBias = desc.rasterizer.depthBias; + rsDesc.DepthBiasClamp = desc.rasterizer.depthBiasClamp; + rsDesc.SlopeScaledDepthBias = desc.rasterizer.slopeScaledDepthBias; + rsDesc.DepthClipEnable = desc.rasterizer.depthClipEnable; + rsDesc.ScissorEnable = desc.rasterizer.scissorEnable; + rsDesc.MultisampleEnable = desc.rasterizer.multisampleEnable; + rsDesc.AntialiasedLineEnable = desc.rasterizer.antialiasedLineEnable; + + SLANG_RETURN_ON_FAIL(m_device->CreateRasterizerState( + &rsDesc, + rasterizerState.writeRef())); + + } + + ComPtr blendState; + { + auto& srcDesc = desc.blend; + D3D11_BLEND_DESC dstDesc = {}; + + TargetBlendDesc defaultTargetBlendDesc; + + static const UInt kMaxTargets = D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; + if(srcDesc.targetCount > kMaxTargets) return SLANG_FAIL; + + for(UInt ii = 0; ii < kMaxTargets; ++ii) + { + TargetBlendDesc const* srcTargetBlendDescPtr = nullptr; + if(ii < srcDesc.targetCount) + { + srcTargetBlendDescPtr = &srcDesc.targets[ii]; + } + else if(srcDesc.targetCount == 0) + { + srcTargetBlendDescPtr = &defaultTargetBlendDesc; + } + else + { + srcTargetBlendDescPtr = &srcDesc.targets[srcDesc.targetCount-1]; + } + + auto& srcTargetBlendDesc = *srcTargetBlendDescPtr; + auto& dstTargetBlendDesc = dstDesc.RenderTarget[ii]; + + if(isBlendDisabled(srcTargetBlendDesc)) + { + dstTargetBlendDesc.BlendEnable = false; + dstTargetBlendDesc.BlendOp = D3D11_BLEND_OP_ADD; + dstTargetBlendDesc.BlendOpAlpha = D3D11_BLEND_OP_ADD; + dstTargetBlendDesc.SrcBlend = D3D11_BLEND_ONE; + dstTargetBlendDesc.SrcBlendAlpha = D3D11_BLEND_ONE; + dstTargetBlendDesc.DestBlend = D3D11_BLEND_ZERO; + dstTargetBlendDesc.DestBlendAlpha = D3D11_BLEND_ZERO; + } + else + { + dstTargetBlendDesc.BlendEnable = true; + dstTargetBlendDesc.BlendOp = translateBlendOp(srcTargetBlendDesc.color.op); + dstTargetBlendDesc.BlendOpAlpha = translateBlendOp(srcTargetBlendDesc.alpha.op); + dstTargetBlendDesc.SrcBlend = translateBlendFactor(srcTargetBlendDesc.color.srcFactor); + dstTargetBlendDesc.SrcBlendAlpha = translateBlendFactor(srcTargetBlendDesc.alpha.srcFactor); + dstTargetBlendDesc.DestBlend = translateBlendFactor(srcTargetBlendDesc.color.dstFactor); + dstTargetBlendDesc.DestBlendAlpha = translateBlendFactor(srcTargetBlendDesc.alpha.dstFactor); + } + + dstTargetBlendDesc.RenderTargetWriteMask = translateRenderTargetWriteMask(srcTargetBlendDesc.writeMask); + } + + dstDesc.IndependentBlendEnable = srcDesc.targetCount > 1; + dstDesc.AlphaToCoverageEnable = srcDesc.alphaToCoverateEnable; + + SLANG_RETURN_ON_FAIL(m_device->CreateBlendState( + &dstDesc, + blendState.writeRef())); + } + + RefPtr state = new GraphicsPipelineStateImpl(); + state->m_program = programImpl; + state->m_stencilRef = desc.depthStencil.stencilRef; + state->m_depthStencilState = depthStencilState; + state->m_rasterizerState = rasterizerState; + state->m_blendState = blendState; + state->m_pipelineLayout = (PipelineLayoutImpl*) desc.pipelineLayout; + state->m_inputLayout = (InputLayoutImpl*) desc.inputLayout; + state->m_rtvCount = UINT(desc.renderTargetCount); + state->m_blendColor[0] = 0; + state->m_blendColor[1] = 0; + state->m_blendColor[2] = 0; + state->m_blendColor[3] = 0; + state->m_sampleMask = 0xFFFFFFFF; + + *outState = state.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + + RefPtr state = new ComputePipelineStateImpl(); + state->m_program = programImpl; + state->m_pipelineLayout = pipelineLayoutImpl; + + *outState = state.detach(); + return SLANG_OK; +} + +void D3D11Renderer::dispatchCompute(int x, int y, int z) +{ + _flushComputeState(); + m_immediateContext->Dispatch(x, y, z); +} + +Result D3D11Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) +{ + RefPtr descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(); + + UInt counts[int(D3D11DescriptorSlotType::CountOf)] = { 0, }; + + UInt rangeCount = desc.slotRangeCount; + for(UInt rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + + DescriptorSetLayoutImpl::RangeInfo rangeInfo; + + switch(rangeDesc.type) + { + default: + assert(!"invalid slot type"); + return SLANG_FAIL; + + case DescriptorSlotType::Sampler: + rangeInfo.type = D3D11DescriptorSlotType::Sampler; + break; + + case DescriptorSlotType::CombinedImageSampler: + rangeInfo.type = D3D11DescriptorSlotType::CombinedTextureSampler; + break; + + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::DynamicUniformBuffer: + rangeInfo.type = D3D11DescriptorSlotType::ConstantBuffer; + break; + + case DescriptorSlotType::SampledImage: + case DescriptorSlotType::UniformTexelBuffer: + case DescriptorSlotType::InputAttachment: + rangeInfo.type = D3D11DescriptorSlotType::ShaderResourceView; + break; + + case DescriptorSlotType::StorageImage: + case DescriptorSlotType::StorageTexelBuffer: + case DescriptorSlotType::StorageBuffer: + case DescriptorSlotType::DynamicStorageBuffer: + rangeInfo.type = D3D11DescriptorSlotType::UnorderedAccessView; + break; + } + + if(rangeInfo.type == D3D11DescriptorSlotType::CombinedTextureSampler) + { + auto srvTypeIndex = int(D3D11DescriptorSlotType::ShaderResourceView); + auto samplerTypeIndex = int(D3D11DescriptorSlotType::Sampler); + + rangeInfo.arrayIndex = counts[srvTypeIndex]; + rangeInfo.pairedSamplerArrayIndex = counts[samplerTypeIndex]; + + counts[srvTypeIndex] += rangeDesc.count; + counts[samplerTypeIndex] += rangeDesc.count; + } + else + { + auto typeIndex = int(rangeInfo.type); + + rangeInfo.arrayIndex = counts[typeIndex]; + counts[typeIndex] += rangeDesc.count; + } + descriptorSetLayoutImpl->m_ranges.add(rangeInfo); + } + + for(int ii = 0; ii < int(D3D11DescriptorSlotType::CountOf); ++ii) + { + descriptorSetLayoutImpl->m_counts[ii] = counts[ii]; + } + + *outLayout = descriptorSetLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) +{ + RefPtr pipelineLayoutImpl = new PipelineLayoutImpl(); + + UInt counts[int(D3D11DescriptorSlotType::CountOf)] = { 0, }; + + UInt setCount = desc.descriptorSetCount; + for(UInt ii = 0; ii < setCount; ++ii) + { + auto setDesc = desc.descriptorSets[ii]; + PipelineLayoutImpl::DescriptorSetInfo setInfo; + + setInfo.layout = (DescriptorSetLayoutImpl*) setDesc.layout; + + for(int jj = 0; jj < int(D3D11DescriptorSlotType::CountOf); ++jj) + { + setInfo.baseIndices[jj] = counts[jj]; + counts[jj] += setInfo.layout->m_counts[jj]; + } + + pipelineLayoutImpl->m_descriptorSets.add(setInfo); + } + + pipelineLayoutImpl->m_uavCount = UINT(counts[int(D3D11DescriptorSlotType::UnorderedAccessView)]); + + *outLayout = pipelineLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) +{ + auto layoutImpl = (DescriptorSetLayoutImpl*)layout; + + RefPtr descriptorSetImpl = new DescriptorSetImpl(); + + descriptorSetImpl->m_layout = layoutImpl; + descriptorSetImpl->m_cbs .setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ConstantBuffer)]); + descriptorSetImpl->m_srvs .setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ShaderResourceView)]); + descriptorSetImpl->m_uavs .setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::UnorderedAccessView)]); + descriptorSetImpl->m_samplers.setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::Sampler)]); + + *outDescriptorSet = descriptorSetImpl.detach(); + return SLANG_OK; +} + + +#if 0 +BindingState* D3D11Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); + + const auto& srcBindings = bindingStateDesc.m_bindings; + const int numBindings = int(srcBindings.Count()); + + auto& dstDetails = bindingState->m_bindingDetails; + dstDetails.SetSize(numBindings); + + for (int i = 0; i < numBindings; ++i) + { + auto& dstDetail = dstDetails[i]; + const auto& srcBinding = srcBindings[i]; + + assert(srcBinding.registerRange.isSingle()); + + switch (srcBinding.bindingType) + { + case BindingType::Buffer: + { + assert(srcBinding.resource && srcBinding.resource->isBuffer()); + + BufferResourceImpl* buffer = static_cast(srcBinding.resource.Ptr()); + const BufferResource::Desc& desc = buffer->getDesc(); + + const int elemSize = bufferDesc.elementSize <= 0 ? 1 : bufferDesc.elementSize; + + if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) + { + D3D11_UNORDERED_ACCESS_VIEW_DESC viewDesc; + memset(&viewDesc, 0, sizeof(viewDesc)); + viewDesc.Buffer.FirstElement = 0; + viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); + viewDesc.Buffer.Flags = 0; + viewDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + viewDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); + + if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) + { + viewDesc.Buffer.Flags |= D3D11_BUFFER_UAV_FLAG_RAW; + viewDesc.Format = DXGI_FORMAT_R32_TYPELESS; + } + + SLANG_RETURN_NULL_ON_FAIL(m_device->CreateUnorderedAccessView(buffer->m_buffer, &viewDesc, dstDetail.m_uav.writeRef())); + } + if (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource)) + { + D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; + memset(&viewDesc, 0, sizeof(viewDesc)); + viewDesc.Buffer.FirstElement = 0; + viewDesc.Buffer.ElementWidth = elemSize; + viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); + viewDesc.Buffer.ElementOffset = 0; + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; + viewDesc.Format = DXGI_FORMAT_UNKNOWN; + + if (bufferDesc.elementSize == 0) + { + viewDesc.Format = DXGI_FORMAT_R32_FLOAT; + } + + SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(buffer->m_buffer, &viewDesc, dstDetail.m_srv.writeRef())); + } + break; + } + case BindingType::Texture: + case BindingType::CombinedTextureSampler: + { + assert(srcBinding.resource && srcBinding.resource->isTexture()); + + TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); + + const TextureResource::Desc& textureDesc = texture->getDesc(); + + D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; + viewDesc.Format = D3DUtil::getMapFormat(textureDesc.format); + + switch (texture->getType()) + { + case Resource::Type::Texture1D: + { + if (textureDesc.arraySize <= 0) + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; + viewDesc.Texture1D.MipLevels = textureDesc.numMipLevels; + viewDesc.Texture1D.MostDetailedMip = 0; + } + else + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; + viewDesc.Texture1DArray.ArraySize = textureDesc.arraySize; + viewDesc.Texture1DArray.FirstArraySlice = 0; + viewDesc.Texture1DArray.MipLevels = textureDesc.numMipLevels; + viewDesc.Texture1DArray.MostDetailedMip = 0; + } + break; + } + case Resource::Type::Texture2D: + { + if (textureDesc.arraySize <= 0) + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + viewDesc.Texture2D.MipLevels = textureDesc.numMipLevels; + viewDesc.Texture2D.MostDetailedMip = 0; + } + else + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + viewDesc.Texture2DArray.ArraySize = textureDesc.arraySize; + viewDesc.Texture2DArray.FirstArraySlice = 0; + viewDesc.Texture2DArray.MipLevels = textureDesc.numMipLevels; + viewDesc.Texture2DArray.MostDetailedMip = 0; + } + break; + } + case Resource::Type::TextureCube: + { + if (textureDesc.arraySize <= 0) + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + viewDesc.TextureCube.MipLevels = textureDesc.numMipLevels; + viewDesc.TextureCube.MostDetailedMip = 0; + } + else + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; + viewDesc.TextureCubeArray.MipLevels = textureDesc.numMipLevels; + viewDesc.TextureCubeArray.MostDetailedMip = 0; + viewDesc.TextureCubeArray.First2DArrayFace = 0; + viewDesc.TextureCubeArray.NumCubes = textureDesc.arraySize; + } + break; + } + case Resource::Type::Texture3D: + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + viewDesc.Texture3D.MipLevels = textureDesc.numMipLevels; // Old code fixed as one + viewDesc.Texture3D.MostDetailedMip = 0; + break; + } + default: + { + assert(!"Unhandled type"); + return nullptr; + } + } + + SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(texture->m_resource, &viewDesc, dstDetail.m_srv.writeRef())); + break; + } + case BindingType::Sampler: + { + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; + + D3D11_SAMPLER_DESC desc = {}; + desc.AddressU = desc.AddressV = desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + + if (samplerDesc.isCompareSampler) + { + desc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; + desc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + desc.MinLOD = desc.MaxLOD = 0.0f; + } + else + { + desc.Filter = D3D11_FILTER_ANISOTROPIC; + desc.MaxAnisotropy = 8; + desc.MinLOD = 0.0f; + desc.MaxLOD = 100.0f; + } + SLANG_RETURN_NULL_ON_FAIL(m_device->CreateSamplerState(&desc, dstDetail.m_samplerState.writeRef())); + break; + } + default: + { + assert(!"Unhandled type"); + return nullptr; + } + } + } + + // Done + return bindingState.detach(); +} + +void D3D11Renderer::_applyBindingState(bool isCompute) +{ + auto context = m_immediateContext.get(); + + const auto& details = m_currentBindings->m_bindingDetails; + const auto& bindings = m_currentBindings->getDesc().m_bindings; + + const int numBindings = int(bindings.Count()); + + for (int i = 0; i < numBindings; ++i) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + const int bindingIndex = binding.registerRange.getSingleIndex(); + + switch (binding.bindingType) + { + case BindingType::Buffer: + { + assert(binding.resource && binding.resource->isBuffer()); + if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) + { + ID3D11Buffer* buffer = static_cast(binding.resource.Ptr())->m_buffer; + if (isCompute) + context->CSSetConstantBuffers(bindingIndex, 1, &buffer); + else + { + context->VSSetConstantBuffers(bindingIndex, 1, &buffer); + context->PSSetConstantBuffers(bindingIndex, 1, &buffer); + } + } + else if (detail.m_uav) + { + if (isCompute) + context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); + else + context->OMSetRenderTargetsAndUnorderedAccessViews( + m_currentBindings->getDesc().m_numRenderTargets, + m_renderTargetViews.getBuffer()->readRef(), + m_depthStencilView, + bindingIndex, + 1, + detail.m_uav.readRef(), + nullptr); + } + else + { + if (isCompute) + context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + else + { + context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + } + } + break; + } + case BindingType::Texture: + { + if (detail.m_uav) + { + if (isCompute) + context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); + else + context->OMSetRenderTargetsAndUnorderedAccessViews(D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, + nullptr, nullptr, bindingIndex, 1, detail.m_uav.readRef(), nullptr); + } + else + { + if (isCompute) + context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + else + { + context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + } + } + break; + } + case BindingType::Sampler: + { + if (isCompute) + context->CSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); + else + { + context->PSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); + context->VSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); + } + break; + } + default: + { + assert(!"Not implemented"); + return; + } + } + } +} + +void D3D11Renderer::setBindingState(BindingState* state) +{ + m_currentBindings = static_cast(state); +} +#endif + +void D3D11Renderer::_flushGraphicsState() +{ + auto pipelineType = int(PipelineType::Graphics); + if(m_targetBindingsDirty[pipelineType]) + { + m_targetBindingsDirty[pipelineType] = false; + + auto pipelineState = m_currentGraphicsState.Ptr(); + + auto rtvCount = pipelineState->m_rtvCount; + auto uavCount = pipelineState->m_pipelineLayout->m_uavCount; + + m_immediateContext->OMSetRenderTargetsAndUnorderedAccessViews( + rtvCount, + m_rtvBindings[0].readRef(), + m_dsvBinding, + rtvCount, + uavCount, + m_uavBindings[pipelineType][0].readRef(), + nullptr); + } +} + +void D3D11Renderer::_flushComputeState() +{ + auto pipelineType = int(PipelineType::Compute); + if(m_targetBindingsDirty[pipelineType]) + { + m_targetBindingsDirty[pipelineType] = false; + + auto pipelineState = m_currentComputeState.Ptr(); + + auto uavCount = pipelineState->m_pipelineLayout->m_uavCount; + + m_immediateContext->CSSetUnorderedAccessViews( + 0, + uavCount, + m_uavBindings[pipelineType][0].readRef(), + nullptr); + } +} + +void D3D11Renderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) +{ + auto bufferImpl = (BufferResourceImpl*) buffer; + auto& rangeInfo = m_layout->m_ranges[range]; + + assert(rangeInfo.type == D3D11DescriptorSlotType::ConstantBuffer); + + m_cbs[rangeInfo.arrayIndex + index] = bufferImpl->m_buffer; +} + +void D3D11Renderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) +{ + auto viewImpl = (ResourceViewImpl*)view; + auto& rangeInfo = m_layout->m_ranges[range]; + + switch (rangeInfo.type) + { + case D3D11DescriptorSlotType::ShaderResourceView: + { + assert(viewImpl->m_type == ResourceViewImpl::Type::SRV); + auto srvImpl = (ShaderResourceViewImpl*)viewImpl; + m_srvs[rangeInfo.arrayIndex + index] = srvImpl->m_srv; + } + break; + + case D3D11DescriptorSlotType::UnorderedAccessView: + { + assert(viewImpl->m_type == ResourceViewImpl::Type::UAV); + auto uavImpl = (UnorderedAccessViewImpl*)viewImpl; + m_uavs[rangeInfo.arrayIndex + index] = uavImpl->m_uav; + } + break; + + default: + assert(!"invalid to bind a resource view to this descriptor range"); + break; + } +} + +void D3D11Renderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) +{ + auto samplerImpl = (SamplerStateImpl*) sampler; + auto& rangeInfo = m_layout->m_ranges[range]; + + assert(rangeInfo.type == D3D11DescriptorSlotType::Sampler); + + m_samplers[rangeInfo.arrayIndex + index] = samplerImpl->m_sampler; +} + +void D3D11Renderer::DescriptorSetImpl::setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) +{ + auto viewImpl = (ResourceViewImpl*) textureView; + auto samplerImpl = (SamplerStateImpl*)sampler; + + auto& rangeInfo = m_layout->m_ranges[range]; + assert(rangeInfo.type == D3D11DescriptorSlotType::CombinedTextureSampler); + + assert(viewImpl->m_type == ResourceViewImpl::Type::SRV); + auto srvImpl = (ShaderResourceViewImpl*)viewImpl; + m_srvs[rangeInfo.arrayIndex + index] = srvImpl->m_srv; + + m_samplers[rangeInfo.arrayIndex + index] = samplerImpl->m_sampler; + + // TODO: need a place to bind the matching sampler... + m_srvs[rangeInfo.pairedSamplerArrayIndex + index] = srvImpl->m_srv; +} + +void D3D11Renderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) +{ + auto pipelineLayoutImpl = (PipelineLayoutImpl*)layout; + auto descriptorSetImpl = (DescriptorSetImpl*) descriptorSet; + + auto descriptorSetLayoutImpl = descriptorSetImpl->m_layout; + auto& setInfo = pipelineLayoutImpl->m_descriptorSets[index]; + + // Note: `setInfo->layout` and `descriptorSetLayoutImpl` need to be compatible + + // TODO: If/when we add per-stage visibility masks, it would be best to organize + // this as a loop over stages, so that we only do the binding that is required + // for each stage. + + { + const int slotType = int(D3D11DescriptorSlotType::ConstantBuffer); + const UINT slotCount = UINT(setInfo.layout->m_counts[slotType]); + if(slotCount) + { + const UINT startSlot = UINT(setInfo.baseIndices[slotType]); + + auto cbs = descriptorSetImpl->m_cbs[0].readRef(); + + m_immediateContext->VSSetConstantBuffers(startSlot, slotCount, cbs); + // ... + m_immediateContext->PSSetConstantBuffers(startSlot, slotCount, cbs); + + m_immediateContext->CSSetConstantBuffers(startSlot, slotCount, cbs); + } + } + + { + const int slotType = int(D3D11DescriptorSlotType::ShaderResourceView); + const UINT slotCount = UINT(setInfo.layout->m_counts[slotType]); + if(slotCount) + { + const UINT startSlot = UINT(setInfo.baseIndices[slotType]); + + auto srvs = descriptorSetImpl->m_srvs[0].readRef(); + + m_immediateContext->VSSetShaderResources(startSlot, slotCount, srvs); + // ... + m_immediateContext->PSSetShaderResources(startSlot, slotCount, srvs); + + m_immediateContext->CSSetShaderResources(startSlot, slotCount, srvs); + } + } + + { + const int slotType = int(D3D11DescriptorSlotType::Sampler); + const UINT slotCount = UINT(setInfo.layout->m_counts[slotType]); + if(slotCount) + { + const UINT startSlot = UINT(setInfo.baseIndices[slotType]); + + auto samplers = descriptorSetImpl->m_samplers[0].readRef(); + + m_immediateContext->VSSetSamplers(startSlot, slotCount, samplers); + // ... + m_immediateContext->PSSetSamplers(startSlot, slotCount, samplers); + + m_immediateContext->CSSetSamplers(startSlot, slotCount, samplers); + } + } + + { + // Note: UAVs are handled differently from other bindings, because + // D3D11 requires all UAVs to be set with a single call, rather + // than allowing incremental updates. We will therefore shadow + // the UAV bindings with `m_uavBindings` and then flush them + // as needed right before a draw/dispatch. + // + const int slotType = int(D3D11DescriptorSlotType::UnorderedAccessView); + const UInt slotCount = setInfo.layout->m_counts[slotType]; + if(slotCount) + { + UInt startSlot = setInfo.baseIndices[slotType]; + + auto uavs = descriptorSetImpl->m_uavs[0].readRef(); + + for(UINT ii = 0; ii < slotCount; ++ii) + { + m_uavBindings[int(pipelineType)][startSlot + ii] = uavs[ii]; + } + m_targetBindingsDirty[int(pipelineType)] = true; + } + } + + +} + +} // renderer_test diff --git a/tools/gfx/d3d11/render-d3d11.h b/tools/gfx/d3d11/render-d3d11.h new file mode 100644 index 000000000..9e671d541 --- /dev/null +++ b/tools/gfx/d3d11/render-d3d11.h @@ -0,0 +1,10 @@ +// render-d3d11.h +#pragma once + +namespace gfx { + +class Renderer; + +Renderer* createD3D11Renderer(); + +} // gfx diff --git a/tools/gfx/d3d12/circular-resource-heap-d3d12.cpp b/tools/gfx/d3d12/circular-resource-heap-d3d12.cpp new file mode 100644 index 000000000..685dd364f --- /dev/null +++ b/tools/gfx/d3d12/circular-resource-heap-d3d12.cpp @@ -0,0 +1,222 @@ +#include "circular-resource-heap-d3d12.h" + +namespace gfx { +using namespace Slang; + +D3D12CircularResourceHeap::D3D12CircularResourceHeap(): + m_fence(nullptr), + m_device(nullptr), + m_blockFreeList(sizeof(Block), SLANG_ALIGN_OF(Block), 16), + m_blocks(nullptr) +{ + m_back.m_block = nullptr; + m_back.m_position = nullptr; + m_front.m_block = nullptr; + m_front.m_position = nullptr; +} + +D3D12CircularResourceHeap::~D3D12CircularResourceHeap() +{ + _freeBlockListResources(m_blocks); +} + +void D3D12CircularResourceHeap::_freeBlockListResources(const Block* start) +{ + if (start) + { + const Block* block = start; + do + { + ID3D12Resource* resource = block->m_resource; + + resource->Unmap(0, nullptr); + resource->Release(); + + // Next in list + block = block->m_next; + + } while (block != start); + } +} + +Result D3D12CircularResourceHeap::init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence) +{ + assert(m_blocks == nullptr); + assert(desc.m_blockSize > 0); + + m_fence = fence; + m_desc = desc; + m_device = device; + + return SLANG_OK; +} + +void D3D12CircularResourceHeap::addSync(uint64_t signalValue) +{ + assert(signalValue == m_fence->getCurrentValue()); + PendingEntry entry; + entry.m_completedValue = signalValue; + entry.m_cursor = m_front; + m_pendingQueue.add(entry); +} + +void D3D12CircularResourceHeap::updateCompleted() +{ + const uint64_t completedValue = m_fence->getCompletedValue(); + +#if 0 + while (m_pendingQueue.getCount() != 0) + { + const PendingEntry& entry = m_pendingQueue[0]; + if (entry.m_completedValue <= completedValue) + { + m_back = entry.m_cursor; + m_pendingQueue.removeAt(0); + } + else + { + break; + } + } +#else + // A more efficient implementation is m_pendingQueue is implemented as a vector like type + const Index size = m_pendingQueue.getCount(); + Index end = 0; + while (end < size && m_pendingQueue[end].m_completedValue <= completedValue) + { + end++; + } + + if (end > 0) + { + // Set the back position + m_back = m_pendingQueue[end - 1].m_cursor; + if (end == size) + { + m_pendingQueue.clear(); + } + else + { + m_pendingQueue.removeRange(0, size); + } + } +#endif +} + +D3D12CircularResourceHeap::Block* D3D12CircularResourceHeap::_newBlock() +{ + D3D12_RESOURCE_DESC desc; + + desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + desc.Alignment = 0; + desc.Width = m_desc.m_blockSize; + desc.Height = 1; + desc.DepthOrArraySize = 1; + desc.MipLevels = 1; + desc.Format = DXGI_FORMAT_UNKNOWN; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + desc.Flags = D3D12_RESOURCE_FLAG_NONE; + + ComPtr resource; + Result res = m_device->CreateCommittedResource(&m_desc.m_heapProperties, m_desc.m_heapFlags, &desc, m_desc.m_initialState, nullptr, IID_PPV_ARGS(resource.writeRef())); + if (SLANG_FAILED(res)) + { + assert(!"Resource allocation failed"); + return nullptr; + } + + uint8_t* data = nullptr; + if (m_desc.m_heapProperties.Type == D3D12_HEAP_TYPE_READBACK) + { + } + else + { + // Map it, and keep it mapped + resource->Map(0, nullptr, (void**)&data); + } + + // We have no blocks -> so lets allocate the first + Block* block = (Block*)m_blockFreeList.allocate(); + block->m_next = nullptr; + + block->m_resource = resource.detach(); + block->m_start = data; + return block; +} + +D3D12CircularResourceHeap::Cursor D3D12CircularResourceHeap::allocate(size_t size, size_t alignment) +{ + const size_t blockSize = getBlockSize(); + + assert(size <= blockSize); + + // If nothing is allocated add the first block + if (m_blocks == nullptr) + { + Block* block = _newBlock(); + if (!block) + { + Cursor cursor = {}; + return cursor; + } + m_blocks = block; + // Make circular + block->m_next = block; + + // Point front and back to same position, as currently it is all free + m_back = { block, block->m_start }; + m_front = m_back; + } + + // If front and back are in the same block then front MUST be ahead of back (as that defined as + // an invariant and is required for block insertion to be possible + Block* block = m_front.m_block; + + // Check the invariant + assert(block != m_back.m_block || m_front.m_position >= m_back.m_position); + + { + uint8_t* cur = (uint8_t*)((size_t(m_front.m_position) + alignment - 1) & ~(alignment - 1)); + // Does the the allocation fit? + if (cur + size <= block->m_start + blockSize) + { + // It fits + // Move the front forward + m_front.m_position = cur + size; + Cursor cursor = { block, cur }; + return cursor; + } + } + + // Okay I can't fit into current block... + + // If the next block contains front, we need to add a block, else we can use that block + if (block->m_next == m_back.m_block) + { + Block* newBlock = _newBlock(); + // Insert into the list + newBlock->m_next = block->m_next; + block->m_next = newBlock; + } + + // Use the block we are going to add to + block = block->m_next; + uint8_t* cur = (uint8_t*)((size_t(block->m_start) + alignment - 1) & ~(alignment - 1)); + // Does the the allocation fit? + if (cur + size > block->m_start + blockSize) + { + assert(!"Couldn't fit into a free block(!) Alignment breaks it?"); + Cursor cursor = {}; + return cursor; + } + // It fits + // Move the front forward + m_front.m_block = block; + m_front.m_position = cur + size; + Cursor cursor = { block, cur }; + return cursor; +} + +} // namespace gfx diff --git a/tools/gfx/d3d12/circular-resource-heap-d3d12.h b/tools/gfx/d3d12/circular-resource-heap-d3d12.h new file mode 100644 index 000000000..bf9f412cf --- /dev/null +++ b/tools/gfx/d3d12/circular-resource-heap-d3d12.h @@ -0,0 +1,206 @@ +#pragma once + +#include "../../slang-com-ptr.h" +#include "../../source/core/slang-list.h" +#include "../../source/core/slang-free-list.h" + +#include "resource-d3d12.h" + +namespace gfx { + +/*! \brief The D3D12CircularResourceHeap is a heap that is suited for size constrained real-time resources allocation that +is transitory in nature. It is designed to allocate resources which are used and discarded, often used where in +previous versions of DirectX the 'DISCARD' flag was used. + +The idea is to have a heap which chunks of resource can be allocated, and used for GPU execution, +and that the heap is able through the addSync/updateCompleted idiom is able to track when the usage of the resources is +completed allowing them to be reused. The heap is arranged as circularly, with new allocations made from the front, and the back +being updated as the GPU updating the back when it is informed anything using prior parts of the heap have completed. In this +arrangement all the heap between the back and the front can be thought of as in use or potentially in use by the GPU. All the heap +from the front back around to the back, is free and can be allocated from. It is the responsibility of the user of the Heap to make +sure the invariant holds, but in most normal usage it does so simply. + +Another feature of the heap is that it does not require upfront knowledge of how big a heap is needed. The backing resources will be expanded +dynamically with requests as needed. The only requirement is that know single request can be larger than m_blockSize specified in the Desc +used to initialize the heap. This is because all the backing resources are allocated to a single size. This limitation means the D3D12CircularResourceHeap +may not be the best use for example for uploading a texture - because it's design is really around transitory uploads or write backs, and so more suited +to constant buffers, vertex buffer, index buffers and the like. + +To upload a texture at program startup it is most likely better to use a D3D12ResourceScopeManager. + +\code{.cpp} + +typedef D3D12CircularResourceHeap Heap; + +Heap::Cursor cursor = heap.allocateVertexBuffer(sizeof(Vertex) * numVerts); +Memory:copy(cursor.m_position, verts, sizeof(Vertex) * numVerts); + +// Do a command using the GPU handle +m_commandList->... +// Do another command using the GPU handle + +m_commandList->... + +// Execute the command list on the command queue +{ + ID3D12CommandList* lists[] = { m_commandList }; + m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(lists), lists); +} + +// Add a sync point +const uint64_t signalValue = m_fence.nextSignal(m_commandQueue); +heap.addSync(signalValue) + +// The cursors cannot be used anymore + +// At some later point call updateCompleted. This will see where the GPU is at, and make resources available that the GPU no longer accesses. +heap.updateCompleted(); + +\endcode + +### Implementation + +Front and back can be in the same block, but ONLY if back is behind front, because we have to always be able to insert +new blocks in front of front. So it must be possible to do an block insertion between the two of them. + +|--B---F-----| |----------| + +When B and F are on top of one another it means there is nothing in the list. NOTE this also means that a move of front can never place it +top of the back. + +https://msdn.microsoft.com/en-us/library/windows/desktop/dn899125%28v=vs.85%29.aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx +*/ + +class D3D12CircularResourceHeap +{ + protected: + struct Block; + public: + typedef D3D12CircularResourceHeap ThisType; + + /// The alignment used for VERTEX_BUFFER allocations + /// Strictly speaking it seems the hardware can handle 4 byte alignment, but since often in use + /// data will be copied from CPU memory to the allocation, using 16 byte alignment is superior as allows + /// significantly faster memcpy. + /// The sample that shows sizeof(float) - 4 bytes is appropriate is at the link below. + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx + enum + { + VERTEX_BUFFER_ALIGNMENT = 16, + }; + + struct Desc + { + void init() + { + { + D3D12_HEAP_PROPERTIES& props = m_heapProperties; + + props.Type = D3D12_HEAP_TYPE_UPLOAD; + props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + props.CreationNodeMask = 1; + props.VisibleNodeMask = 1; + } + m_heapFlags = D3D12_HEAP_FLAG_NONE; + m_initialState = D3D12_RESOURCE_STATE_GENERIC_READ; + m_blockSize = 0; + } + + D3D12_HEAP_PROPERTIES m_heapProperties; + D3D12_HEAP_FLAGS m_heapFlags; + D3D12_RESOURCE_STATES m_initialState; + size_t m_blockSize; + }; + + /// Cursor position + struct Cursor + { + /// Get GpuHandle + SLANG_FORCE_INLINE D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle() const { return m_block->m_resource->GetGPUVirtualAddress() + size_t(m_position - m_block->m_start); } + /// Must have a block and position + SLANG_FORCE_INLINE bool isValid() const { return m_block != nullptr; } + /// Calculate the offset into the underlying resource + SLANG_FORCE_INLINE size_t getOffset() const { return size_t(m_position - m_block->m_start); } + /// Get the underlying resource + SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_block->m_resource; } + + Block* m_block; ///< The block index + uint8_t* m_position; ///< The current position + }; + + /// Get the desc used to initialize the heap + SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } + + /// Must be called before used + /// Block size must be at least as large as the _largest_ thing allocated + /// Also note depending on alignment of a resource allocation, the block size might also need to take into account the + /// maximum alignment use. It is a REQUIREMENT that a newly allocated resource block is large enough to hold any + /// allocation taking into account the alignment used. + Slang::Result init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence); + + /// Get the block size + SLANG_FORCE_INLINE size_t getBlockSize() const { return m_desc.m_blockSize; } + + /// Allocate constant buffer of specified size + Cursor allocate(size_t size, size_t alignment); + + /// Allocate a constant buffer + SLANG_FORCE_INLINE Cursor allocateConstantBuffer(size_t size) { return allocate(size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); } + /// Allocate a vertex buffer + SLANG_FORCE_INLINE Cursor allocateVertexBuffer(size_t size) { return allocate(size, VERTEX_BUFFER_ALIGNMENT); } + + /// Create filled in constant buffer + SLANG_FORCE_INLINE Cursor newConstantBuffer(const void* data, size_t size) { Cursor cursor = allocateConstantBuffer(size); ::memcpy(cursor.m_position, data, size); return cursor; } + /// Create in filled in constant buffer + template + SLANG_FORCE_INLINE Cursor newConstantBuffer(const T& in) { return newConstantBuffer(&in, sizeof(T)); } + + /// Look where the GPU has got to and release anything not currently used + void updateCompleted(); + /// Add a sync point - meaning that when this point is hit in the queue + /// all of the resources up to this point will no longer be used. + void addSync(uint64_t signalValue); + + /// Get the gpu address of this cursor + D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle(const Cursor& cursor) const { return cursor.m_block->m_resource->GetGPUVirtualAddress() + size_t(cursor.m_position - cursor.m_block->m_start); } + + /// Ctor + D3D12CircularResourceHeap(); + /// Dtor + ~D3D12CircularResourceHeap(); + + protected: + + struct Block + { + ID3D12Resource* m_resource; ///< The mapped resource + uint8_t* m_start; ///< Once created the resource is mapped to here + Block* m_next; ///< Points to next block in the list + }; + struct PendingEntry + { + uint64_t m_completedValue; ///< The value when this is completed + Cursor m_cursor; ///< the cursor at that point + }; + void _freeBlockListResources(const Block* block); + /// Create a new block (with associated resource), do not add the block list + Block* _newBlock(); + + Block* m_blocks; ///< Circular singly linked list of block. nullptr initially + Slang::FreeList m_blockFreeList; ///< Free list of actual allocations of blocks + Slang::List m_pendingQueue; ///< Holds the list of pending positions. When the fence value is greater than the value on the queue entry, the entry is done. + + // Allocation is made from the front, and freed from the back. + Cursor m_back; ///< Current back position. + Cursor m_front; ///< Current front position. + + Desc m_desc; ///< Describes the heap + + D3D12CounterFence* m_fence; ///< The fence to use + ID3D12Device* m_device; ///< The device that resources will be constructed on +}; + +} // namespace gfx + diff --git a/tools/gfx/d3d12/descriptor-heap-d3d12.cpp b/tools/gfx/d3d12/descriptor-heap-d3d12.cpp new file mode 100644 index 000000000..382fc3219 --- /dev/null +++ b/tools/gfx/d3d12/descriptor-heap-d3d12.cpp @@ -0,0 +1,47 @@ + +#include "descriptor-heap-d3d12.h" + +namespace gfx { +using namespace Slang; + +D3D12DescriptorHeap::D3D12DescriptorHeap(): + m_totalSize(0), + m_currentIndex(0), + m_descriptorSize(0) +{ +} + +Result D3D12DescriptorHeap::init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags) +{ + D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {}; + srvHeapDesc.NumDescriptors = size; + srvHeapDesc.Flags = flags; + srvHeapDesc.Type = type; + SLANG_RETURN_ON_FAIL(device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(m_heap.writeRef()))); + + m_descriptorSize = device->GetDescriptorHandleIncrementSize(type); + m_totalSize = size; + + return SLANG_OK; +} + +Result D3D12DescriptorHeap::init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags) +{ + SLANG_RETURN_ON_FAIL(init(device, numHandles, type, flags)); + D3D12_CPU_DESCRIPTOR_HANDLE dst = m_heap->GetCPUDescriptorHandleForHeapStart(); + + // Copy them all + for (int i = 0; i < numHandles; i++, dst.ptr += m_descriptorSize) + { + D3D12_CPU_DESCRIPTOR_HANDLE src = handles[i]; + if (src.ptr != 0) + { + device->CopyDescriptorsSimple(1, dst, src, type); + } + } + + return SLANG_OK; +} + +} // namespace gfx + diff --git a/tools/gfx/d3d12/descriptor-heap-d3d12.h b/tools/gfx/d3d12/descriptor-heap-d3d12.h new file mode 100644 index 000000000..a546395d8 --- /dev/null +++ b/tools/gfx/d3d12/descriptor-heap-d3d12.h @@ -0,0 +1,198 @@ +#pragma once + + +#include +#include + +#include "../../slang-com-ptr.h" +#include "../../source/core/slang-list.h" + +namespace gfx { + +/*! \brief A simple class to manage an underlying Dx12 Descriptor Heap. Allocations are made linearly in order. It is not possible to free +individual allocations, but all allocations can be deallocated with 'deallocateAll'. */ +class D3D12DescriptorHeap +{ + public: + typedef D3D12DescriptorHeap ThisType; + + /// Initialize + Slang::Result init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags); + /// Initialize with an array of handles copying over the representation + Slang::Result init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags); + + /// Returns the number of slots that have been used + SLANG_FORCE_INLINE int getUsedSize() const { return m_currentIndex; } + + /// Get the total amount of descriptors possible on the heap + SLANG_FORCE_INLINE int getTotalSize() const { return m_totalSize; } + /// Allocate a descriptor. Returns the index, or -1 if none left. + SLANG_FORCE_INLINE int allocate(); + /// Allocate a number of descriptors. Returns the start index (or -1 if not possible) + SLANG_FORCE_INLINE int allocate(int numDescriptors); + + /// + SLANG_FORCE_INLINE int placeAt(int index); + + /// Deallocates all allocations, and starts allocation from the start of the underlying heap again + SLANG_FORCE_INLINE void deallocateAll() { m_currentIndex = 0; } + + /// Get the size of each + SLANG_FORCE_INLINE int getDescriptorSize() const { return m_descriptorSize; } + + /// Get the GPU heap start + SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuStart() const { return m_heap->GetGPUDescriptorHandleForHeapStart(); } + /// Get the CPU heap start + SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuStart() const { return m_heap->GetCPUDescriptorHandleForHeapStart(); } + + /// Get the GPU handle at the specified index + SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(int index) const; + /// Get the CPU handle at the specified index + SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(int index) const; + + /// Get the underlying heap + SLANG_FORCE_INLINE ID3D12DescriptorHeap* getHeap() const { return m_heap; } + + /// Ctor + D3D12DescriptorHeap(); + +protected: + Slang::ComPtr m_heap; ///< The underlying heap being allocated from + int m_totalSize; ///< Total amount of allocations available on the heap + int m_currentIndex; ///< The current descriptor + int m_descriptorSize; ///< The size of each descriptor +}; + +/// A host-visible descriptor, used as "backing storage" for a view. +/// +/// This type is intended to be used to represent descriptors that +/// are allocated and freed through a `HostVisibleDescriptorAllocator`. +struct D3D12HostVisibleDescriptor +{ + D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle; +}; + +/// An allocator for host-visible descriptors. +/// +/// Unlike the `D3D12DescriptorHeap` type, this class allows for both +/// allocation and freeing of descriptors, by maintaining a free list. +/// In order to keep the implementation simple, this class only supports +/// allocation of single descriptors and not ranges. +/// +class D3D12HostVisibleDescriptorAllocator +{ + ID3D12Device* m_device; + int m_chunkSize; + D3D12_DESCRIPTOR_HEAP_TYPE m_type; + + D3D12DescriptorHeap m_heap; + Slang::List m_freeList; + Slang::List m_heaps; + +public: + D3D12HostVisibleDescriptorAllocator() + {} + + Slang::Result init(ID3D12Device* device, int chunkSize, D3D12_DESCRIPTOR_HEAP_TYPE type) + { + m_device = device; + m_chunkSize = chunkSize; + m_type = type; + + SLANG_RETURN_ON_FAIL(m_heap.init(m_device, m_chunkSize, m_type, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); + + return SLANG_OK; + } + + Slang::Result allocate(D3D12HostVisibleDescriptor* outDescriptor) + { + // TODO: this allocator would take some work to make thread-safe + + if(m_freeList.getCount() > 0) + { + auto descriptor = m_freeList[0]; + m_freeList.fastRemoveAt(0); + + *outDescriptor = descriptor; + return SLANG_OK; + } + + int index = m_heap.allocate(); + if(index < 0) + { + // Allocate a new heap and try again. + m_heaps.add(m_heap); + SLANG_RETURN_ON_FAIL(m_heap.init(m_device, m_chunkSize, m_type, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); + + int index = m_heap.allocate(); + if(index < 0) + { + assert(!"descriptor allocation failed on fresh heap"); + return SLANG_FAIL; + } + } + + D3D12HostVisibleDescriptor descriptor; + descriptor.cpuHandle = m_heap.getCpuHandle(index); + + *outDescriptor = descriptor; + return SLANG_OK; + } + + void free(D3D12HostVisibleDescriptor descriptor) + { + m_freeList.add(descriptor); + } +}; + +// --------------------------------------------------------------------------- +int D3D12DescriptorHeap::allocate() +{ + assert(m_currentIndex < m_totalSize); + if (m_currentIndex < m_totalSize) + { + return m_currentIndex++; + } + return -1; +} +// --------------------------------------------------------------------------- +int D3D12DescriptorHeap::allocate(int numDescriptors) +{ + assert(m_currentIndex + numDescriptors <= m_totalSize); + if (m_currentIndex + numDescriptors <= m_totalSize) + { + const int index = m_currentIndex; + m_currentIndex += numDescriptors; + return index; + } + return -1; +} +// --------------------------------------------------------------------------- +SLANG_FORCE_INLINE int D3D12DescriptorHeap::placeAt(int index) +{ + assert(index >= 0 && index < m_totalSize); + m_currentIndex = index + 1; + return index; +} + +// --------------------------------------------------------------------------- +SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getCpuHandle(int index) const +{ + assert(index >= 0 && index < m_totalSize); + D3D12_CPU_DESCRIPTOR_HANDLE start = m_heap->GetCPUDescriptorHandleForHeapStart(); + D3D12_CPU_DESCRIPTOR_HANDLE dst; + dst.ptr = start.ptr + m_descriptorSize * index; + return dst; +} +// --------------------------------------------------------------------------- +SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getGpuHandle(int index) const +{ + assert(index >= 0 && index < m_totalSize); + D3D12_GPU_DESCRIPTOR_HANDLE start = m_heap->GetGPUDescriptorHandleForHeapStart(); + D3D12_GPU_DESCRIPTOR_HANDLE dst; + dst.ptr = start.ptr + m_descriptorSize * index; + return dst; +} + +} // namespace gfx + diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp new file mode 100644 index 000000000..ad7b898f5 --- /dev/null +++ b/tools/gfx/d3d12/render-d3d12.cpp @@ -0,0 +1,3722 @@ +// render-d3d12.cpp +#define _CRT_SECURE_NO_WARNINGS + +#include "render-d3d12.h" + +//WORKING:#include "options.h" +#include "../render.h" + +#include "../surface.h" + +// In order to use the Slang API, we need to include its header + +//WORKING:#include + +// We will be rendering with Direct3D 12, so we need to include +// the Windows and D3D12 headers + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#include +#include +#include + +#include "../../slang-com-ptr.h" +#include "../flag-combiner.h" + +#include "resource-d3d12.h" +#include "descriptor-heap-d3d12.h" +#include "circular-resource-heap-d3d12.h" + +#include "../d3d/d3d-util.h" + +// We will use the C standard library just for printing error messages. +#include + +#ifdef _MSC_VER +#include +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif +// + +#define ENABLE_DEBUG_LAYER 1 + +namespace gfx { +using namespace Slang; + +class D3D12Renderer : public Renderer +{ +public: + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual const List& getFeatures() override { return m_features; } + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + TextureResource::Desc getSwapChainTextureDesc() override; + + Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; + Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; + Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; + + Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; + Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; + + Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; + + Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; + Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; + Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; + + Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; + Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; + Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; + + virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; +// virtual void setInputLayout(InputLayout* inputLayout) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; + virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + void setViewports(UInt count, Viewport const* viewports) override; + void setScissorRects(UInt count, ScissorRect const* rects) override; + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override; + virtual void waitForGpu() override; + virtual RendererType getRendererType() const override { return RendererType::DirectX12; } + + ~D3D12Renderer(); + +protected: + + static const Int kMaxNumRenderFrames = 4; + static const Int kMaxNumRenderTargets = 3; + + static const Int kMaxRTVCount = 8; + static const Int kMaxDescriptorSetCount = 16; + + struct DeviceInfo + { + void clear() + { + m_dxgiFactory.setNull(); + m_device.setNull(); + m_adapter.setNull(); + m_desc = {}; + m_desc1 = {}; + m_isWarp = false; + } + + bool m_isWarp; + ComPtr m_dxgiFactory; + ComPtr m_device; + ComPtr m_adapter; + DXGI_ADAPTER_DESC m_desc; + DXGI_ADAPTER_DESC1 m_desc1; + }; + + struct Submitter + { + virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0; + virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor) = 0; + virtual void setRootSignature(ID3D12RootSignature* rootSignature) = 0; + }; + + struct FrameInfo + { + FrameInfo() :m_fenceValue(0) {} + void reset() + { + m_commandAllocator.setNull(); + } + ComPtr m_commandAllocator; ///< The command allocator for this frame + UINT64 m_fenceValue; ///< The fence value when rendering this Frame is complete + }; + + class ShaderProgramImpl: public ShaderProgram + { + public: + PipelineType m_pipelineType; + List m_vertexShader; + List m_pixelShader; + List m_computeShader; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + enum class BackingStyle + { + Unknown, + ResourceBacked, ///< The contents is only held within the resource + MemoryBacked, ///< The current contents is held in m_memory and copied to GPU every time it's used (typically used for constant buffers) + }; + + void bindConstantBufferView(D3D12CircularResourceHeap& circularHeap, int index, Submitter* submitter) const + { + switch (m_backingStyle) + { + case BackingStyle::MemoryBacked: + { + const size_t bufferSize = m_memory.getCount(); + D3D12CircularResourceHeap::Cursor cursor = circularHeap.allocateConstantBuffer(bufferSize); + ::memcpy(cursor.m_position, m_memory.getBuffer(), bufferSize); + // Set the constant buffer + submitter->setRootConstantBufferView(index, circularHeap.getGpuHandle(cursor)); + break; + } + case BackingStyle::ResourceBacked: + { + // Set the constant buffer + submitter->setRootConstantBufferView(index, m_resource.getResource()->GetGPUVirtualAddress()); + break; + } + default: break; + } + } + + BufferResourceImpl(Resource::Usage initialUsage, const Desc& desc): + Parent(desc), + m_mapFlavor(MapFlavor::HostRead), + m_initialUsage(initialUsage) + { + } + + static BackingStyle _calcResourceBackingStyle(Usage usage) + { + // Note: the D3D12 back-end has support for "versioning" of constant buffers, + // where the same logical `BufferResource` can actually point to different + // backing storage over its lifetime, to emulate the ability to modify the + // buffer contents as in D3D11, etc. + // + // The VK back-end doesn't have the same behavior, and it is difficult + // to both support this degree of flexibility *and* efficeintly exploit + // descriptor tables (since any table referencing the buffer would need + // to be updated when a new buffer "version" gets allocated). + // + // I'm choosing to disable this for now, and make all buffers be memory-backed, + // although this creates synchronization issues that we'll have to address + // next. + + return BackingStyle::ResourceBacked; +#if 0 + switch (usage) + { + case Usage::ConstantBuffer: return BackingStyle::MemoryBacked; + default: return BackingStyle::ResourceBacked; + } +#endif + } + + BackingStyle m_backingStyle; ///< How the resource is 'backed' - either as a resource or cpu memory. Cpu memory is typically used for constant buffers. + D3D12Resource m_resource; ///< The resource typically in gpu memory + D3D12Resource m_uploadResource; ///< If the resource can be written to, and is in gpu memory (ie not Memory backed), will have upload resource + + Usage m_initialUsage; + + List m_memory; ///< Cpu memory buffer, used if the m_backingStyle is MemoryBacked + MapFlavor m_mapFlavor; ///< If the resource is mapped holds the current mapping flavor + }; + + class TextureResourceImpl: public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(const Desc& desc): + Parent(desc) + { + } + + D3D12Resource m_resource; + }; + + class SamplerStateImpl : public SamplerState + { + public: + D3D12_CPU_DESCRIPTOR_HANDLE m_cpuHandle; + }; + + class ResourceViewImpl : public ResourceView + { + public: + RefPtr m_resource; + D3D12HostVisibleDescriptor m_descriptor; + }; + + class InputLayoutImpl: public InputLayout + { + public: + List m_elements; + List m_text; ///< Holds all strings to keep in scope + }; + +#if 0 + struct BindingDetail + { + int m_srvIndex = -1; + int m_uavIndex = -1; + int m_samplerIndex = -1; + }; + + class BindingStateImpl: public BindingState + { + public: + typedef BindingState Parent; + + Result init(ID3D12Device* device) + { + // Set up descriptor heaps + SLANG_RETURN_ON_FAIL(m_viewHeap.init(device, 256, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); + SLANG_RETURN_ON_FAIL(m_samplerHeap.init(device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); + return SLANG_OK; + } + + /// Ctor + BindingStateImpl(const Desc& desc) : + Parent(desc) + {} + + List m_bindingDetails; ///< These match 1-1 to the bindings in the m_desc + }; +#endif + + class DescriptorSetLayoutImpl : public DescriptorSetLayout + { + public: + struct RangeInfo + { + DescriptorSlotType type; + Int count; + Int arrayIndex; + }; + + List m_ranges; + + List m_dxRanges; + List m_dxRootParameters; + + Int m_resourceCount; + Int m_samplerCount; + }; + + class PipelineLayoutImpl : public PipelineLayout + { + public: + ComPtr m_rootSignature; + UInt m_descriptorSetCount; + }; + + class DescriptorSetImpl : public DescriptorSet + { + public: + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; + virtual void setResource(UInt range, UInt index, ResourceView* view) override; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) override; + + D3D12Renderer* m_renderer = nullptr; ///< Weak pointer - must be because if set on Renderer, will have a circular reference + RefPtr m_layout; + + D3D12DescriptorHeap* m_resourceHeap = nullptr; + D3D12DescriptorHeap* m_samplerHeap = nullptr; + + Int m_resourceTable = 0; + Int m_samplerTable = 0; + + // The following arrays are used to retain the relevant + // objects so that they will not be released while this + // descriptor-set is still alive. + // + // For the `m_resourceObjects` array, the values are either + // the relevant `ResourceViewImpl` for SRV/UAV slots, or + // a `BufferResourceImpl` for a CBV slot. + // + List> m_resourceObjects; + List> m_samplerObjects; + }; + + + // During command submission, we need all the descriptor tables that get + // used to come from a single heap (for each descriptor heap type). + // + // We will thus keep a single heap of each type that we hope will hold + // all the descriptors that actually get needed in a frame. + // + // TODO: we need an allocation policy to reallocate and resize these + // if/when we run out of space during a frame. + // + D3D12DescriptorHeap m_viewHeap; ///< Cbv, Srv, Uav + D3D12DescriptorHeap m_samplerHeap; ///< Heap for samplers + + D3D12HostVisibleDescriptorAllocator m_rtvAllocator; + D3D12HostVisibleDescriptorAllocator m_dsvAllocator; + + D3D12HostVisibleDescriptorAllocator m_viewAllocator; + D3D12HostVisibleDescriptorAllocator m_samplerAllocator; + + // Space in the GPU-visible heaps is precious, so we will also keep + // around CPU-visible heaps for storing descriptors in a format + // that is ready for copying into the GPU-visible heaps as needed. + // + D3D12DescriptorHeap m_cpuViewHeap; ///< Cbv, Srv, Uav + D3D12DescriptorHeap m_cpuSamplerHeap; ///< Heap for samplers + + class PipelineStateImpl : public PipelineState + { + public: + PipelineType m_pipelineType; + RefPtr m_pipelineLayout; + ComPtr m_pipelineState; + }; + + struct BoundVertexBuffer + { + RefPtr m_buffer; + int m_stride; + int m_offset; + }; + +#if 0 + struct BindParameters + { + enum + { + kMaxRanges = 16, + kMaxParameters = 32 + }; + + D3D12_DESCRIPTOR_RANGE& nextRange() { return m_ranges[m_rangeIndex++]; } + D3D12_ROOT_PARAMETER& nextParameter() { return m_parameters[m_paramIndex++]; } + + BindParameters(): + m_rangeIndex(0), + m_paramIndex(0) + {} + + D3D12_DESCRIPTOR_RANGE m_ranges[kMaxRanges]; + int m_rangeIndex; + D3D12_ROOT_PARAMETER m_parameters[kMaxParameters]; + int m_paramIndex; + }; +#endif + + struct GraphicsSubmitter : public Submitter + { + virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override + { + m_commandList->SetGraphicsRootConstantBufferView(index, gpuBufferLocation); + } + virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override + { + m_commandList->SetGraphicsRootDescriptorTable(index, baseDescriptor); + } + void setRootSignature(ID3D12RootSignature* rootSignature) + { + m_commandList->SetGraphicsRootSignature(rootSignature); + } + + GraphicsSubmitter(ID3D12GraphicsCommandList* commandList): + m_commandList(commandList) + { + } + + ID3D12GraphicsCommandList* m_commandList; + }; + + struct ComputeSubmitter : public Submitter + { + virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override + { + m_commandList->SetComputeRootConstantBufferView(index, gpuBufferLocation); + } + virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override + { + m_commandList->SetComputeRootDescriptorTable(index, baseDescriptor); + } + void setRootSignature(ID3D12RootSignature* rootSignature) + { + m_commandList->SetComputeRootSignature(rootSignature); + } + + ComputeSubmitter(ID3D12GraphicsCommandList* commandList) : + m_commandList(commandList) + { + } + + ID3D12GraphicsCommandList* m_commandList; + }; + + static PROC loadProc(HMODULE module, char const* name); + Result createFrameResources(); + /// Blocks until gpu has completed all work + void releaseFrameResources(); + + Result createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, size_t srcDataSize, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut); + + void beginRender(); + + void endRender(); + + void submitGpuWorkAndWait(); + void _resetCommandList(); + + Result captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut); + + FrameInfo& getFrame() { return m_frameInfos[m_frameIndex]; } + const FrameInfo& getFrame() const { return m_frameInfos[m_frameIndex]; } + + ID3D12GraphicsCommandList* getCommandList() const { return m_commandList; } + +// RenderState* calcRenderState(); + + /// From current bindings calculate the root signature and pipeline state +// Result calcGraphicsPipelineState(ComPtr& sigOut, ComPtr& pipelineStateOut); +// Result calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut); + + Result _bindRenderState(PipelineStateImpl* pipelineStateImpl, ID3D12GraphicsCommandList* commandList, Submitter* submitter); + +// Result _calcBindParameters(BindParameters& params); +// RenderState* findRenderState(PipelineType pipelineType); + + Result _createDevice(DeviceCheckFlags deviceCheckFlags, const UnownedStringSlice& nameMatch, D3D_FEATURE_LEVEL featureLevel, DeviceInfo& outDeviceInfo); + + D3D12CircularResourceHeap m_circularResourceHeap; + + int m_commandListOpenCount = 0; ///< If >0 the command list should be open + + List m_boundVertexBuffers; + + RefPtr m_boundIndexBuffer; + DXGI_FORMAT m_boundIndexFormat; + UINT m_boundIndexOffset; + + RefPtr m_currentPipelineState; + +// RefPtr m_boundShaderProgram; +// RefPtr m_boundInputLayout; + +// RefPtr m_boundBindingState; + RefPtr m_boundDescriptorSets[int(PipelineType::CountOf)][kMaxDescriptorSetCount]; + + DXGI_FORMAT m_targetFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + DXGI_FORMAT m_depthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; + bool m_hasVsync = true; + bool m_isFullSpeed = false; + bool m_allowFullScreen = false; + bool m_isMultiSampled = false; + int m_numTargetSamples = 1; ///< The number of multi sample samples + int m_targetSampleQuality = 0; ///< The multi sample quality + + Desc m_desc; + + bool m_isInitialized = false; + + D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + D3D12_PRIMITIVE_TOPOLOGY m_primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + + float m_clearColor[4] = { 0, 0, 0, 0 }; + + D3D12_VIEWPORT m_viewport = {}; + + ComPtr m_dxDebug; + + DeviceInfo m_deviceInfo; + ID3D12Device* m_device = nullptr; + + ComPtr m_swapChain; + ComPtr m_commandQueue; +// ComPtr m_rtvHeap; + ComPtr m_commandList; + + D3D12_RECT m_scissorRect = {}; + +// List > m_renderStates; ///< Holds list of all render state combinations +// RenderState* m_currentRenderState = nullptr; ///< The current combination + + UINT m_rtvDescriptorSize = 0; + +// ComPtr m_dsvHeap; + UINT m_dsvDescriptorSize = 0; + + // Synchronization objects. + D3D12CounterFence m_fence; + + HANDLE m_swapChainWaitableObject; + + // Frame specific data + int m_numRenderFrames = 0; + UINT m_frameIndex = 0; + FrameInfo m_frameInfos[kMaxNumRenderFrames]; + + int m_numRenderTargets = 2; + int m_renderTargetIndex = 0; + + D3D12Resource* m_backBuffers[kMaxNumRenderTargets]; + D3D12Resource* m_renderTargets[kMaxNumRenderTargets]; + + D3D12Resource m_backBufferResources[kMaxNumRenderTargets]; + D3D12Resource m_renderTargetResources[kMaxNumRenderTargets]; + + + RefPtr m_rtvs[kMaxRTVCount]; + RefPtr m_dsv; + + int32_t m_depthStencilUsageFlags = 0; ///< D3DUtil::UsageFlag combination for depth stencil + int32_t m_targetUsageFlags = 0; ///< D3DUtil::UsageFlag combination for target + + // Dll entry points + PFN_D3D12_GET_DEBUG_INTERFACE m_D3D12GetDebugInterface = nullptr; + PFN_D3D12_CREATE_DEVICE m_D3D12CreateDevice = nullptr; + PFN_D3D12_SERIALIZE_ROOT_SIGNATURE m_D3D12SerializeRootSignature = nullptr; + + HWND m_hwnd = nullptr; + + List m_features; +}; + +Renderer* createD3D12Renderer() +{ + return new D3D12Renderer; +} + +/* static */PROC D3D12Renderer::loadProc(HMODULE module, char const* name) +{ + PROC proc = ::GetProcAddress(module, name); + if (!proc) + { + fprintf(stderr, "error: failed load symbol '%s'\n", name); + return nullptr; + } + return proc; +} + +void D3D12Renderer::releaseFrameResources() +{ + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb174577%28v=vs.85%29.aspx + + // Release the resources holding references to the swap chain (requirement of + // IDXGISwapChain::ResizeBuffers) and reset the frame fence values to the + // current fence value. + for (int i = 0; i < m_numRenderFrames; i++) + { + FrameInfo& info = m_frameInfos[i]; + info.reset(); + info.m_fenceValue = m_fence.getCurrentValue(); + } + for (int i = 0; i < m_numRenderTargets; i++) + { + m_backBuffers[i]->setResourceNull(); + m_renderTargets[i]->setResourceNull(); + } +} + +void D3D12Renderer::waitForGpu() +{ + m_fence.nextSignalAndWait(m_commandQueue); +} + +D3D12Renderer::~D3D12Renderer() +{ + if (m_isInitialized) + { + // Ensure that the GPU is no longer referencing resources that are about to be + // cleaned up by the destructor. + waitForGpu(); + } +} + +static void _initSrvDesc(Resource::Type resourceType, const TextureResource::Desc& textureDesc, const D3D12_RESOURCE_DESC& desc, DXGI_FORMAT pixelFormat, D3D12_SHADER_RESOURCE_VIEW_DESC& descOut) +{ + // create SRV + descOut = D3D12_SHADER_RESOURCE_VIEW_DESC(); + + descOut.Format = (pixelFormat == DXGI_FORMAT_UNKNOWN) ? D3DUtil::calcFormat(D3DUtil::USAGE_SRV, desc.Format) : pixelFormat; + descOut.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + if (desc.DepthOrArraySize == 1) + { + switch (desc.Dimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; break; + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; break; + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; break; + default: assert(!"Unknown dimension"); + } + + descOut.Texture2D.MipLevels = desc.MipLevels; + descOut.Texture2D.MostDetailedMip = 0; + descOut.Texture2D.PlaneSlice = 0; + descOut.Texture2D.ResourceMinLODClamp = 0.0f; + } + else if (resourceType == Resource::Type::TextureCube) + { + if (textureDesc.arraySize > 1) + { + descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; + + descOut.TextureCubeArray.NumCubes = textureDesc.arraySize; + descOut.TextureCubeArray.First2DArrayFace = 0; + descOut.TextureCubeArray.MipLevels = desc.MipLevels; + descOut.TextureCubeArray.MostDetailedMip = 0; + descOut.TextureCubeArray.ResourceMinLODClamp = 0; + } + else + { + descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; + + descOut.TextureCube.MipLevels = desc.MipLevels; + descOut.TextureCube.MostDetailedMip = 0; + descOut.TextureCube.ResourceMinLODClamp = 0; + } + } + else + { + assert(desc.DepthOrArraySize > 1); + + switch (desc.Dimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY; break; + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; break; + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; break; + + default: assert(!"Unknown dimension"); + } + + descOut.Texture2DArray.ArraySize = desc.DepthOrArraySize; + descOut.Texture2DArray.MostDetailedMip = 0; + descOut.Texture2DArray.MipLevels = desc.MipLevels; + descOut.Texture2DArray.FirstArraySlice = 0; + descOut.Texture2DArray.PlaneSlice = 0; + descOut.Texture2DArray.ResourceMinLODClamp = 0; + } +} + +static void _initBufferResourceDesc(size_t bufferSize, D3D12_RESOURCE_DESC& out) +{ + out = {}; + + out.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + out.Alignment = 0; + out.Width = bufferSize; + out.Height = 1; + out.DepthOrArraySize = 1; + out.MipLevels = 1; + out.Format = DXGI_FORMAT_UNKNOWN; + out.SampleDesc.Count = 1; + out.SampleDesc.Quality = 0; + out.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + out.Flags = D3D12_RESOURCE_FLAG_NONE; +} + +Result D3D12Renderer::createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, size_t srcDataSize, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut) +{ + const size_t bufferSize = size_t(resourceDesc.Width); + + { + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + const D3D12_RESOURCE_STATES initialState = srcData ? D3D12_RESOURCE_STATE_COPY_DEST : finalState; + + SLANG_RETURN_ON_FAIL(resourceOut.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, initialState, nullptr)); + } + + { + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC uploadResourceDesc(resourceDesc); + uploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + SLANG_RETURN_ON_FAIL(uploadResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, uploadResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr)); + } + + if (srcData) + { + // Copy data to the intermediate upload heap and then schedule a copy + // from the upload heap to the vertex buffer. + UINT8* dstData; + D3D12_RANGE readRange = {}; // We do not intend to read from this resource on the CPU. + + ID3D12Resource* dxUploadResource = uploadResource.getResource(); + + SLANG_RETURN_ON_FAIL(dxUploadResource->Map(0, &readRange, reinterpret_cast(&dstData))); + ::memcpy(dstData, srcData, srcDataSize); + dxUploadResource->Unmap(0, nullptr); + + m_commandList->CopyBufferRegion(resourceOut, 0, uploadResource, 0, bufferSize); + + // Make sure it's in the right state + { + D3D12BarrierSubmitter submitter(m_commandList); + resourceOut.transition(finalState, submitter); + } + + submitGpuWorkAndWait(); + } + + return SLANG_OK; +} + +void D3D12Renderer::_resetCommandList() +{ + const FrameInfo& frame = getFrame(); + + ID3D12GraphicsCommandList* commandList = getCommandList(); + commandList->Reset(frame.m_commandAllocator, nullptr); + + // TIM: when should this get set? +// commandList->OMSetRenderTargets( +// 1, +// &m_rtvs[0]->m_descriptor.cpuHandle, +// FALSE, +// m_dsv ? &m_dsv->m_descriptor.cpuHandle : nullptr); + + // Set necessary state. + commandList->RSSetViewports(1, &m_viewport); + commandList->RSSetScissorRects(1, &m_scissorRect); +} + +void D3D12Renderer::beginRender() +{ + // Should currently not be open! + assert(m_commandListOpenCount == 0); + + m_circularResourceHeap.updateCompleted(); + + getFrame().m_commandAllocator->Reset(); + + _resetCommandList(); + + // Indicate that the render target needs to be writable + { + D3D12BarrierSubmitter submitter(m_commandList); + m_renderTargets[m_renderTargetIndex]->transition(D3D12_RESOURCE_STATE_RENDER_TARGET, submitter); + } + + m_commandListOpenCount = 1; +} + +void D3D12Renderer::endRender() +{ + assert(m_commandListOpenCount == 1); + + { + const UInt64 signalValue = m_fence.nextSignal(m_commandQueue); + m_circularResourceHeap.addSync(signalValue); + } + + D3D12Resource& backBuffer = *m_backBuffers[m_renderTargetIndex]; + if (m_isMultiSampled) + { + // MSAA resolve + D3D12Resource& renderTarget = *m_renderTargets[m_renderTargetIndex]; + assert(&renderTarget != &backBuffer); + // Barriers to wait for the render target, and the backbuffer to be in correct state + { + D3D12BarrierSubmitter submitter(m_commandList); + renderTarget.transition(D3D12_RESOURCE_STATE_RESOLVE_SOURCE, submitter); + backBuffer.transition(D3D12_RESOURCE_STATE_RESOLVE_DEST, submitter); + } + + // Do the resolve... + m_commandList->ResolveSubresource(backBuffer, 0, renderTarget, 0, m_targetFormat); + } + + // Make the back buffer presentable + { + D3D12BarrierSubmitter submitter(m_commandList); + backBuffer.transition(D3D12_RESOURCE_STATE_PRESENT, submitter); + } + + SLANG_ASSERT_VOID_ON_FAIL(m_commandList->Close()); + + { + // Execute the command list. + ID3D12CommandList* commandLists[] = { m_commandList }; + m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists); + } + + assert(m_commandListOpenCount == 1); + // Must be 0 + m_commandListOpenCount = 0; +} + +void D3D12Renderer::submitGpuWork() +{ + assert(m_commandListOpenCount); + ID3D12GraphicsCommandList* commandList = getCommandList(); + + SLANG_ASSERT_VOID_ON_FAIL(commandList->Close()); + { + // Execute the command list. + ID3D12CommandList* commandLists[] = { commandList }; + m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists); + } + + // Reset the render target + _resetCommandList(); +} + +void D3D12Renderer::submitGpuWorkAndWait() +{ + submitGpuWork(); + waitForGpu(); +} + +Result D3D12Renderer::captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut) +{ + const D3D12_RESOURCE_STATES initialState = resource.getState(); + + const D3D12_RESOURCE_DESC desc = resource.getResource()->GetDesc(); + + // Don't bother supporting MSAA for right now + if (desc.SampleDesc.Count > 1) + { + fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); + return SLANG_FAIL; + } + + size_t bytesPerPixel = sizeof(uint32_t); + size_t rowPitch = int(desc.Width) * bytesPerPixel; + size_t bufferSize = rowPitch * int(desc.Height); + + D3D12Resource stagingResource; + { + D3D12_RESOURCE_DESC stagingDesc; + _initBufferResourceDesc(bufferSize, stagingDesc); + + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_READBACK; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + SLANG_RETURN_ON_FAIL(stagingResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); + } + + { + D3D12BarrierSubmitter submitter(m_commandList); + resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter); + } + + // Do the copy + { + D3D12_TEXTURE_COPY_LOCATION srcLoc; + srcLoc.pResource = resource; + srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + srcLoc.SubresourceIndex = 0; + + D3D12_TEXTURE_COPY_LOCATION dstLoc; + dstLoc.pResource = stagingResource; + dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + dstLoc.PlacedFootprint.Offset = 0; + dstLoc.PlacedFootprint.Footprint.Format = desc.Format; + dstLoc.PlacedFootprint.Footprint.Width = UINT(desc.Width); + dstLoc.PlacedFootprint.Footprint.Height = UINT(desc.Height); + dstLoc.PlacedFootprint.Footprint.Depth = 1; + dstLoc.PlacedFootprint.Footprint.RowPitch = UINT(rowPitch); + + m_commandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr); + } + + { + D3D12BarrierSubmitter submitter(m_commandList); + resource.transition(initialState, submitter); + } + + // Submit the copy, and wait for copy to complete + submitGpuWorkAndWait(); + + { + ID3D12Resource* dxResource = stagingResource; + + UINT8* data; + D3D12_RANGE readRange = {0, bufferSize}; + + SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast(&data))); + + Result res = surfaceOut.set(int(desc.Width), int(desc.Height), Format::RGBA_Unorm_UInt8, int(rowPitch), data, SurfaceAllocator::getMallocAllocator()); + + dxResource->Unmap(0, nullptr); + return res; + } +} + +#if 0 +Result D3D12Renderer::calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut) +{ + BindParameters bindParameters; + _calcBindParameters(bindParameters); + + ComPtr rootSignature; + ComPtr pipelineState; + + { + D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc; + rootSignatureDesc.NumParameters = bindParameters.m_paramIndex; + rootSignatureDesc.pParameters = bindParameters.m_parameters; + rootSignatureDesc.NumStaticSamplers = 0; + rootSignatureDesc.pStaticSamplers = nullptr; + rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; + + ComPtr signature; + ComPtr error; + SLANG_RETURN_ON_FAIL(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef())); + SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef()))); + } + + { + // Describe and create the compute pipeline state object + D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {}; + computeDesc.pRootSignature = rootSignature; + computeDesc.CS = { m_boundShaderProgram->m_computeShader.getBuffer(), m_boundShaderProgram->m_computeShader.Count() }; + SLANG_RETURN_ON_FAIL(m_device->CreateComputePipelineState(&computeDesc, IID_PPV_ARGS(pipelineState.writeRef()))); + } + + signatureOut.swap(rootSignature); + pipelineStateOut.swap(pipelineState); + + return SLANG_OK; +} +#endif + +#if 0 +D3D12Renderer::RenderState* D3D12Renderer::findRenderState(PipelineType pipelineType) +{ + switch (pipelineType) + { + case PipelineType::Compute: + { + // Check if current state is a match + if (m_currentRenderState) + { + if (m_currentRenderState->m_bindingState == m_boundBindingState && + m_currentRenderState->m_shaderProgram == m_boundShaderProgram) + { + return m_currentRenderState; + } + } + + const int num = int(m_renderStates.Count()); + for (int i = 0; i < num; i++) + { + RenderState* renderState = m_renderStates[i]; + if (renderState->m_bindingState == m_boundBindingState && + renderState->m_shaderProgram == m_boundShaderProgram) + { + return renderState; + } + } + break; + } + case PipelineType::Graphics: + { + if (m_currentRenderState) + { + if (m_currentRenderState->m_bindingState == m_boundBindingState && + m_currentRenderState->m_inputLayout == m_boundInputLayout && + m_currentRenderState->m_shaderProgram == m_boundShaderProgram && + m_currentRenderState->m_primitiveTopologyType == m_primitiveTopologyType) + { + return m_currentRenderState; + } + } + // See if matches one in the list + { + const int num = int(m_renderStates.Count()); + for (int i = 0; i < num; i++) + { + RenderState* renderState = m_renderStates[i]; + if (renderState->m_bindingState == m_boundBindingState && + renderState->m_inputLayout == m_boundInputLayout && + renderState->m_shaderProgram == m_boundShaderProgram && + renderState->m_primitiveTopologyType == m_primitiveTopologyType) + { + // Okay we have a match + return renderState; + } + } + } + break; + } + default: break; + } + return nullptr; +} + +D3D12Renderer::RenderState* D3D12Renderer::calcRenderState() +{ + if (!m_boundShaderProgram) + { + return nullptr; + } + m_currentRenderState = findRenderState(m_boundShaderProgram->m_pipelineType); + if (m_currentRenderState) + { + return m_currentRenderState; + } + + ComPtr rootSignature; + ComPtr pipelineState; + + switch (m_boundShaderProgram->m_pipelineType) + { + case PipelineType::Compute: + { + if (SLANG_FAILED(calcComputePipelineState(rootSignature, pipelineState))) + { + return nullptr; + } + break; + } + case PipelineType::Graphics: + { + if (SLANG_FAILED(calcGraphicsPipelineState(rootSignature, pipelineState))) + { + return nullptr; + } + break; + } + default: return nullptr; + } + + RenderState* renderState = new RenderState; + + renderState->m_primitiveTopologyType = m_primitiveTopologyType; + renderState->m_bindingState = m_boundBindingState; + renderState->m_inputLayout = m_boundInputLayout; + renderState->m_shaderProgram = m_boundShaderProgram; + + renderState->m_rootSignature.swap(rootSignature); + renderState->m_pipelineState.swap(pipelineState); + + m_renderStates.Add(renderState); + + m_currentRenderState = renderState; + + return renderState; +} + +Result D3D12Renderer::_calcBindParameters(BindParameters& params) +{ + int numConstantBuffers = 0; + { + if (m_boundBindingState) + { + const int numBoundConstantBuffers = numConstantBuffers; + + const BindingState::Desc& bindingStateDesc = m_boundBindingState->getDesc(); + + const auto& bindings = bindingStateDesc.m_bindings; + const auto& details = m_boundBindingState->m_bindingDetails; + + const int numBindings = int(bindings.Count()); + + for (int i = 0; i < numBindings; i++) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + const int bindingIndex = binding.registerRange.getSingleIndex(); + + if (binding.bindingType == BindingType::Buffer) + { + assert(binding.resource && binding.resource->isBuffer()); + if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) + { + // Make sure it's not overlapping the ones we just statically defined + //assert(binding.m_binding < numBoundConstantBuffers); + + D3D12_ROOT_PARAMETER& param = params.nextParameter(); + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + D3D12_ROOT_DESCRIPTOR& descriptor = param.Descriptor; + descriptor.ShaderRegister = bindingIndex; + descriptor.RegisterSpace = 0; + + numConstantBuffers++; + } + } + + if (detail.m_srvIndex >= 0) + { + D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); + + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + range.NumDescriptors = 1; + range.BaseShaderRegister = bindingIndex; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_PARAMETER& param = params.nextParameter(); + + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; + table.NumDescriptorRanges = 1; + table.pDescriptorRanges = ⦥ + } + + if (detail.m_uavIndex >= 0) + { + D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); + + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + range.NumDescriptors = 1; + range.BaseShaderRegister = bindingIndex; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_PARAMETER& param = params.nextParameter(); + + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; + table.NumDescriptorRanges = 1; + table.pDescriptorRanges = ⦥ + } + } + } + } + + // All the samplers are in one continuous section of the sampler heap + if (m_boundBindingState && m_boundBindingState->m_samplerHeap.getUsedSize() > 0) + { + D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); + + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + range.NumDescriptors = m_boundBindingState->m_samplerHeap.getUsedSize(); + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_PARAMETER& param = params.nextParameter(); + + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; + table.NumDescriptorRanges = 1; + table.pDescriptorRanges = ⦥ + } + return SLANG_OK; +} +#endif + +Result D3D12Renderer::_bindRenderState(PipelineStateImpl* pipelineStateImpl, ID3D12GraphicsCommandList* commandList, Submitter* submitter) +{ + // TODO: we should only set some of this state as needed... + + auto pipelineTypeIndex = (int) pipelineStateImpl->m_pipelineType; + auto pipelineLayout = pipelineStateImpl->m_pipelineLayout; + + submitter->setRootSignature(pipelineLayout->m_rootSignature); + commandList->SetPipelineState(pipelineStateImpl->m_pipelineState); + + ID3D12DescriptorHeap* heaps[] = + { + m_viewHeap.getHeap(), + m_samplerHeap.getHeap(), + }; + commandList->SetDescriptorHeaps(SLANG_COUNT_OF(heaps), heaps); + + // We need to copy descriptors over from the descriptor sets + // (where they are stored in CPU-visible heaps) to the GPU-visible + // heaps so that they can be accessed by shader code. + + Int descriptorSetCount = pipelineLayout->m_descriptorSetCount; + Int rootParameterIndex = 0; + for(Int dd = 0; dd < descriptorSetCount; ++dd) + { + auto descriptorSet = m_boundDescriptorSets[pipelineTypeIndex][dd]; + auto descriptorSetLayout = descriptorSet->m_layout; + + // TODO: require that `descriptorSetLayout` is compatible with + // `pipelineLayout->descriptorSetlayouts[dd]`. + + { + if(auto descriptorCount = descriptorSetLayout->m_resourceCount) + { + auto& gpuHeap = m_viewHeap; + auto gpuDescriptorTable = gpuHeap.allocate(int(descriptorCount)); + + auto& cpuHeap = *descriptorSet->m_resourceHeap; + auto cpuDescriptorTable = descriptorSet->m_resourceTable; + + m_device->CopyDescriptorsSimple( + UINT(descriptorCount), + gpuHeap.getCpuHandle(gpuDescriptorTable), + cpuHeap.getCpuHandle(int(cpuDescriptorTable)), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + submitter->setRootDescriptorTable(int(rootParameterIndex++), gpuHeap.getGpuHandle(gpuDescriptorTable)); + } + } + { + if(auto descriptorCount = descriptorSetLayout->m_samplerCount) + { + auto& gpuHeap = m_samplerHeap; + auto gpuDescriptorTable = gpuHeap.allocate(int(descriptorCount)); + + auto& cpuHeap = *descriptorSet->m_samplerHeap; + auto cpuDescriptorTable = descriptorSet->m_samplerTable; + + m_device->CopyDescriptorsSimple( + UINT(descriptorCount), + gpuHeap.getCpuHandle(gpuDescriptorTable), + cpuHeap.getCpuHandle(int(cpuDescriptorTable)), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + + submitter->setRootDescriptorTable(int(rootParameterIndex++), gpuHeap.getGpuHandle(gpuDescriptorTable)); + } + } + } + + return SLANG_OK; +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +Result D3D12Renderer::_createDevice(DeviceCheckFlags deviceCheckFlags, const UnownedStringSlice& nameMatch, D3D_FEATURE_LEVEL featureLevel, DeviceInfo& outDeviceInfo) +{ + outDeviceInfo.clear(); + + ComPtr dxgiFactory; + SLANG_RETURN_ON_FAIL(D3DUtil::createFactory(deviceCheckFlags, dxgiFactory)); + + List> dxgiAdapters; + SLANG_RETURN_ON_FAIL(D3DUtil::findAdapters(deviceCheckFlags, nameMatch, dxgiFactory, dxgiAdapters)); + + ComPtr device; + ComPtr adapter; + + for (Index i = 0; i < dxgiAdapters.getCount(); ++i) + { + IDXGIAdapter* dxgiAdapter = dxgiAdapters[i]; + if (SLANG_SUCCEEDED(m_D3D12CreateDevice(dxgiAdapter, featureLevel, IID_PPV_ARGS(device.writeRef())))) + { + adapter = dxgiAdapter; + break; + } + } + + if (!device) + { + return SLANG_FAIL; + } + + if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug)) + { + m_dxDebug->EnableDebugLayer(); + + ComPtr infoQueue; + if (SLANG_SUCCEEDED(device->QueryInterface(infoQueue.writeRef()))) + { + // Make break + infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); + infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); + // infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true); + + // Apparently there is a problem with sm 6.3 with spurious errors, with debug layer enabled + D3D12_FEATURE_DATA_SHADER_MODEL featureShaderModel; + featureShaderModel.HighestShaderModel = D3D_SHADER_MODEL(0x63); + SLANG_SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &featureShaderModel, sizeof(featureShaderModel))); + + if (featureShaderModel.HighestShaderModel >= D3D_SHADER_MODEL(0x63)) + { + // Filter out any messages that cause issues + // TODO: Remove this when the debug layers work properly + D3D12_MESSAGE_ID messageIds[] = + { + // When the debug layer is enabled this error is triggered sometimes after a CopyDescriptorsSimple + // call The failed check validates that the source and destination ranges of the copy do not + // overlap. The check assumes descriptor handles are pointers to memory, but this is not always the + // case and the check fails (even though everything is okay). + D3D12_MESSAGE_ID_COPY_DESCRIPTORS_INVALID_RANGES, + }; + + // We filter INFO messages because they are way too many + D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO }; + + D3D12_INFO_QUEUE_FILTER infoQueueFilter = {}; + infoQueueFilter.DenyList.NumSeverities = SLANG_COUNT_OF(severities); + infoQueueFilter.DenyList.pSeverityList = severities; + infoQueueFilter.DenyList.NumIDs = SLANG_COUNT_OF(messageIds); + infoQueueFilter.DenyList.pIDList = messageIds; + + infoQueue->PushStorageFilter(&infoQueueFilter); + } + } + } + + // Get the descs + { + adapter->GetDesc(&outDeviceInfo.m_desc); + + // Look up GetDesc1 info + ComPtr adapter1; + if (SLANG_SUCCEEDED(adapter->QueryInterface(adapter1.writeRef()))) + { + adapter1->GetDesc1(&outDeviceInfo.m_desc1); + } + } + + // Save other info + outDeviceInfo.m_device = device; + outDeviceInfo.m_dxgiFactory = dxgiFactory; + outDeviceInfo.m_adapter = adapter; + outDeviceInfo.m_isWarp = D3DUtil::isWarp(dxgiFactory, adapter); + + return SLANG_OK; +} + +Result D3D12Renderer::initialize(const Desc& desc, void* inWindowHandle) +{ + m_hwnd = (HWND)inWindowHandle; + // Rather than statically link against D3D, we load it dynamically. + + HMODULE d3dModule = LoadLibraryA("d3d12.dll"); + if (!d3dModule) + { + fprintf(stderr, "error: failed load 'd3d12.dll'\n"); + return SLANG_FAIL; + } + + // Get all the dll entry points + m_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)loadProc(d3dModule, "D3D12SerializeRootSignature"); + if (!m_D3D12SerializeRootSignature) + { + return SLANG_FAIL; + } + +#if ENABLE_DEBUG_LAYER + m_D3D12GetDebugInterface = (PFN_D3D12_GET_DEBUG_INTERFACE)loadProc(d3dModule, "D3D12GetDebugInterface"); + if (m_D3D12GetDebugInterface) + { + if (SLANG_SUCCEEDED(m_D3D12GetDebugInterface(IID_PPV_ARGS(m_dxDebug.writeRef())))) + { +#if 0 + // Can enable for extra validation. NOTE! That d3d12 warns if you do.... + // D3D12 MESSAGE : Device Debug Layer Startup Options : GPU - Based Validation is enabled(disabled by default). + // This results in new validation not possible during API calls on the CPU, by creating patched shaders that have validation + // added directly to the shader. However, it can slow things down a lot, especially for applications with numerous + // PSOs.Time to see the first render frame may take several minutes. + // [INITIALIZATION MESSAGE #1016: CREATEDEVICE_DEBUG_LAYER_STARTUP_OPTIONS] + + ComPtr debug1; + if (SLANG_SUCCEEDED(m_dxDebug->QueryInterface(debug1.writeRef()))) + { + debug1->SetEnableGPUBasedValidation(true); + } +#endif + + m_dxDebug->EnableDebugLayer(); + } + } +#endif + + m_D3D12CreateDevice = (PFN_D3D12_CREATE_DEVICE)loadProc(d3dModule, "D3D12CreateDevice"); + if (!m_D3D12CreateDevice) + { + return SLANG_FAIL; + } + + FlagCombiner combiner; + // TODO: we should probably provide a command-line option + // to override UseDebug of default rather than leave it + // up to each back-end to specify. +#if ENABLE_DEBUG_LAYER + combiner.add(DeviceCheckFlag::UseDebug, ChangeType::OnOff); ///< First try debug then non debug +#else + combiner.add(DeviceCheckFlag::UseDebug, ChangeType::Off); ///< Don't bother with debug +#endif + combiner.add(DeviceCheckFlag::UseHardwareDevice, ChangeType::OnOff); ///< First try hardware, then reference + + const D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; + + const int numCombinations = combiner.getNumCombinations(); + for (int i = 0; i < numCombinations; ++i) + { + if (SLANG_SUCCEEDED(_createDevice(combiner.getCombination(i), desc.adapter.getUnownedSlice(), featureLevel, m_deviceInfo))) + { + break; + } + } + + if (!m_deviceInfo.m_adapter) + { + // Couldn't find an adapter + return SLANG_FAIL; + } + + // Set the device + m_device = m_deviceInfo.m_device; + + // Find what features are supported + { + // Check this is how this is laid out... + SLANG_COMPILE_TIME_ASSERT(D3D_SHADER_MODEL_6_0 == 0x60); + + { + D3D12_FEATURE_DATA_SHADER_MODEL featureShaderModel; + featureShaderModel.HighestShaderModel = D3D_SHADER_MODEL(0x62); + + // TODO: Currently warp causes a crash when using half, so disable for now + if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &featureShaderModel, sizeof(featureShaderModel))) && + m_deviceInfo.m_isWarp == false && + featureShaderModel.HighestShaderModel >= 0x62) + { + // With sm_6_2 we have half + m_features.add("half"); + } + } + // Check what min precision support we have + { + D3D12_FEATURE_DATA_D3D12_OPTIONS options; + if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)))) + { + auto minPrecisionSupport = options.MinPrecisionSupport; + } + } + } + + m_numRenderFrames = 3; + m_numRenderTargets = 2; + + m_desc = desc; + + // set viewport + { + m_viewport.Width = float(m_desc.width); + m_viewport.Height = float(m_desc.height); + m_viewport.MinDepth = 0; + m_viewport.MaxDepth = 1; + m_viewport.TopLeftX = 0; + m_viewport.TopLeftY = 0; + } + + { + m_scissorRect.left = 0; + m_scissorRect.top = 0; + m_scissorRect.right = m_desc.width; + m_scissorRect.bottom = m_desc.height; + } + + // Describe and create the command queue. + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + SLANG_RETURN_ON_FAIL(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(m_commandQueue.writeRef()))); + + // Describe the swap chain. + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferCount = m_numRenderTargets; + swapChainDesc.BufferDesc.Width = m_desc.width; + swapChainDesc.BufferDesc.Height = m_desc.height; + swapChainDesc.BufferDesc.Format = m_targetFormat; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.OutputWindow = m_hwnd; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.Windowed = TRUE; + + if (m_isFullSpeed) + { + m_hasVsync = false; + m_allowFullScreen = false; + } + + if (!m_hasVsync) + { + swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + } + + // Swap chain needs the queue so that it can force a flush on it. + ComPtr swapChain; + SLANG_RETURN_ON_FAIL(m_deviceInfo.m_dxgiFactory->CreateSwapChain(m_commandQueue, &swapChainDesc, swapChain.writeRef())); + SLANG_RETURN_ON_FAIL(swapChain->QueryInterface(m_swapChain.writeRef())); + + if (!m_hasVsync) + { + m_swapChainWaitableObject = m_swapChain->GetFrameLatencyWaitableObject(); + + int maxLatency = m_numRenderTargets - 2; + + // Make sure the maximum latency is in the range required by dx12 runtime + maxLatency = (maxLatency < 1) ? 1 : maxLatency; + maxLatency = (maxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS) ? DXGI_MAX_SWAP_CHAIN_BUFFERS : maxLatency; + + m_swapChain->SetMaximumFrameLatency(maxLatency); + } + + // This sample does not support fullscreen transitions. + SLANG_RETURN_ON_FAIL(m_deviceInfo.m_dxgiFactory->MakeWindowAssociation(m_hwnd, DXGI_MWA_NO_ALT_ENTER)); + + m_renderTargetIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // Create descriptor heaps. + + SLANG_RETURN_ON_FAIL(m_viewHeap.init (m_device, 256, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); + SLANG_RETURN_ON_FAIL(m_samplerHeap.init(m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); + + SLANG_RETURN_ON_FAIL(m_cpuViewHeap.init (m_device, 1024, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); + SLANG_RETURN_ON_FAIL(m_cpuSamplerHeap.init(m_device, 64, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); + + SLANG_RETURN_ON_FAIL(m_rtvAllocator.init (m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_RTV)); + SLANG_RETURN_ON_FAIL(m_dsvAllocator.init (m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_DSV)); + SLANG_RETURN_ON_FAIL(m_viewAllocator.init (m_device, 64, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); + SLANG_RETURN_ON_FAIL(m_samplerAllocator.init(m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)); + + // Setup frame resources + { + SLANG_RETURN_ON_FAIL(createFrameResources()); + } + + // Setup fence, and close the command list (as default state without begin/endRender is closed) + { + SLANG_RETURN_ON_FAIL(m_fence.init(m_device)); + // Create the command list. When command lists are created they are open, so close it. + FrameInfo& frame = m_frameInfos[m_frameIndex]; + SLANG_RETURN_ON_FAIL(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frame.m_commandAllocator, nullptr, IID_PPV_ARGS(m_commandList.writeRef()))); + m_commandList->Close(); + } + + { + D3D12CircularResourceHeap::Desc desc; + desc.init(); + // Define size + desc.m_blockSize = 65536; + // Set up the heap + m_circularResourceHeap.init(m_device, desc, &m_fence); + } + + // Setup for rendering + beginRender(); + + m_isInitialized = true; + return SLANG_OK; +} + +Result D3D12Renderer::createFrameResources() +{ + // Create back buffers + { +// D3D12_CPU_DESCRIPTOR_HANDLE rtvStart(m_rtvHeap->GetCPUDescriptorHandleForHeapStart()); + + // Work out target format + D3D12_RESOURCE_DESC resourceDesc; + { + ComPtr backBuffer; + SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, IID_PPV_ARGS(backBuffer.writeRef()))); + resourceDesc = backBuffer->GetDesc(); + } + const DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_TARGET, m_targetUsageFlags, resourceDesc.Format); + const DXGI_FORMAT targetFormat = D3DUtil::calcFormat(D3DUtil::USAGE_TARGET, resourceFormat); + + // Set the target format + m_targetFormat = targetFormat; + + // Create a RTV, and a command allocator for each frame. + for (int i = 0; i < m_numRenderTargets; i++) + { + // Get the back buffer + ComPtr backBuffer; + SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(UINT(i), IID_PPV_ARGS(backBuffer.writeRef()))); + + // Set up resource for back buffer + m_backBufferResources[i].setResource(backBuffer, D3D12_RESOURCE_STATE_COMMON); + m_backBuffers[i] = &m_backBufferResources[i]; + // Assume they are the same thing for now... + m_renderTargets[i] = &m_backBufferResources[i]; + + // If we are multi-sampling - create a render target separate from the back buffer + if (m_isMultiSampled) + { + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + D3D12_CLEAR_VALUE clearValue = {}; + clearValue.Format = m_targetFormat; + + // Don't know targets alignment, so just memory copy + ::memcpy(clearValue.Color, m_clearColor, sizeof(m_clearColor)); + + D3D12_RESOURCE_DESC desc(resourceDesc); + + desc.Format = resourceFormat; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc.SampleDesc.Count = m_numTargetSamples; + desc.SampleDesc.Quality = m_targetSampleQuality; + desc.Alignment = 0; + + SLANG_RETURN_ON_FAIL(m_renderTargetResources[i].initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, desc, D3D12_RESOURCE_STATE_RENDER_TARGET, &clearValue)); + m_renderTargets[i] = &m_renderTargetResources[i]; + } + + D3D12HostVisibleDescriptor rtvDescriptor; + SLANG_RETURN_ON_FAIL(m_rtvAllocator.allocate(&rtvDescriptor)); + + m_device->CreateRenderTargetView(*m_renderTargets[i], nullptr, rtvDescriptor.cpuHandle); + } + } + + // Set up frames + for (int i = 0; i < m_numRenderFrames; i++) + { + FrameInfo& frame = m_frameInfos[i]; + SLANG_RETURN_ON_FAIL(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(frame.m_commandAllocator.writeRef()))); + } + + { + D3D12_RESOURCE_DESC desc = m_backBuffers[0]->getResource()->GetDesc(); + assert(desc.Width == UINT64(m_desc.width) && desc.Height == UINT64(m_desc.height)); + } + + // Create the depth stencil view. + { + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_DEPTH_STENCIL, m_depthStencilUsageFlags, m_depthStencilFormat); + DXGI_FORMAT depthStencilFormat = D3DUtil::calcFormat(D3DUtil::USAGE_DEPTH_STENCIL, resourceFormat); + + // Set the depth stencil format + m_depthStencilFormat = depthStencilFormat; + + // Setup default clear + D3D12_CLEAR_VALUE clearValue = {}; + clearValue.Format = depthStencilFormat; + clearValue.DepthStencil.Depth = 1.0f; + clearValue.DepthStencil.Stencil = 0; + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + resourceDesc.Format = resourceFormat; + resourceDesc.Width = m_desc.width; + resourceDesc.Height = m_desc.height; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.SampleDesc.Count = m_numTargetSamples; + resourceDesc.SampleDesc.Quality = m_targetSampleQuality; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + resourceDesc.Alignment = 0; + +#if 0 + SLANG_RETURN_ON_FAIL(m_depthStencil.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue)); + + // Set the depth stencil + D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {}; + depthStencilDesc.Format = depthStencilFormat; + depthStencilDesc.ViewDimension = m_isMultiSampled ? D3D12_DSV_DIMENSION_TEXTURE2DMS : D3D12_DSV_DIMENSION_TEXTURE2D; + depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE; + + // Set up as the depth stencil view + m_device->CreateDepthStencilView(m_depthStencil, &depthStencilDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart()); + m_depthStencilView = m_dsvHeap->GetCPUDescriptorHandleForHeapStart(); +#endif + } + + m_viewport.Width = static_cast(m_desc.width); + m_viewport.Height = static_cast(m_desc.height); + m_viewport.MaxDepth = 1.0f; + + m_scissorRect.right = static_cast(m_desc.width); + m_scissorRect.bottom = static_cast(m_desc.height); + + return SLANG_OK; +} + +void D3D12Renderer::setClearColor(const float color[4]) +{ + memcpy(m_clearColor, color, sizeof(m_clearColor)); +} + +void D3D12Renderer::clearFrame() +{ + // Record commands + if(auto rtv = m_rtvs[0]) + { + m_commandList->ClearRenderTargetView(rtv->m_descriptor.cpuHandle, m_clearColor, 0, nullptr); + } + if (m_dsv) + { + m_commandList->ClearDepthStencilView(m_dsv->m_descriptor.cpuHandle, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); + } +} + +void D3D12Renderer::presentFrame() +{ + endRender(); + + if (m_swapChainWaitableObject) + { + // check if now is good time to present + // This doesn't wait - because the wait time is 0. If it returns WAIT_TIMEOUT it means that no frame is waiting to be be displayed + // so there is no point doing a present. + const bool shouldPresent = (WaitForSingleObjectEx(m_swapChainWaitableObject, 0, TRUE) != WAIT_TIMEOUT); + if (shouldPresent) + { + m_swapChain->Present(0, 0); + } + } + else + { + if (SLANG_FAILED(m_swapChain->Present(1, 0))) + { + assert(!"Problem presenting"); + beginRender(); + return; + } + } + + // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >= + m_frameInfos[m_frameIndex].m_fenceValue = m_fence.nextSignal(m_commandQueue); + + // increment frame index after signal + m_frameIndex = (m_frameIndex + 1) % m_numRenderFrames; + // Update the render target index. + m_renderTargetIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // On the current frame wait until it is completed + { + FrameInfo& frame = m_frameInfos[m_frameIndex]; + // If the next frame is not ready to be rendered yet, wait until it is ready. + m_fence.waitUntilCompleted(frame.m_fenceValue); + } + + // Setup such that rendering can restart + beginRender(); +} + +TextureResource::Desc D3D12Renderer::getSwapChainTextureDesc() +{ + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1); + + return desc; +} + +SlangResult D3D12Renderer::captureScreenSurface(Surface& surfaceOut) +{ + return captureTextureToSurface(*m_renderTargets[m_renderTargetIndex], surfaceOut); +} + +static D3D12_RESOURCE_STATES _calcResourceState(Resource::Usage usage) +{ + typedef Resource::Usage Usage; + switch (usage) + { + case Usage::VertexBuffer: return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; + case Usage::IndexBuffer: return D3D12_RESOURCE_STATE_INDEX_BUFFER; + case Usage::ConstantBuffer: return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; + case Usage::StreamOutput: return D3D12_RESOURCE_STATE_STREAM_OUT; + case Usage::RenderTarget: return D3D12_RESOURCE_STATE_RENDER_TARGET; + case Usage::DepthWrite: return D3D12_RESOURCE_STATE_DEPTH_WRITE; + case Usage::DepthRead: return D3D12_RESOURCE_STATE_DEPTH_READ; + case Usage::UnorderedAccess: return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + case Usage::PixelShaderResource: return D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + case Usage::NonPixelShaderResource: return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + case Usage::GenericRead: return D3D12_RESOURCE_STATE_GENERIC_READ; + default: return D3D12_RESOURCE_STATES(0); + } +} + +static D3D12_RESOURCE_FLAGS _calcResourceFlag(Resource::BindFlag::Enum bindFlag) +{ + typedef Resource::BindFlag BindFlag; + switch (bindFlag) + { + case BindFlag::RenderTarget: return D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + case BindFlag::DepthStencil: return D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + case BindFlag::UnorderedAccess: return D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + default: return D3D12_RESOURCE_FLAG_NONE; + } +} + +static D3D12_RESOURCE_FLAGS _calcResourceBindFlags(Resource::Usage initialUsage, int bindFlags) +{ + int dstFlags = 0; + while (bindFlags) + { + int lsb = bindFlags & -bindFlags; + + dstFlags |= _calcResourceFlag(Resource::BindFlag::Enum(lsb)); + bindFlags &= ~lsb; + } + return D3D12_RESOURCE_FLAGS(dstFlags); +} + +static D3D12_RESOURCE_DIMENSION _calcResourceDimension(Resource::Type type) +{ + switch (type) + { + case Resource::Type::Buffer: return D3D12_RESOURCE_DIMENSION_BUFFER; + case Resource::Type::Texture1D: return D3D12_RESOURCE_DIMENSION_TEXTURE1D; + case Resource::Type::TextureCube: + case Resource::Type::Texture2D: + { + return D3D12_RESOURCE_DIMENSION_TEXTURE2D; + } + case Resource::Type::Texture3D: return D3D12_RESOURCE_DIMENSION_TEXTURE3D; + default: return D3D12_RESOURCE_DIMENSION_UNKNOWN; + } +} + +Result D3D12Renderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) +{ + // Description of uploading on Dx12 + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn899215%28v=vs.85%29.aspx + + TextureResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + const DXGI_FORMAT pixelFormat = D3DUtil::getMapFormat(srcDesc.format); + if (pixelFormat == DXGI_FORMAT_UNKNOWN) + { + return SLANG_FAIL; + } + + const int arraySize = srcDesc.calcEffectiveArraySize(); + + const D3D12_RESOURCE_DIMENSION dimension = _calcResourceDimension(srcDesc.type); + if (dimension == D3D12_RESOURCE_DIMENSION_UNKNOWN) + { + return SLANG_FAIL; + } + + const int numMipMaps = srcDesc.numMipLevels; + + // Setup desc + D3D12_RESOURCE_DESC resourceDesc; + + resourceDesc.Dimension = dimension; + resourceDesc.Format = pixelFormat; + resourceDesc.Width = srcDesc.size.width; + resourceDesc.Height = srcDesc.size.height; + resourceDesc.DepthOrArraySize = (srcDesc.size.depth > 1) ? srcDesc.size.depth : arraySize; + + resourceDesc.MipLevels = numMipMaps; + resourceDesc.SampleDesc.Count = srcDesc.sampleDesc.numSamples; + resourceDesc.SampleDesc.Quality = srcDesc.sampleDesc.quality; + + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resourceDesc.Alignment = 0; + + RefPtr texture(new TextureResourceImpl(srcDesc)); + + // Create the target resource + { + D3D12_HEAP_PROPERTIES heapProps; + + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + SLANG_RETURN_ON_FAIL(texture->m_resource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); + + texture->m_resource.setDebugName(L"Texture"); + } + + // Calculate the layout + List layouts; + layouts.setCount(numMipMaps); + List mipRowSizeInBytes; + mipRowSizeInBytes.setCount(numMipMaps); + List mipNumRows; + mipNumRows.setCount(numMipMaps); + + // Since textures are effectively immutable currently initData must be set + assert(initData); + // We should have this many sub resources + assert(initData->numSubResources == numMipMaps * srcDesc.size.depth * arraySize); + + // NOTE! This is just the size for one array upload -> not for the whole texture + UInt64 requiredSize = 0; + m_device->GetCopyableFootprints(&resourceDesc, 0, numMipMaps, 0, layouts.begin(), mipNumRows.begin(), mipRowSizeInBytes.begin(), &requiredSize); + + // Sub resource indexing + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn705766(v=vs.85).aspx#subresource_indexing + { + // Create the upload texture + D3D12Resource uploadTexture; + + { + D3D12_HEAP_PROPERTIES heapProps; + + heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC uploadResourceDesc; + + uploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + uploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN; + uploadResourceDesc.Width = requiredSize; + uploadResourceDesc.Height = 1; + uploadResourceDesc.DepthOrArraySize = 1; + uploadResourceDesc.MipLevels = 1; + uploadResourceDesc.SampleDesc.Count = 1; + uploadResourceDesc.SampleDesc.Quality = 0; + uploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + uploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + uploadResourceDesc.Alignment = 0; + + SLANG_RETURN_ON_FAIL(uploadTexture.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, uploadResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr)); + + uploadTexture.setDebugName(L"TextureUpload"); + } + // Get the pointer to the upload resource + ID3D12Resource* uploadResource = uploadTexture; + + int subResourceIndex = 0; + for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) + { + uint8_t* p; + uploadResource->Map(0, nullptr, reinterpret_cast(&p)); + + for (int j = 0; j < numMipMaps; ++j) + { + const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& layout = layouts[j]; + const D3D12_SUBRESOURCE_FOOTPRINT& footprint = layout.Footprint; + + const TextureResource::Size mipSize = srcDesc.size.calcMipSize(j); + + assert(footprint.Width == mipSize.width && footprint.Height == mipSize.height && footprint.Depth == mipSize.depth); + + const ptrdiff_t dstMipRowPitch = ptrdiff_t(layouts[j].Footprint.RowPitch); + const ptrdiff_t srcMipRowPitch = ptrdiff_t(initData->mipRowStrides[j]); + + assert(dstMipRowPitch >= srcMipRowPitch); + + const uint8_t* srcRow = (const uint8_t*)initData->subResources[subResourceIndex]; + uint8_t* dstRow = p + layouts[j].Offset; + + // Copy the depth each mip + for (int l = 0; l < mipSize.depth; l++) + { + // Copy rows + for (int k = 0; k < mipSize.height; ++k) + { + ::memcpy(dstRow, srcRow, srcMipRowPitch); + + srcRow += srcMipRowPitch; + dstRow += dstMipRowPitch; + } + } + + //assert(srcRow == (const uint8_t*)(srcMip.getBuffer() + srcMip.getCount())); + } + uploadResource->Unmap(0, nullptr); + + for (int mipIndex = 0; mipIndex < numMipMaps; ++mipIndex) + { + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn903862(v=vs.85).aspx + + D3D12_TEXTURE_COPY_LOCATION src; + src.pResource = uploadTexture; + src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + src.PlacedFootprint = layouts[mipIndex]; + + D3D12_TEXTURE_COPY_LOCATION dst; + dst.pResource = texture->m_resource; + dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dst.SubresourceIndex = subResourceIndex; + m_commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); + + subResourceIndex++; + } + + // Block - waiting for copy to complete (so can drop upload texture) + submitGpuWorkAndWait(); + } + } + { + const D3D12_RESOURCE_STATES finalState = _calcResourceState(initialUsage); + D3D12BarrierSubmitter submitter(m_commandList); + texture->m_resource.transition(finalState, submitter); + + submitGpuWorkAndWait(); + } + + *outResource = texture.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) +{ + typedef BufferResourceImpl::BackingStyle Style; + + BufferResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + // Always align up to 256 bytes, since that is required for constant buffers. + // + // TODO: only do this for buffers that could potentially be bound as constant buffers... + // + const size_t alignedSizeInBytes = D3DUtil::calcAligned(srcDesc.sizeInBytes, 256); + + RefPtr buffer(new BufferResourceImpl(initialUsage, srcDesc)); + + // Save the style + buffer->m_backingStyle = BufferResourceImpl::_calcResourceBackingStyle(initialUsage); + + D3D12_RESOURCE_DESC bufferDesc; + _initBufferResourceDesc(alignedSizeInBytes, bufferDesc); + + bufferDesc.Flags = _calcResourceBindFlags(initialUsage, srcDesc.bindFlags); + + switch (buffer->m_backingStyle) + { + case Style::MemoryBacked: + { + // Assume the constant buffer will change every frame. We'll just keep a copy of the contents + // in regular memory until it needed + buffer->m_memory.setCount(UInt(alignedSizeInBytes)); + // Initialize + if (initData) + { + ::memcpy(buffer->m_memory.getBuffer(), initData, srcDesc.sizeInBytes); + } + break; + } + case Style::ResourceBacked: + { + const D3D12_RESOURCE_STATES initialState = _calcResourceState(initialUsage); + SLANG_RETURN_ON_FAIL(createBuffer(bufferDesc, initData, srcDesc.sizeInBytes, buffer->m_uploadResource, initialState, buffer->m_resource)); + break; + } + default: + return SLANG_FAIL; + } + + *outResource = buffer.detach(); + return SLANG_OK; +} + +D3D12_FILTER_TYPE translateFilterMode(TextureFilteringMode mode) +{ + switch (mode) + { + default: + return D3D12_FILTER_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureFilteringMode::SRC: return D3D12_FILTER_TYPE_##DST + + CASE(Point, POINT); + CASE(Linear, LINEAR); + +#undef CASE + } +} + +D3D12_FILTER_REDUCTION_TYPE translateFilterReduction(TextureReductionOp op) +{ + switch (op) + { + default: + return D3D12_FILTER_REDUCTION_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureReductionOp::SRC: return D3D12_FILTER_REDUCTION_TYPE_##DST + + CASE(Average, STANDARD); + CASE(Comparison, COMPARISON); + CASE(Minimum, MINIMUM); + CASE(Maximum, MAXIMUM); + +#undef CASE + } +} + +D3D12_TEXTURE_ADDRESS_MODE translateAddressingMode(TextureAddressingMode mode) +{ + switch (mode) + { + default: + return D3D12_TEXTURE_ADDRESS_MODE(0); + +#define CASE(SRC, DST) \ + case TextureAddressingMode::SRC: return D3D12_TEXTURE_ADDRESS_MODE_##DST + + CASE(Wrap, WRAP); + CASE(ClampToEdge, CLAMP); + CASE(ClampToBorder, BORDER); + CASE(MirrorRepeat, MIRROR); + CASE(MirrorOnce, MIRROR_ONCE); + +#undef CASE + } +} + +static D3D12_COMPARISON_FUNC translateComparisonFunc(ComparisonFunc func) +{ + switch (func) + { + default: + // TODO: need to report failures + return D3D12_COMPARISON_FUNC_ALWAYS; + +#define CASE(FROM, TO) \ + case ComparisonFunc::FROM: return D3D12_COMPARISON_FUNC_##TO + + CASE(Never, NEVER); + CASE(Less, LESS); + CASE(Equal, EQUAL); + CASE(LessEqual, LESS_EQUAL); + CASE(Greater, GREATER); + CASE(NotEqual, NOT_EQUAL); + CASE(GreaterEqual, GREATER_EQUAL); + CASE(Always, ALWAYS); +#undef CASE + } +} + +Result D3D12Renderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) +{ + D3D12_FILTER_REDUCTION_TYPE dxReduction = translateFilterReduction(desc.reductionOp); + D3D12_FILTER dxFilter; + if (desc.maxAnisotropy > 1) + { + dxFilter = D3D12_ENCODE_ANISOTROPIC_FILTER(dxReduction); + } + else + { + D3D12_FILTER_TYPE dxMin = translateFilterMode(desc.minFilter); + D3D12_FILTER_TYPE dxMag = translateFilterMode(desc.magFilter); + D3D12_FILTER_TYPE dxMip = translateFilterMode(desc.mipFilter); + + dxFilter = D3D12_ENCODE_BASIC_FILTER(dxMin, dxMag, dxMip, dxReduction); + } + + D3D12_SAMPLER_DESC dxDesc = {}; + dxDesc.Filter = dxFilter; + dxDesc.AddressU = translateAddressingMode(desc.addressU); + dxDesc.AddressV = translateAddressingMode(desc.addressV); + dxDesc.AddressW = translateAddressingMode(desc.addressW); + dxDesc.MipLODBias = desc.mipLODBias; + dxDesc.MaxAnisotropy = desc.maxAnisotropy; + dxDesc.ComparisonFunc = translateComparisonFunc(desc.comparisonFunc); + for (int ii = 0; ii < 4; ++ii) + dxDesc.BorderColor[ii] = desc.borderColor[ii]; + dxDesc.MinLOD = desc.minLOD; + dxDesc.MaxLOD = desc.maxLOD; + + auto samplerHeap = &m_cpuSamplerHeap; + + int indexInSamplerHeap = samplerHeap->allocate(); + if(indexInSamplerHeap < 0) + { + // We ran out of room in our CPU sampler heap. + // + // TODO: this should not be a catastrophic failure, because + // we should just allocate another CPU sampler heap that + // can service subsequent allocation. + // + return SLANG_FAIL; + } + auto cpuDescriptorHandle = samplerHeap->getCpuHandle(indexInSamplerHeap); + + m_device->CreateSampler(&dxDesc, cpuDescriptorHandle); + + // TODO: We really ought to have a free-list of sampler-heap + // entries that we check before we go to the heap, and then + // when we are done with a sampler we simply add it to the free list. + // + RefPtr samplerImpl = new SamplerStateImpl(); + samplerImpl->m_cpuHandle = cpuDescriptorHandle; + *outSampler = samplerImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (TextureResourceImpl*) texture; + + RefPtr viewImpl = new ResourceViewImpl(); + viewImpl->m_resource = resourceImpl; + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::RenderTarget: + { + SLANG_RETURN_ON_FAIL(m_rtvAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateRenderTargetView(resourceImpl->m_resource, nullptr, viewImpl->m_descriptor.cpuHandle); + } + break; + + case ResourceView::Type::DepthStencil: + { + SLANG_RETURN_ON_FAIL(m_dsvAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateDepthStencilView(resourceImpl->m_resource, nullptr, viewImpl->m_descriptor.cpuHandle); + } + break; + + case ResourceView::Type::UnorderedAccess: + { + // TODO: need to support the separate "counter resource" for the case + // of append/consume buffers with attached counters. + + SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, nullptr, viewImpl->m_descriptor.cpuHandle); + } + break; + + case ResourceView::Type::ShaderResource: + { + SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); + + // Need to construct the D3D12_SHADER_RESOURCE_VIEW_DESC because otherwise TextureCube is not accessed + // appropriately (rather than just passing nullptr to CreateShaderResourceView) + const D3D12_RESOURCE_DESC resourceDesc = resourceImpl->m_resource.getResource()->GetDesc(); + const DXGI_FORMAT pixelFormat = resourceDesc.Format; + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; + _initSrvDesc(resourceImpl->getType(), resourceImpl->getDesc(), resourceDesc, pixelFormat, srvDesc); + + m_device->CreateShaderResourceView(resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle); + } + break; + } + + *outView = viewImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + auto resourceDesc = resourceImpl->getDesc(); + + RefPtr viewImpl = new ResourceViewImpl(); + viewImpl->m_resource = resourceImpl; + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::UnorderedAccess: + { + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uavDesc.Format = D3DUtil::getMapFormat(desc.format); + uavDesc.Buffer.FirstElement = 0; + + if(resourceDesc.elementSize) + { + uavDesc.Buffer.StructureByteStride = resourceDesc.elementSize; + uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / resourceDesc.elementSize); + } + else if(desc.format == Format::Unknown) + { + uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / 4); + } + else + { + uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / RendererUtil::getFormatSize(desc.format)); + } + + + // TODO: need to support the separate "counter resource" for the case + // of append/consume buffers with attached counters. + + SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, &uavDesc, viewImpl->m_descriptor.cpuHandle); + } + break; + + case ResourceView::Type::ShaderResource: + { + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Format = D3DUtil::getMapFormat(desc.format); + srvDesc.Buffer.StructureByteStride = 0; + srvDesc.Buffer.FirstElement = 0; + + if(resourceDesc.elementSize) + { + srvDesc.Buffer.StructureByteStride = resourceDesc.elementSize; + srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / resourceDesc.elementSize); + } + else if(desc.format == Format::Unknown) + { + srvDesc.Buffer.Flags |= D3D12_BUFFER_SRV_FLAG_RAW; + srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; + srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / 4); + } + else + { + srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / RendererUtil::getFormatSize(desc.format)); + } + + SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateShaderResourceView(resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle); + } + break; + } + + *outView = viewImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) +{ + RefPtr layout(new InputLayoutImpl); + + // Work out a buffer size to hold all text + size_t textSize = 0; + for (int i = 0; i < Int(inputElementCount); ++i) + { + const char* text = inputElements[i].semanticName; + textSize += text ? (::strlen(text) + 1) : 0; + } + layout->m_text.setCount(textSize); + char* textPos = layout->m_text.getBuffer(); + + // + List& elements = layout->m_elements; + elements.setCount(inputElementCount); + + + for (UInt i = 0; i < inputElementCount; ++i) + { + const InputElementDesc& srcEle = inputElements[i]; + D3D12_INPUT_ELEMENT_DESC& dstEle = elements[i]; + + // Add text to the buffer + const char* semanticName = srcEle.semanticName; + if (semanticName) + { + const int len = int(::strlen(semanticName)); + ::memcpy(textPos, semanticName, len + 1); + semanticName = textPos; + textPos += len + 1; + } + + dstEle.SemanticName = semanticName; + dstEle.SemanticIndex = (UINT)srcEle.semanticIndex; + dstEle.Format = D3DUtil::getMapFormat(srcEle.format); + dstEle.InputSlot = 0; + dstEle.AlignedByteOffset = (UINT)srcEle.offset; + dstEle.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; + dstEle.InstanceDataStepRate = 0; + } + + *outLayout = layout.detach(); + return SLANG_OK; +} + +void* D3D12Renderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + typedef BufferResourceImpl::BackingStyle Style; + + BufferResourceImpl* buffer = static_cast(bufferIn); + buffer->m_mapFlavor = flavor; + + const size_t bufferSize = buffer->getDesc().sizeInBytes; + + switch (buffer->m_backingStyle) + { + case Style::ResourceBacked: + { + // We need this in a state so we can upload + switch (flavor) + { + case MapFlavor::HostWrite: + case MapFlavor::WriteDiscard: + { + D3D12BarrierSubmitter submitter(m_commandList); + buffer->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter); + buffer->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter); + + const D3D12_RANGE readRange = {}; + + void* uploadData; + SLANG_RETURN_NULL_ON_FAIL(buffer->m_uploadResource.getResource()->Map(0, &readRange, reinterpret_cast(&uploadData))); + return uploadData; + + break; + } + case MapFlavor::HostRead: + { + // This will be slow!!! - it blocks CPU on GPU completion + D3D12Resource& resource = buffer->m_resource; + + // Readback heap + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_READBACK; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + // Resource to readback to + D3D12_RESOURCE_DESC stagingDesc; + _initBufferResourceDesc(bufferSize, stagingDesc); + + D3D12Resource stageBuf; + SLANG_RETURN_NULL_ON_FAIL(stageBuf.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); + + const D3D12_RESOURCE_STATES initialState = resource.getState(); + + // Make it a source + { + D3D12BarrierSubmitter submitter(m_commandList); + resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter); + } + // Do the copy + m_commandList->CopyBufferRegion(stageBuf, 0, resource, 0, bufferSize); + // Switch it back + { + D3D12BarrierSubmitter submitter(m_commandList); + resource.transition(initialState, submitter); + } + + // Wait until complete + submitGpuWorkAndWait(); + + // Map and copy + { + UINT8* data; + D3D12_RANGE readRange = { 0, bufferSize }; + + SLANG_RETURN_NULL_ON_FAIL(stageBuf.getResource()->Map(0, &readRange, reinterpret_cast(&data))); + + // Copy to memory buffer + buffer->m_memory.setCount(bufferSize); + ::memcpy(buffer->m_memory.getBuffer(), data, bufferSize); + + stageBuf.getResource()->Unmap(0, nullptr); + } + + return buffer->m_memory.getBuffer(); + } + } + break; + } + case Style::MemoryBacked: + { + return buffer->m_memory.getBuffer(); + } + default: return nullptr; + } + + return nullptr; +} + +void D3D12Renderer::unmap(BufferResource* bufferIn) +{ + typedef BufferResourceImpl::BackingStyle Style; + BufferResourceImpl* buffer = static_cast(bufferIn); + + switch (buffer->m_backingStyle) + { + case Style::MemoryBacked: + { + // Don't need to do anything, as will be uploaded automatically when used + break; + } + case Style::ResourceBacked: + { + // We need this in a state so we can upload + switch (buffer->m_mapFlavor) + { + case MapFlavor::HostWrite: + case MapFlavor::WriteDiscard: + { + // Unmap + ID3D12Resource* uploadResource = buffer->m_uploadResource; + ID3D12Resource* resource = buffer->m_resource; + + uploadResource->Unmap(0, nullptr); + + const D3D12_RESOURCE_STATES initialState = buffer->m_resource.getState(); + + { + D3D12BarrierSubmitter submitter(m_commandList); + buffer->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter); + buffer->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter); + } + + m_commandList->CopyBufferRegion(resource, 0, uploadResource, 0, buffer->getDesc().sizeInBytes); + + { + D3D12BarrierSubmitter submitter(m_commandList); + buffer->m_resource.transition(initialState, submitter); + } + break; + } + case MapFlavor::HostRead: + { + break; + } + } + } + } +} + +#if 0 +void D3D12Renderer::setInputLayout(InputLayout* inputLayout) +{ + m_boundInputLayout = static_cast(inputLayout); +} +#endif + +void D3D12Renderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + switch (topology) + { + case PrimitiveTopology::TriangleList: + { + m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + m_primitiveTopology = D3DUtil::getPrimitiveTopology(topology); + break; + } + default: + { + assert(!"Unhandled type"); + } + } +} + +void D3D12Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) +{ + { + const Index num = startSlot + slotCount; + if (num > m_boundVertexBuffers.getCount()) + { + m_boundVertexBuffers.setCount(num); + } + } + + for (UInt i = 0; i < slotCount; i++) + { + BufferResourceImpl* buffer = static_cast(buffers[i]); + if (buffer) + { + assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer); + } + + BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i]; + boundBuffer.m_buffer = buffer; + boundBuffer.m_stride = int(strides[i]); + boundBuffer.m_offset = int(offsets[i]); + } +} + +void D3D12Renderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) +{ + m_boundIndexBuffer = (BufferResourceImpl*) buffer; + m_boundIndexFormat = D3DUtil::getMapFormat(indexFormat); + m_boundIndexOffset = UINT(offset); +} + +void D3D12Renderer::setDepthStencilTarget(ResourceView* depthStencilView) +{ +} + +void D3D12Renderer::setViewports(UInt count, Viewport const* viewports) +{ + static const int kMaxViewports = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; + assert(count <= kMaxViewports); + + D3D12_VIEWPORT dxViewports[kMaxViewports]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inViewport = viewports[ii]; + auto& dxViewport = dxViewports[ii]; + + dxViewport.TopLeftX = inViewport.originX; + dxViewport.TopLeftY = inViewport.originY; + dxViewport.Width = inViewport.extentX; + dxViewport.Height = inViewport.extentY; + dxViewport.MinDepth = inViewport.minZ; + dxViewport.MaxDepth = inViewport.maxZ; + } + + m_commandList->RSSetViewports(UINT(count), dxViewports); +} + +void D3D12Renderer::setScissorRects(UInt count, ScissorRect const* rects) +{ + static const int kMaxScissorRects = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; + assert(count <= kMaxScissorRects); + + D3D12_RECT dxRects[kMaxScissorRects]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inRect = rects[ii]; + auto& dxRect = dxRects[ii]; + + dxRect.left = LONG(inRect.minX); + dxRect.top = LONG(inRect.minY); + dxRect.right = LONG(inRect.maxX); + dxRect.bottom = LONG(inRect.maxY); + } + + m_commandList->RSSetScissorRects(UINT(count), dxRects); +} + +void D3D12Renderer::setPipelineState(PipelineType pipelineType, PipelineState* state) +{ + m_currentPipelineState = (PipelineStateImpl*)state; +} + +void D3D12Renderer::draw(UInt vertexCount, UInt startVertex) +{ + ID3D12GraphicsCommandList* commandList = m_commandList; + + auto pipelineState = m_currentPipelineState.Ptr(); + if (!pipelineState || (pipelineState->m_pipelineType != PipelineType::Graphics)) + { + assert(!"No graphics pipeline state set"); + return; + } + + // Submit - setting for graphics + { + GraphicsSubmitter submitter(commandList); + _bindRenderState(pipelineState, commandList, &submitter); + } + + commandList->IASetPrimitiveTopology(m_primitiveTopology); + + // Set up vertex buffer views + { + int numVertexViews = 0; + D3D12_VERTEX_BUFFER_VIEW vertexViews[16]; + for (Index i = 0; i < m_boundVertexBuffers.getCount(); i++) + { + const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[i]; + BufferResourceImpl* buffer = boundVertexBuffer.m_buffer; + if (buffer) + { + D3D12_VERTEX_BUFFER_VIEW& vertexView = vertexViews[numVertexViews++]; + vertexView.BufferLocation = buffer->m_resource.getResource()->GetGPUVirtualAddress() + + boundVertexBuffer.m_offset; + vertexView.SizeInBytes = UINT(buffer->getDesc().sizeInBytes - boundVertexBuffer.m_offset); + vertexView.StrideInBytes = UINT(boundVertexBuffer.m_stride); + } + } + commandList->IASetVertexBuffers(0, numVertexViews, vertexViews); + } + + // Set up index buffer + if(m_boundIndexBuffer) + { + D3D12_INDEX_BUFFER_VIEW indexBufferView; + indexBufferView.BufferLocation = m_boundIndexBuffer->m_resource.getResource()->GetGPUVirtualAddress() + + m_boundIndexOffset; + indexBufferView.SizeInBytes = UINT(m_boundIndexBuffer->getDesc().sizeInBytes - m_boundIndexOffset); + indexBufferView.Format = m_boundIndexFormat; + + commandList->IASetIndexBuffer(&indexBufferView); + } + + commandList->DrawInstanced(UINT(vertexCount), 1, UINT(startVertex), 0); +} + +void D3D12Renderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) +{ +} + +void D3D12Renderer::dispatchCompute(int x, int y, int z) +{ + ID3D12GraphicsCommandList* commandList = m_commandList; + auto pipelineStateImpl = m_currentPipelineState; + + // Submit binding for compute + { + ComputeSubmitter submitter(commandList); + _bindRenderState(pipelineStateImpl, commandList, &submitter); + } + + commandList->Dispatch(x, y, z); +} + +#if 0 +BindingState* D3D12Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); + + SLANG_RETURN_NULL_ON_FAIL(bindingState->init(m_device)); + + const auto& srcBindings = bindingStateDesc.m_bindings; + const int numBindings = int(srcBindings.Count()); + + auto& dstDetails = bindingState->m_bindingDetails; + dstDetails.SetSize(numBindings); + + for (int i = 0; i < numBindings; ++i) + { + const auto& srcEntry = srcBindings[i]; + auto& dstDetail = dstDetails[i]; + + const int bindingIndex = srcEntry.registerRange.getSingleIndex(); + + switch (srcEntry.bindingType) + { + case BindingType::Buffer: + { + assert(srcEntry.resource && srcEntry.resource->isBuffer()); + BufferResourceImpl* bufferResource = static_cast(srcEntry.resource.Ptr()); + const BufferResource::Desc& desc = bufferResource->getDesc(); + + const size_t bufferSize = bufferDesc.sizeInBytes; + const int elemSize = bufferDesc.elementSize <= 0 ? sizeof(uint32_t) : bufferDesc.elementSize; + + const bool createSrv = false; + + // NOTE! In this arrangement the buffer can either be a ConstantBuffer or a 'StorageBuffer'. + // If it's a storage buffer then it has a 'uav'. + // In neither circumstance is there an associated srv + // This departs a little from dx11 code - in that it will create srv and uav for a storage buffer. + if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) + { + dstDetail.m_uavIndex = bindingState->m_viewHeap.allocate(); + if (dstDetail.m_uavIndex < 0) + { + return nullptr; + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uavDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); + + uavDesc.Buffer.StructureByteStride = elemSize; + + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); + uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE; + + if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) + { + uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + + uavDesc.Buffer.StructureByteStride = 0; + } + else if( bufferDesc.format != Format::Unknown ) + { + uavDesc.Buffer.StructureByteStride = 0; + } + + m_device->CreateUnorderedAccessView(bufferResource->m_resource, nullptr, &uavDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_uavIndex)); + } + if (createSrv && (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource))) + { + dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); + if (dstDetail.m_srvIndex < 0) + { + return nullptr; + } + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; + + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + + srvDesc.Buffer.FirstElement = 0; + srvDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); + srvDesc.Buffer.StructureByteStride = elemSize; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + + if (bufferDesc.elementSize == 0) + { + srvDesc.Format = DXGI_FORMAT_R32_FLOAT; + } + + m_device->CreateShaderResourceView(bufferResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); + } + + break; + } + case BindingType::Texture: + { + assert(srcEntry.resource && srcEntry.resource->isTexture()); + + TextureResourceImpl* textureResource = static_cast(srcEntry.resource.Ptr()); + + dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); + if (dstDetail.m_srvIndex < 0) + { + return nullptr; + } + + { + const D3D12_RESOURCE_DESC resourceDesc = textureResource->m_resource.getResource()->GetDesc(); + const DXGI_FORMAT pixelFormat = resourceDesc.Format; + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; + _initSrvDesc(textureResource->getType(), textureResource->getDesc(), resourceDesc, pixelFormat, srvDesc); + + // Create descriptor + m_device->CreateShaderResourceView(textureResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); + } + + break; + } + case BindingType::Sampler: + { + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcEntry.descIndex]; + + const int samplerIndex = bindingIndex; + dstDetail.m_samplerIndex = samplerIndex; + bindingState->m_samplerHeap.placeAt(samplerIndex); + + D3D12_SAMPLER_DESC desc = {}; + desc.AddressU = desc.AddressV = desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + desc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; + + if (samplerDesc.isCompareSampler) + { + desc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + desc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; + } + else + { + desc.Filter = D3D12_FILTER_ANISOTROPIC; + desc.MaxAnisotropy = 8; + desc.MinLOD = 0.0f; + desc.MaxLOD = 100.0f; + } + + m_device->CreateSampler(&desc, bindingState->m_samplerHeap.getCpuHandle(samplerIndex)); + + break; + } + case BindingType::CombinedTextureSampler: + { + assert(!"Not implemented"); + return nullptr; + } + } + } + + return bindingState.detach(); +} + +void D3D12Renderer::setBindingState(BindingState* state) +{ + m_boundBindingState = static_cast(state); +} +#endif + +void D3D12Renderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) +{ + auto dxDevice = m_renderer->m_device; + + auto resourceImpl = (BufferResourceImpl*) buffer; + auto resourceDesc = resourceImpl->getDesc(); + + // Constant buffer view size must be a multiple of 256 bytes, so we round it up here. + const size_t alignedSizeInBytes = D3DUtil::calcAligned(resourceDesc.sizeInBytes, 256); + + D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {}; + cbvDesc.BufferLocation = resourceImpl->m_resource.getResource()->GetGPUVirtualAddress(); + cbvDesc.SizeInBytes = UINT(alignedSizeInBytes); + + auto& rangeInfo = m_layout->m_ranges[range]; + +#ifdef _DEBUG + switch(rangeInfo.type) + { + default: + assert(!"incorrect slot type"); + break; + + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::DynamicUniformBuffer: + break; + } +#endif + + auto arrayIndex = rangeInfo.arrayIndex + index; + auto descriptorIndex = m_resourceTable + arrayIndex; + + m_resourceObjects[arrayIndex] = resourceImpl; + dxDevice->CreateConstantBufferView( + &cbvDesc, + m_resourceHeap->getCpuHandle(int(descriptorIndex))); +} + +void D3D12Renderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) +{ + auto dxDevice = m_renderer->m_device; + + auto viewImpl = (ResourceViewImpl*) view; + + auto& rangeInfo = m_layout->m_ranges[range]; + + // TODO: validation that slot type matches view + + auto arrayIndex = rangeInfo.arrayIndex + index; + auto descriptorIndex = m_resourceTable + arrayIndex; + + m_resourceObjects[arrayIndex] = viewImpl; + dxDevice->CopyDescriptorsSimple( + 1, + m_resourceHeap->getCpuHandle(int(descriptorIndex)), + viewImpl->m_descriptor.cpuHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); +} + +void D3D12Renderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) +{ + auto dxDevice = m_renderer->m_device; + + auto samplerImpl = (SamplerStateImpl*) sampler; + + auto& rangeInfo = m_layout->m_ranges[range]; + +#ifdef _DEBUG + switch(rangeInfo.type) + { + default: + assert(!"incorrect slot type"); + break; + + case DescriptorSlotType::Sampler: + break; + } +#endif + + auto arrayIndex = rangeInfo.arrayIndex + index; + auto descriptorIndex = m_resourceTable + arrayIndex; + + m_samplerObjects[arrayIndex] = samplerImpl; + dxDevice->CopyDescriptorsSimple( + 1, + m_samplerHeap->getCpuHandle(int(descriptorIndex)), + samplerImpl->m_cpuHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); +} + +void D3D12Renderer::DescriptorSetImpl::setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) +{ + auto dxDevice = m_renderer->m_device; + + auto viewImpl = (ResourceViewImpl*) textureView; + auto samplerImpl = (SamplerStateImpl*) sampler; + + auto& rangeInfo = m_layout->m_ranges[range]; + +#ifdef _DEBUG + switch(rangeInfo.type) + { + default: + assert(!"incorrect slot type"); + break; + + case DescriptorSlotType::CombinedImageSampler: + break; + } +#endif + + auto arrayIndex = rangeInfo.arrayIndex + index; + auto resourceDescriptorIndex = m_resourceTable + arrayIndex; + auto samplerDescriptorIndex = m_samplerTable + arrayIndex; + + m_resourceObjects[arrayIndex] = viewImpl; + dxDevice->CopyDescriptorsSimple( + 1, + m_resourceHeap->getCpuHandle(int(resourceDescriptorIndex)), + viewImpl->m_descriptor.cpuHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + m_samplerObjects[arrayIndex] = samplerImpl; + dxDevice->CopyDescriptorsSimple( + 1, + m_samplerHeap->getCpuHandle(int(samplerDescriptorIndex)), + samplerImpl->m_cpuHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); +} + +void D3D12Renderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) +{ + // In D3D12, unlike Vulkan, binding a root signature invalidates *all* descriptor table + // bindings (rather than preserving those that are part of the longest common prefix + // between the old and new layout). + // + // In order to accomodate having descriptor-set bindings that persist across changes + // in pipeline state (which may also change pipeline layout), we will shadow the + // descriptor-set bindings and only flush them on-demand at draw tiume once the final + // pipline layout is known. + // + + auto descriptorSetImpl = (DescriptorSetImpl*) descriptorSet; + m_boundDescriptorSets[int(pipelineType)][index] = descriptorSetImpl; +} + +Result D3D12Renderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) +{ + RefPtr program(new ShaderProgramImpl()); + program->m_pipelineType = desc.pipelineType; + + if (desc.pipelineType == PipelineType::Compute) + { + auto computeKernel = desc.findKernel(StageType::Compute); + program->m_computeShader.insertRange(0, (const uint8_t*) computeKernel->codeBegin, computeKernel->getCodeSize()); + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + program->m_vertexShader.insertRange(0, (const uint8_t*) vertexKernel->codeBegin, vertexKernel->getCodeSize()); + program->m_pixelShader.insertRange(0, (const uint8_t*) fragmentKernel->codeBegin, fragmentKernel->getCodeSize()); + } + + *outProgram = program.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) +{ + Int rangeCount = desc.slotRangeCount; + + // For our purposes, there are three main cases of descriptor ranges to consider: + // + // 1. Resources: CBV, SRV, UAV + // + // 2. Samplers + // + // 3. Combined texture/sampler pairs + // + // The combined case presents challenges, because we will implement + // them as both a resource slot and a sampler slot, and for conveience + // in the indexing logic, it would be nice it they "lined up." + // + // We will start by counting how many ranges, and how many + // descriptors, of each type we have. + // + + Int dedicatedResourceCount = 0; + Int dedicatedSamplerCount = 0; + Int combinedCount = 0; + + Int dedicatedResourceRangeCount = 0; + Int dedicatedSamplerRangeCount = 0; + Int combinedRangeCount = 0; + + for(Int rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + switch(rangeDesc.type) + { + case DescriptorSlotType::Sampler: + dedicatedSamplerCount += rangeDesc.count; + dedicatedSamplerRangeCount++; + break; + + case DescriptorSlotType::CombinedImageSampler: + combinedCount += rangeDesc.count; + combinedRangeCount++; + break; + + default: + dedicatedResourceCount += rangeDesc.count; + dedicatedResourceRangeCount++; + break; + } + } + + // Now we know how many ranges we have to allocate space for, + // and also how they need to be arranged. + // + // Each "combined" range will map to two ranges in the D3D + // descriptor tables. + + RefPtr descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(); + + // We know the total number of resource and sampler "slots" that an instance + // of this descriptor-set layout would need: + // + descriptorSetLayoutImpl->m_resourceCount = combinedCount + dedicatedResourceCount; + descriptorSetLayoutImpl->m_samplerCount = combinedCount + dedicatedSamplerCount; + + // We can start by allocating the D3D root parameter info needed for the + // descriptor set, based on the total number or ranges we need, which + // we can compute from the combined and dedicated counts: + // + Int totalResourceRangeCount = combinedRangeCount + dedicatedResourceRangeCount; + Int totalSamplerRangeCount = combinedRangeCount + dedicatedSamplerRangeCount; + + if( totalResourceRangeCount ) + { + D3D12_ROOT_PARAMETER dxRootParameter = {}; + dxRootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + dxRootParameter.DescriptorTable.NumDescriptorRanges = UINT(totalResourceRangeCount); + descriptorSetLayoutImpl->m_dxRootParameters.add(dxRootParameter); + } + if( totalSamplerRangeCount ) + { + D3D12_ROOT_PARAMETER dxRootParameter = {}; + dxRootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + dxRootParameter.DescriptorTable.NumDescriptorRanges = UINT(totalSamplerRangeCount); + descriptorSetLayoutImpl->m_dxRootParameters.add(dxRootParameter); + } + + // Next we can allocate space for all the D3D register ranges we need, + // again based on totals that we can compute easily: + // + Int totalRangeCount = totalResourceRangeCount + totalSamplerRangeCount; + descriptorSetLayoutImpl->m_dxRanges.setCount(totalRangeCount); + + // Now we will walk through the ranges in the order they were + // specified, so that we can fill in the "range info" required for + // binding parameters into descriptor sets allocated with this layout. + // + // This effectively determines the space required in two arrays + // in each descriptor set: one for resources, and one for samplers. + // A "combined" descriptor requires space in both arrays. The entries + // for "dedicated" samplers/resources always come after those for + // "combined" descriptors in the same array, so that a single index + // can be used for both arrays in the combined case. + // + + { + Int samplerCounter = 0; + Int resourceCounter = 0; + Int combinedCounter = 0; + for(Int rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + + DescriptorSetLayoutImpl::RangeInfo rangeInfo; + + rangeInfo.type = rangeDesc.type; + rangeInfo.count = rangeDesc.count; + + switch(rangeDesc.type) + { + default: + // Default case is a dedicated resource, and its index in the + // resource array will come after all the combined entries. + rangeInfo.arrayIndex = combinedCount + resourceCounter; + resourceCounter += rangeInfo.count; + break; + + case DescriptorSlotType::Sampler: + // A dedicated sampler comes after all the entries for + // combined texture/samplers in the sampler array. + rangeInfo.arrayIndex = combinedCount + samplerCounter; + samplerCounter += rangeInfo.count; + break; + + case DescriptorSlotType::CombinedImageSampler: + // Combined descriptors take entries at the front of + // the resource and sampler arrays. + rangeInfo.arrayIndex = combinedCounter; + combinedCounter += rangeInfo.count; + break; + } + + descriptorSetLayoutImpl->m_ranges.add(rangeInfo); + } + } + + // Finally, we will go through and fill in ready-to-go D3D + // register range information. + { + UInt cbvCounter = 0; + UInt srvCounter = 0; + UInt uavCounter = 0; + UInt samplerCounter = 0; + + Int resourceRangeCounter = 0; + Int samplerRangeCounter = 0; + Int combinedRangeCounter = 0; + + for(Int rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + Int bindingCount = rangeDesc.count; + + // All of these descriptor ranges will be initialized + // with a "space" of zero, with the assumption that + // the actual space number will come from when they are + // used as part of a pipeline layout. + // + Int bindingSpace = 0; + + Int dxRangeIndex = -1; + Int dxPairedSamplerRangeIndex = -1; + + switch(rangeDesc.type) + { + default: + // Default case is a dedicated resource, and its index in the + // resource array will come after all the combined entries. + dxRangeIndex = combinedRangeCount + resourceRangeCounter; + resourceRangeCounter++; + break; + + case DescriptorSlotType::Sampler: + // A dedicated sampler comes after all the entries for + // combined texture/samplers in the sampler array. + dxRangeIndex = totalResourceRangeCount + combinedRangeCount + samplerRangeCounter; + samplerRangeCounter++; + break; + + case DescriptorSlotType::CombinedImageSampler: + // Combined descriptors take entries at the front of + // the resource and sampler arrays. + dxRangeIndex = combinedRangeCounter; + dxPairedSamplerRangeIndex = totalResourceRangeCount + combinedRangeCounter; + combinedRangeCounter++; + break; + } + + D3D12_DESCRIPTOR_RANGE& dxRange = descriptorSetLayoutImpl->m_dxRanges[dxRangeIndex]; + memset(&dxRange, 0, sizeof(dxRange)); + + switch(rangeDesc.type) + { + default: + // ERROR: unsupported slot type. + break; + + case DescriptorSlotType::Sampler: + { + UInt bindingIndex = samplerCounter; samplerCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + dxRange.NumDescriptors = UINT(bindingCount); + dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.RegisterSpace = UINT(bindingSpace); + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + break; + + case DescriptorSlotType::SampledImage: + case DescriptorSlotType::UniformTexelBuffer: + { + UInt bindingIndex = srvCounter; srvCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + dxRange.NumDescriptors = UINT(bindingCount); + dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.RegisterSpace = UINT(bindingSpace); + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + break; + + case DescriptorSlotType::CombinedImageSampler: + { + // The combined texture/sampler case basically just + // does the work of both the SRV and sampler cases above. + + { + // Here's the SRV logic: + + UInt bindingIndex = srvCounter; srvCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + dxRange.NumDescriptors = UINT(bindingCount); + dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.RegisterSpace = UINT(bindingSpace); + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + + { + // And here we do the sampler logic at the "paired" index. + D3D12_DESCRIPTOR_RANGE& dxPairedSamplerRange = descriptorSetLayoutImpl->m_dxRanges[dxPairedSamplerRangeIndex]; + memset(&dxPairedSamplerRange, 0, sizeof(dxPairedSamplerRange)); + + UInt pairedSamplerBindingIndex = srvCounter; srvCounter += bindingCount; + + dxPairedSamplerRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + dxPairedSamplerRange.NumDescriptors = UINT(bindingCount); + dxPairedSamplerRange.BaseShaderRegister = UINT(pairedSamplerBindingIndex); + dxPairedSamplerRange.RegisterSpace = UINT(bindingSpace); + dxPairedSamplerRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + + } + break; + + + case DescriptorSlotType::InputAttachment: + case DescriptorSlotType::StorageImage: + case DescriptorSlotType::StorageTexelBuffer: + case DescriptorSlotType::StorageBuffer: + case DescriptorSlotType::DynamicStorageBuffer: + { + UInt bindingIndex = uavCounter; uavCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + dxRange.NumDescriptors = UINT(bindingCount); + dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.RegisterSpace = UINT(bindingSpace); + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + break; + + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::DynamicUniformBuffer: + { + UInt bindingIndex = cbvCounter; cbvCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; + dxRange.NumDescriptors = UINT(bindingCount); + dxRange.BaseShaderRegister = UINT(bindingIndex); + dxRange.RegisterSpace = UINT(bindingSpace); + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + break; + } + } + } + + *outLayout = descriptorSetLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) +{ + static const UInt kMaxRanges = 16; + static const UInt kMaxRootParameters = 32; + + D3D12_DESCRIPTOR_RANGE ranges[kMaxRanges]; + D3D12_ROOT_PARAMETER rootParameters[kMaxRootParameters]; + + UInt rangeCount = 0; + UInt rootParameterCount = 0; + + auto descriptorSetCount = desc.descriptorSetCount; + + // We are going to make two passes over the descriptor set layouts + // that are being used to build the pipeline layout. In the first + // pass we will collect all the descriptor ranges that have been + // specified, applying an offset to their register spaces as needed. + // + for(UInt dd = 0; dd < descriptorSetCount; ++dd) + { + auto& descriptorSetInfo = desc.descriptorSets[dd]; + auto descriptorSetLayout = (DescriptorSetLayoutImpl*) descriptorSetInfo.layout; + + // For now we assume that the register space used for + // logical descriptor set #N will be space N. + // + // TODO: This might need to be revisited in the future because + // a single logical descriptor set might need to encompass stuff + // that comes from multiple spaces (e.g., if it contains an unbounded + // array). + // + UInt bindingSpace = dd; + + // Copy descriptor range information from the set layout into our + // temporary copy (this is required because the same set layout + // might be applied to different ranges). + // + // API design note: this copy step could be avoided if the D3D + // API allowed for a "space offset" to be applied as part of + // a descriptor-table root parameter. + // + for(auto setDescriptorRange : descriptorSetLayout->m_dxRanges) + { + auto& range = ranges[rangeCount++]; + range = setDescriptorRange; + range.RegisterSpace = UINT(bindingSpace); + + // HACK: in order to deal with SM5.0 shaders, `u` registers + // in `space0` need to start with a number *after* the number + // of `SV_Target` outputs that will be used. + // + // TODO: This is clearly a mess, and doing this behavior here + // means it *won't* work for SM5.1 where the restriction is + // lifted. The only real alternative is to rely on explicit + // register numbers (e.g., from shader reflection) but that + // goes against the simplicity that this API layer strives for + // (everything so far has been set up to work correctly with + // automatic assignment of bindings). + // + if( range.RegisterSpace == 0 + && range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV ) + { + range.BaseShaderRegister += UINT(desc.renderTargetCount); + } + } + } + + // In our second pass, we will copy over root parameters, which + // may end up pointing into the list of ranges from the first step. + // + auto rangePtr = &ranges[0]; + for(UInt dd = 0; dd < descriptorSetCount; ++dd) + { + auto& descriptorSetInfo = desc.descriptorSets[dd]; + auto descriptorSetLayout = (DescriptorSetLayoutImpl*) descriptorSetInfo.layout; + + // Copy root parameter information from the set layout to our + // overall pipeline layout. + for( auto setRootParameter : descriptorSetLayout->m_dxRootParameters ) + { + auto& rootParameter = rootParameters[rootParameterCount++]; + rootParameter = setRootParameter; + + // In the case where this parameter is a descriptor table, it + // needs to point into our array of ranges (with offsets applied), + // so we will fix up those pointers here. + // + if(rootParameter.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) + { + rootParameter.DescriptorTable.pDescriptorRanges = rangePtr; + rangePtr += rootParameter.DescriptorTable.NumDescriptorRanges; + } + } + } + + D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {}; + rootSignatureDesc.NumParameters = UINT(rootParameterCount); + rootSignatureDesc.pParameters = rootParameters; + + // TODO: static samplers should be reasonably easy to support... + rootSignatureDesc.NumStaticSamplers = 0; + rootSignatureDesc.pStaticSamplers = nullptr; + + // TODO: only set this flag if needed (requires creating root + // signature at same time as pipeline state...). + // + rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + + ComPtr signature; + ComPtr error; + if( SLANG_FAILED(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef())) ) + { + fprintf(stderr, "error: D3D12SerializeRootSignature failed"); + if( error ) + { + fprintf(stderr, ": %s\n", (const char*) error->GetBufferPointer()); + } + return SLANG_FAIL; + } + + ComPtr rootSignature; + SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef()))); + + + RefPtr pipelineLayoutImpl = new PipelineLayoutImpl(); + pipelineLayoutImpl->m_rootSignature = rootSignature; + pipelineLayoutImpl->m_descriptorSetCount = descriptorSetCount; + *outLayout = pipelineLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) +{ + auto layoutImpl = (DescriptorSetLayoutImpl*) layout; + + RefPtr descriptorSetImpl = new DescriptorSetImpl(); + descriptorSetImpl->m_renderer = this; + descriptorSetImpl->m_layout = layoutImpl; + + // We allocate CPU-visible descriptor tables to providing the + // backing storage for each descriptor set. GPU-visible storage + // will only be allocated as needed during per-frame logic in + // order to ensure that a descriptor set it available for use + // in rendering. + // + Int resourceCount = layoutImpl->m_resourceCount; + if( resourceCount ) + { + auto resourceHeap = &m_cpuViewHeap; + descriptorSetImpl->m_resourceHeap = resourceHeap; + descriptorSetImpl->m_resourceTable = resourceHeap->allocate(int(resourceCount)); + descriptorSetImpl->m_resourceObjects.setCount(resourceCount); + } + + Int samplerCount = layoutImpl->m_samplerCount; + if( samplerCount ) + { + auto samplerHeap = &m_cpuSamplerHeap; + descriptorSetImpl->m_samplerHeap = samplerHeap; + descriptorSetImpl->m_samplerTable = samplerHeap->allocate(int(samplerCount)); + descriptorSetImpl->m_samplerObjects.setCount(samplerCount); + } + + *outDescriptorSet = descriptorSetImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +{ + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + auto programImpl = (ShaderProgramImpl*) desc.program; + auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; + + // Describe and create the graphics pipeline state object (PSO) + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + + psoDesc.pRootSignature = pipelineLayoutImpl->m_rootSignature; + + psoDesc.VS = { programImpl->m_vertexShader.getBuffer(), SIZE_T(programImpl->m_vertexShader.getCount()) }; + psoDesc.PS = { programImpl->m_pixelShader .getBuffer(), SIZE_T(programImpl->m_pixelShader .getCount()) }; + + psoDesc.InputLayout = { inputLayoutImpl->m_elements.getBuffer(), UINT(inputLayoutImpl->m_elements.getCount()) }; + psoDesc.PrimitiveTopologyType = m_primitiveTopologyType; + + { + const int numRenderTargets = int(desc.renderTargetCount); + + psoDesc.DSVFormat = m_depthStencilFormat; + psoDesc.NumRenderTargets = numRenderTargets; + for (Int i = 0; i < numRenderTargets; i++) + { + psoDesc.RTVFormats[i] = m_targetFormat; + } + + psoDesc.SampleDesc.Count = 1; + psoDesc.SampleDesc.Quality = 0; + + psoDesc.SampleMask = UINT_MAX; + } + + { + auto& rs = psoDesc.RasterizerState; + rs.FillMode = D3D12_FILL_MODE_SOLID; + rs.CullMode = D3D12_CULL_MODE_NONE; + rs.FrontCounterClockwise = FALSE; + rs.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + rs.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + rs.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + rs.DepthClipEnable = TRUE; + rs.MultisampleEnable = FALSE; + rs.AntialiasedLineEnable = FALSE; + rs.ForcedSampleCount = 0; + rs.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + } + + { + D3D12_BLEND_DESC& blend = psoDesc.BlendState; + + blend.AlphaToCoverageEnable = FALSE; + blend.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = + { + FALSE,FALSE, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + { + blend.RenderTarget[i] = defaultRenderTargetBlendDesc; + } + } + + { + auto& ds = psoDesc.DepthStencilState; + + ds.DepthEnable = FALSE; + ds.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + ds.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + //ds.DepthFunc = D3D12_COMPARISON_FUNC_LESS; + ds.StencilEnable = FALSE; + ds.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + ds.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = + { + D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS + }; + ds.FrontFace = defaultStencilOp; + ds.BackFace = defaultStencilOp; + } + + psoDesc.PrimitiveTopologyType = m_primitiveTopologyType; + + ComPtr pipelineState; + SLANG_RETURN_ON_FAIL(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(pipelineState.writeRef()))); + + RefPtr pipelineStateImpl = new PipelineStateImpl(); + pipelineStateImpl->m_pipelineType = PipelineType::Graphics; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_pipelineState = pipelineState; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +{ + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + auto programImpl = (ShaderProgramImpl*) desc.program; + + // Describe and create the compute pipeline state object + D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {}; + computeDesc.pRootSignature = pipelineLayoutImpl->m_rootSignature; + computeDesc.CS = { programImpl->m_computeShader.getBuffer(), SIZE_T(programImpl->m_computeShader.getCount()) }; + + ComPtr pipelineState; + SLANG_RETURN_ON_FAIL(m_device->CreateComputePipelineState(&computeDesc, IID_PPV_ARGS(pipelineState.writeRef()))); + + RefPtr pipelineStateImpl = new PipelineStateImpl(); + pipelineStateImpl->m_pipelineType = PipelineType::Compute; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_pipelineState = pipelineState; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + +} // renderer_test diff --git a/tools/gfx/d3d12/render-d3d12.h b/tools/gfx/d3d12/render-d3d12.h new file mode 100644 index 000000000..b8a3104c0 --- /dev/null +++ b/tools/gfx/d3d12/render-d3d12.h @@ -0,0 +1,10 @@ +// render-d3d12.h +#pragma once + +namespace gfx { + +class Renderer; + +Renderer* createD3D12Renderer(); + +} // gfx diff --git a/tools/gfx/d3d12/resource-d3d12.cpp b/tools/gfx/d3d12/resource-d3d12.cpp new file mode 100644 index 000000000..27de868b6 --- /dev/null +++ b/tools/gfx/d3d12/resource-d3d12.cpp @@ -0,0 +1,214 @@ +// resource-d3d12.cpp +#include "resource-d3d12.h" + +namespace gfx { +using namespace Slang; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12BarrierSubmitter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +void D3D12BarrierSubmitter::_flush() +{ + assert(m_numBarriers > 0); + + if (m_commandList) + { + m_commandList->ResourceBarrier(UINT(m_numBarriers), m_barriers); + } + m_numBarriers = 0; +} + +D3D12_RESOURCE_BARRIER& D3D12BarrierSubmitter::_expandOne() +{ + _flush(); + return m_barriers[m_numBarriers++]; +} + +void D3D12BarrierSubmitter::transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState) +{ + if (nextState != prevState) + { + D3D12_RESOURCE_BARRIER& barrier = expandOne(); + + const UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + const D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + + ::memset(&barrier, 0, sizeof(barrier)); + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = flags; + barrier.Transition.pResource = resource; + barrier.Transition.StateBefore = prevState; + barrier.Transition.StateAfter = nextState; + barrier.Transition.Subresource = subresource; + } + else + { + if (nextState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS) + { + D3D12_RESOURCE_BARRIER& barrier = expandOne(); + + ::memset(&barrier, 0, sizeof(barrier)); + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + barrier.UAV.pResource = resource; + } + } +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12ResourceBase !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +/* static */DXGI_FORMAT D3D12ResourceBase::calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource) +{ + return resource ? D3DUtil::calcFormat(usage, resource->GetDesc().Format) : DXGI_FORMAT_UNKNOWN; +} + +void D3D12ResourceBase::transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter) +{ + // Transition only if there is a resource + if (m_resource) + { + submitter.transition(m_resource, m_state, nextState); + m_state = nextState; + } +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12CounterFence !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +D3D12CounterFence::~D3D12CounterFence() +{ + if (m_event) + { + CloseHandle(m_event); + } +} + +Result D3D12CounterFence::init(ID3D12Device* device, uint64_t initialValue) +{ + m_currentValue = initialValue; + + SLANG_RETURN_ON_FAIL(device->CreateFence(m_currentValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef()))); + // Create an event handle to use for frame synchronization. + m_event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (m_event == nullptr) + { + Result res = HRESULT_FROM_WIN32(GetLastError()); + return SLANG_FAILED(res) ? res : SLANG_FAIL; + } + return SLANG_OK; +} + +UInt64 D3D12CounterFence::nextSignal(ID3D12CommandQueue* commandQueue) +{ + // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >= + m_currentValue++; + // Schedule a Signal command in the queue. + Result res = commandQueue->Signal(m_fence, m_currentValue); + if (SLANG_FAILED(res)) + { + assert(!"Signal failed"); + } + return m_currentValue; +} + +void D3D12CounterFence::waitUntilCompleted(uint64_t completedValue) +{ + // You can only wait for a value that is less than or equal to the current value + assert(completedValue <= m_currentValue); + + // Wait until the previous frame is finished. + while (m_fence->GetCompletedValue() < completedValue) + { + // Make it signal with the current value + SLANG_ASSERT_VOID_ON_FAIL(m_fence->SetEventOnCompletion(completedValue, m_event)); + WaitForSingleObject(m_event, INFINITE); + } +} + +void D3D12CounterFence::nextSignalAndWait(ID3D12CommandQueue* commandQueue) +{ + waitUntilCompleted(nextSignal(commandQueue)); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! D3D12Resource !!!!!!!!!!!!!!!!!!!!!!!! */ + +/* static */void D3D12Resource::setDebugName(ID3D12Resource* resource, const char* name) +{ + if (resource) + { + size_t len = ::strlen(name); + List buf; + buf.setCount(len + 1); + + D3DUtil::appendWideChars(name, buf); + resource->SetName(buf.begin()); + } +} + +void D3D12Resource::setDebugName(const char* name) +{ + setDebugName(m_resource, name); +} + +void D3D12Resource::setDebugName(const wchar_t* name) +{ + if (m_resource) + { + m_resource->SetName(name); + } +} + +void D3D12Resource::setResource(ID3D12Resource* resource, D3D12_RESOURCE_STATES initialState) +{ + if (resource != m_resource) + { + if (resource) + { + resource->AddRef(); + } + if (m_resource) + { + m_resource->Release(); + } + m_resource = resource; + } + m_prevState = initialState; + m_state = initialState; +} + +void D3D12Resource::setResourceNull() +{ + if (m_resource) + { + m_resource->Release(); + m_resource = nullptr; + } +} + +Result D3D12Resource::initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue) +{ + setResourceNull(); + ComPtr resource; + SLANG_RETURN_ON_FAIL(device->CreateCommittedResource(&heapProps, heapFlags, &resourceDesc, initState, clearValue, IID_PPV_ARGS(resource.writeRef()))); + setResource(resource, initState); + return SLANG_OK; +} + +ID3D12Resource* D3D12Resource::detach() +{ + ID3D12Resource* resource = m_resource; + m_resource = nullptr; + return resource; +} + +void D3D12Resource::swap(ComPtr& resourceInOut) +{ + ID3D12Resource* tmp = m_resource; + m_resource = resourceInOut.detach(); + resourceInOut.attach(tmp); +} + +void D3D12Resource::setState(D3D12_RESOURCE_STATES state) +{ + m_prevState = state; + m_state = state; +} + +} // renderer_test diff --git a/tools/gfx/d3d12/resource-d3d12.h b/tools/gfx/d3d12/resource-d3d12.h new file mode 100644 index 000000000..9e3c3262b --- /dev/null +++ b/tools/gfx/d3d12/resource-d3d12.h @@ -0,0 +1,178 @@ +// resource-d3d12.h +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#include +#include + +#include "../../slang-com-ptr.h" +#include "../d3d/d3d-util.h" + +namespace gfx { + +// Enables more conservative barriers - restoring the state of resources after they are used. +// Should not need to be enabled in normal builds, as the barriers should correctly sync resources +// If enabling fixes an issue it implies regular barriers are not correctly used. +#define SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS 0 + +struct D3D12BarrierSubmitter +{ + enum { MAX_BARRIERS = 8 }; + + /// Expand one space to hold a barrier + SLANG_FORCE_INLINE D3D12_RESOURCE_BARRIER& expandOne() { return (m_numBarriers < MAX_BARRIERS) ? m_barriers[m_numBarriers++] : _expandOne(); } + /// Flush barriers to command list + SLANG_FORCE_INLINE void flush() { if (m_numBarriers > 0) _flush(); } + + /// Transition resource from prevState to nextState + void transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState); + + /// Ctor + SLANG_FORCE_INLINE D3D12BarrierSubmitter(ID3D12GraphicsCommandList* commandList) : m_numBarriers(0), m_commandList(commandList) { } + /// Dtor + SLANG_FORCE_INLINE ~D3D12BarrierSubmitter() { flush(); } + +protected: + D3D12_RESOURCE_BARRIER& _expandOne(); + void _flush(); + + ID3D12GraphicsCommandList* m_commandList; + int m_numBarriers; + D3D12_RESOURCE_BARRIER m_barriers[MAX_BARRIERS]; +}; + +/*! \brief A class to simplify using Dx12 fences. + +A fence is a mechanism to track GPU work. This is achieved by having a counter that the CPU holds +called the current value. Calling nextSignal will increase the CPU counter, and add a fence +with that value to the commandQueue. When the GPU has completed all the work before the fence it will +update the completed value. This is typically used when +the CPU needs to know the GPU has finished some piece of work has completed. To do this the CPU +can check the completed value, and when it is greater or equal to the value returned by nextSignal the +CPU will know that all the work prior to when the nextSignal was added to the queue will have completed. + +NOTE! This cannot be used across threads, as for amongst other reasons SetEventOnCompletion +only works with a single value. + +Signal on the CommandQueue updates the fence on the GPU side. Signal on the fence object changes +the value on the CPU side (not used here). + +Useful article describing how Dx12 synchronization works: +https://msdn.microsoft.com/en-us/library/windows/desktop/dn899217%28v=vs.85%29.aspx +*/ +class D3D12CounterFence +{ +public: + /// Must be called before used + SlangResult init(ID3D12Device* device, uint64_t initialValue = 0); + /// Increases the counter, signals the queue and waits for the signal to be hit + void nextSignalAndWait(ID3D12CommandQueue* queue); + /// Signals with next counter value. Returns the value the signal was called on + uint64_t nextSignal(ID3D12CommandQueue* commandQueue); + /// Get the current value + SLANG_FORCE_INLINE uint64_t getCurrentValue() const { return m_currentValue; } + /// Get the completed value + SLANG_FORCE_INLINE uint64_t getCompletedValue() const { return m_fence->GetCompletedValue(); } + + /// Waits for the the specified value + void waitUntilCompleted(uint64_t completedValue); + + /// Ctor + D3D12CounterFence() :m_event(nullptr), m_currentValue(0) {} + /// Dtor + ~D3D12CounterFence(); + +protected: + HANDLE m_event; + Slang::ComPtr m_fence; + UINT64 m_currentValue; +}; + +/** The base class for resource types allows for tracking of state. It does not allow for setting of the resource though, such that +an interface can return a D3D12ResourceBase, and a client cant manipulate it's state, but it cannot replace/change the actual resource */ +struct D3D12ResourceBase +{ + /// Add a transition if necessary to the list + void transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter); + /// Get the current state + SLANG_FORCE_INLINE D3D12_RESOURCE_STATES getState() const { return m_state; } + + /// Get the associated resource + SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_resource; } + + /// True if a resource is set + SLANG_FORCE_INLINE bool isSet() const { return m_resource != nullptr; } + + /// Coercible into ID3D12Resource + SLANG_FORCE_INLINE operator ID3D12Resource*() const { return m_resource; } + + /// restore previous state +#if SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS + SLANG_FORCE_INLINE Void restore(D3D12BarrierSubmitter& submitter) { transition(m_prevState, submitter); } +#else + SLANG_FORCE_INLINE void restore(D3D12BarrierSubmitter& submitter) { SLANG_UNUSED(submitter) } +#endif + + /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible + static DXGI_FORMAT calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource); + + /// Ctor + SLANG_FORCE_INLINE D3D12ResourceBase() : + m_state(D3D12_RESOURCE_STATE_COMMON), + m_prevState(D3D12_RESOURCE_STATE_COMMON), + m_resource(nullptr) + {} + +protected: + /// This is protected so as clients cannot slice the class, and so state tracking is lost + ~D3D12ResourceBase() {} + + ID3D12Resource* m_resource; ///< The resource (ref counted) + D3D12_RESOURCE_STATES m_state; ///< The current tracked expected state, if all associated transitions have completed on ID3D12CommandList + D3D12_RESOURCE_STATES m_prevState; ///< The previous state +}; + +struct D3D12Resource : public D3D12ResourceBase +{ + + /// Dtor + ~D3D12Resource() + { + if (m_resource) + { + m_resource->Release(); + } + } + + /// Initialize as committed resource + Slang::Result initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue); + + /// Set a resource with an initial state + void setResource(ID3D12Resource* resource, D3D12_RESOURCE_STATES initialState); + /// Make the resource null + void setResourceNull(); + /// Returns the attached resource (with any ref counts) and sets to nullptr on this. + ID3D12Resource* detach(); + + /// Swaps the resource contents with the contents of the smart pointer + void swap(Slang::ComPtr& resourceInOut); + + /// Sets the current state of the resource (the current state is taken to be the future state once the command list has executed) + /// NOTE! This must be used with care, otherwise state tracking can be made incorrect. + void setState(D3D12_RESOURCE_STATES state); + + /// Set the debug name on a resource + static void setDebugName(ID3D12Resource* resource, const char* name); + + /// Set the the debug name on the resource + void setDebugName(const wchar_t* name); + /// Set the debug name + void setDebugName(const char* name); +}; + +} // renderer_test diff --git a/tools/gfx/descriptor-heap-d3d12.cpp b/tools/gfx/descriptor-heap-d3d12.cpp deleted file mode 100644 index 382fc3219..000000000 --- a/tools/gfx/descriptor-heap-d3d12.cpp +++ /dev/null @@ -1,47 +0,0 @@ - -#include "descriptor-heap-d3d12.h" - -namespace gfx { -using namespace Slang; - -D3D12DescriptorHeap::D3D12DescriptorHeap(): - m_totalSize(0), - m_currentIndex(0), - m_descriptorSize(0) -{ -} - -Result D3D12DescriptorHeap::init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags) -{ - D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {}; - srvHeapDesc.NumDescriptors = size; - srvHeapDesc.Flags = flags; - srvHeapDesc.Type = type; - SLANG_RETURN_ON_FAIL(device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(m_heap.writeRef()))); - - m_descriptorSize = device->GetDescriptorHandleIncrementSize(type); - m_totalSize = size; - - return SLANG_OK; -} - -Result D3D12DescriptorHeap::init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags) -{ - SLANG_RETURN_ON_FAIL(init(device, numHandles, type, flags)); - D3D12_CPU_DESCRIPTOR_HANDLE dst = m_heap->GetCPUDescriptorHandleForHeapStart(); - - // Copy them all - for (int i = 0; i < numHandles; i++, dst.ptr += m_descriptorSize) - { - D3D12_CPU_DESCRIPTOR_HANDLE src = handles[i]; - if (src.ptr != 0) - { - device->CopyDescriptorsSimple(1, dst, src, type); - } - } - - return SLANG_OK; -} - -} // namespace gfx - diff --git a/tools/gfx/descriptor-heap-d3d12.h b/tools/gfx/descriptor-heap-d3d12.h deleted file mode 100644 index a546395d8..000000000 --- a/tools/gfx/descriptor-heap-d3d12.h +++ /dev/null @@ -1,198 +0,0 @@ -#pragma once - - -#include -#include - -#include "../../slang-com-ptr.h" -#include "../../source/core/slang-list.h" - -namespace gfx { - -/*! \brief A simple class to manage an underlying Dx12 Descriptor Heap. Allocations are made linearly in order. It is not possible to free -individual allocations, but all allocations can be deallocated with 'deallocateAll'. */ -class D3D12DescriptorHeap -{ - public: - typedef D3D12DescriptorHeap ThisType; - - /// Initialize - Slang::Result init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags); - /// Initialize with an array of handles copying over the representation - Slang::Result init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags); - - /// Returns the number of slots that have been used - SLANG_FORCE_INLINE int getUsedSize() const { return m_currentIndex; } - - /// Get the total amount of descriptors possible on the heap - SLANG_FORCE_INLINE int getTotalSize() const { return m_totalSize; } - /// Allocate a descriptor. Returns the index, or -1 if none left. - SLANG_FORCE_INLINE int allocate(); - /// Allocate a number of descriptors. Returns the start index (or -1 if not possible) - SLANG_FORCE_INLINE int allocate(int numDescriptors); - - /// - SLANG_FORCE_INLINE int placeAt(int index); - - /// Deallocates all allocations, and starts allocation from the start of the underlying heap again - SLANG_FORCE_INLINE void deallocateAll() { m_currentIndex = 0; } - - /// Get the size of each - SLANG_FORCE_INLINE int getDescriptorSize() const { return m_descriptorSize; } - - /// Get the GPU heap start - SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuStart() const { return m_heap->GetGPUDescriptorHandleForHeapStart(); } - /// Get the CPU heap start - SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuStart() const { return m_heap->GetCPUDescriptorHandleForHeapStart(); } - - /// Get the GPU handle at the specified index - SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(int index) const; - /// Get the CPU handle at the specified index - SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(int index) const; - - /// Get the underlying heap - SLANG_FORCE_INLINE ID3D12DescriptorHeap* getHeap() const { return m_heap; } - - /// Ctor - D3D12DescriptorHeap(); - -protected: - Slang::ComPtr m_heap; ///< The underlying heap being allocated from - int m_totalSize; ///< Total amount of allocations available on the heap - int m_currentIndex; ///< The current descriptor - int m_descriptorSize; ///< The size of each descriptor -}; - -/// A host-visible descriptor, used as "backing storage" for a view. -/// -/// This type is intended to be used to represent descriptors that -/// are allocated and freed through a `HostVisibleDescriptorAllocator`. -struct D3D12HostVisibleDescriptor -{ - D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle; -}; - -/// An allocator for host-visible descriptors. -/// -/// Unlike the `D3D12DescriptorHeap` type, this class allows for both -/// allocation and freeing of descriptors, by maintaining a free list. -/// In order to keep the implementation simple, this class only supports -/// allocation of single descriptors and not ranges. -/// -class D3D12HostVisibleDescriptorAllocator -{ - ID3D12Device* m_device; - int m_chunkSize; - D3D12_DESCRIPTOR_HEAP_TYPE m_type; - - D3D12DescriptorHeap m_heap; - Slang::List m_freeList; - Slang::List m_heaps; - -public: - D3D12HostVisibleDescriptorAllocator() - {} - - Slang::Result init(ID3D12Device* device, int chunkSize, D3D12_DESCRIPTOR_HEAP_TYPE type) - { - m_device = device; - m_chunkSize = chunkSize; - m_type = type; - - SLANG_RETURN_ON_FAIL(m_heap.init(m_device, m_chunkSize, m_type, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); - - return SLANG_OK; - } - - Slang::Result allocate(D3D12HostVisibleDescriptor* outDescriptor) - { - // TODO: this allocator would take some work to make thread-safe - - if(m_freeList.getCount() > 0) - { - auto descriptor = m_freeList[0]; - m_freeList.fastRemoveAt(0); - - *outDescriptor = descriptor; - return SLANG_OK; - } - - int index = m_heap.allocate(); - if(index < 0) - { - // Allocate a new heap and try again. - m_heaps.add(m_heap); - SLANG_RETURN_ON_FAIL(m_heap.init(m_device, m_chunkSize, m_type, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); - - int index = m_heap.allocate(); - if(index < 0) - { - assert(!"descriptor allocation failed on fresh heap"); - return SLANG_FAIL; - } - } - - D3D12HostVisibleDescriptor descriptor; - descriptor.cpuHandle = m_heap.getCpuHandle(index); - - *outDescriptor = descriptor; - return SLANG_OK; - } - - void free(D3D12HostVisibleDescriptor descriptor) - { - m_freeList.add(descriptor); - } -}; - -// --------------------------------------------------------------------------- -int D3D12DescriptorHeap::allocate() -{ - assert(m_currentIndex < m_totalSize); - if (m_currentIndex < m_totalSize) - { - return m_currentIndex++; - } - return -1; -} -// --------------------------------------------------------------------------- -int D3D12DescriptorHeap::allocate(int numDescriptors) -{ - assert(m_currentIndex + numDescriptors <= m_totalSize); - if (m_currentIndex + numDescriptors <= m_totalSize) - { - const int index = m_currentIndex; - m_currentIndex += numDescriptors; - return index; - } - return -1; -} -// --------------------------------------------------------------------------- -SLANG_FORCE_INLINE int D3D12DescriptorHeap::placeAt(int index) -{ - assert(index >= 0 && index < m_totalSize); - m_currentIndex = index + 1; - return index; -} - -// --------------------------------------------------------------------------- -SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getCpuHandle(int index) const -{ - assert(index >= 0 && index < m_totalSize); - D3D12_CPU_DESCRIPTOR_HANDLE start = m_heap->GetCPUDescriptorHandleForHeapStart(); - D3D12_CPU_DESCRIPTOR_HANDLE dst; - dst.ptr = start.ptr + m_descriptorSize * index; - return dst; -} -// --------------------------------------------------------------------------- -SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getGpuHandle(int index) const -{ - assert(index >= 0 && index < m_totalSize); - D3D12_GPU_DESCRIPTOR_HANDLE start = m_heap->GetGPUDescriptorHandleForHeapStart(); - D3D12_GPU_DESCRIPTOR_HANDLE dst; - dst.ptr = start.ptr + m_descriptorSize * index; - return dst; -} - -} // namespace gfx - diff --git a/tools/gfx/gfx.vcxproj b/tools/gfx/gfx.vcxproj index 059a99618..d4b1885a8 100644 --- a/tools/gfx/gfx.vcxproj +++ b/tools/gfx/gfx.vcxproj @@ -171,47 +171,47 @@ - - - + + + + + + - - - - + - - - - - - + + + + + + - - - + + + + + + - - - - + - - - - - - - + + + + + + + diff --git a/tools/gfx/gfx.vcxproj.filters b/tools/gfx/gfx.vcxproj.filters index 95b5f31d2..1c27e4f24 100644 --- a/tools/gfx/gfx.vcxproj.filters +++ b/tools/gfx/gfx.vcxproj.filters @@ -9,61 +9,61 @@ - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files Header Files - - Header Files - Header Files Header Files - + + Header Files + + Header Files - + Header Files - + Header Files - + Header Files - + Header Files @@ -71,61 +71,61 @@ - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files diff --git a/tools/gfx/open-gl/render-gl.cpp b/tools/gfx/open-gl/render-gl.cpp new file mode 100644 index 000000000..91c31b71d --- /dev/null +++ b/tools/gfx/open-gl/render-gl.cpp @@ -0,0 +1,1504 @@ +// render-gl.cpp +#include "render-gl.h" + +//WORKING:#include "options.h" +#include "../render.h" + +#include +#include +#include "core/slang-basic.h" +#include "core/slang-secure-crt.h" +#include "external/stb/stb_image_write.h" + +#include "../surface.h" + +// TODO(tfoley): eventually we should be able to run these +// tests on non-Windows targets to confirm that cross-compilation +// at least *works* on those platforms... +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#ifdef _MSC_VER +#include +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif + +#pragma comment(lib, "opengl32") + +#include +#include "external/glext.h" + +// We define an "X-macro" for mapping over loadable OpenGL +// extension entry point that we will use, so that we can +// easily write generic code to iterate over them. +#define MAP_GL_EXTENSION_FUNCS(F) \ + F(glCreateProgram, PFNGLCREATEPROGRAMPROC) \ + F(glCreateShader, PFNGLCREATESHADERPROC) \ + F(glShaderSource, PFNGLSHADERSOURCEPROC) \ + F(glCompileShader, PFNGLCOMPILESHADERPROC) \ + F(glGetShaderiv, PFNGLGETSHADERIVPROC) \ + F(glDeleteShader, PFNGLDELETESHADERPROC) \ + F(glAttachShader, PFNGLATTACHSHADERPROC) \ + F(glLinkProgram, PFNGLLINKPROGRAMPROC) \ + F(glGetProgramiv, PFNGLGETPROGRAMIVPROC) \ + F(glGetProgramInfoLog, PFNGLGETPROGRAMINFOLOGPROC) \ + F(glDeleteProgram, PFNGLDELETEPROGRAMPROC) \ + F(glGetShaderInfoLog, PFNGLGETSHADERINFOLOGPROC) \ + F(glGenBuffers, PFNGLGENBUFFERSPROC) \ + F(glBindBuffer, PFNGLBINDBUFFERPROC) \ + F(glBufferData, PFNGLBUFFERDATAPROC) \ + F(glDeleteBuffers, PFNGLDELETEBUFFERSPROC) \ + F(glMapBuffer, PFNGLMAPBUFFERPROC) \ + F(glUnmapBuffer, PFNGLUNMAPBUFFERPROC) \ + F(glUseProgram, PFNGLUSEPROGRAMPROC) \ + F(glBindBufferBase, PFNGLBINDBUFFERBASEPROC) \ + F(glVertexAttribPointer, PFNGLVERTEXATTRIBPOINTERPROC) \ + F(glEnableVertexAttribArray, PFNGLENABLEVERTEXATTRIBARRAYPROC) \ + F(glDisableVertexAttribArray, PFNGLDISABLEVERTEXATTRIBARRAYPROC) \ + F(glDebugMessageCallback, PFNGLDEBUGMESSAGECALLBACKPROC) \ + F(glDispatchCompute, PFNGLDISPATCHCOMPUTEPROC) \ + F(glActiveTexture, PFNGLACTIVETEXTUREPROC) \ + F(glCreateSamplers, PFNGLCREATESAMPLERSPROC) \ + F(glDeleteSamplers, PFNGLDELETESAMPLERSPROC) \ + F(glBindSampler, PFNGLBINDSAMPLERPROC) \ + F(glTexImage3D, PFNGLTEXIMAGE3DPROC) \ + F(glSamplerParameteri, PFNGLSAMPLERPARAMETERIPROC) \ + /* end */ + +using namespace Slang; + +namespace gfx { + +class GLRenderer : public Renderer +{ +public: + + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual const List& getFeatures() override { return m_features; } + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + TextureResource::Desc getSwapChainTextureDesc() override; + + Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; + Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; + Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; + + Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; + Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; + + Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; + + Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; + Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; + Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; + + Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; + Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; + Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; + + virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; + virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + void setViewports(UInt count, Viewport const* viewports) override; + void setScissorRects(UInt count, ScissorRect const* rects) override; + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override {} + virtual void waitForGpu() override {} + virtual RendererType getRendererType() const override { return RendererType::OpenGl; } + + GLRenderer(); + ~GLRenderer(); + + protected: + enum + { + kMaxVertexStreams = 16, + kMaxDescriptorSetCount = 8, + }; + struct VertexAttributeFormat + { + GLint componentCount; + GLenum componentType; + GLboolean normalized; + }; + + struct VertexAttributeDesc + { + VertexAttributeFormat format; + GLuint streamIndex; + GLsizei offset; + }; + + class InputLayoutImpl: public InputLayout + { + public: + VertexAttributeDesc m_attributes[kMaxVertexStreams]; + UInt m_attributeCount = 0; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + BufferResourceImpl(Usage initialUsage, const Desc& desc, WeakSink* renderer, GLuint id, GLenum target): + Parent(desc), + m_renderer(renderer), + m_handle(id), + m_initialUsage(initialUsage), + m_target(target) + {} + ~BufferResourceImpl() + { + if (auto renderer = m_renderer->get()) + { + renderer->glDeleteBuffers(1, &m_handle); + } + } + + Usage m_initialUsage; + RefPtr > m_renderer; + GLuint m_handle; + GLenum m_target; + }; + + class TextureResourceImpl: public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(Usage initialUsage, const Desc& desc, WeakSink* renderer): + Parent(desc), + m_initialUsage(initialUsage), + m_renderer(renderer) + { + m_target = 0; + m_handle = 0; + } + + ~TextureResourceImpl() + { + if (m_handle) + { + glDeleteTextures(1, &m_handle); + } + } + + Usage m_initialUsage; + RefPtr > m_renderer; + GLenum m_target; + GLuint m_handle; + }; + + class SamplerStateImpl : public SamplerState + { + public: + GLuint m_samplerID; + }; + + class ResourceViewImpl : public ResourceView + { + }; + + class TextureViewImpl : public ResourceViewImpl + { + public: + RefPtr m_resource; + GLuint m_textureID; + }; + + class BufferViewImpl : public ResourceViewImpl + { + public: + RefPtr m_resource; + GLuint m_bufferID; + }; + + enum class GLDescriptorSlotType + { + ConstantBuffer, + CombinedTextureSampler, + + CountOf, + }; + + class DescriptorSetLayoutImpl : public DescriptorSetLayout + { + public: + struct RangeInfo + { + GLDescriptorSlotType type; + UInt arrayIndex; + }; + List m_ranges; + Int m_counts[int(GLDescriptorSlotType::CountOf)]; + }; + + class PipelineLayoutImpl : public PipelineLayout + { + public: + struct DescriptorSetInfo + { + RefPtr layout; + UInt baseArrayIndex[int(GLDescriptorSlotType::CountOf)]; + }; + + List m_sets; + }; + + class DescriptorSetImpl : public DescriptorSet + { + public: + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; + virtual void setResource(UInt range, UInt index, ResourceView* view) override; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) override; + + RefPtr m_layout; + List> m_constantBuffers; + List> m_textures; + List> m_samplers; + }; + + class ShaderProgramImpl : public ShaderProgram + { + public: + ShaderProgramImpl(WeakSink* renderer, GLuint id): + m_renderer(renderer), + m_id(id) + { + } + ~ShaderProgramImpl() + { + if (auto renderer = m_renderer->get()) + { + renderer->glDeleteProgram(m_id); + } + } + + GLuint m_id; + RefPtr > m_renderer; + }; + + class PipelineStateImpl : public PipelineState + { + public: + RefPtr m_program; + RefPtr m_pipelineLayout; + RefPtr m_inputLayout; + }; + + enum class GlPixelFormat + { + Unknown, + RGBA_Unorm_UInt8, + CountOf, + }; + + struct GlPixelFormatInfo + { + GLint internalFormat; // such as GL_RGBA8 + GLenum format; // such as GL_RGBA + GLenum formatType; // such as GL_UNSIGNED_BYTE + }; + +// void destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details); + + void bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets); + void flushStateForDraw(); + GLuint loadShader(GLenum stage, char const* source); + void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message); + + /// Returns GlPixelFormat::Unknown if not an equivalent + static GlPixelFormat _getGlPixelFormat(Format format); + + static void APIENTRY staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); + static VertexAttributeFormat getVertexAttributeFormat(Format format); + + static void compileTimeAsserts(); + + HDC m_hdc; + HGLRC m_glContext; + float m_clearColor[4] = { 0, 0, 0, 0 }; + + RefPtr m_currentPipelineState; + RefPtr > m_weakRenderer; + + RefPtr m_boundDescriptorSets[kMaxDescriptorSetCount]; + + GLenum m_boundPrimitiveTopology = GL_TRIANGLES; + GLuint m_boundVertexStreamBuffers[kMaxVertexStreams]; + UInt m_boundVertexStreamStrides[kMaxVertexStreams]; + UInt m_boundVertexStreamOffsets[kMaxVertexStreams]; + + Desc m_desc; + + List m_features; + + // Declare a function pointer for each OpenGL + // extension function we need to load +#define DECLARE_GL_EXTENSION_FUNC(NAME, TYPE) TYPE NAME; + MAP_GL_EXTENSION_FUNCS(DECLARE_GL_EXTENSION_FUNC) +#undef DECLARE_GL_EXTENSION_FUNC + + static const GlPixelFormatInfo s_pixelFormatInfos[]; /// Maps GlPixelFormat to a format info +}; + +/* static */GLRenderer::GlPixelFormat GLRenderer::_getGlPixelFormat(Format format) +{ + switch (format) + { + case Format::RGBA_Unorm_UInt8: return GlPixelFormat::RGBA_Unorm_UInt8; + default: return GlPixelFormat::Unknown; + } +} + +/* static */ const GLRenderer::GlPixelFormatInfo GLRenderer::s_pixelFormatInfos[] = +{ + // internalType, format, formatType + { 0, 0, 0}, // GlPixelFormat::Unknown + { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, // GlPixelFormat::RGBA_Unorm_UInt8 +}; + +/* static */void GLRenderer::compileTimeAsserts() +{ + SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_pixelFormatInfos) == int(GlPixelFormat::CountOf)); +} + +Renderer* createGLRenderer() +{ + return new GLRenderer(); +} + +void GLRenderer::debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message) +{ + ::OutputDebugStringA("GL: "); + ::OutputDebugStringA(message); + ::OutputDebugStringA("\n"); + + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + break; + default: + break; + } +} + + +GLRenderer::GLRenderer() +{ + m_weakRenderer = new WeakSink(this); +} + +GLRenderer::~GLRenderer() +{ + // We can destroy things whilst in this state + m_currentPipelineState.setNull(); + + // By resetting the weak pointer, other objects accessing through WeakSink will no longer + // be able to access this object which is entering a 'being destroyed' to 'destroyed' state + if (m_weakRenderer) + { + SLANG_ASSERT(m_weakRenderer->get() == this); + m_weakRenderer->detach(); + } +} + +/* static */void APIENTRY GLRenderer::staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) +{ + ((GLRenderer*)userParam)->debugCallback(source, type, id, severity, length, message); +} + +/* static */GLRenderer::VertexAttributeFormat GLRenderer::getVertexAttributeFormat(Format format) +{ + switch (format) + { + default: assert(!"unexpected"); return VertexAttributeFormat(); + +#define CASE(NAME, COUNT, TYPE, NORMALIZED) \ + case Format::NAME: do { VertexAttributeFormat result = {COUNT, TYPE, NORMALIZED}; return result; } while (0) + + CASE(RGBA_Float32, 4, GL_FLOAT, GL_FALSE); + CASE(RGB_Float32, 3, GL_FLOAT, GL_FALSE); + CASE(RG_Float32, 2, GL_FLOAT, GL_FALSE); + CASE(R_Float32, 1, GL_FLOAT, GL_FALSE); +#undef CASE + } +} + +void GLRenderer::bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) +{ + for (UInt ii = 0; ii < slotCount; ++ii) + { + UInt slot = startSlot + ii; + + BufferResourceImpl* buffer = static_cast(buffers[ii]); + GLuint bufferID = buffer ? buffer->m_handle : 0; + + assert(!offsets || !offsets[ii]); + + glBindBufferBase(target, (GLuint)slot, bufferID); + } +} + +void GLRenderer::flushStateForDraw() +{ + auto inputLayout = m_currentPipelineState->m_inputLayout.Ptr(); + auto attrCount = Index(inputLayout->m_attributeCount); + for (Index ii = 0; ii < attrCount; ++ii) + { + auto& attr = inputLayout->m_attributes[ii]; + + auto streamIndex = attr.streamIndex; + + glBindBuffer(GL_ARRAY_BUFFER, m_boundVertexStreamBuffers[streamIndex]); + + glVertexAttribPointer( + (GLuint)ii, + attr.format.componentCount, + attr.format.componentType, + attr.format.normalized, + (GLsizei)m_boundVertexStreamStrides[streamIndex], + (GLvoid*)(attr.offset + m_boundVertexStreamOffsets[streamIndex])); + + glEnableVertexAttribArray((GLuint)ii); + } + for (Index ii = attrCount; ii < kMaxVertexStreams; ++ii) + { + glDisableVertexAttribArray((GLuint)ii); + } + + // Next bind the descriptor sets as required by the layout + auto pipelineLayout = m_currentPipelineState->m_pipelineLayout; + auto descriptorSetCount = pipelineLayout->m_sets.getCount(); + for(Index ii = 0; ii < descriptorSetCount; ++ii) + { + auto descriptorSet = m_boundDescriptorSets[ii]; + auto descriptorSetInfo = pipelineLayout->m_sets[ii]; + auto descriptorSetLayout = descriptorSetInfo.layout; + + // TODO: need to validate that `descriptorSet->m_layout` matches + // `descriptorSetLayout`. + + { + // First we will bind any uniform buffers that were specified. + + auto slotTypeIndex = int(GLDescriptorSlotType::ConstantBuffer); + auto count = descriptorSetLayout->m_counts[slotTypeIndex]; + auto baseIndex = descriptorSetInfo.baseArrayIndex[slotTypeIndex]; + + for(Int ii = 0; ii < count; ++ii) + { + auto bufferImpl = descriptorSet->m_constantBuffers[ii]; + glBindBufferBase(GL_UNIFORM_BUFFER, GLuint(ii), bufferImpl->m_handle); + } + } + + + { + // Next we will bind any combined texture/sampler slots. + + auto slotTypeIndex = int(GLDescriptorSlotType::CombinedTextureSampler); + auto count = descriptorSetLayout->m_counts[slotTypeIndex]; + auto baseIndex = descriptorSetInfo.baseArrayIndex[slotTypeIndex]; + + // TODO: We should be able to use a single call to glBindTextures here, + // rather than a loop. This would also eliminate the need to retain + // the appropriate target (e.g., `GL_TEXTURE_2D` for binding). + + for(Int ii = 0; ii < count; ++ii) + { + auto textureViewImpl = descriptorSet->m_textures[ii]; + auto samplerImpl = descriptorSet->m_samplers[ii]; + + glActiveTexture(GLuint(GL_TEXTURE0 + ii)); + glBindTexture(GL_TEXTURE_2D, textureViewImpl->m_textureID); + + glBindSampler(GLuint(baseIndex + ii), samplerImpl->m_samplerID); + } + } + } +} + +GLuint GLRenderer::loadShader(GLenum stage, const char* source) +{ + // GLSL is monumentally stupid. It officially requires the `#version` directive + // to be the first thing in the file, which wouldn't be so bad but the API + // doesn't provide a way to pass a `#define` into your shader other than by + // prepending it to the whole thing. + // + // We are going to solve this problem by doing some surgery on the source + // that was passed in. + + const char* sourceBegin = source; + const char* sourceEnd = source + strlen(source); + + // Look for a version directive in the user-provided source. + const char* versionBegin = strstr(source, "#version"); + const char* versionEnd = nullptr; + if (versionBegin) + { + // If we found a directive, then scan for the end-of-line + // after it, and use that to specify the slice. + versionEnd = strchr(versionBegin, '\n'); + if (!versionEnd) + { + versionEnd = sourceEnd; + } + else + { + versionEnd = versionEnd + 1; + } + } + else + { + // If we didn't find a directive, then treat it as being + // a zero-byte slice at the start of the string + versionBegin = sourceBegin; + versionEnd = sourceBegin; + } + + enum { kMaxSourceStringCount = 16 }; + const GLchar* sourceStrings[kMaxSourceStringCount]; + GLint sourceStringLengths[kMaxSourceStringCount]; + + int sourceStringCount = 0; + + const char* stagePrelude = "\n"; + switch (stage) + { +#define CASE(NAME) case GL_##NAME##_SHADER: stagePrelude = "#define __GLSL_" #NAME "__ 1\n"; break + + CASE(VERTEX); + CASE(TESS_CONTROL); + CASE(TESS_EVALUATION); + CASE(GEOMETRY); + CASE(FRAGMENT); + CASE(COMPUTE); + +#undef CASE + } + + const char* prelude = + "#define __GLSL__ 1\n" + ; + +#define ADD_SOURCE_STRING_SPAN(BEGIN, END) \ + sourceStrings[sourceStringCount] = BEGIN; \ + sourceStringLengths[sourceStringCount++] = GLint(END - BEGIN) \ + /* end */ + +#define ADD_SOURCE_STRING(BEGIN) \ + sourceStrings[sourceStringCount] = BEGIN; \ + sourceStringLengths[sourceStringCount++] = GLint(strlen(BEGIN)) \ + /* end */ + + ADD_SOURCE_STRING_SPAN(versionBegin, versionEnd); + ADD_SOURCE_STRING(stagePrelude); + ADD_SOURCE_STRING(prelude); + ADD_SOURCE_STRING_SPAN(sourceBegin, versionBegin); + ADD_SOURCE_STRING_SPAN(versionEnd, sourceEnd); + + auto shaderID = glCreateShader(stage); + glShaderSource( + shaderID, + sourceStringCount, + &sourceStrings[0], + &sourceStringLengths[0]); + glCompileShader(shaderID); + + GLint success = GL_FALSE; + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); + if (!success) + { + int maxSize = 0; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxSize); + + auto infoBuffer = (char*)malloc(maxSize); + + int infoSize = 0; + glGetShaderInfoLog(shaderID, maxSize, &infoSize, infoBuffer); + if (infoSize > 0) + { + fprintf(stderr, "%s", infoBuffer); + ::OutputDebugStringA(infoBuffer); + } + + glDeleteShader(shaderID); + return 0; + } + + return shaderID; +} + +#if 0 +void GLRenderer::destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details) +{ + const auto& bindings = desc.m_bindings; + const int numBindings = int(bindings.Count()); + for (int i = 0; i < numBindings; ++i) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + if (binding.bindingType == BindingType::Sampler && detail.m_samplerHandle != 0) + { + glDeleteSamplers(1, &detail.m_samplerHandle); + } + } +} +#endif + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult GLRenderer::initialize(const Desc& desc, void* inWindowHandle) +{ + auto windowHandle = (HWND)inWindowHandle; + m_desc = desc; + + m_hdc = ::GetDC(windowHandle); + + PIXELFORMATDESCRIPTOR pixelFormatDesc = { sizeof(PIXELFORMATDESCRIPTOR) }; + pixelFormatDesc.nVersion = 1; + pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pixelFormatDesc.iPixelType = PFD_TYPE_RGBA; + pixelFormatDesc.cColorBits = 32; + pixelFormatDesc.cDepthBits = 24; + pixelFormatDesc.cStencilBits = 8; + pixelFormatDesc.iLayerType = PFD_MAIN_PLANE; + + int pixelFormatIndex = ChoosePixelFormat(m_hdc, &pixelFormatDesc); + SetPixelFormat(m_hdc, pixelFormatIndex, &pixelFormatDesc); + + m_glContext = wglCreateContext(m_hdc); + wglMakeCurrent(m_hdc, m_glContext); + + auto renderer = glGetString(GL_RENDERER); + + if (renderer && desc.adapter.getLength() > 0) + { + String lowerAdapter = desc.adapter.toLower(); + String lowerRenderer = String((const char*)renderer).toLower(); + + // The adapter is not available + if (lowerRenderer.indexOf(lowerAdapter) == Index(-1)) + { + return SLANG_E_NOT_AVAILABLE; + } + } + + auto extensions = glGetString(GL_EXTENSIONS); + + // Load each of our extension functions by name + +#define LOAD_GL_EXTENSION_FUNC(NAME, TYPE) NAME = (TYPE) wglGetProcAddress(#NAME); + MAP_GL_EXTENSION_FUNCS(LOAD_GL_EXTENSION_FUNC) +#undef LOAD_GL_EXTENSION_FUNC + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glViewport(0, 0, desc.width, desc.height); + + if (glDebugMessageCallback) + { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(staticDebugCallback, this); + } + + return SLANG_OK; +} + +void GLRenderer::setClearColor(const float color[4]) +{ + glClearColor(color[0], color[1], color[2], color[3]); +} + +void GLRenderer::clearFrame() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +void GLRenderer::presentFrame() +{ + glFlush(); + ::SwapBuffers(m_hdc); +} + +TextureResource::Desc GLRenderer::getSwapChainTextureDesc() +{ + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1); + return desc; +} + +SlangResult GLRenderer::captureScreenSurface(Surface& surfaceOut) +{ + SLANG_RETURN_ON_FAIL(surfaceOut.allocate(m_desc.width, m_desc.height, Format::RGBA_Unorm_UInt8, 1, SurfaceAllocator::getMallocAllocator())); + glReadPixels(0, 0, m_desc.width, m_desc.height, GL_RGBA, GL_UNSIGNED_BYTE, surfaceOut.m_data); + surfaceOut.flipInplaceVertically(); + return SLANG_OK; +} + +Result GLRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) +{ + TextureResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + GlPixelFormat pixelFormat = _getGlPixelFormat(srcDesc.format); + if (pixelFormat == GlPixelFormat::Unknown) + { + return SLANG_FAIL; + } + + const GlPixelFormatInfo& info = s_pixelFormatInfos[int(pixelFormat)]; + + const GLint internalFormat = info.internalFormat; + const GLenum format = info.format; + const GLenum formatType = info.formatType; + + RefPtr texture(new TextureResourceImpl(initialUsage, srcDesc, m_weakRenderer)); + + GLenum target = 0; + GLuint handle = 0; + glGenTextures(1, &handle); + + const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); + + assert(initData); + assert(initData->numSubResources == srcDesc.numMipLevels * srcDesc.size.depth * effectiveArraySize); + + // Set on texture so will be freed if failure + texture->m_handle = handle; + const void*const*const data = initData->subResources; + + switch (srcDesc.type) + { + case Resource::Type::Texture1D: + { + if (srcDesc.arraySize > 0) + { + target = GL_TEXTURE_1D_ARRAY; + glBindTexture(target, handle); + + int slice = 0; + for (int i = 0; i < effectiveArraySize; i++) + { + for (int j = 0; j < srcDesc.numMipLevels; j++) + { + glTexImage2D(target, j, internalFormat, srcDesc.size.width, i, 0, format, formatType, data[slice++]); + } + } + } + else + { + target = GL_TEXTURE_1D; + glBindTexture(target, handle); + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage1D(target, i, internalFormat, srcDesc.size.width, 0, format, formatType, data[i]); + } + } + break; + } + case Resource::Type::TextureCube: + case Resource::Type::Texture2D: + { + if (srcDesc.arraySize > 0) + { + if (srcDesc.type == Resource::Type::TextureCube) + { + target = GL_TEXTURE_CUBE_MAP_ARRAY; + } + else + { + target = GL_TEXTURE_2D_ARRAY; + } + + glBindTexture(target, handle); + + int slice = 0; + for (int i = 0; i < effectiveArraySize; i++) + { + for (int j = 0; j < srcDesc.numMipLevels; j++) + { + glTexImage3D(target, j, internalFormat, srcDesc.size.width, srcDesc.size.height, slice, 0, format, formatType, data[slice++]); + } + } + } + else + { + if (srcDesc.type == Resource::Type::TextureCube) + { + target = GL_TEXTURE_CUBE_MAP; + glBindTexture(target, handle); + + int slice = 0; + for (int j = 0; j < 6; j++) + { + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[slice++]); + } + } + } + else + { + target = GL_TEXTURE_2D; + glBindTexture(target, handle); + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage2D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[i]); + } + } + } + break; + } + case Resource::Type::Texture3D: + { + target = GL_TEXTURE_3D; + glBindTexture(target, handle); + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage3D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, srcDesc.size.depth, 0, format, formatType, data[i]); + } + break; + } + default: + return SLANG_FAIL; + } + + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); + + // Assume regular sampling (might be superseded - if a combined sampler wanted) + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); + + texture->m_target = target; + + *outResource = texture.detach(); + return SLANG_OK; +} + +static GLenum _calcUsage(Resource::Usage usage) +{ + typedef Resource::Usage Usage; + switch (usage) + { + case Usage::ConstantBuffer: return GL_DYNAMIC_DRAW; + default: return GL_STATIC_READ; + } +} + +static GLenum _calcTarget(Resource::Usage usage) +{ + typedef Resource::Usage Usage; + switch (usage) + { + case Usage::ConstantBuffer: return GL_UNIFORM_BUFFER; + default: return GL_SHADER_STORAGE_BUFFER; + } +} + +Result GLRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) +{ + BufferResource::Desc desc(descIn); + desc.setDefaults(initialUsage); + + const GLenum target = _calcTarget(initialUsage); + // TODO: should derive from desc... + const GLenum usage = _calcUsage(initialUsage); + + GLuint bufferID = 0; + glGenBuffers(1, &bufferID); + glBindBuffer(target, bufferID); + + glBufferData(target, descIn.sizeInBytes, initData, usage); + + RefPtr resourceImpl = new BufferResourceImpl(initialUsage, desc, m_weakRenderer, bufferID, target); + *outResource = resourceImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) +{ + GLuint samplerID; + glCreateSamplers(1, &samplerID); + + RefPtr samplerImpl = new SamplerStateImpl(); + samplerImpl->m_samplerID = samplerID; + *outSampler = samplerImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (TextureResourceImpl*) texture; + + // TODO: actually do something? + + RefPtr viewImpl = new TextureViewImpl(); + viewImpl->m_resource = resourceImpl; + viewImpl->m_textureID = resourceImpl->m_handle; + *outView = viewImpl; + return SLANG_OK; +} + +Result GLRenderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + + // TODO: actually do something? + + RefPtr viewImpl = new BufferViewImpl(); + viewImpl->m_resource = resourceImpl; + viewImpl->m_bufferID = resourceImpl->m_handle; + *outView = viewImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) +{ + RefPtr inputLayout = new InputLayoutImpl; + + inputLayout->m_attributeCount = inputElementCount; + for (UInt ii = 0; ii < inputElementCount; ++ii) + { + auto& inputAttr = inputElements[ii]; + auto& glAttr = inputLayout->m_attributes[ii]; + + glAttr.streamIndex = 0; + glAttr.format = getVertexAttributeFormat(inputAttr.format); + glAttr.offset = (GLsizei)inputAttr.offset; + } + + *outLayout = inputLayout.detach(); + return SLANG_OK; +} + +void* GLRenderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + BufferResourceImpl* buffer = static_cast(bufferIn); + + //GLenum target = GL_UNIFORM_BUFFER; + + GLuint access = 0; + switch (flavor) + { + case MapFlavor::WriteDiscard: + case MapFlavor::HostWrite: + access = GL_WRITE_ONLY; + break; + case MapFlavor::HostRead: + access = GL_READ_ONLY; + break; + } + + glBindBuffer(buffer->m_target, buffer->m_handle); + + return glMapBuffer(buffer->m_target, access); +} + +void GLRenderer::unmap(BufferResource* bufferIn) +{ + BufferResourceImpl* buffer = static_cast(bufferIn); + glUnmapBuffer(buffer->m_target); +} + +void GLRenderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + GLenum glTopology = 0; + switch (topology) + { +#define CASE(NAME, VALUE) case PrimitiveTopology::NAME: glTopology = VALUE; break + + CASE(TriangleList, GL_TRIANGLES); + +#undef CASE + } + m_boundPrimitiveTopology = glTopology; +} + +void GLRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) +{ + for (UInt ii = 0; ii < slotCount; ++ii) + { + UInt slot = startSlot + ii; + + BufferResourceImpl* buffer = static_cast(buffers[ii]); + GLuint bufferID = buffer ? buffer->m_handle : 0; + + m_boundVertexStreamBuffers[slot] = bufferID; + m_boundVertexStreamStrides[slot] = strides[ii]; + m_boundVertexStreamOffsets[slot] = offsets[ii]; + } +} + +void GLRenderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) +{ +} + +void GLRenderer::setDepthStencilTarget(ResourceView* depthStencilView) +{ +} + +void GLRenderer::setViewports(UInt count, Viewport const* viewports) +{ + assert(count == 1); + auto viewport = viewports[0]; + glViewport( + (GLint) viewport.originX, + (GLint) viewport.originY, + (GLsizei) viewport.extentX, + (GLsizei) viewport.extentY); + glDepthRange(viewport.minZ, viewport.maxZ); +} + +void GLRenderer::setScissorRects(UInt count, ScissorRect const* rects) +{ + assert(count <= 1); + if( count ) + { + // TODO: this isn't goign to be quite right because of the + // flipped coordinate system in GL. + // + // The best way around this is probably to *always* render + // things internally into textures with "flipped" conventions, + // and then only deal with the flipping as part of a final + // "present" step that copies to the primary back-buffer. + // + auto rect = rects[0]; + glScissor( + GLint(rect.minX), + GLint(rect.minY), + GLsizei(rect.maxX - rect.minX), + GLsizei(rect.maxY - rect.minY)); + + glEnable(GL_SCISSOR_TEST); + } + else + { + glDisable(GL_SCISSOR_TEST); + } +} + +void GLRenderer::setPipelineState(PipelineType pipelineType, PipelineState* state) +{ + auto pipelineStateImpl = (PipelineStateImpl*) state; + + m_currentPipelineState = pipelineStateImpl; + + auto program = pipelineStateImpl->m_program; + GLuint programID = program ? program->m_id : 0; + glUseProgram(programID); +} + +void GLRenderer::draw(UInt vertexCount, UInt startVertex = 0) +{ + flushStateForDraw(); + + glDrawArrays(m_boundPrimitiveTopology, (GLint)startVertex, (GLsizei)vertexCount); +} + +void GLRenderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) +{ + assert(!"unimplemented"); +} + +void GLRenderer::dispatchCompute(int x, int y, int z) +{ + glDispatchCompute(x, y, z); +} + +#if 0 +BindingState* GLRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr bindingState(new BindingStateImpl(bindingStateDesc, this)); + + const auto& srcBindings = bindingStateDesc.m_bindings; + const int numBindings = int(srcBindings.Count()); + + auto& dstDetails = bindingState->m_bindingDetails; + dstDetails.SetSize(numBindings); + + for (int i = 0; i < numBindings; ++i) + { + auto& dstDetail = dstDetails[i]; + const auto& srcBinding = srcBindings[i]; + + + switch (srcBinding.bindingType) + { + case BindingType::Texture: + case BindingType::Buffer: + { + break; + } + case BindingType::CombinedTextureSampler: + { + assert(srcBinding.resource && srcBinding.resource->isTexture()); + TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; + + if (samplerDesc.isCompareSampler) + { + auto target = texture->m_target; + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + break; + } + case BindingType::Sampler: + { + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; + + GLuint handle; + + glCreateSamplers(1, &handle); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, GL_REPEAT); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, GL_REPEAT); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, GL_REPEAT); + + if (samplerDesc.isCompareSampler) + { + glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + else + { + glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); + } + + dstDetail.m_samplerHandle = handle; + break; + } + } + } + + return bindingState.detach(); +} + +void GLRenderer::setBindingState(BindingState* stateIn) +{ + BindingStateImpl* state = static_cast(stateIn); + + const auto& bindingDesc = state->getDesc(); + + const auto& details = state->m_bindingDetails; + const auto& bindings = bindingDesc.m_bindings; + const int numBindings = int(bindings.Count()); + + for (int i = 0; i < numBindings; ++i) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + switch (binding.bindingType) + { + case BindingType::Buffer: + { + const int bindingIndex = binding.registerRange.getSingleIndex(); + + BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); + glBindBufferBase(buffer->m_target, bindingIndex, buffer->m_handle); + break; + } + case BindingType::Sampler: + { + for (int index = binding.registerRange.index; index < binding.registerRange.index + binding.registerRange.size; ++index) + { + glBindSampler(index, detail.m_samplerHandle); + } + break; + } + case BindingType::Texture: + case BindingType::CombinedTextureSampler: + { + BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); + + const int bindingIndex = binding.registerRange.getSingleIndex(); + + glActiveTexture(GL_TEXTURE0 + bindingIndex); + glBindTexture(buffer->m_target, buffer->m_handle); + break; + } + } + } +} +#endif + +void GLRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + + auto layout = m_layout; + auto rangeInfo = layout->m_ranges[range]; + auto arrayIndex = rangeInfo.arrayIndex + index; + + m_constantBuffers[arrayIndex] = resourceImpl; +} + +void GLRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) +{ + auto viewImpl = (ResourceViewImpl*) view; + + auto layout = m_layout; + auto rangeInfo = layout->m_ranges[range]; + auto arrayIndex = rangeInfo.arrayIndex + index; + + assert(!"unimplemented"); +} + +void GLRenderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) +{ + assert(!"unsupported"); +} + +void GLRenderer::DescriptorSetImpl::setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) +{ + auto viewImpl = (TextureViewImpl*) textureView; + auto samplerImpl = (SamplerStateImpl*) sampler; + + auto layout = m_layout; + auto rangeInfo = layout->m_ranges[range]; + auto arrayIndex = rangeInfo.arrayIndex + index; + + m_textures[arrayIndex] = viewImpl; + m_samplers[arrayIndex] = samplerImpl; +} + +void GLRenderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) +{ + auto descriptorSetImpl = (DescriptorSetImpl*)descriptorSet; + + // TODO: can we just bind things immediately here, rather than shadowing the state? + + m_boundDescriptorSets[index] = descriptorSetImpl; +} + +Result GLRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) +{ + RefPtr layoutImpl = new DescriptorSetLayoutImpl(); + + Int counts[int(GLDescriptorSlotType::CountOf)] = { 0, }; + + Int rangeCount = desc.slotRangeCount; + for(Int rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + DescriptorSetLayoutImpl::RangeInfo rangeInfo; + + GLDescriptorSlotType glSlotType; + switch( rangeDesc.type ) + { + default: + assert(!"unsupported"); + break; + + // TODO: There are many other slot types we could support here, + // in particular including storage buffers. + + case DescriptorSlotType::CombinedImageSampler: + glSlotType = GLDescriptorSlotType::CombinedTextureSampler; + break; + + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::DynamicUniformBuffer: + glSlotType = GLDescriptorSlotType::ConstantBuffer; + break; + } + + rangeInfo.type = glSlotType; + rangeInfo.arrayIndex = counts[int(glSlotType)]; + counts[int(glSlotType)] += rangeDesc.count; + + layoutImpl->m_ranges.add(rangeInfo); + } + + for( Int ii = 0; ii < int(GLDescriptorSlotType::CountOf); ++ii ) + { + layoutImpl->m_counts[ii] = counts[ii]; + } + + *outLayout = layoutImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) +{ + RefPtr layoutImpl = new PipelineLayoutImpl(); + + static const int kSlotTypeCount = int(GLDescriptorSlotType::CountOf); + Int counts[kSlotTypeCount] = { 0, }; + + Int setCount = desc.descriptorSetCount; + for( Int ii = 0; ii < setCount; ++ii ) + { + auto setLayout = (DescriptorSetLayoutImpl*) desc.descriptorSets[ii].layout; + + PipelineLayoutImpl::DescriptorSetInfo setInfo; + setInfo.layout = setLayout; + + for( Int ii = 0; ii < int(GLDescriptorSlotType::CountOf); ++ii ) + { + setInfo.baseArrayIndex[ii] = counts[ii]; + counts[ii] += setLayout->m_counts[ii]; + } + + layoutImpl->m_sets.add(setInfo); + } + + *outLayout = layoutImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) +{ + auto layoutImpl = (DescriptorSetLayoutImpl*) layout; + + RefPtr descriptorSetImpl = new DescriptorSetImpl(); + + descriptorSetImpl->m_layout = layoutImpl; + + // TODO: storage for the arrays of bound objects could be tail allocated + // as part of the descriptor set, with offsets pre-computed in the + // descriptor set layout. + + { + auto slotTypeIndex = int(GLDescriptorSlotType::ConstantBuffer); + auto slotCount = layoutImpl->m_counts[slotTypeIndex]; + descriptorSetImpl->m_constantBuffers.setCount(slotCount); + } + + { + auto slotTypeIndex = int(GLDescriptorSlotType::CombinedTextureSampler); + auto slotCount = layoutImpl->m_counts[slotTypeIndex]; + + descriptorSetImpl->m_textures.setCount(slotCount); + descriptorSetImpl->m_samplers.setCount(slotCount); + } + + *outDescriptorSet = descriptorSetImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) +{ + auto programID = glCreateProgram(); + if(desc.pipelineType == PipelineType::Compute ) + { + auto computeKernel = desc.findKernel(StageType::Compute); + auto computeShaderID = loadShader(GL_COMPUTE_SHADER, (char const*) computeKernel->codeBegin); + glAttachShader(programID, computeShaderID); + glLinkProgram(programID); + glDeleteShader(computeShaderID); + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + auto vertexShaderID = loadShader(GL_VERTEX_SHADER, (char const*) vertexKernel->codeBegin); + auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, (char const*) fragmentKernel->codeBegin); + + glAttachShader(programID, vertexShaderID); + glAttachShader(programID, fragmentShaderID); + + + glLinkProgram(programID); + + glDeleteShader(vertexShaderID); + glDeleteShader(fragmentShaderID); + } + GLint success = GL_FALSE; + glGetProgramiv(programID, GL_LINK_STATUS, &success); + if (!success) + { + int maxSize = 0; + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxSize); + + auto infoBuffer = (char*)::malloc(maxSize); + + int infoSize = 0; + glGetProgramInfoLog(programID, maxSize, &infoSize, infoBuffer); + if (infoSize > 0) + { + fprintf(stderr, "%s", infoBuffer); + OutputDebugStringA(infoBuffer); + } + + ::free(infoBuffer); + + glDeleteProgram(programID); + return SLANG_FAIL; + } + + *outProgram = new ShaderProgramImpl(m_weakRenderer, programID); + return SLANG_OK; +} + +Result GLRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; + + RefPtr pipelineStateImpl = new PipelineStateImpl(); + pipelineStateImpl->m_program = programImpl; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_inputLayout = inputLayoutImpl; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + + RefPtr pipelineStateImpl = new PipelineStateImpl(); + pipelineStateImpl->m_program = programImpl; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + + +} // renderer_test diff --git a/tools/gfx/open-gl/render-gl.h b/tools/gfx/open-gl/render-gl.h new file mode 100644 index 000000000..055031d38 --- /dev/null +++ b/tools/gfx/open-gl/render-gl.h @@ -0,0 +1,10 @@ +// render-d3d11.h +#pragma once + +namespace gfx { + +class Renderer; + +Renderer* createGLRenderer(); + +} // gfx diff --git a/tools/gfx/render-d3d11.cpp b/tools/gfx/render-d3d11.cpp deleted file mode 100644 index 5911d6e9c..000000000 --- a/tools/gfx/render-d3d11.cpp +++ /dev/null @@ -1,2371 +0,0 @@ -// render-d3d11.cpp - -#define _CRT_SECURE_NO_WARNINGS - -#include "render-d3d11.h" - -//WORKING: #include "options.h" -#include "render.h" -#include "d3d-util.h" - -#include "surface.h" - -// In order to use the Slang API, we need to include its header - -//#include - -#include "../../slang-com-ptr.h" -#include "flag-combiner.h" - -// We will be rendering with Direct3D 11, so we need to include -// the Windows and D3D11 headers - -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#include -#include - -// We will use the C standard library just for printing error messages. -#include - -#ifdef _MSC_VER -#include -#if (_MSC_VER < 1900) -#define snprintf sprintf_s -#endif -#endif -// -using namespace Slang; - -namespace gfx { - -class D3D11Renderer : public Renderer -{ -public: - enum - { - kMaxUAVs = 64, - kMaxRTVs = 8, - }; - - // Renderer implementation - virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; - virtual const List& getFeatures() override { return m_features; } - virtual void setClearColor(const float color[4]) override; - virtual void clearFrame() override; - virtual void presentFrame() override; - TextureResource::Desc getSwapChainTextureDesc() override; - - Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; - Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; - Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; - - Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; - Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; - - Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; - - Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; - Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; - Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; - - Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; - Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; - Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; - - virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; - - virtual void* map(BufferResource* buffer, MapFlavor flavor) override; - virtual void unmap(BufferResource* buffer) override; - virtual void setPrimitiveTopology(PrimitiveTopology topology) override; - - virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; - - virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; - virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; - virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; - void setViewports(UInt count, Viewport const* viewports) override; - void setScissorRects(UInt count, ScissorRect const* rects) override; - virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; - virtual void draw(UInt vertexCount, UInt startVertex) override; - virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; - virtual void dispatchCompute(int x, int y, int z) override; - virtual void submitGpuWork() override {} - virtual void waitForGpu() override {} - virtual RendererType getRendererType() const override { return RendererType::DirectX11; } - - ~D3D11Renderer() {} - - protected: - -#if 0 - struct BindingDetail - { - ComPtr m_srv; - ComPtr m_uav; - ComPtr m_samplerState; - }; - - class BindingStateImpl: public BindingState - { - public: - typedef BindingState Parent; - - /// Ctor - BindingStateImpl(const Desc& desc): - Parent(desc) - {} - - List m_bindingDetails; - }; -#endif - - enum class D3D11DescriptorSlotType - { - ConstantBuffer, - ShaderResourceView, - UnorderedAccessView, - Sampler, - - CombinedTextureSampler, - - CountOf, - }; - - class DescriptorSetLayoutImpl : public DescriptorSetLayout - { - public: - struct RangeInfo - { - D3D11DescriptorSlotType type; - UInt arrayIndex; - UInt pairedSamplerArrayIndex; - }; - List m_ranges; - - UInt m_counts[int(D3D11DescriptorSlotType::CountOf)]; - }; - - class PipelineLayoutImpl : public PipelineLayout - { - public: - struct DescriptorSetInfo - { - RefPtr layout; - UInt baseIndices[int(D3D11DescriptorSlotType::CountOf)]; - }; - - List m_descriptorSets; - UINT m_uavCount; - }; - - class DescriptorSetImpl : public DescriptorSet - { - public: - virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; - virtual void setResource(UInt range, UInt index, ResourceView* view) override; - virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; - virtual void setCombinedTextureSampler( - UInt range, - UInt index, - ResourceView* textureView, - SamplerState* sampler) override; - - RefPtr m_layout; - - List> m_cbs; - List> m_srvs; - List> m_uavs; - List> m_samplers; - }; - - class ShaderProgramImpl: public ShaderProgram - { - public: - ComPtr m_vertexShader; - ComPtr m_pixelShader; - ComPtr m_computeShader; - }; - - class BufferResourceImpl: public BufferResource - { - public: - typedef BufferResource Parent; - - BufferResourceImpl(const Desc& desc, Usage initialUsage): - Parent(desc), - m_initialUsage(initialUsage) - { - } - - MapFlavor m_mapFlavor; - Usage m_initialUsage; - ComPtr m_buffer; - ComPtr m_staging; - }; - class TextureResourceImpl : public TextureResource - { - public: - typedef TextureResource Parent; - - TextureResourceImpl(const Desc& desc, Usage initialUsage) : - Parent(desc), - m_initialUsage(initialUsage) - { - } - Usage m_initialUsage; - ComPtr m_resource; - - }; - - class SamplerStateImpl : public SamplerState - { - public: - ComPtr m_sampler; - }; - - - class ResourceViewImpl : public ResourceView - { - public: - enum class Type - { - SRV, - UAV, - DSV, - RTV, - }; - Type m_type; - }; - - class ShaderResourceViewImpl : public ResourceViewImpl - { - public: - ComPtr m_srv; - }; - - class UnorderedAccessViewImpl : public ResourceViewImpl - { - public: - ComPtr m_uav; - }; - - class DepthStencilViewImpl : public ResourceViewImpl - { - public: - ComPtr m_dsv; - }; - - class RenderTargetViewImpl : public ResourceViewImpl - { - public: - ComPtr m_rtv; - }; - - class InputLayoutImpl: public InputLayout - { - public: - ComPtr m_layout; - }; - - class PipelineStateImpl : public PipelineState - { - public: - RefPtr m_program; - RefPtr m_pipelineLayout; - }; - - - class GraphicsPipelineStateImpl : public PipelineStateImpl - { - public: - UINT m_rtvCount; - - RefPtr m_inputLayout; - ComPtr m_depthStencilState; - ComPtr m_rasterizerState; - ComPtr m_blendState; - - UINT m_stencilRef; - float m_blendColor[4]; - UINT m_sampleMask; - }; - - class ComputePipelineStateImpl : public PipelineStateImpl - { - public: - }; - - /// Capture a texture to a file - static HRESULT captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut); - - void _flushGraphicsState(); - void _flushComputeState(); - - ComPtr m_swapChain; - ComPtr m_device; - ComPtr m_immediateContext; - ComPtr m_backBufferTexture; - - RefPtr m_primaryRenderTargetTexture; - RefPtr m_primaryRenderTargetView; - -// List > m_renderTargetViews; -// List > m_renderTargetTextures; - - bool m_renderTargetBindingsDirty = false; - - RefPtr m_currentGraphicsState; - RefPtr m_currentComputeState; - - ComPtr m_rtvBindings[kMaxRTVs]; - ComPtr m_dsvBinding; - ComPtr m_uavBindings[int(PipelineType::CountOf)][kMaxUAVs]; - bool m_targetBindingsDirty[int(PipelineType::CountOf)]; - - Desc m_desc; - - float m_clearColor[4] = { 0, 0, 0, 0 }; - - List m_features; -}; - -Renderer* createD3D11Renderer() -{ - return new D3D11Renderer(); -} - -/* static */HRESULT D3D11Renderer::captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut) -{ - if (!context) return E_INVALIDARG; - if (!texture) return E_INVALIDARG; - - D3D11_TEXTURE2D_DESC textureDesc; - texture->GetDesc(&textureDesc); - - // Don't bother supporting MSAA for right now - if (textureDesc.SampleDesc.Count > 1) - { - fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); - return E_INVALIDARG; - } - - HRESULT hr = S_OK; - ComPtr stagingTexture; - - if (textureDesc.Usage == D3D11_USAGE_STAGING && (textureDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) - { - stagingTexture = texture; - } - else - { - // Modify the descriptor to give us a staging texture - textureDesc.BindFlags = 0; - textureDesc.MiscFlags &= ~D3D11_RESOURCE_MISC_TEXTURECUBE; - textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - textureDesc.Usage = D3D11_USAGE_STAGING; - - hr = device->CreateTexture2D(&textureDesc, 0, stagingTexture.writeRef()); - if (FAILED(hr)) - { - fprintf(stderr, "ERROR: failed to create staging texture\n"); - return hr; - } - - context->CopyResource(stagingTexture, texture); - } - - // Now just read back texels from the staging textures - { - D3D11_MAPPED_SUBRESOURCE mappedResource; - SLANG_RETURN_ON_FAIL(context->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource)); - - Result res = surfaceOut.set(textureDesc.Width, textureDesc.Height, Format::RGBA_Unorm_UInt8, mappedResource.RowPitch, mappedResource.pData, SurfaceAllocator::getMallocAllocator()); - - // Make sure to unmap - context->Unmap(stagingTexture, 0); - return res; - } -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! - -SlangResult D3D11Renderer::initialize(const Desc& desc, void* inWindowHandle) -{ - auto windowHandle = (HWND)inWindowHandle; - m_desc = desc; - - // Rather than statically link against D3D, we load it dynamically. - HMODULE d3dModule = LoadLibraryA("d3d11.dll"); - if (!d3dModule) - { - fprintf(stderr, "error: failed load 'd3d11.dll'\n"); - return SLANG_FAIL; - } - - PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN D3D11CreateDeviceAndSwapChain_ = - (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress(d3dModule, "D3D11CreateDeviceAndSwapChain"); - if (!D3D11CreateDeviceAndSwapChain_) - { - fprintf(stderr, - "error: failed load symbol 'D3D11CreateDeviceAndSwapChain'\n"); - return SLANG_FAIL; - } - - // Our swap chain uses RGBA8 with sRGB, with double buffering. - DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - - // Note(tfoley): Disabling sRGB for DX back buffer for now, so that we - // can get consistent output with OpenGL, where setting up sRGB will - // probably be more involved. - // swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - - swapChainDesc.SampleDesc.Count = 1; - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferCount = 2; - swapChainDesc.OutputWindow = windowHandle; - swapChainDesc.Windowed = TRUE; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - swapChainDesc.Flags = 0; - - // We will ask for the highest feature level that can be supported. - const D3D_FEATURE_LEVEL featureLevels[] = { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1, - }; - D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_9_1; - const int totalNumFeatureLevels = SLANG_COUNT_OF(featureLevels); - - { - // On a machine that does not have an up-to-date version of D3D installed, - // the `D3D11CreateDeviceAndSwapChain` call will fail with `E_INVALIDARG` - // if you ask for feature level 11_1 (DeviceCheckFlag::UseFullFeatureLevel). - // The workaround is to call `D3D11CreateDeviceAndSwapChain` the first time - // with 11_1 and then back off to 11_0 if that fails. - - FlagCombiner combiner; - // TODO: we should probably provide a command-line option - // to override UseDebug of default rather than leave it - // up to each back-end to specify. - -#if _DEBUG - combiner.add(DeviceCheckFlag::UseDebug, ChangeType::OnOff); ///< First try debug then non debug -#else - combiner.add(DeviceCheckFlag::UseDebug, ChangeType::Off); ///< Don't bother with debug -#endif - combiner.add(DeviceCheckFlag::UseHardwareDevice, ChangeType::OnOff); ///< First try hardware, then reference - combiner.add(DeviceCheckFlag::UseFullFeatureLevel, ChangeType::OnOff); ///< First try fully featured, then degrade features - - const int numCombinations = combiner.getNumCombinations(); - Result res = SLANG_FAIL; - for (int i = 0; i < numCombinations; ++i) - { - const auto deviceCheckFlags = combiner.getCombination(i); - - // If we have an adapter set on the desc, look it up. We only need to do so for hardware - ComPtr adapter; - if (desc.adapter.getLength() && (deviceCheckFlags & DeviceCheckFlag::UseHardwareDevice)) - { - List> dxgiAdapters; - D3DUtil::findAdapters(deviceCheckFlags, desc.adapter.getUnownedSlice(), dxgiAdapters); - if (dxgiAdapters.getCount() == 0) - { - continue; - } - adapter = dxgiAdapters[0]; - } - - // The adapter can be nullptr - that just means 'default', but when so we need to select the driver type - D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_UNKNOWN; - if (adapter == nullptr) - { - // If we don't have an adapter, select directly - driverType = (deviceCheckFlags & DeviceCheckFlag::UseHardwareDevice) ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_REFERENCE; - } - - const int startFeatureIndex = (deviceCheckFlags & DeviceCheckFlag::UseFullFeatureLevel) ? 0 : 1; - const UINT deviceFlags = (deviceCheckFlags & DeviceCheckFlag::UseDebug) ? D3D11_CREATE_DEVICE_DEBUG : 0; - - res = D3D11CreateDeviceAndSwapChain_( - adapter, - driverType, - nullptr, // software - deviceFlags, - &featureLevels[startFeatureIndex], - totalNumFeatureLevels - startFeatureIndex, - D3D11_SDK_VERSION, - &swapChainDesc, - m_swapChain.writeRef(), - m_device.writeRef(), - &featureLevel, - m_immediateContext.writeRef()); - - // Check if successfully constructed - if so we are done. - if (SLANG_SUCCEEDED(res)) - { - break; - } - } - // If res is failure, means all styles have have failed, and so initialization fails. - if (SLANG_FAILED(res)) - { - return res; - } - // Check we have a swap chain, context and device - SLANG_ASSERT(m_immediateContext && m_swapChain && m_device); - } - - // TODO: Add support for debugging to help detect leaks: - // - // ComPtr gDebug; - // m_device->QueryInterface(IID_PPV_ARGS(gDebug.writeRef())); - // - - // After we've created the swap chain, we can request a pointer to the - // back buffer as a D3D11 texture, and create a render-target view from it. - - static const IID kIID_ID3D11Texture2D = { - 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, - 0x95, 0x35, 0xd3, 0x4f, 0x9c }; - - SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, kIID_ID3D11Texture2D, (void**)m_backBufferTexture.writeRef())); - -// for (int i = 0; i < 8; i++) - { - ComPtr texture; - D3D11_TEXTURE2D_DESC textureDesc; - m_backBufferTexture->GetDesc(&textureDesc); - SLANG_RETURN_ON_FAIL(m_device->CreateTexture2D(&textureDesc, nullptr, texture.writeRef())); - - ComPtr rtv; - D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; - rtvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - rtvDesc.Texture2D.MipSlice = 0; - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - SLANG_RETURN_ON_FAIL(m_device->CreateRenderTargetView(texture, &rtvDesc, rtv.writeRef())); - - TextureResource::Desc resourceDesc; - resourceDesc.init2D(Resource::Type::Texture2D, Format::RGBA_Unorm_UInt8, textureDesc.Width, textureDesc.Height, 1); - - RefPtr primaryRenderTargetTexture; - SLANG_RETURN_ON_FAIL(createTextureResource(Resource::Usage::RenderTarget, resourceDesc, nullptr, primaryRenderTargetTexture.writeRef())); - - ResourceView::Desc viewDesc; - viewDesc.format = resourceDesc.format; - viewDesc.type = ResourceView::Type::RenderTarget; - RefPtr primaryRenderTargetView; - SLANG_RETURN_ON_FAIL(createTextureView(primaryRenderTargetTexture, viewDesc, primaryRenderTargetView.writeRef())); - - m_primaryRenderTargetTexture = (TextureResourceImpl*) primaryRenderTargetTexture.Ptr(); - m_primaryRenderTargetView = (RenderTargetViewImpl*) primaryRenderTargetView.Ptr(); - } - -// m_immediateContext->OMSetRenderTargets(1, m_primaryRenderTargetView->m_rtv.readRef(), nullptr); - m_rtvBindings[0] = m_primaryRenderTargetView->m_rtv; - m_targetBindingsDirty[int(PipelineType::Graphics)] = true; - - // Similarly, we are going to set up a viewport once, and then never - // switch, since this is a simple test app. - D3D11_VIEWPORT viewport; - viewport.TopLeftX = 0; - viewport.TopLeftY = 0; - viewport.Width = (float)desc.width; - viewport.Height = (float)desc.height; - viewport.MaxDepth = 1; // TODO(tfoley): use reversed depth - viewport.MinDepth = 0; - m_immediateContext->RSSetViewports(1, &viewport); - - return SLANG_OK; -} - -void D3D11Renderer::setClearColor(const float color[4]) -{ - memcpy(m_clearColor, color, sizeof(m_clearColor)); -} - -void D3D11Renderer::clearFrame() -{ - m_immediateContext->ClearRenderTargetView(m_primaryRenderTargetView->m_rtv, m_clearColor); - - if(m_dsvBinding) - { - m_immediateContext->ClearDepthStencilView(m_dsvBinding, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); - } -} - -void D3D11Renderer::presentFrame() -{ - m_immediateContext->CopyResource(m_backBufferTexture, m_primaryRenderTargetTexture->m_resource); - m_swapChain->Present(0, 0); -} - -TextureResource::Desc D3D11Renderer::getSwapChainTextureDesc() -{ - D3D11_TEXTURE2D_DESC dxDesc; - ((ID3D11Texture2D*)m_primaryRenderTargetTexture->m_resource.get())->GetDesc(&dxDesc); - - TextureResource::Desc desc; - desc.init2D(Resource::Type::Texture2D, Format::Unknown, dxDesc.Width, dxDesc.Height, 1); - - return desc; -} - -SlangResult D3D11Renderer::captureScreenSurface(Surface& surfaceOut) -{ - return captureTextureToSurface(m_device, m_immediateContext, (ID3D11Texture2D*) m_primaryRenderTargetTexture->m_resource.get(), surfaceOut); -} - -static D3D11_BIND_FLAG _calcResourceFlag(Resource::BindFlag::Enum bindFlag) -{ - typedef Resource::BindFlag BindFlag; - switch (bindFlag) - { - case BindFlag::VertexBuffer: return D3D11_BIND_VERTEX_BUFFER; - case BindFlag::IndexBuffer: return D3D11_BIND_INDEX_BUFFER; - case BindFlag::ConstantBuffer: return D3D11_BIND_CONSTANT_BUFFER; - case BindFlag::StreamOutput: return D3D11_BIND_STREAM_OUTPUT; - case BindFlag::RenderTarget: return D3D11_BIND_RENDER_TARGET; - case BindFlag::DepthStencil: return D3D11_BIND_DEPTH_STENCIL; - case BindFlag::UnorderedAccess: return D3D11_BIND_UNORDERED_ACCESS; - case BindFlag::PixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; - case BindFlag::NonPixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; - default: return D3D11_BIND_FLAG(0); - } -} - -static int _calcResourceBindFlags(int bindFlags) -{ - int dstFlags = 0; - while (bindFlags) - { - int lsb = bindFlags & -bindFlags; - - dstFlags |= _calcResourceFlag(Resource::BindFlag::Enum(lsb)); - bindFlags &= ~lsb; - } - return dstFlags; -} - -static int _calcResourceAccessFlags(int accessFlags) -{ - switch (accessFlags) - { - case 0: return 0; - case Resource::AccessFlag::Read: return D3D11_CPU_ACCESS_READ; - case Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_WRITE; - case Resource::AccessFlag::Read | - Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; - default: assert(!"Invalid flags"); return 0; - } -} - -Result D3D11Renderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) -{ - TextureResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); - - if(initData) - { - assert(initData->numSubResources == srcDesc.numMipLevels * effectiveArraySize * srcDesc.size.depth); - } - - const DXGI_FORMAT format = D3DUtil::getMapFormat(srcDesc.format); - if (format == DXGI_FORMAT_UNKNOWN) - { - return SLANG_FAIL; - } - - const int bindFlags = _calcResourceBindFlags(srcDesc.bindFlags); - - // Set up the initialize data - List subRes; - D3D11_SUBRESOURCE_DATA* subResourcesPtr = nullptr; - if(initData) - { - subRes.setCount(srcDesc.numMipLevels * effectiveArraySize); - { - int subResourceIndex = 0; - for (int i = 0; i < effectiveArraySize; i++) - { - for (int j = 0; j < srcDesc.numMipLevels; j++) - { - const int mipHeight = TextureResource::calcMipSize(srcDesc.size.height, j); - - D3D11_SUBRESOURCE_DATA& data = subRes[subResourceIndex]; - - data.pSysMem = initData->subResources[subResourceIndex]; - - data.SysMemPitch = UINT(initData->mipRowStrides[j]); - data.SysMemSlicePitch = UINT(initData->mipRowStrides[j] * mipHeight); - - subResourceIndex++; - } - } - } - subResourcesPtr = subRes.getBuffer(); - } - - const int accessFlags = _calcResourceAccessFlags(srcDesc.cpuAccessFlags); - - RefPtr texture(new TextureResourceImpl(srcDesc, initialUsage)); - - switch (srcDesc.type) - { - case Resource::Type::Texture1D: - { - D3D11_TEXTURE1D_DESC desc = { 0 }; - desc.BindFlags = bindFlags; - desc.CPUAccessFlags = accessFlags; - desc.Format = format; - desc.MiscFlags = 0; - desc.MipLevels = srcDesc.numMipLevels; - desc.ArraySize = effectiveArraySize; - desc.Width = srcDesc.size.width; - desc.Usage = D3D11_USAGE_DEFAULT; - - ComPtr texture1D; - SLANG_RETURN_ON_FAIL(m_device->CreateTexture1D(&desc, subResourcesPtr, texture1D.writeRef())); - - texture->m_resource = texture1D; - break; - } - case Resource::Type::TextureCube: - case Resource::Type::Texture2D: - { - D3D11_TEXTURE2D_DESC desc = { 0 }; - desc.BindFlags = bindFlags; - desc.CPUAccessFlags = accessFlags; - desc.Format = format; - desc.MiscFlags = 0; - desc.MipLevels = srcDesc.numMipLevels; - desc.ArraySize = effectiveArraySize; - - desc.Width = srcDesc.size.width; - desc.Height = srcDesc.size.height; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.SampleDesc.Count = srcDesc.sampleDesc.numSamples; - desc.SampleDesc.Quality = srcDesc.sampleDesc.quality; - - if (srcDesc.type == Resource::Type::TextureCube) - { - desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE; - } - - ComPtr texture2D; - SLANG_RETURN_ON_FAIL(m_device->CreateTexture2D(&desc, subResourcesPtr, texture2D.writeRef())); - - texture->m_resource = texture2D; - break; - } - case Resource::Type::Texture3D: - { - D3D11_TEXTURE3D_DESC desc = { 0 }; - desc.BindFlags = bindFlags; - desc.CPUAccessFlags = accessFlags; - desc.Format = format; - desc.MiscFlags = 0; - desc.MipLevels = srcDesc.numMipLevels; - desc.Width = srcDesc.size.width; - desc.Height = srcDesc.size.height; - desc.Depth = srcDesc.size.depth; - desc.Usage = D3D11_USAGE_DEFAULT; - - ComPtr texture3D; - SLANG_RETURN_ON_FAIL(m_device->CreateTexture3D(&desc, subResourcesPtr, texture3D.writeRef())); - - texture->m_resource = texture3D; - break; - } - default: - return SLANG_FAIL; - } - - *outResource = texture.detach(); - return SLANG_OK; -} - -Result D3D11Renderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) -{ - BufferResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - auto d3dBindFlags = _calcResourceBindFlags(srcDesc.bindFlags); - - size_t alignedSizeInBytes = srcDesc.sizeInBytes; - - if(d3dBindFlags & D3D11_BIND_CONSTANT_BUFFER) - { - // Make aligned to 256 bytes... not sure why, but if you remove this the tests do fail. - alignedSizeInBytes = D3DUtil::calcAligned(alignedSizeInBytes, 256); - } - - // Hack to make the initialization never read from out of bounds memory, by copying into a buffer - List initDataBuffer; - if (initData && alignedSizeInBytes > srcDesc.sizeInBytes) - { - initDataBuffer.setCount(alignedSizeInBytes); - ::memcpy(initDataBuffer.getBuffer(), initData, srcDesc.sizeInBytes); - initData = initDataBuffer.getBuffer(); - } - - D3D11_BUFFER_DESC bufferDesc = { 0 }; - bufferDesc.ByteWidth = UINT(alignedSizeInBytes); - bufferDesc.BindFlags = d3dBindFlags; - // For read we'll need to do some staging - bufferDesc.CPUAccessFlags = _calcResourceAccessFlags(descIn.cpuAccessFlags & Resource::AccessFlag::Write); - bufferDesc.Usage = D3D11_USAGE_DEFAULT; - - // If written by CPU, make it dynamic - if (descIn.cpuAccessFlags & Resource::AccessFlag::Write) - { - bufferDesc.Usage = D3D11_USAGE_DYNAMIC; - } - - switch (initialUsage) - { - case Resource::Usage::ConstantBuffer: - { - // We'll just assume ConstantBuffers are dynamic for now - bufferDesc.Usage = D3D11_USAGE_DYNAMIC; - break; - } - default: break; - } - - if (bufferDesc.BindFlags & (D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE)) - { - //desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; - if (srcDesc.elementSize != 0) - { - bufferDesc.StructureByteStride = srcDesc.elementSize; - bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; - } - else - { - bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; - } - } - - D3D11_SUBRESOURCE_DATA subResourceData = { 0 }; - subResourceData.pSysMem = initData; - - RefPtr buffer(new BufferResourceImpl(srcDesc, initialUsage)); - - SLANG_RETURN_ON_FAIL(m_device->CreateBuffer(&bufferDesc, initData ? &subResourceData : nullptr, buffer->m_buffer.writeRef())); - - if (srcDesc.cpuAccessFlags & Resource::AccessFlag::Read) - { - D3D11_BUFFER_DESC bufDesc = {}; - bufDesc.BindFlags = 0; - bufDesc.ByteWidth = (UINT)alignedSizeInBytes; - bufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - bufDesc.Usage = D3D11_USAGE_STAGING; - - SLANG_RETURN_ON_FAIL(m_device->CreateBuffer(&bufDesc, nullptr, buffer->m_staging.writeRef())); - } - - *outResource = buffer.detach(); - return SLANG_OK; -} - -D3D11_FILTER_TYPE translateFilterMode(TextureFilteringMode mode) -{ - switch (mode) - { - default: - return D3D11_FILTER_TYPE(0); - -#define CASE(SRC, DST) \ - case TextureFilteringMode::SRC: return D3D11_FILTER_TYPE_##DST - - CASE(Point, POINT); - CASE(Linear, LINEAR); - -#undef CASE - } -} - -D3D11_FILTER_REDUCTION_TYPE translateFilterReduction(TextureReductionOp op) -{ - switch (op) - { - default: - return D3D11_FILTER_REDUCTION_TYPE(0); - -#define CASE(SRC, DST) \ - case TextureReductionOp::SRC: return D3D11_FILTER_REDUCTION_TYPE_##DST - - CASE(Average, STANDARD); - CASE(Comparison, COMPARISON); - CASE(Minimum, MINIMUM); - CASE(Maximum, MAXIMUM); - -#undef CASE - } -} - -D3D11_TEXTURE_ADDRESS_MODE translateAddressingMode(TextureAddressingMode mode) -{ - switch (mode) - { - default: - return D3D11_TEXTURE_ADDRESS_MODE(0); - -#define CASE(SRC, DST) \ - case TextureAddressingMode::SRC: return D3D11_TEXTURE_ADDRESS_##DST - - CASE(Wrap, WRAP); - CASE(ClampToEdge, CLAMP); - CASE(ClampToBorder, BORDER); - CASE(MirrorRepeat, MIRROR); - CASE(MirrorOnce, MIRROR_ONCE); - -#undef CASE - } -} - -static D3D11_COMPARISON_FUNC translateComparisonFunc(ComparisonFunc func) -{ - switch (func) - { - default: - // TODO: need to report failures - return D3D11_COMPARISON_ALWAYS; - -#define CASE(FROM, TO) \ - case ComparisonFunc::FROM: return D3D11_COMPARISON_##TO - - CASE(Never, NEVER); - CASE(Less, LESS); - CASE(Equal, EQUAL); - CASE(LessEqual, LESS_EQUAL); - CASE(Greater, GREATER); - CASE(NotEqual, NOT_EQUAL); - CASE(GreaterEqual, GREATER_EQUAL); - CASE(Always, ALWAYS); -#undef CASE - } -} - -Result D3D11Renderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) -{ - D3D11_FILTER_REDUCTION_TYPE dxReduction = translateFilterReduction(desc.reductionOp); - D3D11_FILTER dxFilter; - if (desc.maxAnisotropy > 1) - { - dxFilter = D3D11_ENCODE_ANISOTROPIC_FILTER(dxReduction); - } - else - { - D3D11_FILTER_TYPE dxMin = translateFilterMode(desc.minFilter); - D3D11_FILTER_TYPE dxMag = translateFilterMode(desc.magFilter); - D3D11_FILTER_TYPE dxMip = translateFilterMode(desc.mipFilter); - - dxFilter = D3D11_ENCODE_BASIC_FILTER(dxMin, dxMag, dxMip, dxReduction); - } - - D3D11_SAMPLER_DESC dxDesc = {}; - dxDesc.Filter = dxFilter; - dxDesc.AddressU = translateAddressingMode(desc.addressU); - dxDesc.AddressV = translateAddressingMode(desc.addressV); - dxDesc.AddressW = translateAddressingMode(desc.addressW); - dxDesc.MipLODBias = desc.mipLODBias; - dxDesc.MaxAnisotropy = desc.maxAnisotropy; - dxDesc.ComparisonFunc = translateComparisonFunc(desc.comparisonFunc); - for (int ii = 0; ii < 4; ++ii) - dxDesc.BorderColor[ii] = desc.borderColor[ii]; - dxDesc.MinLOD = desc.minLOD; - dxDesc.MaxLOD = desc.maxLOD; - - ComPtr sampler; - SLANG_RETURN_ON_FAIL(m_device->CreateSamplerState( - &dxDesc, - sampler.writeRef())); - - RefPtr samplerImpl = new SamplerStateImpl(); - samplerImpl->m_sampler = sampler; - *outSampler = samplerImpl.detach(); - return SLANG_OK; -} - -Result D3D11Renderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) -{ - auto resourceImpl = (TextureResourceImpl*) texture; - - switch (desc.type) - { - default: - return SLANG_FAIL; - - case ResourceView::Type::RenderTarget: - { - ComPtr rtv; - SLANG_RETURN_ON_FAIL(m_device->CreateRenderTargetView(resourceImpl->m_resource, nullptr, rtv.writeRef())); - - RefPtr viewImpl = new RenderTargetViewImpl(); - viewImpl->m_type = ResourceViewImpl::Type::RTV; - viewImpl->m_rtv = rtv; - *outView = viewImpl.detach(); - return SLANG_OK; - } - break; - - case ResourceView::Type::DepthStencil: - { - ComPtr dsv; - SLANG_RETURN_ON_FAIL(m_device->CreateDepthStencilView(resourceImpl->m_resource, nullptr, dsv.writeRef())); - - RefPtr viewImpl = new DepthStencilViewImpl(); - viewImpl->m_type = ResourceViewImpl::Type::DSV; - viewImpl->m_dsv = dsv; - *outView = viewImpl.detach(); - return SLANG_OK; - } - break; - - case ResourceView::Type::UnorderedAccess: - { - ComPtr uav; - SLANG_RETURN_ON_FAIL(m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, uav.writeRef())); - - RefPtr viewImpl = new UnorderedAccessViewImpl(); - viewImpl->m_type = ResourceViewImpl::Type::UAV; - viewImpl->m_uav = uav; - *outView = viewImpl.detach(); - return SLANG_OK; - } - break; - - case ResourceView::Type::ShaderResource: - { - ComPtr srv; - SLANG_RETURN_ON_FAIL(m_device->CreateShaderResourceView(resourceImpl->m_resource, nullptr, srv.writeRef())); - - RefPtr viewImpl = new ShaderResourceViewImpl(); - viewImpl->m_type = ResourceViewImpl::Type::SRV; - viewImpl->m_srv = srv; - *outView = viewImpl.detach(); - return SLANG_OK; - } - break; - } -} - -Result D3D11Renderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) -{ - auto resourceImpl = (BufferResourceImpl*) buffer; - auto resourceDesc = resourceImpl->getDesc(); - - switch (desc.type) - { - default: - return SLANG_FAIL; - - case ResourceView::Type::UnorderedAccess: - { - D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; - uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; - uavDesc.Format = D3DUtil::getMapFormat(desc.format); - uavDesc.Buffer.FirstElement = 0; - - if(resourceDesc.elementSize) - { - uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / resourceDesc.elementSize); - } - else if(desc.format == Format::Unknown) - { - uavDesc.Buffer.Flags |= D3D11_BUFFER_UAV_FLAG_RAW; - uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; - uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / 4); - } - else - { - uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / RendererUtil::getFormatSize(desc.format)); - } - - ComPtr uav; - SLANG_RETURN_ON_FAIL(m_device->CreateUnorderedAccessView(resourceImpl->m_buffer, &uavDesc, uav.writeRef())); - - RefPtr viewImpl = new UnorderedAccessViewImpl(); - viewImpl->m_type = ResourceViewImpl::Type::UAV; - viewImpl->m_uav = uav; - *outView = viewImpl.detach(); - return SLANG_OK; - } - break; - - case ResourceView::Type::ShaderResource: - { - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; - srvDesc.Format = D3DUtil::getMapFormat(desc.format); - srvDesc.Buffer.FirstElement = 0; - - if(resourceDesc.elementSize) - { - srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / resourceDesc.elementSize); - } - else if(desc.format == Format::Unknown) - { - // We need to switch to a different member of the `union`, - // so that we can set the `BufferEx.Flags` member. - // - srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; - - // Because we've switched, we need to re-set the `FirstElement` - // field to be valid, since we can't count on all compilers - // to respect that `Buffer.FirstElement` and `BufferEx.FirstElement` - // alias in memory. - // - srvDesc.BufferEx.FirstElement = 0; - - srvDesc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW; - srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; - srvDesc.BufferEx.NumElements = UINT(resourceDesc.sizeInBytes / 4); - } - else - { - srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / RendererUtil::getFormatSize(desc.format)); - } - - ComPtr srv; - SLANG_RETURN_ON_FAIL(m_device->CreateShaderResourceView(resourceImpl->m_buffer, &srvDesc, srv.writeRef())); - - RefPtr viewImpl = new ShaderResourceViewImpl(); - viewImpl->m_type = ResourceViewImpl::Type::SRV; - viewImpl->m_srv = srv; - *outView = viewImpl.detach(); - return SLANG_OK; - } - break; - } -} - -Result D3D11Renderer::createInputLayout(const InputElementDesc* inputElementsIn, UInt inputElementCount, InputLayout** outLayout) -{ - D3D11_INPUT_ELEMENT_DESC inputElements[16] = {}; - - char hlslBuffer[1024]; - char* hlslCursor = &hlslBuffer[0]; - - hlslCursor += sprintf(hlslCursor, "float4 main(\n"); - - for (UInt ii = 0; ii < inputElementCount; ++ii) - { - inputElements[ii].SemanticName = inputElementsIn[ii].semanticName; - inputElements[ii].SemanticIndex = (UINT)inputElementsIn[ii].semanticIndex; - inputElements[ii].Format = D3DUtil::getMapFormat(inputElementsIn[ii].format); - inputElements[ii].InputSlot = 0; - inputElements[ii].AlignedByteOffset = (UINT)inputElementsIn[ii].offset; - inputElements[ii].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - inputElements[ii].InstanceDataStepRate = 0; - - if (ii != 0) - { - hlslCursor += sprintf(hlslCursor, ",\n"); - } - - char const* typeName = "Unknown"; - switch (inputElementsIn[ii].format) - { - case Format::RGBA_Float32: - case Format::RGBA_Unorm_UInt8: - typeName = "float4"; - break; - case Format::RGB_Float32: - typeName = "float3"; - break; - case Format::RG_Float32: - typeName = "float2"; - break; - case Format::R_Float32: - typeName = "float"; - break; - default: - return SLANG_FAIL; - } - - hlslCursor += sprintf(hlslCursor, "%s a%d : %s%d", - typeName, - (int)ii, - inputElementsIn[ii].semanticName, - (int)inputElementsIn[ii].semanticIndex); - } - - hlslCursor += sprintf(hlslCursor, "\n) : SV_Position { return 0; }"); - - ComPtr vertexShaderBlob; - SLANG_RETURN_ON_FAIL(D3DUtil::compileHLSLShader("inputLayout", hlslBuffer, "main", "vs_5_0", vertexShaderBlob)); - - ComPtr inputLayout; - SLANG_RETURN_ON_FAIL(m_device->CreateInputLayout(&inputElements[0], (UINT)inputElementCount, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), - inputLayout.writeRef())); - - RefPtr impl = new InputLayoutImpl; - impl->m_layout.swap(inputLayout); - - *outLayout = impl.detach(); - return SLANG_OK; -} - -void* D3D11Renderer::map(BufferResource* bufferIn, MapFlavor flavor) -{ - BufferResourceImpl* bufferResource = static_cast(bufferIn); - - D3D11_MAP mapType; - ID3D11Buffer* buffer = bufferResource->m_buffer; - - switch (flavor) - { - case MapFlavor::WriteDiscard: - mapType = D3D11_MAP_WRITE_DISCARD; - break; - case MapFlavor::HostWrite: - mapType = D3D11_MAP_WRITE; - break; - case MapFlavor::HostRead: - mapType = D3D11_MAP_READ; - - buffer = bufferResource->m_staging; - if (!buffer) - { - return nullptr; - } - - // Okay copy the data over - m_immediateContext->CopyResource(buffer, bufferResource->m_buffer); - - break; - default: - return nullptr; - } - - // We update our constant buffer per-frame, just for the purposes - // of the example, but we don't actually load different data - // per-frame (we always use an identity projection). - D3D11_MAPPED_SUBRESOURCE mappedSub; - SLANG_RETURN_NULL_ON_FAIL(m_immediateContext->Map(buffer, 0, mapType, 0, &mappedSub)); - - bufferResource->m_mapFlavor = flavor; - - return mappedSub.pData; -} - -void D3D11Renderer::unmap(BufferResource* bufferIn) -{ - BufferResourceImpl* bufferResource = static_cast(bufferIn); - ID3D11Buffer* buffer = (bufferResource->m_mapFlavor == MapFlavor::HostRead) ? bufferResource->m_staging : bufferResource->m_buffer; - m_immediateContext->Unmap(buffer, 0); -} - -#if 0 -void D3D11Renderer::setInputLayout(InputLayout* inputLayoutIn) -{ - auto inputLayout = static_cast(inputLayoutIn); - m_immediateContext->IASetInputLayout(inputLayout->m_layout); -} -#endif - -void D3D11Renderer::setPrimitiveTopology(PrimitiveTopology topology) -{ - m_immediateContext->IASetPrimitiveTopology(D3DUtil::getPrimitiveTopology(topology)); -} - -void D3D11Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffersIn, const UInt* stridesIn, const UInt* offsetsIn) -{ - static const int kMaxVertexBuffers = 16; - assert(slotCount <= kMaxVertexBuffers); - - UINT vertexStrides[kMaxVertexBuffers]; - UINT vertexOffsets[kMaxVertexBuffers]; - ID3D11Buffer* dxBuffers[kMaxVertexBuffers]; - - auto buffers = (BufferResourceImpl*const*)buffersIn; - - for (UInt ii = 0; ii < slotCount; ++ii) - { - vertexStrides[ii] = (UINT)stridesIn[ii]; - vertexOffsets[ii] = (UINT)offsetsIn[ii]; - dxBuffers[ii] = buffers[ii]->m_buffer; - } - - m_immediateContext->IASetVertexBuffers((UINT)startSlot, (UINT)slotCount, dxBuffers, &vertexStrides[0], &vertexOffsets[0]); -} - -void D3D11Renderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) -{ - DXGI_FORMAT dxFormat = D3DUtil::getMapFormat(indexFormat); - m_immediateContext->IASetIndexBuffer(((BufferResourceImpl*)buffer)->m_buffer, dxFormat, UINT(offset)); -} - -void D3D11Renderer::setDepthStencilTarget(ResourceView* depthStencilView) -{ - m_dsvBinding = ((DepthStencilViewImpl*) depthStencilView)->m_dsv; - m_targetBindingsDirty[int(PipelineType::Graphics)] = true; -} - -void D3D11Renderer::setViewports(UInt count, Viewport const* viewports) -{ - static const int kMaxViewports = D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX + 1; - assert(count <= kMaxViewports); - - D3D11_VIEWPORT dxViewports[kMaxViewports]; - for(UInt ii = 0; ii < count; ++ii) - { - auto& inViewport = viewports[ii]; - auto& dxViewport = dxViewports[ii]; - - dxViewport.TopLeftX = inViewport.originX; - dxViewport.TopLeftY = inViewport.originY; - dxViewport.Width = inViewport.extentX; - dxViewport.Height = inViewport.extentY; - dxViewport.MinDepth = inViewport.minZ; - dxViewport.MaxDepth = inViewport.maxZ; - } - - m_immediateContext->RSSetViewports(UINT(count), dxViewports); -} - -void D3D11Renderer::setScissorRects(UInt count, ScissorRect const* rects) -{ - static const int kMaxScissorRects = D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX + 1; - assert(count <= kMaxScissorRects); - - D3D11_RECT dxRects[kMaxScissorRects]; - for(UInt ii = 0; ii < count; ++ii) - { - auto& inRect = rects[ii]; - auto& dxRect = dxRects[ii]; - - dxRect.left = LONG(inRect.minX); - dxRect.top = LONG(inRect.minY); - dxRect.right = LONG(inRect.maxX); - dxRect.bottom = LONG(inRect.maxY); - } - - m_immediateContext->RSSetScissorRects(UINT(count), dxRects); -} - - -void D3D11Renderer::setPipelineState(PipelineType pipelineType, PipelineState* state) -{ - switch(pipelineType) - { - default: - break; - - case PipelineType::Graphics: - { - auto stateImpl = (GraphicsPipelineStateImpl*) state; - auto programImpl = stateImpl->m_program; - - // TODO: We could conceivably do some lightweight state - // differencing here (e.g., check if `programImpl` is the - // same as the program that is currently bound). - // - // It isn't clear how much that would pay off given that - // the D3D11 runtime seems to do its own state diffing. - - // IA - - m_immediateContext->IASetInputLayout(stateImpl->m_inputLayout->m_layout); - - // VS - - m_immediateContext->VSSetShader(programImpl->m_vertexShader, nullptr, 0); - - // HS - - // DS - - // GS - - // RS - - m_immediateContext->RSSetState(stateImpl->m_rasterizerState); - - // PS - - m_immediateContext->PSSetShader(programImpl->m_pixelShader, nullptr, 0); - - // OM - - m_immediateContext->OMSetBlendState(stateImpl->m_blendState, stateImpl->m_blendColor, stateImpl->m_sampleMask); - m_immediateContext->OMSetDepthStencilState(stateImpl->m_depthStencilState, stateImpl->m_stencilRef); - - m_currentGraphicsState = stateImpl; - } - break; - - case PipelineType::Compute: - { - auto stateImpl = (ComputePipelineStateImpl*) state; - auto programImpl = stateImpl->m_program; - - // CS - - m_immediateContext->CSSetShader(programImpl->m_computeShader, nullptr, 0); - - m_currentComputeState = stateImpl; - } - break; - } - - /// ... -} - -void D3D11Renderer::draw(UInt vertexCount, UInt startVertex) -{ - _flushGraphicsState(); - m_immediateContext->Draw((UINT)vertexCount, (UINT)startVertex); -} - -void D3D11Renderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) -{ - _flushGraphicsState(); - m_immediateContext->DrawIndexed((UINT)indexCount, (UINT)startIndex, (INT)baseVertex); -} - -Result D3D11Renderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) -{ - if (desc.pipelineType == PipelineType::Compute) - { - auto computeKernel = desc.findKernel(StageType::Compute); - - ComPtr computeShader; - SLANG_RETURN_ON_FAIL(m_device->CreateComputeShader(computeKernel->codeBegin, computeKernel->getCodeSize(), nullptr, computeShader.writeRef())); - - RefPtr shaderProgram = new ShaderProgramImpl(); - shaderProgram->m_computeShader.swap(computeShader); - - *outProgram = shaderProgram.detach(); - return SLANG_OK; - } - else - { - auto vertexKernel = desc.findKernel(StageType::Vertex); - auto fragmentKernel = desc.findKernel(StageType::Fragment); - - ComPtr vertexShader; - ComPtr pixelShader; - - SLANG_RETURN_ON_FAIL(m_device->CreateVertexShader(vertexKernel->codeBegin, vertexKernel->getCodeSize(), nullptr, vertexShader.writeRef())); - SLANG_RETURN_ON_FAIL(m_device->CreatePixelShader(fragmentKernel->codeBegin, fragmentKernel->getCodeSize(), nullptr, pixelShader.writeRef())); - - RefPtr shaderProgram = new ShaderProgramImpl(); - shaderProgram->m_vertexShader.swap(vertexShader); - shaderProgram->m_pixelShader.swap(pixelShader); - - *outProgram = shaderProgram.detach(); - return SLANG_OK; - } -} - -static D3D11_STENCIL_OP translateStencilOp(StencilOp op) -{ - switch(op) - { - default: - // TODO: need to report failures - return D3D11_STENCIL_OP_KEEP; - -#define CASE(FROM, TO) \ - case StencilOp::FROM: return D3D11_STENCIL_OP_##TO - - CASE(Keep, KEEP); - CASE(Zero, ZERO); - CASE(Replace, REPLACE); - CASE(IncrementSaturate, INCR_SAT); - CASE(DecrementSaturate, DECR_SAT); - CASE(Invert, INVERT); - CASE(IncrementWrap, INCR); - CASE(DecrementWrap, DECR); -#undef CASE - - } -} - -static D3D11_FILL_MODE translateFillMode(FillMode mode) -{ - switch(mode) - { - default: - // TODO: need to report failures - return D3D11_FILL_SOLID; - - case FillMode::Solid: return D3D11_FILL_SOLID; - case FillMode::Wireframe: return D3D11_FILL_WIREFRAME; - } -} - -static D3D11_CULL_MODE translateCullMode(CullMode mode) -{ - switch(mode) - { - default: - // TODO: need to report failures - return D3D11_CULL_NONE; - - case CullMode::None: return D3D11_CULL_NONE; - case CullMode::Back: return D3D11_CULL_BACK; - case CullMode::Front: return D3D11_CULL_FRONT; - } -} - -bool isBlendDisabled(AspectBlendDesc const& desc) -{ - return desc.op == BlendOp::Add - && desc.srcFactor == BlendFactor::One - && desc.dstFactor == BlendFactor::Zero; -} - - -bool isBlendDisabled(TargetBlendDesc const& desc) -{ - return isBlendDisabled(desc.color) - && isBlendDisabled(desc.alpha); -} - -D3D11_BLEND_OP translateBlendOp(BlendOp op) -{ - switch(op) - { - default: - assert(!"unimplemented"); - return (D3D11_BLEND_OP) -1; - -#define CASE(FROM, TO) case BlendOp::FROM: return D3D11_BLEND_OP_##TO - CASE(Add, ADD); - CASE(Subtract, SUBTRACT); - CASE(ReverseSubtract, REV_SUBTRACT); - CASE(Min, MIN); - CASE(Max, MAX); -#undef CASE - } -} - -D3D11_BLEND translateBlendFactor(BlendFactor factor) -{ - switch(factor) - { - default: - assert(!"unimplemented"); - return (D3D11_BLEND) -1; - -#define CASE(FROM, TO) case BlendFactor::FROM: return D3D11_BLEND_##TO - CASE(Zero, ZERO); - CASE(One, ONE); - CASE(SrcColor, SRC_COLOR); - CASE(InvSrcColor, INV_SRC_COLOR); - CASE(SrcAlpha, SRC_ALPHA); - CASE(InvSrcAlpha, INV_SRC_ALPHA); - CASE(DestAlpha, DEST_ALPHA); - CASE(InvDestAlpha, INV_DEST_ALPHA); - CASE(DestColor, DEST_COLOR); - CASE(InvDestColor, INV_DEST_ALPHA); - CASE(SrcAlphaSaturate, SRC_ALPHA_SAT); - CASE(BlendColor, BLEND_FACTOR); - CASE(InvBlendColor, INV_BLEND_FACTOR); - CASE(SecondarySrcColor, SRC1_COLOR); - CASE(InvSecondarySrcColor, INV_SRC1_COLOR); - CASE(SecondarySrcAlpha, SRC1_ALPHA); - CASE(InvSecondarySrcAlpha, INV_SRC1_ALPHA); -#undef CASE - } -} - -D3D11_COLOR_WRITE_ENABLE translateRenderTargetWriteMask(RenderTargetWriteMaskT mask) -{ - UINT result = 0; -#define CASE(FROM, TO) if(mask & RenderTargetWriteMask::Enable##FROM) result |= D3D11_COLOR_WRITE_ENABLE_##TO - - CASE(Red, RED); - CASE(Green, GREEN); - CASE(Blue, BLUE); - CASE(Alpha, ALPHA); - -#undef CASE - return D3D11_COLOR_WRITE_ENABLE(result); -} - -Result D3D11Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) -{ - auto programImpl = (ShaderProgramImpl*) desc.program; - - ComPtr depthStencilState; - { - D3D11_DEPTH_STENCIL_DESC dsDesc; - dsDesc.DepthEnable = desc.depthStencil.depthTestEnable; - dsDesc.DepthWriteMask = desc.depthStencil.depthWriteEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; - dsDesc.DepthFunc = translateComparisonFunc(desc.depthStencil.depthFunc); - dsDesc.StencilEnable = desc.depthStencil.stencilEnable; - dsDesc.StencilReadMask = desc.depthStencil.stencilReadMask; - dsDesc.StencilWriteMask = desc.depthStencil.stencilWriteMask; - - #define FACE(DST, SRC) \ - dsDesc.DST.StencilFailOp = translateStencilOp( desc.depthStencil.SRC.stencilFailOp); \ - dsDesc.DST.StencilDepthFailOp = translateStencilOp( desc.depthStencil.SRC.stencilDepthFailOp); \ - dsDesc.DST.StencilPassOp = translateStencilOp( desc.depthStencil.SRC.stencilPassOp); \ - dsDesc.DST.StencilFunc = translateComparisonFunc(desc.depthStencil.SRC.stencilFunc); \ - /* end */ - - FACE(FrontFace, frontFace); - FACE(BackFace, backFace); - - SLANG_RETURN_ON_FAIL(m_device->CreateDepthStencilState( - &dsDesc, - depthStencilState.writeRef())); - } - - ComPtr rasterizerState; - { - D3D11_RASTERIZER_DESC rsDesc; - rsDesc.FillMode = translateFillMode(desc.rasterizer.fillMode); - rsDesc.CullMode = translateCullMode(desc.rasterizer.cullMode); - rsDesc.FrontCounterClockwise = desc.rasterizer.frontFace == FrontFaceMode::Clockwise; - rsDesc.DepthBias = desc.rasterizer.depthBias; - rsDesc.DepthBiasClamp = desc.rasterizer.depthBiasClamp; - rsDesc.SlopeScaledDepthBias = desc.rasterizer.slopeScaledDepthBias; - rsDesc.DepthClipEnable = desc.rasterizer.depthClipEnable; - rsDesc.ScissorEnable = desc.rasterizer.scissorEnable; - rsDesc.MultisampleEnable = desc.rasterizer.multisampleEnable; - rsDesc.AntialiasedLineEnable = desc.rasterizer.antialiasedLineEnable; - - SLANG_RETURN_ON_FAIL(m_device->CreateRasterizerState( - &rsDesc, - rasterizerState.writeRef())); - - } - - ComPtr blendState; - { - auto& srcDesc = desc.blend; - D3D11_BLEND_DESC dstDesc = {}; - - TargetBlendDesc defaultTargetBlendDesc; - - static const UInt kMaxTargets = D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; - if(srcDesc.targetCount > kMaxTargets) return SLANG_FAIL; - - for(UInt ii = 0; ii < kMaxTargets; ++ii) - { - TargetBlendDesc const* srcTargetBlendDescPtr = nullptr; - if(ii < srcDesc.targetCount) - { - srcTargetBlendDescPtr = &srcDesc.targets[ii]; - } - else if(srcDesc.targetCount == 0) - { - srcTargetBlendDescPtr = &defaultTargetBlendDesc; - } - else - { - srcTargetBlendDescPtr = &srcDesc.targets[srcDesc.targetCount-1]; - } - - auto& srcTargetBlendDesc = *srcTargetBlendDescPtr; - auto& dstTargetBlendDesc = dstDesc.RenderTarget[ii]; - - if(isBlendDisabled(srcTargetBlendDesc)) - { - dstTargetBlendDesc.BlendEnable = false; - dstTargetBlendDesc.BlendOp = D3D11_BLEND_OP_ADD; - dstTargetBlendDesc.BlendOpAlpha = D3D11_BLEND_OP_ADD; - dstTargetBlendDesc.SrcBlend = D3D11_BLEND_ONE; - dstTargetBlendDesc.SrcBlendAlpha = D3D11_BLEND_ONE; - dstTargetBlendDesc.DestBlend = D3D11_BLEND_ZERO; - dstTargetBlendDesc.DestBlendAlpha = D3D11_BLEND_ZERO; - } - else - { - dstTargetBlendDesc.BlendEnable = true; - dstTargetBlendDesc.BlendOp = translateBlendOp(srcTargetBlendDesc.color.op); - dstTargetBlendDesc.BlendOpAlpha = translateBlendOp(srcTargetBlendDesc.alpha.op); - dstTargetBlendDesc.SrcBlend = translateBlendFactor(srcTargetBlendDesc.color.srcFactor); - dstTargetBlendDesc.SrcBlendAlpha = translateBlendFactor(srcTargetBlendDesc.alpha.srcFactor); - dstTargetBlendDesc.DestBlend = translateBlendFactor(srcTargetBlendDesc.color.dstFactor); - dstTargetBlendDesc.DestBlendAlpha = translateBlendFactor(srcTargetBlendDesc.alpha.dstFactor); - } - - dstTargetBlendDesc.RenderTargetWriteMask = translateRenderTargetWriteMask(srcTargetBlendDesc.writeMask); - } - - dstDesc.IndependentBlendEnable = srcDesc.targetCount > 1; - dstDesc.AlphaToCoverageEnable = srcDesc.alphaToCoverateEnable; - - SLANG_RETURN_ON_FAIL(m_device->CreateBlendState( - &dstDesc, - blendState.writeRef())); - } - - RefPtr state = new GraphicsPipelineStateImpl(); - state->m_program = programImpl; - state->m_stencilRef = desc.depthStencil.stencilRef; - state->m_depthStencilState = depthStencilState; - state->m_rasterizerState = rasterizerState; - state->m_blendState = blendState; - state->m_pipelineLayout = (PipelineLayoutImpl*) desc.pipelineLayout; - state->m_inputLayout = (InputLayoutImpl*) desc.inputLayout; - state->m_rtvCount = UINT(desc.renderTargetCount); - state->m_blendColor[0] = 0; - state->m_blendColor[1] = 0; - state->m_blendColor[2] = 0; - state->m_blendColor[3] = 0; - state->m_sampleMask = 0xFFFFFFFF; - - *outState = state.detach(); - return SLANG_OK; -} - -Result D3D11Renderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) -{ - auto programImpl = (ShaderProgramImpl*) desc.program; - auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; - - RefPtr state = new ComputePipelineStateImpl(); - state->m_program = programImpl; - state->m_pipelineLayout = pipelineLayoutImpl; - - *outState = state.detach(); - return SLANG_OK; -} - -void D3D11Renderer::dispatchCompute(int x, int y, int z) -{ - _flushComputeState(); - m_immediateContext->Dispatch(x, y, z); -} - -Result D3D11Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) -{ - RefPtr descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(); - - UInt counts[int(D3D11DescriptorSlotType::CountOf)] = { 0, }; - - UInt rangeCount = desc.slotRangeCount; - for(UInt rr = 0; rr < rangeCount; ++rr) - { - auto rangeDesc = desc.slotRanges[rr]; - - DescriptorSetLayoutImpl::RangeInfo rangeInfo; - - switch(rangeDesc.type) - { - default: - assert(!"invalid slot type"); - return SLANG_FAIL; - - case DescriptorSlotType::Sampler: - rangeInfo.type = D3D11DescriptorSlotType::Sampler; - break; - - case DescriptorSlotType::CombinedImageSampler: - rangeInfo.type = D3D11DescriptorSlotType::CombinedTextureSampler; - break; - - case DescriptorSlotType::UniformBuffer: - case DescriptorSlotType::DynamicUniformBuffer: - rangeInfo.type = D3D11DescriptorSlotType::ConstantBuffer; - break; - - case DescriptorSlotType::SampledImage: - case DescriptorSlotType::UniformTexelBuffer: - case DescriptorSlotType::InputAttachment: - rangeInfo.type = D3D11DescriptorSlotType::ShaderResourceView; - break; - - case DescriptorSlotType::StorageImage: - case DescriptorSlotType::StorageTexelBuffer: - case DescriptorSlotType::StorageBuffer: - case DescriptorSlotType::DynamicStorageBuffer: - rangeInfo.type = D3D11DescriptorSlotType::UnorderedAccessView; - break; - } - - if(rangeInfo.type == D3D11DescriptorSlotType::CombinedTextureSampler) - { - auto srvTypeIndex = int(D3D11DescriptorSlotType::ShaderResourceView); - auto samplerTypeIndex = int(D3D11DescriptorSlotType::Sampler); - - rangeInfo.arrayIndex = counts[srvTypeIndex]; - rangeInfo.pairedSamplerArrayIndex = counts[samplerTypeIndex]; - - counts[srvTypeIndex] += rangeDesc.count; - counts[samplerTypeIndex] += rangeDesc.count; - } - else - { - auto typeIndex = int(rangeInfo.type); - - rangeInfo.arrayIndex = counts[typeIndex]; - counts[typeIndex] += rangeDesc.count; - } - descriptorSetLayoutImpl->m_ranges.add(rangeInfo); - } - - for(int ii = 0; ii < int(D3D11DescriptorSlotType::CountOf); ++ii) - { - descriptorSetLayoutImpl->m_counts[ii] = counts[ii]; - } - - *outLayout = descriptorSetLayoutImpl.detach(); - return SLANG_OK; -} - -Result D3D11Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) -{ - RefPtr pipelineLayoutImpl = new PipelineLayoutImpl(); - - UInt counts[int(D3D11DescriptorSlotType::CountOf)] = { 0, }; - - UInt setCount = desc.descriptorSetCount; - for(UInt ii = 0; ii < setCount; ++ii) - { - auto setDesc = desc.descriptorSets[ii]; - PipelineLayoutImpl::DescriptorSetInfo setInfo; - - setInfo.layout = (DescriptorSetLayoutImpl*) setDesc.layout; - - for(int jj = 0; jj < int(D3D11DescriptorSlotType::CountOf); ++jj) - { - setInfo.baseIndices[jj] = counts[jj]; - counts[jj] += setInfo.layout->m_counts[jj]; - } - - pipelineLayoutImpl->m_descriptorSets.add(setInfo); - } - - pipelineLayoutImpl->m_uavCount = UINT(counts[int(D3D11DescriptorSlotType::UnorderedAccessView)]); - - *outLayout = pipelineLayoutImpl.detach(); - return SLANG_OK; -} - -Result D3D11Renderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) -{ - auto layoutImpl = (DescriptorSetLayoutImpl*)layout; - - RefPtr descriptorSetImpl = new DescriptorSetImpl(); - - descriptorSetImpl->m_layout = layoutImpl; - descriptorSetImpl->m_cbs .setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ConstantBuffer)]); - descriptorSetImpl->m_srvs .setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ShaderResourceView)]); - descriptorSetImpl->m_uavs .setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::UnorderedAccessView)]); - descriptorSetImpl->m_samplers.setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::Sampler)]); - - *outDescriptorSet = descriptorSetImpl.detach(); - return SLANG_OK; -} - - -#if 0 -BindingState* D3D11Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - auto& dstDetail = dstDetails[i]; - const auto& srcBinding = srcBindings[i]; - - assert(srcBinding.registerRange.isSingle()); - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - assert(srcBinding.resource && srcBinding.resource->isBuffer()); - - BufferResourceImpl* buffer = static_cast(srcBinding.resource.Ptr()); - const BufferResource::Desc& desc = buffer->getDesc(); - - const int elemSize = bufferDesc.elementSize <= 0 ? 1 : bufferDesc.elementSize; - - if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - D3D11_UNORDERED_ACCESS_VIEW_DESC viewDesc; - memset(&viewDesc, 0, sizeof(viewDesc)); - viewDesc.Buffer.FirstElement = 0; - viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); - viewDesc.Buffer.Flags = 0; - viewDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; - viewDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); - - if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) - { - viewDesc.Buffer.Flags |= D3D11_BUFFER_UAV_FLAG_RAW; - viewDesc.Format = DXGI_FORMAT_R32_TYPELESS; - } - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateUnorderedAccessView(buffer->m_buffer, &viewDesc, dstDetail.m_uav.writeRef())); - } - if (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource)) - { - D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; - memset(&viewDesc, 0, sizeof(viewDesc)); - viewDesc.Buffer.FirstElement = 0; - viewDesc.Buffer.ElementWidth = elemSize; - viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); - viewDesc.Buffer.ElementOffset = 0; - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; - viewDesc.Format = DXGI_FORMAT_UNKNOWN; - - if (bufferDesc.elementSize == 0) - { - viewDesc.Format = DXGI_FORMAT_R32_FLOAT; - } - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(buffer->m_buffer, &viewDesc, dstDetail.m_srv.writeRef())); - } - break; - } - case BindingType::Texture: - case BindingType::CombinedTextureSampler: - { - assert(srcBinding.resource && srcBinding.resource->isTexture()); - - TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); - - const TextureResource::Desc& textureDesc = texture->getDesc(); - - D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; - viewDesc.Format = D3DUtil::getMapFormat(textureDesc.format); - - switch (texture->getType()) - { - case Resource::Type::Texture1D: - { - if (textureDesc.arraySize <= 0) - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; - viewDesc.Texture1D.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture1D.MostDetailedMip = 0; - } - else - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; - viewDesc.Texture1DArray.ArraySize = textureDesc.arraySize; - viewDesc.Texture1DArray.FirstArraySlice = 0; - viewDesc.Texture1DArray.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture1DArray.MostDetailedMip = 0; - } - break; - } - case Resource::Type::Texture2D: - { - if (textureDesc.arraySize <= 0) - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - viewDesc.Texture2D.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture2D.MostDetailedMip = 0; - } - else - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - viewDesc.Texture2DArray.ArraySize = textureDesc.arraySize; - viewDesc.Texture2DArray.FirstArraySlice = 0; - viewDesc.Texture2DArray.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture2DArray.MostDetailedMip = 0; - } - break; - } - case Resource::Type::TextureCube: - { - if (textureDesc.arraySize <= 0) - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - viewDesc.TextureCube.MipLevels = textureDesc.numMipLevels; - viewDesc.TextureCube.MostDetailedMip = 0; - } - else - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; - viewDesc.TextureCubeArray.MipLevels = textureDesc.numMipLevels; - viewDesc.TextureCubeArray.MostDetailedMip = 0; - viewDesc.TextureCubeArray.First2DArrayFace = 0; - viewDesc.TextureCubeArray.NumCubes = textureDesc.arraySize; - } - break; - } - case Resource::Type::Texture3D: - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - viewDesc.Texture3D.MipLevels = textureDesc.numMipLevels; // Old code fixed as one - viewDesc.Texture3D.MostDetailedMip = 0; - break; - } - default: - { - assert(!"Unhandled type"); - return nullptr; - } - } - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(texture->m_resource, &viewDesc, dstDetail.m_srv.writeRef())); - break; - } - case BindingType::Sampler: - { - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; - - D3D11_SAMPLER_DESC desc = {}; - desc.AddressU = desc.AddressV = desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; - - if (samplerDesc.isCompareSampler) - { - desc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; - desc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - desc.MinLOD = desc.MaxLOD = 0.0f; - } - else - { - desc.Filter = D3D11_FILTER_ANISOTROPIC; - desc.MaxAnisotropy = 8; - desc.MinLOD = 0.0f; - desc.MaxLOD = 100.0f; - } - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateSamplerState(&desc, dstDetail.m_samplerState.writeRef())); - break; - } - default: - { - assert(!"Unhandled type"); - return nullptr; - } - } - } - - // Done - return bindingState.detach(); -} - -void D3D11Renderer::_applyBindingState(bool isCompute) -{ - auto context = m_immediateContext.get(); - - const auto& details = m_currentBindings->m_bindingDetails; - const auto& bindings = m_currentBindings->getDesc().m_bindings; - - const int numBindings = int(bindings.Count()); - - for (int i = 0; i < numBindings; ++i) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - const int bindingIndex = binding.registerRange.getSingleIndex(); - - switch (binding.bindingType) - { - case BindingType::Buffer: - { - assert(binding.resource && binding.resource->isBuffer()); - if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) - { - ID3D11Buffer* buffer = static_cast(binding.resource.Ptr())->m_buffer; - if (isCompute) - context->CSSetConstantBuffers(bindingIndex, 1, &buffer); - else - { - context->VSSetConstantBuffers(bindingIndex, 1, &buffer); - context->PSSetConstantBuffers(bindingIndex, 1, &buffer); - } - } - else if (detail.m_uav) - { - if (isCompute) - context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); - else - context->OMSetRenderTargetsAndUnorderedAccessViews( - m_currentBindings->getDesc().m_numRenderTargets, - m_renderTargetViews.getBuffer()->readRef(), - m_depthStencilView, - bindingIndex, - 1, - detail.m_uav.readRef(), - nullptr); - } - else - { - if (isCompute) - context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - else - { - context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - } - } - break; - } - case BindingType::Texture: - { - if (detail.m_uav) - { - if (isCompute) - context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); - else - context->OMSetRenderTargetsAndUnorderedAccessViews(D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, - nullptr, nullptr, bindingIndex, 1, detail.m_uav.readRef(), nullptr); - } - else - { - if (isCompute) - context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - else - { - context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - } - } - break; - } - case BindingType::Sampler: - { - if (isCompute) - context->CSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); - else - { - context->PSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); - context->VSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); - } - break; - } - default: - { - assert(!"Not implemented"); - return; - } - } - } -} - -void D3D11Renderer::setBindingState(BindingState* state) -{ - m_currentBindings = static_cast(state); -} -#endif - -void D3D11Renderer::_flushGraphicsState() -{ - auto pipelineType = int(PipelineType::Graphics); - if(m_targetBindingsDirty[pipelineType]) - { - m_targetBindingsDirty[pipelineType] = false; - - auto pipelineState = m_currentGraphicsState.Ptr(); - - auto rtvCount = pipelineState->m_rtvCount; - auto uavCount = pipelineState->m_pipelineLayout->m_uavCount; - - m_immediateContext->OMSetRenderTargetsAndUnorderedAccessViews( - rtvCount, - m_rtvBindings[0].readRef(), - m_dsvBinding, - rtvCount, - uavCount, - m_uavBindings[pipelineType][0].readRef(), - nullptr); - } -} - -void D3D11Renderer::_flushComputeState() -{ - auto pipelineType = int(PipelineType::Compute); - if(m_targetBindingsDirty[pipelineType]) - { - m_targetBindingsDirty[pipelineType] = false; - - auto pipelineState = m_currentComputeState.Ptr(); - - auto uavCount = pipelineState->m_pipelineLayout->m_uavCount; - - m_immediateContext->CSSetUnorderedAccessViews( - 0, - uavCount, - m_uavBindings[pipelineType][0].readRef(), - nullptr); - } -} - -void D3D11Renderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) -{ - auto bufferImpl = (BufferResourceImpl*) buffer; - auto& rangeInfo = m_layout->m_ranges[range]; - - assert(rangeInfo.type == D3D11DescriptorSlotType::ConstantBuffer); - - m_cbs[rangeInfo.arrayIndex + index] = bufferImpl->m_buffer; -} - -void D3D11Renderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) -{ - auto viewImpl = (ResourceViewImpl*)view; - auto& rangeInfo = m_layout->m_ranges[range]; - - switch (rangeInfo.type) - { - case D3D11DescriptorSlotType::ShaderResourceView: - { - assert(viewImpl->m_type == ResourceViewImpl::Type::SRV); - auto srvImpl = (ShaderResourceViewImpl*)viewImpl; - m_srvs[rangeInfo.arrayIndex + index] = srvImpl->m_srv; - } - break; - - case D3D11DescriptorSlotType::UnorderedAccessView: - { - assert(viewImpl->m_type == ResourceViewImpl::Type::UAV); - auto uavImpl = (UnorderedAccessViewImpl*)viewImpl; - m_uavs[rangeInfo.arrayIndex + index] = uavImpl->m_uav; - } - break; - - default: - assert(!"invalid to bind a resource view to this descriptor range"); - break; - } -} - -void D3D11Renderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) -{ - auto samplerImpl = (SamplerStateImpl*) sampler; - auto& rangeInfo = m_layout->m_ranges[range]; - - assert(rangeInfo.type == D3D11DescriptorSlotType::Sampler); - - m_samplers[rangeInfo.arrayIndex + index] = samplerImpl->m_sampler; -} - -void D3D11Renderer::DescriptorSetImpl::setCombinedTextureSampler( - UInt range, - UInt index, - ResourceView* textureView, - SamplerState* sampler) -{ - auto viewImpl = (ResourceViewImpl*) textureView; - auto samplerImpl = (SamplerStateImpl*)sampler; - - auto& rangeInfo = m_layout->m_ranges[range]; - assert(rangeInfo.type == D3D11DescriptorSlotType::CombinedTextureSampler); - - assert(viewImpl->m_type == ResourceViewImpl::Type::SRV); - auto srvImpl = (ShaderResourceViewImpl*)viewImpl; - m_srvs[rangeInfo.arrayIndex + index] = srvImpl->m_srv; - - m_samplers[rangeInfo.arrayIndex + index] = samplerImpl->m_sampler; - - // TODO: need a place to bind the matching sampler... - m_srvs[rangeInfo.pairedSamplerArrayIndex + index] = srvImpl->m_srv; -} - -void D3D11Renderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) -{ - auto pipelineLayoutImpl = (PipelineLayoutImpl*)layout; - auto descriptorSetImpl = (DescriptorSetImpl*) descriptorSet; - - auto descriptorSetLayoutImpl = descriptorSetImpl->m_layout; - auto& setInfo = pipelineLayoutImpl->m_descriptorSets[index]; - - // Note: `setInfo->layout` and `descriptorSetLayoutImpl` need to be compatible - - // TODO: If/when we add per-stage visibility masks, it would be best to organize - // this as a loop over stages, so that we only do the binding that is required - // for each stage. - - { - const int slotType = int(D3D11DescriptorSlotType::ConstantBuffer); - const UINT slotCount = UINT(setInfo.layout->m_counts[slotType]); - if(slotCount) - { - const UINT startSlot = UINT(setInfo.baseIndices[slotType]); - - auto cbs = descriptorSetImpl->m_cbs[0].readRef(); - - m_immediateContext->VSSetConstantBuffers(startSlot, slotCount, cbs); - // ... - m_immediateContext->PSSetConstantBuffers(startSlot, slotCount, cbs); - - m_immediateContext->CSSetConstantBuffers(startSlot, slotCount, cbs); - } - } - - { - const int slotType = int(D3D11DescriptorSlotType::ShaderResourceView); - const UINT slotCount = UINT(setInfo.layout->m_counts[slotType]); - if(slotCount) - { - const UINT startSlot = UINT(setInfo.baseIndices[slotType]); - - auto srvs = descriptorSetImpl->m_srvs[0].readRef(); - - m_immediateContext->VSSetShaderResources(startSlot, slotCount, srvs); - // ... - m_immediateContext->PSSetShaderResources(startSlot, slotCount, srvs); - - m_immediateContext->CSSetShaderResources(startSlot, slotCount, srvs); - } - } - - { - const int slotType = int(D3D11DescriptorSlotType::Sampler); - const UINT slotCount = UINT(setInfo.layout->m_counts[slotType]); - if(slotCount) - { - const UINT startSlot = UINT(setInfo.baseIndices[slotType]); - - auto samplers = descriptorSetImpl->m_samplers[0].readRef(); - - m_immediateContext->VSSetSamplers(startSlot, slotCount, samplers); - // ... - m_immediateContext->PSSetSamplers(startSlot, slotCount, samplers); - - m_immediateContext->CSSetSamplers(startSlot, slotCount, samplers); - } - } - - { - // Note: UAVs are handled differently from other bindings, because - // D3D11 requires all UAVs to be set with a single call, rather - // than allowing incremental updates. We will therefore shadow - // the UAV bindings with `m_uavBindings` and then flush them - // as needed right before a draw/dispatch. - // - const int slotType = int(D3D11DescriptorSlotType::UnorderedAccessView); - const UInt slotCount = setInfo.layout->m_counts[slotType]; - if(slotCount) - { - UInt startSlot = setInfo.baseIndices[slotType]; - - auto uavs = descriptorSetImpl->m_uavs[0].readRef(); - - for(UINT ii = 0; ii < slotCount; ++ii) - { - m_uavBindings[int(pipelineType)][startSlot + ii] = uavs[ii]; - } - m_targetBindingsDirty[int(pipelineType)] = true; - } - } - - -} - -} // renderer_test diff --git a/tools/gfx/render-d3d11.h b/tools/gfx/render-d3d11.h deleted file mode 100644 index 9e671d541..000000000 --- a/tools/gfx/render-d3d11.h +++ /dev/null @@ -1,10 +0,0 @@ -// render-d3d11.h -#pragma once - -namespace gfx { - -class Renderer; - -Renderer* createD3D11Renderer(); - -} // gfx diff --git a/tools/gfx/render-d3d12.cpp b/tools/gfx/render-d3d12.cpp deleted file mode 100644 index e3ce7161a..000000000 --- a/tools/gfx/render-d3d12.cpp +++ /dev/null @@ -1,3722 +0,0 @@ -// render-d3d12.cpp -#define _CRT_SECURE_NO_WARNINGS - -#include "render-d3d12.h" - -//WORKING:#include "options.h" -#include "render.h" - -#include "surface.h" - -// In order to use the Slang API, we need to include its header - -//WORKING:#include - -// We will be rendering with Direct3D 12, so we need to include -// the Windows and D3D12 headers - -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#include -#include -#include - -#include "../../slang-com-ptr.h" -#include "flag-combiner.h" - -#include "resource-d3d12.h" -#include "descriptor-heap-d3d12.h" -#include "circular-resource-heap-d3d12.h" - -#include "d3d-util.h" - -// We will use the C standard library just for printing error messages. -#include - -#ifdef _MSC_VER -#include -#if (_MSC_VER < 1900) -#define snprintf sprintf_s -#endif -#endif -// - -#define ENABLE_DEBUG_LAYER 1 - -namespace gfx { -using namespace Slang; - -class D3D12Renderer : public Renderer -{ -public: - // Renderer implementation - virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; - virtual const List& getFeatures() override { return m_features; } - virtual void setClearColor(const float color[4]) override; - virtual void clearFrame() override; - virtual void presentFrame() override; - TextureResource::Desc getSwapChainTextureDesc() override; - - Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; - Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; - Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; - - Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; - Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; - - Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; - - Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; - Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; - Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; - - Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; - Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; - Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; - - virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; - - virtual void* map(BufferResource* buffer, MapFlavor flavor) override; - virtual void unmap(BufferResource* buffer) override; -// virtual void setInputLayout(InputLayout* inputLayout) override; - virtual void setPrimitiveTopology(PrimitiveTopology topology) override; - - virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; - - virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; - virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; - virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; - void setViewports(UInt count, Viewport const* viewports) override; - void setScissorRects(UInt count, ScissorRect const* rects) override; - virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; - virtual void draw(UInt vertexCount, UInt startVertex) override; - virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; - virtual void dispatchCompute(int x, int y, int z) override; - virtual void submitGpuWork() override; - virtual void waitForGpu() override; - virtual RendererType getRendererType() const override { return RendererType::DirectX12; } - - ~D3D12Renderer(); - -protected: - - static const Int kMaxNumRenderFrames = 4; - static const Int kMaxNumRenderTargets = 3; - - static const Int kMaxRTVCount = 8; - static const Int kMaxDescriptorSetCount = 16; - - struct DeviceInfo - { - void clear() - { - m_dxgiFactory.setNull(); - m_device.setNull(); - m_adapter.setNull(); - m_desc = {}; - m_desc1 = {}; - m_isWarp = false; - } - - bool m_isWarp; - ComPtr m_dxgiFactory; - ComPtr m_device; - ComPtr m_adapter; - DXGI_ADAPTER_DESC m_desc; - DXGI_ADAPTER_DESC1 m_desc1; - }; - - struct Submitter - { - virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0; - virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor) = 0; - virtual void setRootSignature(ID3D12RootSignature* rootSignature) = 0; - }; - - struct FrameInfo - { - FrameInfo() :m_fenceValue(0) {} - void reset() - { - m_commandAllocator.setNull(); - } - ComPtr m_commandAllocator; ///< The command allocator for this frame - UINT64 m_fenceValue; ///< The fence value when rendering this Frame is complete - }; - - class ShaderProgramImpl: public ShaderProgram - { - public: - PipelineType m_pipelineType; - List m_vertexShader; - List m_pixelShader; - List m_computeShader; - }; - - class BufferResourceImpl: public BufferResource - { - public: - typedef BufferResource Parent; - - enum class BackingStyle - { - Unknown, - ResourceBacked, ///< The contents is only held within the resource - MemoryBacked, ///< The current contents is held in m_memory and copied to GPU every time it's used (typically used for constant buffers) - }; - - void bindConstantBufferView(D3D12CircularResourceHeap& circularHeap, int index, Submitter* submitter) const - { - switch (m_backingStyle) - { - case BackingStyle::MemoryBacked: - { - const size_t bufferSize = m_memory.getCount(); - D3D12CircularResourceHeap::Cursor cursor = circularHeap.allocateConstantBuffer(bufferSize); - ::memcpy(cursor.m_position, m_memory.getBuffer(), bufferSize); - // Set the constant buffer - submitter->setRootConstantBufferView(index, circularHeap.getGpuHandle(cursor)); - break; - } - case BackingStyle::ResourceBacked: - { - // Set the constant buffer - submitter->setRootConstantBufferView(index, m_resource.getResource()->GetGPUVirtualAddress()); - break; - } - default: break; - } - } - - BufferResourceImpl(Resource::Usage initialUsage, const Desc& desc): - Parent(desc), - m_mapFlavor(MapFlavor::HostRead), - m_initialUsage(initialUsage) - { - } - - static BackingStyle _calcResourceBackingStyle(Usage usage) - { - // Note: the D3D12 back-end has support for "versioning" of constant buffers, - // where the same logical `BufferResource` can actually point to different - // backing storage over its lifetime, to emulate the ability to modify the - // buffer contents as in D3D11, etc. - // - // The VK back-end doesn't have the same behavior, and it is difficult - // to both support this degree of flexibility *and* efficeintly exploit - // descriptor tables (since any table referencing the buffer would need - // to be updated when a new buffer "version" gets allocated). - // - // I'm choosing to disable this for now, and make all buffers be memory-backed, - // although this creates synchronization issues that we'll have to address - // next. - - return BackingStyle::ResourceBacked; -#if 0 - switch (usage) - { - case Usage::ConstantBuffer: return BackingStyle::MemoryBacked; - default: return BackingStyle::ResourceBacked; - } -#endif - } - - BackingStyle m_backingStyle; ///< How the resource is 'backed' - either as a resource or cpu memory. Cpu memory is typically used for constant buffers. - D3D12Resource m_resource; ///< The resource typically in gpu memory - D3D12Resource m_uploadResource; ///< If the resource can be written to, and is in gpu memory (ie not Memory backed), will have upload resource - - Usage m_initialUsage; - - List m_memory; ///< Cpu memory buffer, used if the m_backingStyle is MemoryBacked - MapFlavor m_mapFlavor; ///< If the resource is mapped holds the current mapping flavor - }; - - class TextureResourceImpl: public TextureResource - { - public: - typedef TextureResource Parent; - - TextureResourceImpl(const Desc& desc): - Parent(desc) - { - } - - D3D12Resource m_resource; - }; - - class SamplerStateImpl : public SamplerState - { - public: - D3D12_CPU_DESCRIPTOR_HANDLE m_cpuHandle; - }; - - class ResourceViewImpl : public ResourceView - { - public: - RefPtr m_resource; - D3D12HostVisibleDescriptor m_descriptor; - }; - - class InputLayoutImpl: public InputLayout - { - public: - List m_elements; - List m_text; ///< Holds all strings to keep in scope - }; - -#if 0 - struct BindingDetail - { - int m_srvIndex = -1; - int m_uavIndex = -1; - int m_samplerIndex = -1; - }; - - class BindingStateImpl: public BindingState - { - public: - typedef BindingState Parent; - - Result init(ID3D12Device* device) - { - // Set up descriptor heaps - SLANG_RETURN_ON_FAIL(m_viewHeap.init(device, 256, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); - SLANG_RETURN_ON_FAIL(m_samplerHeap.init(device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); - return SLANG_OK; - } - - /// Ctor - BindingStateImpl(const Desc& desc) : - Parent(desc) - {} - - List m_bindingDetails; ///< These match 1-1 to the bindings in the m_desc - }; -#endif - - class DescriptorSetLayoutImpl : public DescriptorSetLayout - { - public: - struct RangeInfo - { - DescriptorSlotType type; - Int count; - Int arrayIndex; - }; - - List m_ranges; - - List m_dxRanges; - List m_dxRootParameters; - - Int m_resourceCount; - Int m_samplerCount; - }; - - class PipelineLayoutImpl : public PipelineLayout - { - public: - ComPtr m_rootSignature; - UInt m_descriptorSetCount; - }; - - class DescriptorSetImpl : public DescriptorSet - { - public: - virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; - virtual void setResource(UInt range, UInt index, ResourceView* view) override; - virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; - virtual void setCombinedTextureSampler( - UInt range, - UInt index, - ResourceView* textureView, - SamplerState* sampler) override; - - D3D12Renderer* m_renderer = nullptr; ///< Weak pointer - must be because if set on Renderer, will have a circular reference - RefPtr m_layout; - - D3D12DescriptorHeap* m_resourceHeap = nullptr; - D3D12DescriptorHeap* m_samplerHeap = nullptr; - - Int m_resourceTable = 0; - Int m_samplerTable = 0; - - // The following arrays are used to retain the relevant - // objects so that they will not be released while this - // descriptor-set is still alive. - // - // For the `m_resourceObjects` array, the values are either - // the relevant `ResourceViewImpl` for SRV/UAV slots, or - // a `BufferResourceImpl` for a CBV slot. - // - List> m_resourceObjects; - List> m_samplerObjects; - }; - - - // During command submission, we need all the descriptor tables that get - // used to come from a single heap (for each descriptor heap type). - // - // We will thus keep a single heap of each type that we hope will hold - // all the descriptors that actually get needed in a frame. - // - // TODO: we need an allocation policy to reallocate and resize these - // if/when we run out of space during a frame. - // - D3D12DescriptorHeap m_viewHeap; ///< Cbv, Srv, Uav - D3D12DescriptorHeap m_samplerHeap; ///< Heap for samplers - - D3D12HostVisibleDescriptorAllocator m_rtvAllocator; - D3D12HostVisibleDescriptorAllocator m_dsvAllocator; - - D3D12HostVisibleDescriptorAllocator m_viewAllocator; - D3D12HostVisibleDescriptorAllocator m_samplerAllocator; - - // Space in the GPU-visible heaps is precious, so we will also keep - // around CPU-visible heaps for storing descriptors in a format - // that is ready for copying into the GPU-visible heaps as needed. - // - D3D12DescriptorHeap m_cpuViewHeap; ///< Cbv, Srv, Uav - D3D12DescriptorHeap m_cpuSamplerHeap; ///< Heap for samplers - - class PipelineStateImpl : public PipelineState - { - public: - PipelineType m_pipelineType; - RefPtr m_pipelineLayout; - ComPtr m_pipelineState; - }; - - struct BoundVertexBuffer - { - RefPtr m_buffer; - int m_stride; - int m_offset; - }; - -#if 0 - struct BindParameters - { - enum - { - kMaxRanges = 16, - kMaxParameters = 32 - }; - - D3D12_DESCRIPTOR_RANGE& nextRange() { return m_ranges[m_rangeIndex++]; } - D3D12_ROOT_PARAMETER& nextParameter() { return m_parameters[m_paramIndex++]; } - - BindParameters(): - m_rangeIndex(0), - m_paramIndex(0) - {} - - D3D12_DESCRIPTOR_RANGE m_ranges[kMaxRanges]; - int m_rangeIndex; - D3D12_ROOT_PARAMETER m_parameters[kMaxParameters]; - int m_paramIndex; - }; -#endif - - struct GraphicsSubmitter : public Submitter - { - virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override - { - m_commandList->SetGraphicsRootConstantBufferView(index, gpuBufferLocation); - } - virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override - { - m_commandList->SetGraphicsRootDescriptorTable(index, baseDescriptor); - } - void setRootSignature(ID3D12RootSignature* rootSignature) - { - m_commandList->SetGraphicsRootSignature(rootSignature); - } - - GraphicsSubmitter(ID3D12GraphicsCommandList* commandList): - m_commandList(commandList) - { - } - - ID3D12GraphicsCommandList* m_commandList; - }; - - struct ComputeSubmitter : public Submitter - { - virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override - { - m_commandList->SetComputeRootConstantBufferView(index, gpuBufferLocation); - } - virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override - { - m_commandList->SetComputeRootDescriptorTable(index, baseDescriptor); - } - void setRootSignature(ID3D12RootSignature* rootSignature) - { - m_commandList->SetComputeRootSignature(rootSignature); - } - - ComputeSubmitter(ID3D12GraphicsCommandList* commandList) : - m_commandList(commandList) - { - } - - ID3D12GraphicsCommandList* m_commandList; - }; - - static PROC loadProc(HMODULE module, char const* name); - Result createFrameResources(); - /// Blocks until gpu has completed all work - void releaseFrameResources(); - - Result createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, size_t srcDataSize, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut); - - void beginRender(); - - void endRender(); - - void submitGpuWorkAndWait(); - void _resetCommandList(); - - Result captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut); - - FrameInfo& getFrame() { return m_frameInfos[m_frameIndex]; } - const FrameInfo& getFrame() const { return m_frameInfos[m_frameIndex]; } - - ID3D12GraphicsCommandList* getCommandList() const { return m_commandList; } - -// RenderState* calcRenderState(); - - /// From current bindings calculate the root signature and pipeline state -// Result calcGraphicsPipelineState(ComPtr& sigOut, ComPtr& pipelineStateOut); -// Result calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut); - - Result _bindRenderState(PipelineStateImpl* pipelineStateImpl, ID3D12GraphicsCommandList* commandList, Submitter* submitter); - -// Result _calcBindParameters(BindParameters& params); -// RenderState* findRenderState(PipelineType pipelineType); - - Result _createDevice(DeviceCheckFlags deviceCheckFlags, const UnownedStringSlice& nameMatch, D3D_FEATURE_LEVEL featureLevel, DeviceInfo& outDeviceInfo); - - D3D12CircularResourceHeap m_circularResourceHeap; - - int m_commandListOpenCount = 0; ///< If >0 the command list should be open - - List m_boundVertexBuffers; - - RefPtr m_boundIndexBuffer; - DXGI_FORMAT m_boundIndexFormat; - UINT m_boundIndexOffset; - - RefPtr m_currentPipelineState; - -// RefPtr m_boundShaderProgram; -// RefPtr m_boundInputLayout; - -// RefPtr m_boundBindingState; - RefPtr m_boundDescriptorSets[int(PipelineType::CountOf)][kMaxDescriptorSetCount]; - - DXGI_FORMAT m_targetFormat = DXGI_FORMAT_R8G8B8A8_UNORM; - DXGI_FORMAT m_depthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; - bool m_hasVsync = true; - bool m_isFullSpeed = false; - bool m_allowFullScreen = false; - bool m_isMultiSampled = false; - int m_numTargetSamples = 1; ///< The number of multi sample samples - int m_targetSampleQuality = 0; ///< The multi sample quality - - Desc m_desc; - - bool m_isInitialized = false; - - D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - D3D12_PRIMITIVE_TOPOLOGY m_primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - - float m_clearColor[4] = { 0, 0, 0, 0 }; - - D3D12_VIEWPORT m_viewport = {}; - - ComPtr m_dxDebug; - - DeviceInfo m_deviceInfo; - ID3D12Device* m_device = nullptr; - - ComPtr m_swapChain; - ComPtr m_commandQueue; -// ComPtr m_rtvHeap; - ComPtr m_commandList; - - D3D12_RECT m_scissorRect = {}; - -// List > m_renderStates; ///< Holds list of all render state combinations -// RenderState* m_currentRenderState = nullptr; ///< The current combination - - UINT m_rtvDescriptorSize = 0; - -// ComPtr m_dsvHeap; - UINT m_dsvDescriptorSize = 0; - - // Synchronization objects. - D3D12CounterFence m_fence; - - HANDLE m_swapChainWaitableObject; - - // Frame specific data - int m_numRenderFrames = 0; - UINT m_frameIndex = 0; - FrameInfo m_frameInfos[kMaxNumRenderFrames]; - - int m_numRenderTargets = 2; - int m_renderTargetIndex = 0; - - D3D12Resource* m_backBuffers[kMaxNumRenderTargets]; - D3D12Resource* m_renderTargets[kMaxNumRenderTargets]; - - D3D12Resource m_backBufferResources[kMaxNumRenderTargets]; - D3D12Resource m_renderTargetResources[kMaxNumRenderTargets]; - - - RefPtr m_rtvs[kMaxRTVCount]; - RefPtr m_dsv; - - int32_t m_depthStencilUsageFlags = 0; ///< D3DUtil::UsageFlag combination for depth stencil - int32_t m_targetUsageFlags = 0; ///< D3DUtil::UsageFlag combination for target - - // Dll entry points - PFN_D3D12_GET_DEBUG_INTERFACE m_D3D12GetDebugInterface = nullptr; - PFN_D3D12_CREATE_DEVICE m_D3D12CreateDevice = nullptr; - PFN_D3D12_SERIALIZE_ROOT_SIGNATURE m_D3D12SerializeRootSignature = nullptr; - - HWND m_hwnd = nullptr; - - List m_features; -}; - -Renderer* createD3D12Renderer() -{ - return new D3D12Renderer; -} - -/* static */PROC D3D12Renderer::loadProc(HMODULE module, char const* name) -{ - PROC proc = ::GetProcAddress(module, name); - if (!proc) - { - fprintf(stderr, "error: failed load symbol '%s'\n", name); - return nullptr; - } - return proc; -} - -void D3D12Renderer::releaseFrameResources() -{ - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb174577%28v=vs.85%29.aspx - - // Release the resources holding references to the swap chain (requirement of - // IDXGISwapChain::ResizeBuffers) and reset the frame fence values to the - // current fence value. - for (int i = 0; i < m_numRenderFrames; i++) - { - FrameInfo& info = m_frameInfos[i]; - info.reset(); - info.m_fenceValue = m_fence.getCurrentValue(); - } - for (int i = 0; i < m_numRenderTargets; i++) - { - m_backBuffers[i]->setResourceNull(); - m_renderTargets[i]->setResourceNull(); - } -} - -void D3D12Renderer::waitForGpu() -{ - m_fence.nextSignalAndWait(m_commandQueue); -} - -D3D12Renderer::~D3D12Renderer() -{ - if (m_isInitialized) - { - // Ensure that the GPU is no longer referencing resources that are about to be - // cleaned up by the destructor. - waitForGpu(); - } -} - -static void _initSrvDesc(Resource::Type resourceType, const TextureResource::Desc& textureDesc, const D3D12_RESOURCE_DESC& desc, DXGI_FORMAT pixelFormat, D3D12_SHADER_RESOURCE_VIEW_DESC& descOut) -{ - // create SRV - descOut = D3D12_SHADER_RESOURCE_VIEW_DESC(); - - descOut.Format = (pixelFormat == DXGI_FORMAT_UNKNOWN) ? D3DUtil::calcFormat(D3DUtil::USAGE_SRV, desc.Format) : pixelFormat; - descOut.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - if (desc.DepthOrArraySize == 1) - { - switch (desc.Dimension) - { - case D3D12_RESOURCE_DIMENSION_TEXTURE1D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; break; - case D3D12_RESOURCE_DIMENSION_TEXTURE2D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; break; - case D3D12_RESOURCE_DIMENSION_TEXTURE3D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; break; - default: assert(!"Unknown dimension"); - } - - descOut.Texture2D.MipLevels = desc.MipLevels; - descOut.Texture2D.MostDetailedMip = 0; - descOut.Texture2D.PlaneSlice = 0; - descOut.Texture2D.ResourceMinLODClamp = 0.0f; - } - else if (resourceType == Resource::Type::TextureCube) - { - if (textureDesc.arraySize > 1) - { - descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; - - descOut.TextureCubeArray.NumCubes = textureDesc.arraySize; - descOut.TextureCubeArray.First2DArrayFace = 0; - descOut.TextureCubeArray.MipLevels = desc.MipLevels; - descOut.TextureCubeArray.MostDetailedMip = 0; - descOut.TextureCubeArray.ResourceMinLODClamp = 0; - } - else - { - descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; - - descOut.TextureCube.MipLevels = desc.MipLevels; - descOut.TextureCube.MostDetailedMip = 0; - descOut.TextureCube.ResourceMinLODClamp = 0; - } - } - else - { - assert(desc.DepthOrArraySize > 1); - - switch (desc.Dimension) - { - case D3D12_RESOURCE_DIMENSION_TEXTURE1D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY; break; - case D3D12_RESOURCE_DIMENSION_TEXTURE2D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; break; - case D3D12_RESOURCE_DIMENSION_TEXTURE3D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; break; - - default: assert(!"Unknown dimension"); - } - - descOut.Texture2DArray.ArraySize = desc.DepthOrArraySize; - descOut.Texture2DArray.MostDetailedMip = 0; - descOut.Texture2DArray.MipLevels = desc.MipLevels; - descOut.Texture2DArray.FirstArraySlice = 0; - descOut.Texture2DArray.PlaneSlice = 0; - descOut.Texture2DArray.ResourceMinLODClamp = 0; - } -} - -static void _initBufferResourceDesc(size_t bufferSize, D3D12_RESOURCE_DESC& out) -{ - out = {}; - - out.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - out.Alignment = 0; - out.Width = bufferSize; - out.Height = 1; - out.DepthOrArraySize = 1; - out.MipLevels = 1; - out.Format = DXGI_FORMAT_UNKNOWN; - out.SampleDesc.Count = 1; - out.SampleDesc.Quality = 0; - out.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - out.Flags = D3D12_RESOURCE_FLAG_NONE; -} - -Result D3D12Renderer::createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, size_t srcDataSize, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut) -{ - const size_t bufferSize = size_t(resourceDesc.Width); - - { - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - const D3D12_RESOURCE_STATES initialState = srcData ? D3D12_RESOURCE_STATE_COPY_DEST : finalState; - - SLANG_RETURN_ON_FAIL(resourceOut.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, initialState, nullptr)); - } - - { - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - D3D12_RESOURCE_DESC uploadResourceDesc(resourceDesc); - uploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; - - SLANG_RETURN_ON_FAIL(uploadResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, uploadResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr)); - } - - if (srcData) - { - // Copy data to the intermediate upload heap and then schedule a copy - // from the upload heap to the vertex buffer. - UINT8* dstData; - D3D12_RANGE readRange = {}; // We do not intend to read from this resource on the CPU. - - ID3D12Resource* dxUploadResource = uploadResource.getResource(); - - SLANG_RETURN_ON_FAIL(dxUploadResource->Map(0, &readRange, reinterpret_cast(&dstData))); - ::memcpy(dstData, srcData, srcDataSize); - dxUploadResource->Unmap(0, nullptr); - - m_commandList->CopyBufferRegion(resourceOut, 0, uploadResource, 0, bufferSize); - - // Make sure it's in the right state - { - D3D12BarrierSubmitter submitter(m_commandList); - resourceOut.transition(finalState, submitter); - } - - submitGpuWorkAndWait(); - } - - return SLANG_OK; -} - -void D3D12Renderer::_resetCommandList() -{ - const FrameInfo& frame = getFrame(); - - ID3D12GraphicsCommandList* commandList = getCommandList(); - commandList->Reset(frame.m_commandAllocator, nullptr); - - // TIM: when should this get set? -// commandList->OMSetRenderTargets( -// 1, -// &m_rtvs[0]->m_descriptor.cpuHandle, -// FALSE, -// m_dsv ? &m_dsv->m_descriptor.cpuHandle : nullptr); - - // Set necessary state. - commandList->RSSetViewports(1, &m_viewport); - commandList->RSSetScissorRects(1, &m_scissorRect); -} - -void D3D12Renderer::beginRender() -{ - // Should currently not be open! - assert(m_commandListOpenCount == 0); - - m_circularResourceHeap.updateCompleted(); - - getFrame().m_commandAllocator->Reset(); - - _resetCommandList(); - - // Indicate that the render target needs to be writable - { - D3D12BarrierSubmitter submitter(m_commandList); - m_renderTargets[m_renderTargetIndex]->transition(D3D12_RESOURCE_STATE_RENDER_TARGET, submitter); - } - - m_commandListOpenCount = 1; -} - -void D3D12Renderer::endRender() -{ - assert(m_commandListOpenCount == 1); - - { - const UInt64 signalValue = m_fence.nextSignal(m_commandQueue); - m_circularResourceHeap.addSync(signalValue); - } - - D3D12Resource& backBuffer = *m_backBuffers[m_renderTargetIndex]; - if (m_isMultiSampled) - { - // MSAA resolve - D3D12Resource& renderTarget = *m_renderTargets[m_renderTargetIndex]; - assert(&renderTarget != &backBuffer); - // Barriers to wait for the render target, and the backbuffer to be in correct state - { - D3D12BarrierSubmitter submitter(m_commandList); - renderTarget.transition(D3D12_RESOURCE_STATE_RESOLVE_SOURCE, submitter); - backBuffer.transition(D3D12_RESOURCE_STATE_RESOLVE_DEST, submitter); - } - - // Do the resolve... - m_commandList->ResolveSubresource(backBuffer, 0, renderTarget, 0, m_targetFormat); - } - - // Make the back buffer presentable - { - D3D12BarrierSubmitter submitter(m_commandList); - backBuffer.transition(D3D12_RESOURCE_STATE_PRESENT, submitter); - } - - SLANG_ASSERT_VOID_ON_FAIL(m_commandList->Close()); - - { - // Execute the command list. - ID3D12CommandList* commandLists[] = { m_commandList }; - m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists); - } - - assert(m_commandListOpenCount == 1); - // Must be 0 - m_commandListOpenCount = 0; -} - -void D3D12Renderer::submitGpuWork() -{ - assert(m_commandListOpenCount); - ID3D12GraphicsCommandList* commandList = getCommandList(); - - SLANG_ASSERT_VOID_ON_FAIL(commandList->Close()); - { - // Execute the command list. - ID3D12CommandList* commandLists[] = { commandList }; - m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists); - } - - // Reset the render target - _resetCommandList(); -} - -void D3D12Renderer::submitGpuWorkAndWait() -{ - submitGpuWork(); - waitForGpu(); -} - -Result D3D12Renderer::captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut) -{ - const D3D12_RESOURCE_STATES initialState = resource.getState(); - - const D3D12_RESOURCE_DESC desc = resource.getResource()->GetDesc(); - - // Don't bother supporting MSAA for right now - if (desc.SampleDesc.Count > 1) - { - fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); - return SLANG_FAIL; - } - - size_t bytesPerPixel = sizeof(uint32_t); - size_t rowPitch = int(desc.Width) * bytesPerPixel; - size_t bufferSize = rowPitch * int(desc.Height); - - D3D12Resource stagingResource; - { - D3D12_RESOURCE_DESC stagingDesc; - _initBufferResourceDesc(bufferSize, stagingDesc); - - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_READBACK; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - SLANG_RETURN_ON_FAIL(stagingResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); - } - - { - D3D12BarrierSubmitter submitter(m_commandList); - resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter); - } - - // Do the copy - { - D3D12_TEXTURE_COPY_LOCATION srcLoc; - srcLoc.pResource = resource; - srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - srcLoc.SubresourceIndex = 0; - - D3D12_TEXTURE_COPY_LOCATION dstLoc; - dstLoc.pResource = stagingResource; - dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - dstLoc.PlacedFootprint.Offset = 0; - dstLoc.PlacedFootprint.Footprint.Format = desc.Format; - dstLoc.PlacedFootprint.Footprint.Width = UINT(desc.Width); - dstLoc.PlacedFootprint.Footprint.Height = UINT(desc.Height); - dstLoc.PlacedFootprint.Footprint.Depth = 1; - dstLoc.PlacedFootprint.Footprint.RowPitch = UINT(rowPitch); - - m_commandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr); - } - - { - D3D12BarrierSubmitter submitter(m_commandList); - resource.transition(initialState, submitter); - } - - // Submit the copy, and wait for copy to complete - submitGpuWorkAndWait(); - - { - ID3D12Resource* dxResource = stagingResource; - - UINT8* data; - D3D12_RANGE readRange = {0, bufferSize}; - - SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast(&data))); - - Result res = surfaceOut.set(int(desc.Width), int(desc.Height), Format::RGBA_Unorm_UInt8, int(rowPitch), data, SurfaceAllocator::getMallocAllocator()); - - dxResource->Unmap(0, nullptr); - return res; - } -} - -#if 0 -Result D3D12Renderer::calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut) -{ - BindParameters bindParameters; - _calcBindParameters(bindParameters); - - ComPtr rootSignature; - ComPtr pipelineState; - - { - D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc; - rootSignatureDesc.NumParameters = bindParameters.m_paramIndex; - rootSignatureDesc.pParameters = bindParameters.m_parameters; - rootSignatureDesc.NumStaticSamplers = 0; - rootSignatureDesc.pStaticSamplers = nullptr; - rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; - - ComPtr signature; - ComPtr error; - SLANG_RETURN_ON_FAIL(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef())); - SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef()))); - } - - { - // Describe and create the compute pipeline state object - D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {}; - computeDesc.pRootSignature = rootSignature; - computeDesc.CS = { m_boundShaderProgram->m_computeShader.getBuffer(), m_boundShaderProgram->m_computeShader.Count() }; - SLANG_RETURN_ON_FAIL(m_device->CreateComputePipelineState(&computeDesc, IID_PPV_ARGS(pipelineState.writeRef()))); - } - - signatureOut.swap(rootSignature); - pipelineStateOut.swap(pipelineState); - - return SLANG_OK; -} -#endif - -#if 0 -D3D12Renderer::RenderState* D3D12Renderer::findRenderState(PipelineType pipelineType) -{ - switch (pipelineType) - { - case PipelineType::Compute: - { - // Check if current state is a match - if (m_currentRenderState) - { - if (m_currentRenderState->m_bindingState == m_boundBindingState && - m_currentRenderState->m_shaderProgram == m_boundShaderProgram) - { - return m_currentRenderState; - } - } - - const int num = int(m_renderStates.Count()); - for (int i = 0; i < num; i++) - { - RenderState* renderState = m_renderStates[i]; - if (renderState->m_bindingState == m_boundBindingState && - renderState->m_shaderProgram == m_boundShaderProgram) - { - return renderState; - } - } - break; - } - case PipelineType::Graphics: - { - if (m_currentRenderState) - { - if (m_currentRenderState->m_bindingState == m_boundBindingState && - m_currentRenderState->m_inputLayout == m_boundInputLayout && - m_currentRenderState->m_shaderProgram == m_boundShaderProgram && - m_currentRenderState->m_primitiveTopologyType == m_primitiveTopologyType) - { - return m_currentRenderState; - } - } - // See if matches one in the list - { - const int num = int(m_renderStates.Count()); - for (int i = 0; i < num; i++) - { - RenderState* renderState = m_renderStates[i]; - if (renderState->m_bindingState == m_boundBindingState && - renderState->m_inputLayout == m_boundInputLayout && - renderState->m_shaderProgram == m_boundShaderProgram && - renderState->m_primitiveTopologyType == m_primitiveTopologyType) - { - // Okay we have a match - return renderState; - } - } - } - break; - } - default: break; - } - return nullptr; -} - -D3D12Renderer::RenderState* D3D12Renderer::calcRenderState() -{ - if (!m_boundShaderProgram) - { - return nullptr; - } - m_currentRenderState = findRenderState(m_boundShaderProgram->m_pipelineType); - if (m_currentRenderState) - { - return m_currentRenderState; - } - - ComPtr rootSignature; - ComPtr pipelineState; - - switch (m_boundShaderProgram->m_pipelineType) - { - case PipelineType::Compute: - { - if (SLANG_FAILED(calcComputePipelineState(rootSignature, pipelineState))) - { - return nullptr; - } - break; - } - case PipelineType::Graphics: - { - if (SLANG_FAILED(calcGraphicsPipelineState(rootSignature, pipelineState))) - { - return nullptr; - } - break; - } - default: return nullptr; - } - - RenderState* renderState = new RenderState; - - renderState->m_primitiveTopologyType = m_primitiveTopologyType; - renderState->m_bindingState = m_boundBindingState; - renderState->m_inputLayout = m_boundInputLayout; - renderState->m_shaderProgram = m_boundShaderProgram; - - renderState->m_rootSignature.swap(rootSignature); - renderState->m_pipelineState.swap(pipelineState); - - m_renderStates.Add(renderState); - - m_currentRenderState = renderState; - - return renderState; -} - -Result D3D12Renderer::_calcBindParameters(BindParameters& params) -{ - int numConstantBuffers = 0; - { - if (m_boundBindingState) - { - const int numBoundConstantBuffers = numConstantBuffers; - - const BindingState::Desc& bindingStateDesc = m_boundBindingState->getDesc(); - - const auto& bindings = bindingStateDesc.m_bindings; - const auto& details = m_boundBindingState->m_bindingDetails; - - const int numBindings = int(bindings.Count()); - - for (int i = 0; i < numBindings; i++) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - const int bindingIndex = binding.registerRange.getSingleIndex(); - - if (binding.bindingType == BindingType::Buffer) - { - assert(binding.resource && binding.resource->isBuffer()); - if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) - { - // Make sure it's not overlapping the ones we just statically defined - //assert(binding.m_binding < numBoundConstantBuffers); - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR& descriptor = param.Descriptor; - descriptor.ShaderRegister = bindingIndex; - descriptor.RegisterSpace = 0; - - numConstantBuffers++; - } - } - - if (detail.m_srvIndex >= 0) - { - D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); - - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - range.NumDescriptors = 1; - range.BaseShaderRegister = bindingIndex; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; - table.NumDescriptorRanges = 1; - table.pDescriptorRanges = ⦥ - } - - if (detail.m_uavIndex >= 0) - { - D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); - - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - range.NumDescriptors = 1; - range.BaseShaderRegister = bindingIndex; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; - table.NumDescriptorRanges = 1; - table.pDescriptorRanges = ⦥ - } - } - } - } - - // All the samplers are in one continuous section of the sampler heap - if (m_boundBindingState && m_boundBindingState->m_samplerHeap.getUsedSize() > 0) - { - D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); - - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; - range.NumDescriptors = m_boundBindingState->m_samplerHeap.getUsedSize(); - range.BaseShaderRegister = 0; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; - table.NumDescriptorRanges = 1; - table.pDescriptorRanges = ⦥ - } - return SLANG_OK; -} -#endif - -Result D3D12Renderer::_bindRenderState(PipelineStateImpl* pipelineStateImpl, ID3D12GraphicsCommandList* commandList, Submitter* submitter) -{ - // TODO: we should only set some of this state as needed... - - auto pipelineTypeIndex = (int) pipelineStateImpl->m_pipelineType; - auto pipelineLayout = pipelineStateImpl->m_pipelineLayout; - - submitter->setRootSignature(pipelineLayout->m_rootSignature); - commandList->SetPipelineState(pipelineStateImpl->m_pipelineState); - - ID3D12DescriptorHeap* heaps[] = - { - m_viewHeap.getHeap(), - m_samplerHeap.getHeap(), - }; - commandList->SetDescriptorHeaps(SLANG_COUNT_OF(heaps), heaps); - - // We need to copy descriptors over from the descriptor sets - // (where they are stored in CPU-visible heaps) to the GPU-visible - // heaps so that they can be accessed by shader code. - - Int descriptorSetCount = pipelineLayout->m_descriptorSetCount; - Int rootParameterIndex = 0; - for(Int dd = 0; dd < descriptorSetCount; ++dd) - { - auto descriptorSet = m_boundDescriptorSets[pipelineTypeIndex][dd]; - auto descriptorSetLayout = descriptorSet->m_layout; - - // TODO: require that `descriptorSetLayout` is compatible with - // `pipelineLayout->descriptorSetlayouts[dd]`. - - { - if(auto descriptorCount = descriptorSetLayout->m_resourceCount) - { - auto& gpuHeap = m_viewHeap; - auto gpuDescriptorTable = gpuHeap.allocate(int(descriptorCount)); - - auto& cpuHeap = *descriptorSet->m_resourceHeap; - auto cpuDescriptorTable = descriptorSet->m_resourceTable; - - m_device->CopyDescriptorsSimple( - UINT(descriptorCount), - gpuHeap.getCpuHandle(gpuDescriptorTable), - cpuHeap.getCpuHandle(int(cpuDescriptorTable)), - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - - submitter->setRootDescriptorTable(int(rootParameterIndex++), gpuHeap.getGpuHandle(gpuDescriptorTable)); - } - } - { - if(auto descriptorCount = descriptorSetLayout->m_samplerCount) - { - auto& gpuHeap = m_samplerHeap; - auto gpuDescriptorTable = gpuHeap.allocate(int(descriptorCount)); - - auto& cpuHeap = *descriptorSet->m_samplerHeap; - auto cpuDescriptorTable = descriptorSet->m_samplerTable; - - m_device->CopyDescriptorsSimple( - UINT(descriptorCount), - gpuHeap.getCpuHandle(gpuDescriptorTable), - cpuHeap.getCpuHandle(int(cpuDescriptorTable)), - D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); - - submitter->setRootDescriptorTable(int(rootParameterIndex++), gpuHeap.getGpuHandle(gpuDescriptorTable)); - } - } - } - - return SLANG_OK; -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! - -Result D3D12Renderer::_createDevice(DeviceCheckFlags deviceCheckFlags, const UnownedStringSlice& nameMatch, D3D_FEATURE_LEVEL featureLevel, DeviceInfo& outDeviceInfo) -{ - outDeviceInfo.clear(); - - ComPtr dxgiFactory; - SLANG_RETURN_ON_FAIL(D3DUtil::createFactory(deviceCheckFlags, dxgiFactory)); - - List> dxgiAdapters; - SLANG_RETURN_ON_FAIL(D3DUtil::findAdapters(deviceCheckFlags, nameMatch, dxgiFactory, dxgiAdapters)); - - ComPtr device; - ComPtr adapter; - - for (Index i = 0; i < dxgiAdapters.getCount(); ++i) - { - IDXGIAdapter* dxgiAdapter = dxgiAdapters[i]; - if (SLANG_SUCCEEDED(m_D3D12CreateDevice(dxgiAdapter, featureLevel, IID_PPV_ARGS(device.writeRef())))) - { - adapter = dxgiAdapter; - break; - } - } - - if (!device) - { - return SLANG_FAIL; - } - - if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug)) - { - m_dxDebug->EnableDebugLayer(); - - ComPtr infoQueue; - if (SLANG_SUCCEEDED(device->QueryInterface(infoQueue.writeRef()))) - { - // Make break - infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); - infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); - // infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true); - - // Apparently there is a problem with sm 6.3 with spurious errors, with debug layer enabled - D3D12_FEATURE_DATA_SHADER_MODEL featureShaderModel; - featureShaderModel.HighestShaderModel = D3D_SHADER_MODEL(0x63); - SLANG_SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &featureShaderModel, sizeof(featureShaderModel))); - - if (featureShaderModel.HighestShaderModel >= D3D_SHADER_MODEL(0x63)) - { - // Filter out any messages that cause issues - // TODO: Remove this when the debug layers work properly - D3D12_MESSAGE_ID messageIds[] = - { - // When the debug layer is enabled this error is triggered sometimes after a CopyDescriptorsSimple - // call The failed check validates that the source and destination ranges of the copy do not - // overlap. The check assumes descriptor handles are pointers to memory, but this is not always the - // case and the check fails (even though everything is okay). - D3D12_MESSAGE_ID_COPY_DESCRIPTORS_INVALID_RANGES, - }; - - // We filter INFO messages because they are way too many - D3D12_MESSAGE_SEVERITY severities[] = { D3D12_MESSAGE_SEVERITY_INFO }; - - D3D12_INFO_QUEUE_FILTER infoQueueFilter = {}; - infoQueueFilter.DenyList.NumSeverities = SLANG_COUNT_OF(severities); - infoQueueFilter.DenyList.pSeverityList = severities; - infoQueueFilter.DenyList.NumIDs = SLANG_COUNT_OF(messageIds); - infoQueueFilter.DenyList.pIDList = messageIds; - - infoQueue->PushStorageFilter(&infoQueueFilter); - } - } - } - - // Get the descs - { - adapter->GetDesc(&outDeviceInfo.m_desc); - - // Look up GetDesc1 info - ComPtr adapter1; - if (SLANG_SUCCEEDED(adapter->QueryInterface(adapter1.writeRef()))) - { - adapter1->GetDesc1(&outDeviceInfo.m_desc1); - } - } - - // Save other info - outDeviceInfo.m_device = device; - outDeviceInfo.m_dxgiFactory = dxgiFactory; - outDeviceInfo.m_adapter = adapter; - outDeviceInfo.m_isWarp = D3DUtil::isWarp(dxgiFactory, adapter); - - return SLANG_OK; -} - -Result D3D12Renderer::initialize(const Desc& desc, void* inWindowHandle) -{ - m_hwnd = (HWND)inWindowHandle; - // Rather than statically link against D3D, we load it dynamically. - - HMODULE d3dModule = LoadLibraryA("d3d12.dll"); - if (!d3dModule) - { - fprintf(stderr, "error: failed load 'd3d12.dll'\n"); - return SLANG_FAIL; - } - - // Get all the dll entry points - m_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)loadProc(d3dModule, "D3D12SerializeRootSignature"); - if (!m_D3D12SerializeRootSignature) - { - return SLANG_FAIL; - } - -#if ENABLE_DEBUG_LAYER - m_D3D12GetDebugInterface = (PFN_D3D12_GET_DEBUG_INTERFACE)loadProc(d3dModule, "D3D12GetDebugInterface"); - if (m_D3D12GetDebugInterface) - { - if (SLANG_SUCCEEDED(m_D3D12GetDebugInterface(IID_PPV_ARGS(m_dxDebug.writeRef())))) - { -#if 0 - // Can enable for extra validation. NOTE! That d3d12 warns if you do.... - // D3D12 MESSAGE : Device Debug Layer Startup Options : GPU - Based Validation is enabled(disabled by default). - // This results in new validation not possible during API calls on the CPU, by creating patched shaders that have validation - // added directly to the shader. However, it can slow things down a lot, especially for applications with numerous - // PSOs.Time to see the first render frame may take several minutes. - // [INITIALIZATION MESSAGE #1016: CREATEDEVICE_DEBUG_LAYER_STARTUP_OPTIONS] - - ComPtr debug1; - if (SLANG_SUCCEEDED(m_dxDebug->QueryInterface(debug1.writeRef()))) - { - debug1->SetEnableGPUBasedValidation(true); - } -#endif - - m_dxDebug->EnableDebugLayer(); - } - } -#endif - - m_D3D12CreateDevice = (PFN_D3D12_CREATE_DEVICE)loadProc(d3dModule, "D3D12CreateDevice"); - if (!m_D3D12CreateDevice) - { - return SLANG_FAIL; - } - - FlagCombiner combiner; - // TODO: we should probably provide a command-line option - // to override UseDebug of default rather than leave it - // up to each back-end to specify. -#if ENABLE_DEBUG_LAYER - combiner.add(DeviceCheckFlag::UseDebug, ChangeType::OnOff); ///< First try debug then non debug -#else - combiner.add(DeviceCheckFlag::UseDebug, ChangeType::Off); ///< Don't bother with debug -#endif - combiner.add(DeviceCheckFlag::UseHardwareDevice, ChangeType::OnOff); ///< First try hardware, then reference - - const D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; - - const int numCombinations = combiner.getNumCombinations(); - for (int i = 0; i < numCombinations; ++i) - { - if (SLANG_SUCCEEDED(_createDevice(combiner.getCombination(i), desc.adapter.getUnownedSlice(), featureLevel, m_deviceInfo))) - { - break; - } - } - - if (!m_deviceInfo.m_adapter) - { - // Couldn't find an adapter - return SLANG_FAIL; - } - - // Set the device - m_device = m_deviceInfo.m_device; - - // Find what features are supported - { - // Check this is how this is laid out... - SLANG_COMPILE_TIME_ASSERT(D3D_SHADER_MODEL_6_0 == 0x60); - - { - D3D12_FEATURE_DATA_SHADER_MODEL featureShaderModel; - featureShaderModel.HighestShaderModel = D3D_SHADER_MODEL(0x62); - - // TODO: Currently warp causes a crash when using half, so disable for now - if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &featureShaderModel, sizeof(featureShaderModel))) && - m_deviceInfo.m_isWarp == false && - featureShaderModel.HighestShaderModel >= 0x62) - { - // With sm_6_2 we have half - m_features.add("half"); - } - } - // Check what min precision support we have - { - D3D12_FEATURE_DATA_D3D12_OPTIONS options; - if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)))) - { - auto minPrecisionSupport = options.MinPrecisionSupport; - } - } - } - - m_numRenderFrames = 3; - m_numRenderTargets = 2; - - m_desc = desc; - - // set viewport - { - m_viewport.Width = float(m_desc.width); - m_viewport.Height = float(m_desc.height); - m_viewport.MinDepth = 0; - m_viewport.MaxDepth = 1; - m_viewport.TopLeftX = 0; - m_viewport.TopLeftY = 0; - } - - { - m_scissorRect.left = 0; - m_scissorRect.top = 0; - m_scissorRect.right = m_desc.width; - m_scissorRect.bottom = m_desc.height; - } - - // Describe and create the command queue. - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - - SLANG_RETURN_ON_FAIL(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(m_commandQueue.writeRef()))); - - // Describe the swap chain. - DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; - swapChainDesc.BufferCount = m_numRenderTargets; - swapChainDesc.BufferDesc.Width = m_desc.width; - swapChainDesc.BufferDesc.Height = m_desc.height; - swapChainDesc.BufferDesc.Format = m_targetFormat; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - swapChainDesc.OutputWindow = m_hwnd; - swapChainDesc.SampleDesc.Count = 1; - swapChainDesc.Windowed = TRUE; - - if (m_isFullSpeed) - { - m_hasVsync = false; - m_allowFullScreen = false; - } - - if (!m_hasVsync) - { - swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; - } - - // Swap chain needs the queue so that it can force a flush on it. - ComPtr swapChain; - SLANG_RETURN_ON_FAIL(m_deviceInfo.m_dxgiFactory->CreateSwapChain(m_commandQueue, &swapChainDesc, swapChain.writeRef())); - SLANG_RETURN_ON_FAIL(swapChain->QueryInterface(m_swapChain.writeRef())); - - if (!m_hasVsync) - { - m_swapChainWaitableObject = m_swapChain->GetFrameLatencyWaitableObject(); - - int maxLatency = m_numRenderTargets - 2; - - // Make sure the maximum latency is in the range required by dx12 runtime - maxLatency = (maxLatency < 1) ? 1 : maxLatency; - maxLatency = (maxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS) ? DXGI_MAX_SWAP_CHAIN_BUFFERS : maxLatency; - - m_swapChain->SetMaximumFrameLatency(maxLatency); - } - - // This sample does not support fullscreen transitions. - SLANG_RETURN_ON_FAIL(m_deviceInfo.m_dxgiFactory->MakeWindowAssociation(m_hwnd, DXGI_MWA_NO_ALT_ENTER)); - - m_renderTargetIndex = m_swapChain->GetCurrentBackBufferIndex(); - - // Create descriptor heaps. - - SLANG_RETURN_ON_FAIL(m_viewHeap.init (m_device, 256, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); - SLANG_RETURN_ON_FAIL(m_samplerHeap.init(m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); - - SLANG_RETURN_ON_FAIL(m_cpuViewHeap.init (m_device, 1024, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); - SLANG_RETURN_ON_FAIL(m_cpuSamplerHeap.init(m_device, 64, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); - - SLANG_RETURN_ON_FAIL(m_rtvAllocator.init (m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_RTV)); - SLANG_RETURN_ON_FAIL(m_dsvAllocator.init (m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_DSV)); - SLANG_RETURN_ON_FAIL(m_viewAllocator.init (m_device, 64, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); - SLANG_RETURN_ON_FAIL(m_samplerAllocator.init(m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)); - - // Setup frame resources - { - SLANG_RETURN_ON_FAIL(createFrameResources()); - } - - // Setup fence, and close the command list (as default state without begin/endRender is closed) - { - SLANG_RETURN_ON_FAIL(m_fence.init(m_device)); - // Create the command list. When command lists are created they are open, so close it. - FrameInfo& frame = m_frameInfos[m_frameIndex]; - SLANG_RETURN_ON_FAIL(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frame.m_commandAllocator, nullptr, IID_PPV_ARGS(m_commandList.writeRef()))); - m_commandList->Close(); - } - - { - D3D12CircularResourceHeap::Desc desc; - desc.init(); - // Define size - desc.m_blockSize = 65536; - // Set up the heap - m_circularResourceHeap.init(m_device, desc, &m_fence); - } - - // Setup for rendering - beginRender(); - - m_isInitialized = true; - return SLANG_OK; -} - -Result D3D12Renderer::createFrameResources() -{ - // Create back buffers - { -// D3D12_CPU_DESCRIPTOR_HANDLE rtvStart(m_rtvHeap->GetCPUDescriptorHandleForHeapStart()); - - // Work out target format - D3D12_RESOURCE_DESC resourceDesc; - { - ComPtr backBuffer; - SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, IID_PPV_ARGS(backBuffer.writeRef()))); - resourceDesc = backBuffer->GetDesc(); - } - const DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_TARGET, m_targetUsageFlags, resourceDesc.Format); - const DXGI_FORMAT targetFormat = D3DUtil::calcFormat(D3DUtil::USAGE_TARGET, resourceFormat); - - // Set the target format - m_targetFormat = targetFormat; - - // Create a RTV, and a command allocator for each frame. - for (int i = 0; i < m_numRenderTargets; i++) - { - // Get the back buffer - ComPtr backBuffer; - SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(UINT(i), IID_PPV_ARGS(backBuffer.writeRef()))); - - // Set up resource for back buffer - m_backBufferResources[i].setResource(backBuffer, D3D12_RESOURCE_STATE_COMMON); - m_backBuffers[i] = &m_backBufferResources[i]; - // Assume they are the same thing for now... - m_renderTargets[i] = &m_backBufferResources[i]; - - // If we are multi-sampling - create a render target separate from the back buffer - if (m_isMultiSampled) - { - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - D3D12_CLEAR_VALUE clearValue = {}; - clearValue.Format = m_targetFormat; - - // Don't know targets alignment, so just memory copy - ::memcpy(clearValue.Color, m_clearColor, sizeof(m_clearColor)); - - D3D12_RESOURCE_DESC desc(resourceDesc); - - desc.Format = resourceFormat; - desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - desc.SampleDesc.Count = m_numTargetSamples; - desc.SampleDesc.Quality = m_targetSampleQuality; - desc.Alignment = 0; - - SLANG_RETURN_ON_FAIL(m_renderTargetResources[i].initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, desc, D3D12_RESOURCE_STATE_RENDER_TARGET, &clearValue)); - m_renderTargets[i] = &m_renderTargetResources[i]; - } - - D3D12HostVisibleDescriptor rtvDescriptor; - SLANG_RETURN_ON_FAIL(m_rtvAllocator.allocate(&rtvDescriptor)); - - m_device->CreateRenderTargetView(*m_renderTargets[i], nullptr, rtvDescriptor.cpuHandle); - } - } - - // Set up frames - for (int i = 0; i < m_numRenderFrames; i++) - { - FrameInfo& frame = m_frameInfos[i]; - SLANG_RETURN_ON_FAIL(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(frame.m_commandAllocator.writeRef()))); - } - - { - D3D12_RESOURCE_DESC desc = m_backBuffers[0]->getResource()->GetDesc(); - assert(desc.Width == UINT64(m_desc.width) && desc.Height == UINT64(m_desc.height)); - } - - // Create the depth stencil view. - { - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_DEPTH_STENCIL, m_depthStencilUsageFlags, m_depthStencilFormat); - DXGI_FORMAT depthStencilFormat = D3DUtil::calcFormat(D3DUtil::USAGE_DEPTH_STENCIL, resourceFormat); - - // Set the depth stencil format - m_depthStencilFormat = depthStencilFormat; - - // Setup default clear - D3D12_CLEAR_VALUE clearValue = {}; - clearValue.Format = depthStencilFormat; - clearValue.DepthStencil.Depth = 1.0f; - clearValue.DepthStencil.Stencil = 0; - - D3D12_RESOURCE_DESC resourceDesc = {}; - resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - resourceDesc.Format = resourceFormat; - resourceDesc.Width = m_desc.width; - resourceDesc.Height = m_desc.height; - resourceDesc.DepthOrArraySize = 1; - resourceDesc.MipLevels = 1; - resourceDesc.SampleDesc.Count = m_numTargetSamples; - resourceDesc.SampleDesc.Quality = m_targetSampleQuality; - resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - resourceDesc.Alignment = 0; - -#if 0 - SLANG_RETURN_ON_FAIL(m_depthStencil.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue)); - - // Set the depth stencil - D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {}; - depthStencilDesc.Format = depthStencilFormat; - depthStencilDesc.ViewDimension = m_isMultiSampled ? D3D12_DSV_DIMENSION_TEXTURE2DMS : D3D12_DSV_DIMENSION_TEXTURE2D; - depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE; - - // Set up as the depth stencil view - m_device->CreateDepthStencilView(m_depthStencil, &depthStencilDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart()); - m_depthStencilView = m_dsvHeap->GetCPUDescriptorHandleForHeapStart(); -#endif - } - - m_viewport.Width = static_cast(m_desc.width); - m_viewport.Height = static_cast(m_desc.height); - m_viewport.MaxDepth = 1.0f; - - m_scissorRect.right = static_cast(m_desc.width); - m_scissorRect.bottom = static_cast(m_desc.height); - - return SLANG_OK; -} - -void D3D12Renderer::setClearColor(const float color[4]) -{ - memcpy(m_clearColor, color, sizeof(m_clearColor)); -} - -void D3D12Renderer::clearFrame() -{ - // Record commands - if(auto rtv = m_rtvs[0]) - { - m_commandList->ClearRenderTargetView(rtv->m_descriptor.cpuHandle, m_clearColor, 0, nullptr); - } - if (m_dsv) - { - m_commandList->ClearDepthStencilView(m_dsv->m_descriptor.cpuHandle, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); - } -} - -void D3D12Renderer::presentFrame() -{ - endRender(); - - if (m_swapChainWaitableObject) - { - // check if now is good time to present - // This doesn't wait - because the wait time is 0. If it returns WAIT_TIMEOUT it means that no frame is waiting to be be displayed - // so there is no point doing a present. - const bool shouldPresent = (WaitForSingleObjectEx(m_swapChainWaitableObject, 0, TRUE) != WAIT_TIMEOUT); - if (shouldPresent) - { - m_swapChain->Present(0, 0); - } - } - else - { - if (SLANG_FAILED(m_swapChain->Present(1, 0))) - { - assert(!"Problem presenting"); - beginRender(); - return; - } - } - - // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >= - m_frameInfos[m_frameIndex].m_fenceValue = m_fence.nextSignal(m_commandQueue); - - // increment frame index after signal - m_frameIndex = (m_frameIndex + 1) % m_numRenderFrames; - // Update the render target index. - m_renderTargetIndex = m_swapChain->GetCurrentBackBufferIndex(); - - // On the current frame wait until it is completed - { - FrameInfo& frame = m_frameInfos[m_frameIndex]; - // If the next frame is not ready to be rendered yet, wait until it is ready. - m_fence.waitUntilCompleted(frame.m_fenceValue); - } - - // Setup such that rendering can restart - beginRender(); -} - -TextureResource::Desc D3D12Renderer::getSwapChainTextureDesc() -{ - TextureResource::Desc desc; - desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1); - - return desc; -} - -SlangResult D3D12Renderer::captureScreenSurface(Surface& surfaceOut) -{ - return captureTextureToSurface(*m_renderTargets[m_renderTargetIndex], surfaceOut); -} - -static D3D12_RESOURCE_STATES _calcResourceState(Resource::Usage usage) -{ - typedef Resource::Usage Usage; - switch (usage) - { - case Usage::VertexBuffer: return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; - case Usage::IndexBuffer: return D3D12_RESOURCE_STATE_INDEX_BUFFER; - case Usage::ConstantBuffer: return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; - case Usage::StreamOutput: return D3D12_RESOURCE_STATE_STREAM_OUT; - case Usage::RenderTarget: return D3D12_RESOURCE_STATE_RENDER_TARGET; - case Usage::DepthWrite: return D3D12_RESOURCE_STATE_DEPTH_WRITE; - case Usage::DepthRead: return D3D12_RESOURCE_STATE_DEPTH_READ; - case Usage::UnorderedAccess: return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - case Usage::PixelShaderResource: return D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - case Usage::NonPixelShaderResource: return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; - case Usage::GenericRead: return D3D12_RESOURCE_STATE_GENERIC_READ; - default: return D3D12_RESOURCE_STATES(0); - } -} - -static D3D12_RESOURCE_FLAGS _calcResourceFlag(Resource::BindFlag::Enum bindFlag) -{ - typedef Resource::BindFlag BindFlag; - switch (bindFlag) - { - case BindFlag::RenderTarget: return D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; - case BindFlag::DepthStencil: return D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - case BindFlag::UnorderedAccess: return D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - default: return D3D12_RESOURCE_FLAG_NONE; - } -} - -static D3D12_RESOURCE_FLAGS _calcResourceBindFlags(Resource::Usage initialUsage, int bindFlags) -{ - int dstFlags = 0; - while (bindFlags) - { - int lsb = bindFlags & -bindFlags; - - dstFlags |= _calcResourceFlag(Resource::BindFlag::Enum(lsb)); - bindFlags &= ~lsb; - } - return D3D12_RESOURCE_FLAGS(dstFlags); -} - -static D3D12_RESOURCE_DIMENSION _calcResourceDimension(Resource::Type type) -{ - switch (type) - { - case Resource::Type::Buffer: return D3D12_RESOURCE_DIMENSION_BUFFER; - case Resource::Type::Texture1D: return D3D12_RESOURCE_DIMENSION_TEXTURE1D; - case Resource::Type::TextureCube: - case Resource::Type::Texture2D: - { - return D3D12_RESOURCE_DIMENSION_TEXTURE2D; - } - case Resource::Type::Texture3D: return D3D12_RESOURCE_DIMENSION_TEXTURE3D; - default: return D3D12_RESOURCE_DIMENSION_UNKNOWN; - } -} - -Result D3D12Renderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) -{ - // Description of uploading on Dx12 - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn899215%28v=vs.85%29.aspx - - TextureResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - const DXGI_FORMAT pixelFormat = D3DUtil::getMapFormat(srcDesc.format); - if (pixelFormat == DXGI_FORMAT_UNKNOWN) - { - return SLANG_FAIL; - } - - const int arraySize = srcDesc.calcEffectiveArraySize(); - - const D3D12_RESOURCE_DIMENSION dimension = _calcResourceDimension(srcDesc.type); - if (dimension == D3D12_RESOURCE_DIMENSION_UNKNOWN) - { - return SLANG_FAIL; - } - - const int numMipMaps = srcDesc.numMipLevels; - - // Setup desc - D3D12_RESOURCE_DESC resourceDesc; - - resourceDesc.Dimension = dimension; - resourceDesc.Format = pixelFormat; - resourceDesc.Width = srcDesc.size.width; - resourceDesc.Height = srcDesc.size.height; - resourceDesc.DepthOrArraySize = (srcDesc.size.depth > 1) ? srcDesc.size.depth : arraySize; - - resourceDesc.MipLevels = numMipMaps; - resourceDesc.SampleDesc.Count = srcDesc.sampleDesc.numSamples; - resourceDesc.SampleDesc.Quality = srcDesc.sampleDesc.quality; - - resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; - resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - resourceDesc.Alignment = 0; - - RefPtr texture(new TextureResourceImpl(srcDesc)); - - // Create the target resource - { - D3D12_HEAP_PROPERTIES heapProps; - - heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - SLANG_RETURN_ON_FAIL(texture->m_resource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); - - texture->m_resource.setDebugName(L"Texture"); - } - - // Calculate the layout - List layouts; - layouts.setCount(numMipMaps); - List mipRowSizeInBytes; - mipRowSizeInBytes.setCount(numMipMaps); - List mipNumRows; - mipNumRows.setCount(numMipMaps); - - // Since textures are effectively immutable currently initData must be set - assert(initData); - // We should have this many sub resources - assert(initData->numSubResources == numMipMaps * srcDesc.size.depth * arraySize); - - // NOTE! This is just the size for one array upload -> not for the whole texture - UInt64 requiredSize = 0; - m_device->GetCopyableFootprints(&resourceDesc, 0, numMipMaps, 0, layouts.begin(), mipNumRows.begin(), mipRowSizeInBytes.begin(), &requiredSize); - - // Sub resource indexing - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn705766(v=vs.85).aspx#subresource_indexing - { - // Create the upload texture - D3D12Resource uploadTexture; - - { - D3D12_HEAP_PROPERTIES heapProps; - - heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - D3D12_RESOURCE_DESC uploadResourceDesc; - - uploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - uploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN; - uploadResourceDesc.Width = requiredSize; - uploadResourceDesc.Height = 1; - uploadResourceDesc.DepthOrArraySize = 1; - uploadResourceDesc.MipLevels = 1; - uploadResourceDesc.SampleDesc.Count = 1; - uploadResourceDesc.SampleDesc.Quality = 0; - uploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; - uploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - uploadResourceDesc.Alignment = 0; - - SLANG_RETURN_ON_FAIL(uploadTexture.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, uploadResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr)); - - uploadTexture.setDebugName(L"TextureUpload"); - } - // Get the pointer to the upload resource - ID3D12Resource* uploadResource = uploadTexture; - - int subResourceIndex = 0; - for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) - { - uint8_t* p; - uploadResource->Map(0, nullptr, reinterpret_cast(&p)); - - for (int j = 0; j < numMipMaps; ++j) - { - const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& layout = layouts[j]; - const D3D12_SUBRESOURCE_FOOTPRINT& footprint = layout.Footprint; - - const TextureResource::Size mipSize = srcDesc.size.calcMipSize(j); - - assert(footprint.Width == mipSize.width && footprint.Height == mipSize.height && footprint.Depth == mipSize.depth); - - const ptrdiff_t dstMipRowPitch = ptrdiff_t(layouts[j].Footprint.RowPitch); - const ptrdiff_t srcMipRowPitch = ptrdiff_t(initData->mipRowStrides[j]); - - assert(dstMipRowPitch >= srcMipRowPitch); - - const uint8_t* srcRow = (const uint8_t*)initData->subResources[subResourceIndex]; - uint8_t* dstRow = p + layouts[j].Offset; - - // Copy the depth each mip - for (int l = 0; l < mipSize.depth; l++) - { - // Copy rows - for (int k = 0; k < mipSize.height; ++k) - { - ::memcpy(dstRow, srcRow, srcMipRowPitch); - - srcRow += srcMipRowPitch; - dstRow += dstMipRowPitch; - } - } - - //assert(srcRow == (const uint8_t*)(srcMip.getBuffer() + srcMip.getCount())); - } - uploadResource->Unmap(0, nullptr); - - for (int mipIndex = 0; mipIndex < numMipMaps; ++mipIndex) - { - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn903862(v=vs.85).aspx - - D3D12_TEXTURE_COPY_LOCATION src; - src.pResource = uploadTexture; - src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - src.PlacedFootprint = layouts[mipIndex]; - - D3D12_TEXTURE_COPY_LOCATION dst; - dst.pResource = texture->m_resource; - dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dst.SubresourceIndex = subResourceIndex; - m_commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); - - subResourceIndex++; - } - - // Block - waiting for copy to complete (so can drop upload texture) - submitGpuWorkAndWait(); - } - } - { - const D3D12_RESOURCE_STATES finalState = _calcResourceState(initialUsage); - D3D12BarrierSubmitter submitter(m_commandList); - texture->m_resource.transition(finalState, submitter); - - submitGpuWorkAndWait(); - } - - *outResource = texture.detach(); - return SLANG_OK; -} - -Result D3D12Renderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) -{ - typedef BufferResourceImpl::BackingStyle Style; - - BufferResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - // Always align up to 256 bytes, since that is required for constant buffers. - // - // TODO: only do this for buffers that could potentially be bound as constant buffers... - // - const size_t alignedSizeInBytes = D3DUtil::calcAligned(srcDesc.sizeInBytes, 256); - - RefPtr buffer(new BufferResourceImpl(initialUsage, srcDesc)); - - // Save the style - buffer->m_backingStyle = BufferResourceImpl::_calcResourceBackingStyle(initialUsage); - - D3D12_RESOURCE_DESC bufferDesc; - _initBufferResourceDesc(alignedSizeInBytes, bufferDesc); - - bufferDesc.Flags = _calcResourceBindFlags(initialUsage, srcDesc.bindFlags); - - switch (buffer->m_backingStyle) - { - case Style::MemoryBacked: - { - // Assume the constant buffer will change every frame. We'll just keep a copy of the contents - // in regular memory until it needed - buffer->m_memory.setCount(UInt(alignedSizeInBytes)); - // Initialize - if (initData) - { - ::memcpy(buffer->m_memory.getBuffer(), initData, srcDesc.sizeInBytes); - } - break; - } - case Style::ResourceBacked: - { - const D3D12_RESOURCE_STATES initialState = _calcResourceState(initialUsage); - SLANG_RETURN_ON_FAIL(createBuffer(bufferDesc, initData, srcDesc.sizeInBytes, buffer->m_uploadResource, initialState, buffer->m_resource)); - break; - } - default: - return SLANG_FAIL; - } - - *outResource = buffer.detach(); - return SLANG_OK; -} - -D3D12_FILTER_TYPE translateFilterMode(TextureFilteringMode mode) -{ - switch (mode) - { - default: - return D3D12_FILTER_TYPE(0); - -#define CASE(SRC, DST) \ - case TextureFilteringMode::SRC: return D3D12_FILTER_TYPE_##DST - - CASE(Point, POINT); - CASE(Linear, LINEAR); - -#undef CASE - } -} - -D3D12_FILTER_REDUCTION_TYPE translateFilterReduction(TextureReductionOp op) -{ - switch (op) - { - default: - return D3D12_FILTER_REDUCTION_TYPE(0); - -#define CASE(SRC, DST) \ - case TextureReductionOp::SRC: return D3D12_FILTER_REDUCTION_TYPE_##DST - - CASE(Average, STANDARD); - CASE(Comparison, COMPARISON); - CASE(Minimum, MINIMUM); - CASE(Maximum, MAXIMUM); - -#undef CASE - } -} - -D3D12_TEXTURE_ADDRESS_MODE translateAddressingMode(TextureAddressingMode mode) -{ - switch (mode) - { - default: - return D3D12_TEXTURE_ADDRESS_MODE(0); - -#define CASE(SRC, DST) \ - case TextureAddressingMode::SRC: return D3D12_TEXTURE_ADDRESS_MODE_##DST - - CASE(Wrap, WRAP); - CASE(ClampToEdge, CLAMP); - CASE(ClampToBorder, BORDER); - CASE(MirrorRepeat, MIRROR); - CASE(MirrorOnce, MIRROR_ONCE); - -#undef CASE - } -} - -static D3D12_COMPARISON_FUNC translateComparisonFunc(ComparisonFunc func) -{ - switch (func) - { - default: - // TODO: need to report failures - return D3D12_COMPARISON_FUNC_ALWAYS; - -#define CASE(FROM, TO) \ - case ComparisonFunc::FROM: return D3D12_COMPARISON_FUNC_##TO - - CASE(Never, NEVER); - CASE(Less, LESS); - CASE(Equal, EQUAL); - CASE(LessEqual, LESS_EQUAL); - CASE(Greater, GREATER); - CASE(NotEqual, NOT_EQUAL); - CASE(GreaterEqual, GREATER_EQUAL); - CASE(Always, ALWAYS); -#undef CASE - } -} - -Result D3D12Renderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) -{ - D3D12_FILTER_REDUCTION_TYPE dxReduction = translateFilterReduction(desc.reductionOp); - D3D12_FILTER dxFilter; - if (desc.maxAnisotropy > 1) - { - dxFilter = D3D12_ENCODE_ANISOTROPIC_FILTER(dxReduction); - } - else - { - D3D12_FILTER_TYPE dxMin = translateFilterMode(desc.minFilter); - D3D12_FILTER_TYPE dxMag = translateFilterMode(desc.magFilter); - D3D12_FILTER_TYPE dxMip = translateFilterMode(desc.mipFilter); - - dxFilter = D3D12_ENCODE_BASIC_FILTER(dxMin, dxMag, dxMip, dxReduction); - } - - D3D12_SAMPLER_DESC dxDesc = {}; - dxDesc.Filter = dxFilter; - dxDesc.AddressU = translateAddressingMode(desc.addressU); - dxDesc.AddressV = translateAddressingMode(desc.addressV); - dxDesc.AddressW = translateAddressingMode(desc.addressW); - dxDesc.MipLODBias = desc.mipLODBias; - dxDesc.MaxAnisotropy = desc.maxAnisotropy; - dxDesc.ComparisonFunc = translateComparisonFunc(desc.comparisonFunc); - for (int ii = 0; ii < 4; ++ii) - dxDesc.BorderColor[ii] = desc.borderColor[ii]; - dxDesc.MinLOD = desc.minLOD; - dxDesc.MaxLOD = desc.maxLOD; - - auto samplerHeap = &m_cpuSamplerHeap; - - int indexInSamplerHeap = samplerHeap->allocate(); - if(indexInSamplerHeap < 0) - { - // We ran out of room in our CPU sampler heap. - // - // TODO: this should not be a catastrophic failure, because - // we should just allocate another CPU sampler heap that - // can service subsequent allocation. - // - return SLANG_FAIL; - } - auto cpuDescriptorHandle = samplerHeap->getCpuHandle(indexInSamplerHeap); - - m_device->CreateSampler(&dxDesc, cpuDescriptorHandle); - - // TODO: We really ought to have a free-list of sampler-heap - // entries that we check before we go to the heap, and then - // when we are done with a sampler we simply add it to the free list. - // - RefPtr samplerImpl = new SamplerStateImpl(); - samplerImpl->m_cpuHandle = cpuDescriptorHandle; - *outSampler = samplerImpl.detach(); - return SLANG_OK; -} - -Result D3D12Renderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) -{ - auto resourceImpl = (TextureResourceImpl*) texture; - - RefPtr viewImpl = new ResourceViewImpl(); - viewImpl->m_resource = resourceImpl; - - switch (desc.type) - { - default: - return SLANG_FAIL; - - case ResourceView::Type::RenderTarget: - { - SLANG_RETURN_ON_FAIL(m_rtvAllocator.allocate(&viewImpl->m_descriptor)); - m_device->CreateRenderTargetView(resourceImpl->m_resource, nullptr, viewImpl->m_descriptor.cpuHandle); - } - break; - - case ResourceView::Type::DepthStencil: - { - SLANG_RETURN_ON_FAIL(m_dsvAllocator.allocate(&viewImpl->m_descriptor)); - m_device->CreateDepthStencilView(resourceImpl->m_resource, nullptr, viewImpl->m_descriptor.cpuHandle); - } - break; - - case ResourceView::Type::UnorderedAccess: - { - // TODO: need to support the separate "counter resource" for the case - // of append/consume buffers with attached counters. - - SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); - m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, nullptr, viewImpl->m_descriptor.cpuHandle); - } - break; - - case ResourceView::Type::ShaderResource: - { - SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); - - // Need to construct the D3D12_SHADER_RESOURCE_VIEW_DESC because otherwise TextureCube is not accessed - // appropriately (rather than just passing nullptr to CreateShaderResourceView) - const D3D12_RESOURCE_DESC resourceDesc = resourceImpl->m_resource.getResource()->GetDesc(); - const DXGI_FORMAT pixelFormat = resourceDesc.Format; - - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - _initSrvDesc(resourceImpl->getType(), resourceImpl->getDesc(), resourceDesc, pixelFormat, srvDesc); - - m_device->CreateShaderResourceView(resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle); - } - break; - } - - *outView = viewImpl.detach(); - return SLANG_OK; -} - -Result D3D12Renderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) -{ - auto resourceImpl = (BufferResourceImpl*) buffer; - auto resourceDesc = resourceImpl->getDesc(); - - RefPtr viewImpl = new ResourceViewImpl(); - viewImpl->m_resource = resourceImpl; - - switch (desc.type) - { - default: - return SLANG_FAIL; - - case ResourceView::Type::UnorderedAccess: - { - D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; - uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; - uavDesc.Format = D3DUtil::getMapFormat(desc.format); - uavDesc.Buffer.FirstElement = 0; - - if(resourceDesc.elementSize) - { - uavDesc.Buffer.StructureByteStride = resourceDesc.elementSize; - uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / resourceDesc.elementSize); - } - else if(desc.format == Format::Unknown) - { - uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW; - uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; - uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / 4); - } - else - { - uavDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / RendererUtil::getFormatSize(desc.format)); - } - - - // TODO: need to support the separate "counter resource" for the case - // of append/consume buffers with attached counters. - - SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); - m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, &uavDesc, viewImpl->m_descriptor.cpuHandle); - } - break; - - case ResourceView::Type::ShaderResource: - { - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - srvDesc.Format = D3DUtil::getMapFormat(desc.format); - srvDesc.Buffer.StructureByteStride = 0; - srvDesc.Buffer.FirstElement = 0; - - if(resourceDesc.elementSize) - { - srvDesc.Buffer.StructureByteStride = resourceDesc.elementSize; - srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / resourceDesc.elementSize); - } - else if(desc.format == Format::Unknown) - { - srvDesc.Buffer.Flags |= D3D12_BUFFER_SRV_FLAG_RAW; - srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; - srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / 4); - } - else - { - srvDesc.Buffer.NumElements = UINT(resourceDesc.sizeInBytes / RendererUtil::getFormatSize(desc.format)); - } - - SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); - m_device->CreateShaderResourceView(resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle); - } - break; - } - - *outView = viewImpl.detach(); - return SLANG_OK; -} - -Result D3D12Renderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) -{ - RefPtr layout(new InputLayoutImpl); - - // Work out a buffer size to hold all text - size_t textSize = 0; - for (int i = 0; i < Int(inputElementCount); ++i) - { - const char* text = inputElements[i].semanticName; - textSize += text ? (::strlen(text) + 1) : 0; - } - layout->m_text.setCount(textSize); - char* textPos = layout->m_text.getBuffer(); - - // - List& elements = layout->m_elements; - elements.setCount(inputElementCount); - - - for (UInt i = 0; i < inputElementCount; ++i) - { - const InputElementDesc& srcEle = inputElements[i]; - D3D12_INPUT_ELEMENT_DESC& dstEle = elements[i]; - - // Add text to the buffer - const char* semanticName = srcEle.semanticName; - if (semanticName) - { - const int len = int(::strlen(semanticName)); - ::memcpy(textPos, semanticName, len + 1); - semanticName = textPos; - textPos += len + 1; - } - - dstEle.SemanticName = semanticName; - dstEle.SemanticIndex = (UINT)srcEle.semanticIndex; - dstEle.Format = D3DUtil::getMapFormat(srcEle.format); - dstEle.InputSlot = 0; - dstEle.AlignedByteOffset = (UINT)srcEle.offset; - dstEle.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; - dstEle.InstanceDataStepRate = 0; - } - - *outLayout = layout.detach(); - return SLANG_OK; -} - -void* D3D12Renderer::map(BufferResource* bufferIn, MapFlavor flavor) -{ - typedef BufferResourceImpl::BackingStyle Style; - - BufferResourceImpl* buffer = static_cast(bufferIn); - buffer->m_mapFlavor = flavor; - - const size_t bufferSize = buffer->getDesc().sizeInBytes; - - switch (buffer->m_backingStyle) - { - case Style::ResourceBacked: - { - // We need this in a state so we can upload - switch (flavor) - { - case MapFlavor::HostWrite: - case MapFlavor::WriteDiscard: - { - D3D12BarrierSubmitter submitter(m_commandList); - buffer->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter); - buffer->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter); - - const D3D12_RANGE readRange = {}; - - void* uploadData; - SLANG_RETURN_NULL_ON_FAIL(buffer->m_uploadResource.getResource()->Map(0, &readRange, reinterpret_cast(&uploadData))); - return uploadData; - - break; - } - case MapFlavor::HostRead: - { - // This will be slow!!! - it blocks CPU on GPU completion - D3D12Resource& resource = buffer->m_resource; - - // Readback heap - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_READBACK; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - // Resource to readback to - D3D12_RESOURCE_DESC stagingDesc; - _initBufferResourceDesc(bufferSize, stagingDesc); - - D3D12Resource stageBuf; - SLANG_RETURN_NULL_ON_FAIL(stageBuf.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); - - const D3D12_RESOURCE_STATES initialState = resource.getState(); - - // Make it a source - { - D3D12BarrierSubmitter submitter(m_commandList); - resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter); - } - // Do the copy - m_commandList->CopyBufferRegion(stageBuf, 0, resource, 0, bufferSize); - // Switch it back - { - D3D12BarrierSubmitter submitter(m_commandList); - resource.transition(initialState, submitter); - } - - // Wait until complete - submitGpuWorkAndWait(); - - // Map and copy - { - UINT8* data; - D3D12_RANGE readRange = { 0, bufferSize }; - - SLANG_RETURN_NULL_ON_FAIL(stageBuf.getResource()->Map(0, &readRange, reinterpret_cast(&data))); - - // Copy to memory buffer - buffer->m_memory.setCount(bufferSize); - ::memcpy(buffer->m_memory.getBuffer(), data, bufferSize); - - stageBuf.getResource()->Unmap(0, nullptr); - } - - return buffer->m_memory.getBuffer(); - } - } - break; - } - case Style::MemoryBacked: - { - return buffer->m_memory.getBuffer(); - } - default: return nullptr; - } - - return nullptr; -} - -void D3D12Renderer::unmap(BufferResource* bufferIn) -{ - typedef BufferResourceImpl::BackingStyle Style; - BufferResourceImpl* buffer = static_cast(bufferIn); - - switch (buffer->m_backingStyle) - { - case Style::MemoryBacked: - { - // Don't need to do anything, as will be uploaded automatically when used - break; - } - case Style::ResourceBacked: - { - // We need this in a state so we can upload - switch (buffer->m_mapFlavor) - { - case MapFlavor::HostWrite: - case MapFlavor::WriteDiscard: - { - // Unmap - ID3D12Resource* uploadResource = buffer->m_uploadResource; - ID3D12Resource* resource = buffer->m_resource; - - uploadResource->Unmap(0, nullptr); - - const D3D12_RESOURCE_STATES initialState = buffer->m_resource.getState(); - - { - D3D12BarrierSubmitter submitter(m_commandList); - buffer->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter); - buffer->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter); - } - - m_commandList->CopyBufferRegion(resource, 0, uploadResource, 0, buffer->getDesc().sizeInBytes); - - { - D3D12BarrierSubmitter submitter(m_commandList); - buffer->m_resource.transition(initialState, submitter); - } - break; - } - case MapFlavor::HostRead: - { - break; - } - } - } - } -} - -#if 0 -void D3D12Renderer::setInputLayout(InputLayout* inputLayout) -{ - m_boundInputLayout = static_cast(inputLayout); -} -#endif - -void D3D12Renderer::setPrimitiveTopology(PrimitiveTopology topology) -{ - switch (topology) - { - case PrimitiveTopology::TriangleList: - { - m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - m_primitiveTopology = D3DUtil::getPrimitiveTopology(topology); - break; - } - default: - { - assert(!"Unhandled type"); - } - } -} - -void D3D12Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) -{ - { - const Index num = startSlot + slotCount; - if (num > m_boundVertexBuffers.getCount()) - { - m_boundVertexBuffers.setCount(num); - } - } - - for (UInt i = 0; i < slotCount; i++) - { - BufferResourceImpl* buffer = static_cast(buffers[i]); - if (buffer) - { - assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer); - } - - BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i]; - boundBuffer.m_buffer = buffer; - boundBuffer.m_stride = int(strides[i]); - boundBuffer.m_offset = int(offsets[i]); - } -} - -void D3D12Renderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) -{ - m_boundIndexBuffer = (BufferResourceImpl*) buffer; - m_boundIndexFormat = D3DUtil::getMapFormat(indexFormat); - m_boundIndexOffset = UINT(offset); -} - -void D3D12Renderer::setDepthStencilTarget(ResourceView* depthStencilView) -{ -} - -void D3D12Renderer::setViewports(UInt count, Viewport const* viewports) -{ - static const int kMaxViewports = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; - assert(count <= kMaxViewports); - - D3D12_VIEWPORT dxViewports[kMaxViewports]; - for(UInt ii = 0; ii < count; ++ii) - { - auto& inViewport = viewports[ii]; - auto& dxViewport = dxViewports[ii]; - - dxViewport.TopLeftX = inViewport.originX; - dxViewport.TopLeftY = inViewport.originY; - dxViewport.Width = inViewport.extentX; - dxViewport.Height = inViewport.extentY; - dxViewport.MinDepth = inViewport.minZ; - dxViewport.MaxDepth = inViewport.maxZ; - } - - m_commandList->RSSetViewports(UINT(count), dxViewports); -} - -void D3D12Renderer::setScissorRects(UInt count, ScissorRect const* rects) -{ - static const int kMaxScissorRects = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; - assert(count <= kMaxScissorRects); - - D3D12_RECT dxRects[kMaxScissorRects]; - for(UInt ii = 0; ii < count; ++ii) - { - auto& inRect = rects[ii]; - auto& dxRect = dxRects[ii]; - - dxRect.left = LONG(inRect.minX); - dxRect.top = LONG(inRect.minY); - dxRect.right = LONG(inRect.maxX); - dxRect.bottom = LONG(inRect.maxY); - } - - m_commandList->RSSetScissorRects(UINT(count), dxRects); -} - -void D3D12Renderer::setPipelineState(PipelineType pipelineType, PipelineState* state) -{ - m_currentPipelineState = (PipelineStateImpl*)state; -} - -void D3D12Renderer::draw(UInt vertexCount, UInt startVertex) -{ - ID3D12GraphicsCommandList* commandList = m_commandList; - - auto pipelineState = m_currentPipelineState.Ptr(); - if (!pipelineState || (pipelineState->m_pipelineType != PipelineType::Graphics)) - { - assert(!"No graphics pipeline state set"); - return; - } - - // Submit - setting for graphics - { - GraphicsSubmitter submitter(commandList); - _bindRenderState(pipelineState, commandList, &submitter); - } - - commandList->IASetPrimitiveTopology(m_primitiveTopology); - - // Set up vertex buffer views - { - int numVertexViews = 0; - D3D12_VERTEX_BUFFER_VIEW vertexViews[16]; - for (Index i = 0; i < m_boundVertexBuffers.getCount(); i++) - { - const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[i]; - BufferResourceImpl* buffer = boundVertexBuffer.m_buffer; - if (buffer) - { - D3D12_VERTEX_BUFFER_VIEW& vertexView = vertexViews[numVertexViews++]; - vertexView.BufferLocation = buffer->m_resource.getResource()->GetGPUVirtualAddress() - + boundVertexBuffer.m_offset; - vertexView.SizeInBytes = UINT(buffer->getDesc().sizeInBytes - boundVertexBuffer.m_offset); - vertexView.StrideInBytes = UINT(boundVertexBuffer.m_stride); - } - } - commandList->IASetVertexBuffers(0, numVertexViews, vertexViews); - } - - // Set up index buffer - if(m_boundIndexBuffer) - { - D3D12_INDEX_BUFFER_VIEW indexBufferView; - indexBufferView.BufferLocation = m_boundIndexBuffer->m_resource.getResource()->GetGPUVirtualAddress() - + m_boundIndexOffset; - indexBufferView.SizeInBytes = UINT(m_boundIndexBuffer->getDesc().sizeInBytes - m_boundIndexOffset); - indexBufferView.Format = m_boundIndexFormat; - - commandList->IASetIndexBuffer(&indexBufferView); - } - - commandList->DrawInstanced(UINT(vertexCount), 1, UINT(startVertex), 0); -} - -void D3D12Renderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) -{ -} - -void D3D12Renderer::dispatchCompute(int x, int y, int z) -{ - ID3D12GraphicsCommandList* commandList = m_commandList; - auto pipelineStateImpl = m_currentPipelineState; - - // Submit binding for compute - { - ComputeSubmitter submitter(commandList); - _bindRenderState(pipelineStateImpl, commandList, &submitter); - } - - commandList->Dispatch(x, y, z); -} - -#if 0 -BindingState* D3D12Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); - - SLANG_RETURN_NULL_ON_FAIL(bindingState->init(m_device)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - const auto& srcEntry = srcBindings[i]; - auto& dstDetail = dstDetails[i]; - - const int bindingIndex = srcEntry.registerRange.getSingleIndex(); - - switch (srcEntry.bindingType) - { - case BindingType::Buffer: - { - assert(srcEntry.resource && srcEntry.resource->isBuffer()); - BufferResourceImpl* bufferResource = static_cast(srcEntry.resource.Ptr()); - const BufferResource::Desc& desc = bufferResource->getDesc(); - - const size_t bufferSize = bufferDesc.sizeInBytes; - const int elemSize = bufferDesc.elementSize <= 0 ? sizeof(uint32_t) : bufferDesc.elementSize; - - const bool createSrv = false; - - // NOTE! In this arrangement the buffer can either be a ConstantBuffer or a 'StorageBuffer'. - // If it's a storage buffer then it has a 'uav'. - // In neither circumstance is there an associated srv - // This departs a little from dx11 code - in that it will create srv and uav for a storage buffer. - if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - dstDetail.m_uavIndex = bindingState->m_viewHeap.allocate(); - if (dstDetail.m_uavIndex < 0) - { - return nullptr; - } - - D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; - - uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; - uavDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); - - uavDesc.Buffer.StructureByteStride = elemSize; - - uavDesc.Buffer.FirstElement = 0; - uavDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); - uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE; - - if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) - { - uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW; - uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; - - uavDesc.Buffer.StructureByteStride = 0; - } - else if( bufferDesc.format != Format::Unknown ) - { - uavDesc.Buffer.StructureByteStride = 0; - } - - m_device->CreateUnorderedAccessView(bufferResource->m_resource, nullptr, &uavDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_uavIndex)); - } - if (createSrv && (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource))) - { - dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); - if (dstDetail.m_srvIndex < 0) - { - return nullptr; - } - - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - srvDesc.Format = DXGI_FORMAT_UNKNOWN; - srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - - srvDesc.Buffer.FirstElement = 0; - srvDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); - srvDesc.Buffer.StructureByteStride = elemSize; - srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; - - if (bufferDesc.elementSize == 0) - { - srvDesc.Format = DXGI_FORMAT_R32_FLOAT; - } - - m_device->CreateShaderResourceView(bufferResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); - } - - break; - } - case BindingType::Texture: - { - assert(srcEntry.resource && srcEntry.resource->isTexture()); - - TextureResourceImpl* textureResource = static_cast(srcEntry.resource.Ptr()); - - dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); - if (dstDetail.m_srvIndex < 0) - { - return nullptr; - } - - { - const D3D12_RESOURCE_DESC resourceDesc = textureResource->m_resource.getResource()->GetDesc(); - const DXGI_FORMAT pixelFormat = resourceDesc.Format; - - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - _initSrvDesc(textureResource->getType(), textureResource->getDesc(), resourceDesc, pixelFormat, srvDesc); - - // Create descriptor - m_device->CreateShaderResourceView(textureResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); - } - - break; - } - case BindingType::Sampler: - { - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcEntry.descIndex]; - - const int samplerIndex = bindingIndex; - dstDetail.m_samplerIndex = samplerIndex; - bindingState->m_samplerHeap.placeAt(samplerIndex); - - D3D12_SAMPLER_DESC desc = {}; - desc.AddressU = desc.AddressV = desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - desc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; - - if (samplerDesc.isCompareSampler) - { - desc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; - desc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; - } - else - { - desc.Filter = D3D12_FILTER_ANISOTROPIC; - desc.MaxAnisotropy = 8; - desc.MinLOD = 0.0f; - desc.MaxLOD = 100.0f; - } - - m_device->CreateSampler(&desc, bindingState->m_samplerHeap.getCpuHandle(samplerIndex)); - - break; - } - case BindingType::CombinedTextureSampler: - { - assert(!"Not implemented"); - return nullptr; - } - } - } - - return bindingState.detach(); -} - -void D3D12Renderer::setBindingState(BindingState* state) -{ - m_boundBindingState = static_cast(state); -} -#endif - -void D3D12Renderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) -{ - auto dxDevice = m_renderer->m_device; - - auto resourceImpl = (BufferResourceImpl*) buffer; - auto resourceDesc = resourceImpl->getDesc(); - - // Constant buffer view size must be a multiple of 256 bytes, so we round it up here. - const size_t alignedSizeInBytes = D3DUtil::calcAligned(resourceDesc.sizeInBytes, 256); - - D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {}; - cbvDesc.BufferLocation = resourceImpl->m_resource.getResource()->GetGPUVirtualAddress(); - cbvDesc.SizeInBytes = UINT(alignedSizeInBytes); - - auto& rangeInfo = m_layout->m_ranges[range]; - -#ifdef _DEBUG - switch(rangeInfo.type) - { - default: - assert(!"incorrect slot type"); - break; - - case DescriptorSlotType::UniformBuffer: - case DescriptorSlotType::DynamicUniformBuffer: - break; - } -#endif - - auto arrayIndex = rangeInfo.arrayIndex + index; - auto descriptorIndex = m_resourceTable + arrayIndex; - - m_resourceObjects[arrayIndex] = resourceImpl; - dxDevice->CreateConstantBufferView( - &cbvDesc, - m_resourceHeap->getCpuHandle(int(descriptorIndex))); -} - -void D3D12Renderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) -{ - auto dxDevice = m_renderer->m_device; - - auto viewImpl = (ResourceViewImpl*) view; - - auto& rangeInfo = m_layout->m_ranges[range]; - - // TODO: validation that slot type matches view - - auto arrayIndex = rangeInfo.arrayIndex + index; - auto descriptorIndex = m_resourceTable + arrayIndex; - - m_resourceObjects[arrayIndex] = viewImpl; - dxDevice->CopyDescriptorsSimple( - 1, - m_resourceHeap->getCpuHandle(int(descriptorIndex)), - viewImpl->m_descriptor.cpuHandle, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); -} - -void D3D12Renderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) -{ - auto dxDevice = m_renderer->m_device; - - auto samplerImpl = (SamplerStateImpl*) sampler; - - auto& rangeInfo = m_layout->m_ranges[range]; - -#ifdef _DEBUG - switch(rangeInfo.type) - { - default: - assert(!"incorrect slot type"); - break; - - case DescriptorSlotType::Sampler: - break; - } -#endif - - auto arrayIndex = rangeInfo.arrayIndex + index; - auto descriptorIndex = m_resourceTable + arrayIndex; - - m_samplerObjects[arrayIndex] = samplerImpl; - dxDevice->CopyDescriptorsSimple( - 1, - m_samplerHeap->getCpuHandle(int(descriptorIndex)), - samplerImpl->m_cpuHandle, - D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); -} - -void D3D12Renderer::DescriptorSetImpl::setCombinedTextureSampler( - UInt range, - UInt index, - ResourceView* textureView, - SamplerState* sampler) -{ - auto dxDevice = m_renderer->m_device; - - auto viewImpl = (ResourceViewImpl*) textureView; - auto samplerImpl = (SamplerStateImpl*) sampler; - - auto& rangeInfo = m_layout->m_ranges[range]; - -#ifdef _DEBUG - switch(rangeInfo.type) - { - default: - assert(!"incorrect slot type"); - break; - - case DescriptorSlotType::CombinedImageSampler: - break; - } -#endif - - auto arrayIndex = rangeInfo.arrayIndex + index; - auto resourceDescriptorIndex = m_resourceTable + arrayIndex; - auto samplerDescriptorIndex = m_samplerTable + arrayIndex; - - m_resourceObjects[arrayIndex] = viewImpl; - dxDevice->CopyDescriptorsSimple( - 1, - m_resourceHeap->getCpuHandle(int(resourceDescriptorIndex)), - viewImpl->m_descriptor.cpuHandle, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - - m_samplerObjects[arrayIndex] = samplerImpl; - dxDevice->CopyDescriptorsSimple( - 1, - m_samplerHeap->getCpuHandle(int(samplerDescriptorIndex)), - samplerImpl->m_cpuHandle, - D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); -} - -void D3D12Renderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) -{ - // In D3D12, unlike Vulkan, binding a root signature invalidates *all* descriptor table - // bindings (rather than preserving those that are part of the longest common prefix - // between the old and new layout). - // - // In order to accomodate having descriptor-set bindings that persist across changes - // in pipeline state (which may also change pipeline layout), we will shadow the - // descriptor-set bindings and only flush them on-demand at draw tiume once the final - // pipline layout is known. - // - - auto descriptorSetImpl = (DescriptorSetImpl*) descriptorSet; - m_boundDescriptorSets[int(pipelineType)][index] = descriptorSetImpl; -} - -Result D3D12Renderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) -{ - RefPtr program(new ShaderProgramImpl()); - program->m_pipelineType = desc.pipelineType; - - if (desc.pipelineType == PipelineType::Compute) - { - auto computeKernel = desc.findKernel(StageType::Compute); - program->m_computeShader.insertRange(0, (const uint8_t*) computeKernel->codeBegin, computeKernel->getCodeSize()); - } - else - { - auto vertexKernel = desc.findKernel(StageType::Vertex); - auto fragmentKernel = desc.findKernel(StageType::Fragment); - - program->m_vertexShader.insertRange(0, (const uint8_t*) vertexKernel->codeBegin, vertexKernel->getCodeSize()); - program->m_pixelShader.insertRange(0, (const uint8_t*) fragmentKernel->codeBegin, fragmentKernel->getCodeSize()); - } - - *outProgram = program.detach(); - return SLANG_OK; -} - -Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) -{ - Int rangeCount = desc.slotRangeCount; - - // For our purposes, there are three main cases of descriptor ranges to consider: - // - // 1. Resources: CBV, SRV, UAV - // - // 2. Samplers - // - // 3. Combined texture/sampler pairs - // - // The combined case presents challenges, because we will implement - // them as both a resource slot and a sampler slot, and for conveience - // in the indexing logic, it would be nice it they "lined up." - // - // We will start by counting how many ranges, and how many - // descriptors, of each type we have. - // - - Int dedicatedResourceCount = 0; - Int dedicatedSamplerCount = 0; - Int combinedCount = 0; - - Int dedicatedResourceRangeCount = 0; - Int dedicatedSamplerRangeCount = 0; - Int combinedRangeCount = 0; - - for(Int rr = 0; rr < rangeCount; ++rr) - { - auto rangeDesc = desc.slotRanges[rr]; - switch(rangeDesc.type) - { - case DescriptorSlotType::Sampler: - dedicatedSamplerCount += rangeDesc.count; - dedicatedSamplerRangeCount++; - break; - - case DescriptorSlotType::CombinedImageSampler: - combinedCount += rangeDesc.count; - combinedRangeCount++; - break; - - default: - dedicatedResourceCount += rangeDesc.count; - dedicatedResourceRangeCount++; - break; - } - } - - // Now we know how many ranges we have to allocate space for, - // and also how they need to be arranged. - // - // Each "combined" range will map to two ranges in the D3D - // descriptor tables. - - RefPtr descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(); - - // We know the total number of resource and sampler "slots" that an instance - // of this descriptor-set layout would need: - // - descriptorSetLayoutImpl->m_resourceCount = combinedCount + dedicatedResourceCount; - descriptorSetLayoutImpl->m_samplerCount = combinedCount + dedicatedSamplerCount; - - // We can start by allocating the D3D root parameter info needed for the - // descriptor set, based on the total number or ranges we need, which - // we can compute from the combined and dedicated counts: - // - Int totalResourceRangeCount = combinedRangeCount + dedicatedResourceRangeCount; - Int totalSamplerRangeCount = combinedRangeCount + dedicatedSamplerRangeCount; - - if( totalResourceRangeCount ) - { - D3D12_ROOT_PARAMETER dxRootParameter = {}; - dxRootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - dxRootParameter.DescriptorTable.NumDescriptorRanges = UINT(totalResourceRangeCount); - descriptorSetLayoutImpl->m_dxRootParameters.add(dxRootParameter); - } - if( totalSamplerRangeCount ) - { - D3D12_ROOT_PARAMETER dxRootParameter = {}; - dxRootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - dxRootParameter.DescriptorTable.NumDescriptorRanges = UINT(totalSamplerRangeCount); - descriptorSetLayoutImpl->m_dxRootParameters.add(dxRootParameter); - } - - // Next we can allocate space for all the D3D register ranges we need, - // again based on totals that we can compute easily: - // - Int totalRangeCount = totalResourceRangeCount + totalSamplerRangeCount; - descriptorSetLayoutImpl->m_dxRanges.setCount(totalRangeCount); - - // Now we will walk through the ranges in the order they were - // specified, so that we can fill in the "range info" required for - // binding parameters into descriptor sets allocated with this layout. - // - // This effectively determines the space required in two arrays - // in each descriptor set: one for resources, and one for samplers. - // A "combined" descriptor requires space in both arrays. The entries - // for "dedicated" samplers/resources always come after those for - // "combined" descriptors in the same array, so that a single index - // can be used for both arrays in the combined case. - // - - { - Int samplerCounter = 0; - Int resourceCounter = 0; - Int combinedCounter = 0; - for(Int rr = 0; rr < rangeCount; ++rr) - { - auto rangeDesc = desc.slotRanges[rr]; - - DescriptorSetLayoutImpl::RangeInfo rangeInfo; - - rangeInfo.type = rangeDesc.type; - rangeInfo.count = rangeDesc.count; - - switch(rangeDesc.type) - { - default: - // Default case is a dedicated resource, and its index in the - // resource array will come after all the combined entries. - rangeInfo.arrayIndex = combinedCount + resourceCounter; - resourceCounter += rangeInfo.count; - break; - - case DescriptorSlotType::Sampler: - // A dedicated sampler comes after all the entries for - // combined texture/samplers in the sampler array. - rangeInfo.arrayIndex = combinedCount + samplerCounter; - samplerCounter += rangeInfo.count; - break; - - case DescriptorSlotType::CombinedImageSampler: - // Combined descriptors take entries at the front of - // the resource and sampler arrays. - rangeInfo.arrayIndex = combinedCounter; - combinedCounter += rangeInfo.count; - break; - } - - descriptorSetLayoutImpl->m_ranges.add(rangeInfo); - } - } - - // Finally, we will go through and fill in ready-to-go D3D - // register range information. - { - UInt cbvCounter = 0; - UInt srvCounter = 0; - UInt uavCounter = 0; - UInt samplerCounter = 0; - - Int resourceRangeCounter = 0; - Int samplerRangeCounter = 0; - Int combinedRangeCounter = 0; - - for(Int rr = 0; rr < rangeCount; ++rr) - { - auto rangeDesc = desc.slotRanges[rr]; - Int bindingCount = rangeDesc.count; - - // All of these descriptor ranges will be initialized - // with a "space" of zero, with the assumption that - // the actual space number will come from when they are - // used as part of a pipeline layout. - // - Int bindingSpace = 0; - - Int dxRangeIndex = -1; - Int dxPairedSamplerRangeIndex = -1; - - switch(rangeDesc.type) - { - default: - // Default case is a dedicated resource, and its index in the - // resource array will come after all the combined entries. - dxRangeIndex = combinedRangeCount + resourceRangeCounter; - resourceRangeCounter++; - break; - - case DescriptorSlotType::Sampler: - // A dedicated sampler comes after all the entries for - // combined texture/samplers in the sampler array. - dxRangeIndex = totalResourceRangeCount + combinedRangeCount + samplerRangeCounter; - samplerRangeCounter++; - break; - - case DescriptorSlotType::CombinedImageSampler: - // Combined descriptors take entries at the front of - // the resource and sampler arrays. - dxRangeIndex = combinedRangeCounter; - dxPairedSamplerRangeIndex = totalResourceRangeCount + combinedRangeCounter; - combinedRangeCounter++; - break; - } - - D3D12_DESCRIPTOR_RANGE& dxRange = descriptorSetLayoutImpl->m_dxRanges[dxRangeIndex]; - memset(&dxRange, 0, sizeof(dxRange)); - - switch(rangeDesc.type) - { - default: - // ERROR: unsupported slot type. - break; - - case DescriptorSlotType::Sampler: - { - UInt bindingIndex = samplerCounter; samplerCounter += bindingCount; - - dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; - dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); - dxRange.RegisterSpace = UINT(bindingSpace); - dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - break; - - case DescriptorSlotType::SampledImage: - case DescriptorSlotType::UniformTexelBuffer: - { - UInt bindingIndex = srvCounter; srvCounter += bindingCount; - - dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); - dxRange.RegisterSpace = UINT(bindingSpace); - dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - break; - - case DescriptorSlotType::CombinedImageSampler: - { - // The combined texture/sampler case basically just - // does the work of both the SRV and sampler cases above. - - { - // Here's the SRV logic: - - UInt bindingIndex = srvCounter; srvCounter += bindingCount; - - dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); - dxRange.RegisterSpace = UINT(bindingSpace); - dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - - { - // And here we do the sampler logic at the "paired" index. - D3D12_DESCRIPTOR_RANGE& dxPairedSamplerRange = descriptorSetLayoutImpl->m_dxRanges[dxPairedSamplerRangeIndex]; - memset(&dxPairedSamplerRange, 0, sizeof(dxPairedSamplerRange)); - - UInt pairedSamplerBindingIndex = srvCounter; srvCounter += bindingCount; - - dxPairedSamplerRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; - dxPairedSamplerRange.NumDescriptors = UINT(bindingCount); - dxPairedSamplerRange.BaseShaderRegister = UINT(pairedSamplerBindingIndex); - dxPairedSamplerRange.RegisterSpace = UINT(bindingSpace); - dxPairedSamplerRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - - } - break; - - - case DescriptorSlotType::InputAttachment: - case DescriptorSlotType::StorageImage: - case DescriptorSlotType::StorageTexelBuffer: - case DescriptorSlotType::StorageBuffer: - case DescriptorSlotType::DynamicStorageBuffer: - { - UInt bindingIndex = uavCounter; uavCounter += bindingCount; - - dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); - dxRange.RegisterSpace = UINT(bindingSpace); - dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - break; - - case DescriptorSlotType::UniformBuffer: - case DescriptorSlotType::DynamicUniformBuffer: - { - UInt bindingIndex = cbvCounter; cbvCounter += bindingCount; - - dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; - dxRange.NumDescriptors = UINT(bindingCount); - dxRange.BaseShaderRegister = UINT(bindingIndex); - dxRange.RegisterSpace = UINT(bindingSpace); - dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - break; - } - } - } - - *outLayout = descriptorSetLayoutImpl.detach(); - return SLANG_OK; -} - -Result D3D12Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) -{ - static const UInt kMaxRanges = 16; - static const UInt kMaxRootParameters = 32; - - D3D12_DESCRIPTOR_RANGE ranges[kMaxRanges]; - D3D12_ROOT_PARAMETER rootParameters[kMaxRootParameters]; - - UInt rangeCount = 0; - UInt rootParameterCount = 0; - - auto descriptorSetCount = desc.descriptorSetCount; - - // We are going to make two passes over the descriptor set layouts - // that are being used to build the pipeline layout. In the first - // pass we will collect all the descriptor ranges that have been - // specified, applying an offset to their register spaces as needed. - // - for(UInt dd = 0; dd < descriptorSetCount; ++dd) - { - auto& descriptorSetInfo = desc.descriptorSets[dd]; - auto descriptorSetLayout = (DescriptorSetLayoutImpl*) descriptorSetInfo.layout; - - // For now we assume that the register space used for - // logical descriptor set #N will be space N. - // - // TODO: This might need to be revisited in the future because - // a single logical descriptor set might need to encompass stuff - // that comes from multiple spaces (e.g., if it contains an unbounded - // array). - // - UInt bindingSpace = dd; - - // Copy descriptor range information from the set layout into our - // temporary copy (this is required because the same set layout - // might be applied to different ranges). - // - // API design note: this copy step could be avoided if the D3D - // API allowed for a "space offset" to be applied as part of - // a descriptor-table root parameter. - // - for(auto setDescriptorRange : descriptorSetLayout->m_dxRanges) - { - auto& range = ranges[rangeCount++]; - range = setDescriptorRange; - range.RegisterSpace = UINT(bindingSpace); - - // HACK: in order to deal with SM5.0 shaders, `u` registers - // in `space0` need to start with a number *after* the number - // of `SV_Target` outputs that will be used. - // - // TODO: This is clearly a mess, and doing this behavior here - // means it *won't* work for SM5.1 where the restriction is - // lifted. The only real alternative is to rely on explicit - // register numbers (e.g., from shader reflection) but that - // goes against the simplicity that this API layer strives for - // (everything so far has been set up to work correctly with - // automatic assignment of bindings). - // - if( range.RegisterSpace == 0 - && range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV ) - { - range.BaseShaderRegister += UINT(desc.renderTargetCount); - } - } - } - - // In our second pass, we will copy over root parameters, which - // may end up pointing into the list of ranges from the first step. - // - auto rangePtr = &ranges[0]; - for(UInt dd = 0; dd < descriptorSetCount; ++dd) - { - auto& descriptorSetInfo = desc.descriptorSets[dd]; - auto descriptorSetLayout = (DescriptorSetLayoutImpl*) descriptorSetInfo.layout; - - // Copy root parameter information from the set layout to our - // overall pipeline layout. - for( auto setRootParameter : descriptorSetLayout->m_dxRootParameters ) - { - auto& rootParameter = rootParameters[rootParameterCount++]; - rootParameter = setRootParameter; - - // In the case where this parameter is a descriptor table, it - // needs to point into our array of ranges (with offsets applied), - // so we will fix up those pointers here. - // - if(rootParameter.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) - { - rootParameter.DescriptorTable.pDescriptorRanges = rangePtr; - rangePtr += rootParameter.DescriptorTable.NumDescriptorRanges; - } - } - } - - D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {}; - rootSignatureDesc.NumParameters = UINT(rootParameterCount); - rootSignatureDesc.pParameters = rootParameters; - - // TODO: static samplers should be reasonably easy to support... - rootSignatureDesc.NumStaticSamplers = 0; - rootSignatureDesc.pStaticSamplers = nullptr; - - // TODO: only set this flag if needed (requires creating root - // signature at same time as pipeline state...). - // - rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; - - ComPtr signature; - ComPtr error; - if( SLANG_FAILED(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef())) ) - { - fprintf(stderr, "error: D3D12SerializeRootSignature failed"); - if( error ) - { - fprintf(stderr, ": %s\n", (const char*) error->GetBufferPointer()); - } - return SLANG_FAIL; - } - - ComPtr rootSignature; - SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef()))); - - - RefPtr pipelineLayoutImpl = new PipelineLayoutImpl(); - pipelineLayoutImpl->m_rootSignature = rootSignature; - pipelineLayoutImpl->m_descriptorSetCount = descriptorSetCount; - *outLayout = pipelineLayoutImpl.detach(); - return SLANG_OK; -} - -Result D3D12Renderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) -{ - auto layoutImpl = (DescriptorSetLayoutImpl*) layout; - - RefPtr descriptorSetImpl = new DescriptorSetImpl(); - descriptorSetImpl->m_renderer = this; - descriptorSetImpl->m_layout = layoutImpl; - - // We allocate CPU-visible descriptor tables to providing the - // backing storage for each descriptor set. GPU-visible storage - // will only be allocated as needed during per-frame logic in - // order to ensure that a descriptor set it available for use - // in rendering. - // - Int resourceCount = layoutImpl->m_resourceCount; - if( resourceCount ) - { - auto resourceHeap = &m_cpuViewHeap; - descriptorSetImpl->m_resourceHeap = resourceHeap; - descriptorSetImpl->m_resourceTable = resourceHeap->allocate(int(resourceCount)); - descriptorSetImpl->m_resourceObjects.setCount(resourceCount); - } - - Int samplerCount = layoutImpl->m_samplerCount; - if( samplerCount ) - { - auto samplerHeap = &m_cpuSamplerHeap; - descriptorSetImpl->m_samplerHeap = samplerHeap; - descriptorSetImpl->m_samplerTable = samplerHeap->allocate(int(samplerCount)); - descriptorSetImpl->m_samplerObjects.setCount(samplerCount); - } - - *outDescriptorSet = descriptorSetImpl.detach(); - return SLANG_OK; -} - -Result D3D12Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) -{ - auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; - auto programImpl = (ShaderProgramImpl*) desc.program; - auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; - - // Describe and create the graphics pipeline state object (PSO) - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; - - psoDesc.pRootSignature = pipelineLayoutImpl->m_rootSignature; - - psoDesc.VS = { programImpl->m_vertexShader.getBuffer(), SIZE_T(programImpl->m_vertexShader.getCount()) }; - psoDesc.PS = { programImpl->m_pixelShader .getBuffer(), SIZE_T(programImpl->m_pixelShader .getCount()) }; - - psoDesc.InputLayout = { inputLayoutImpl->m_elements.getBuffer(), UINT(inputLayoutImpl->m_elements.getCount()) }; - psoDesc.PrimitiveTopologyType = m_primitiveTopologyType; - - { - const int numRenderTargets = int(desc.renderTargetCount); - - psoDesc.DSVFormat = m_depthStencilFormat; - psoDesc.NumRenderTargets = numRenderTargets; - for (Int i = 0; i < numRenderTargets; i++) - { - psoDesc.RTVFormats[i] = m_targetFormat; - } - - psoDesc.SampleDesc.Count = 1; - psoDesc.SampleDesc.Quality = 0; - - psoDesc.SampleMask = UINT_MAX; - } - - { - auto& rs = psoDesc.RasterizerState; - rs.FillMode = D3D12_FILL_MODE_SOLID; - rs.CullMode = D3D12_CULL_MODE_NONE; - rs.FrontCounterClockwise = FALSE; - rs.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; - rs.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; - rs.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; - rs.DepthClipEnable = TRUE; - rs.MultisampleEnable = FALSE; - rs.AntialiasedLineEnable = FALSE; - rs.ForcedSampleCount = 0; - rs.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; - } - - { - D3D12_BLEND_DESC& blend = psoDesc.BlendState; - - blend.AlphaToCoverageEnable = FALSE; - blend.IndependentBlendEnable = FALSE; - const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = - { - FALSE,FALSE, - D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, - D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, - D3D12_LOGIC_OP_NOOP, - D3D12_COLOR_WRITE_ENABLE_ALL, - }; - for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) - { - blend.RenderTarget[i] = defaultRenderTargetBlendDesc; - } - } - - { - auto& ds = psoDesc.DepthStencilState; - - ds.DepthEnable = FALSE; - ds.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; - ds.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; - //ds.DepthFunc = D3D12_COMPARISON_FUNC_LESS; - ds.StencilEnable = FALSE; - ds.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; - ds.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; - const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = - { - D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS - }; - ds.FrontFace = defaultStencilOp; - ds.BackFace = defaultStencilOp; - } - - psoDesc.PrimitiveTopologyType = m_primitiveTopologyType; - - ComPtr pipelineState; - SLANG_RETURN_ON_FAIL(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(pipelineState.writeRef()))); - - RefPtr pipelineStateImpl = new PipelineStateImpl(); - pipelineStateImpl->m_pipelineType = PipelineType::Graphics; - pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; - pipelineStateImpl->m_pipelineState = pipelineState; - *outState = pipelineStateImpl.detach(); - return SLANG_OK; -} - -Result D3D12Renderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) -{ - auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; - auto programImpl = (ShaderProgramImpl*) desc.program; - - // Describe and create the compute pipeline state object - D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {}; - computeDesc.pRootSignature = pipelineLayoutImpl->m_rootSignature; - computeDesc.CS = { programImpl->m_computeShader.getBuffer(), SIZE_T(programImpl->m_computeShader.getCount()) }; - - ComPtr pipelineState; - SLANG_RETURN_ON_FAIL(m_device->CreateComputePipelineState(&computeDesc, IID_PPV_ARGS(pipelineState.writeRef()))); - - RefPtr pipelineStateImpl = new PipelineStateImpl(); - pipelineStateImpl->m_pipelineType = PipelineType::Compute; - pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; - pipelineStateImpl->m_pipelineState = pipelineState; - *outState = pipelineStateImpl.detach(); - return SLANG_OK; -} - -} // renderer_test diff --git a/tools/gfx/render-d3d12.h b/tools/gfx/render-d3d12.h deleted file mode 100644 index b8a3104c0..000000000 --- a/tools/gfx/render-d3d12.h +++ /dev/null @@ -1,10 +0,0 @@ -// render-d3d12.h -#pragma once - -namespace gfx { - -class Renderer; - -Renderer* createD3D12Renderer(); - -} // gfx diff --git a/tools/gfx/render-gl.cpp b/tools/gfx/render-gl.cpp deleted file mode 100644 index 975b4e140..000000000 --- a/tools/gfx/render-gl.cpp +++ /dev/null @@ -1,1504 +0,0 @@ -// render-gl.cpp -#include "render-gl.h" - -//WORKING:#include "options.h" -#include "render.h" - -#include -#include -#include "core/slang-basic.h" -#include "core/slang-secure-crt.h" -#include "external/stb/stb_image_write.h" - -#include "surface.h" - -// TODO(tfoley): eventually we should be able to run these -// tests on non-Windows targets to confirm that cross-compilation -// at least *works* on those platforms... -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#ifdef _MSC_VER -#include -#if (_MSC_VER < 1900) -#define snprintf sprintf_s -#endif -#endif - -#pragma comment(lib, "opengl32") - -#include -#include "external/glext.h" - -// We define an "X-macro" for mapping over loadable OpenGL -// extension entry point that we will use, so that we can -// easily write generic code to iterate over them. -#define MAP_GL_EXTENSION_FUNCS(F) \ - F(glCreateProgram, PFNGLCREATEPROGRAMPROC) \ - F(glCreateShader, PFNGLCREATESHADERPROC) \ - F(glShaderSource, PFNGLSHADERSOURCEPROC) \ - F(glCompileShader, PFNGLCOMPILESHADERPROC) \ - F(glGetShaderiv, PFNGLGETSHADERIVPROC) \ - F(glDeleteShader, PFNGLDELETESHADERPROC) \ - F(glAttachShader, PFNGLATTACHSHADERPROC) \ - F(glLinkProgram, PFNGLLINKPROGRAMPROC) \ - F(glGetProgramiv, PFNGLGETPROGRAMIVPROC) \ - F(glGetProgramInfoLog, PFNGLGETPROGRAMINFOLOGPROC) \ - F(glDeleteProgram, PFNGLDELETEPROGRAMPROC) \ - F(glGetShaderInfoLog, PFNGLGETSHADERINFOLOGPROC) \ - F(glGenBuffers, PFNGLGENBUFFERSPROC) \ - F(glBindBuffer, PFNGLBINDBUFFERPROC) \ - F(glBufferData, PFNGLBUFFERDATAPROC) \ - F(glDeleteBuffers, PFNGLDELETEBUFFERSPROC) \ - F(glMapBuffer, PFNGLMAPBUFFERPROC) \ - F(glUnmapBuffer, PFNGLUNMAPBUFFERPROC) \ - F(glUseProgram, PFNGLUSEPROGRAMPROC) \ - F(glBindBufferBase, PFNGLBINDBUFFERBASEPROC) \ - F(glVertexAttribPointer, PFNGLVERTEXATTRIBPOINTERPROC) \ - F(glEnableVertexAttribArray, PFNGLENABLEVERTEXATTRIBARRAYPROC) \ - F(glDisableVertexAttribArray, PFNGLDISABLEVERTEXATTRIBARRAYPROC) \ - F(glDebugMessageCallback, PFNGLDEBUGMESSAGECALLBACKPROC) \ - F(glDispatchCompute, PFNGLDISPATCHCOMPUTEPROC) \ - F(glActiveTexture, PFNGLACTIVETEXTUREPROC) \ - F(glCreateSamplers, PFNGLCREATESAMPLERSPROC) \ - F(glDeleteSamplers, PFNGLDELETESAMPLERSPROC) \ - F(glBindSampler, PFNGLBINDSAMPLERPROC) \ - F(glTexImage3D, PFNGLTEXIMAGE3DPROC) \ - F(glSamplerParameteri, PFNGLSAMPLERPARAMETERIPROC) \ - /* end */ - -using namespace Slang; - -namespace gfx { - -class GLRenderer : public Renderer -{ -public: - - // Renderer implementation - virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; - virtual const List& getFeatures() override { return m_features; } - virtual void setClearColor(const float color[4]) override; - virtual void clearFrame() override; - virtual void presentFrame() override; - TextureResource::Desc getSwapChainTextureDesc() override; - - Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; - Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; - Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; - - Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; - Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; - - Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; - - Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; - Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; - Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; - - Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; - Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; - Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; - - virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; - - virtual void* map(BufferResource* buffer, MapFlavor flavor) override; - virtual void unmap(BufferResource* buffer) override; - virtual void setPrimitiveTopology(PrimitiveTopology topology) override; - - virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; - - virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; - virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; - virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; - void setViewports(UInt count, Viewport const* viewports) override; - void setScissorRects(UInt count, ScissorRect const* rects) override; - virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; - virtual void draw(UInt vertexCount, UInt startVertex) override; - virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; - virtual void dispatchCompute(int x, int y, int z) override; - virtual void submitGpuWork() override {} - virtual void waitForGpu() override {} - virtual RendererType getRendererType() const override { return RendererType::OpenGl; } - - GLRenderer(); - ~GLRenderer(); - - protected: - enum - { - kMaxVertexStreams = 16, - kMaxDescriptorSetCount = 8, - }; - struct VertexAttributeFormat - { - GLint componentCount; - GLenum componentType; - GLboolean normalized; - }; - - struct VertexAttributeDesc - { - VertexAttributeFormat format; - GLuint streamIndex; - GLsizei offset; - }; - - class InputLayoutImpl: public InputLayout - { - public: - VertexAttributeDesc m_attributes[kMaxVertexStreams]; - UInt m_attributeCount = 0; - }; - - class BufferResourceImpl: public BufferResource - { - public: - typedef BufferResource Parent; - - BufferResourceImpl(Usage initialUsage, const Desc& desc, WeakSink* renderer, GLuint id, GLenum target): - Parent(desc), - m_renderer(renderer), - m_handle(id), - m_initialUsage(initialUsage), - m_target(target) - {} - ~BufferResourceImpl() - { - if (auto renderer = m_renderer->get()) - { - renderer->glDeleteBuffers(1, &m_handle); - } - } - - Usage m_initialUsage; - RefPtr > m_renderer; - GLuint m_handle; - GLenum m_target; - }; - - class TextureResourceImpl: public TextureResource - { - public: - typedef TextureResource Parent; - - TextureResourceImpl(Usage initialUsage, const Desc& desc, WeakSink* renderer): - Parent(desc), - m_initialUsage(initialUsage), - m_renderer(renderer) - { - m_target = 0; - m_handle = 0; - } - - ~TextureResourceImpl() - { - if (m_handle) - { - glDeleteTextures(1, &m_handle); - } - } - - Usage m_initialUsage; - RefPtr > m_renderer; - GLenum m_target; - GLuint m_handle; - }; - - class SamplerStateImpl : public SamplerState - { - public: - GLuint m_samplerID; - }; - - class ResourceViewImpl : public ResourceView - { - }; - - class TextureViewImpl : public ResourceViewImpl - { - public: - RefPtr m_resource; - GLuint m_textureID; - }; - - class BufferViewImpl : public ResourceViewImpl - { - public: - RefPtr m_resource; - GLuint m_bufferID; - }; - - enum class GLDescriptorSlotType - { - ConstantBuffer, - CombinedTextureSampler, - - CountOf, - }; - - class DescriptorSetLayoutImpl : public DescriptorSetLayout - { - public: - struct RangeInfo - { - GLDescriptorSlotType type; - UInt arrayIndex; - }; - List m_ranges; - Int m_counts[int(GLDescriptorSlotType::CountOf)]; - }; - - class PipelineLayoutImpl : public PipelineLayout - { - public: - struct DescriptorSetInfo - { - RefPtr layout; - UInt baseArrayIndex[int(GLDescriptorSlotType::CountOf)]; - }; - - List m_sets; - }; - - class DescriptorSetImpl : public DescriptorSet - { - public: - virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; - virtual void setResource(UInt range, UInt index, ResourceView* view) override; - virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; - virtual void setCombinedTextureSampler( - UInt range, - UInt index, - ResourceView* textureView, - SamplerState* sampler) override; - - RefPtr m_layout; - List> m_constantBuffers; - List> m_textures; - List> m_samplers; - }; - - class ShaderProgramImpl : public ShaderProgram - { - public: - ShaderProgramImpl(WeakSink* renderer, GLuint id): - m_renderer(renderer), - m_id(id) - { - } - ~ShaderProgramImpl() - { - if (auto renderer = m_renderer->get()) - { - renderer->glDeleteProgram(m_id); - } - } - - GLuint m_id; - RefPtr > m_renderer; - }; - - class PipelineStateImpl : public PipelineState - { - public: - RefPtr m_program; - RefPtr m_pipelineLayout; - RefPtr m_inputLayout; - }; - - enum class GlPixelFormat - { - Unknown, - RGBA_Unorm_UInt8, - CountOf, - }; - - struct GlPixelFormatInfo - { - GLint internalFormat; // such as GL_RGBA8 - GLenum format; // such as GL_RGBA - GLenum formatType; // such as GL_UNSIGNED_BYTE - }; - -// void destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details); - - void bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets); - void flushStateForDraw(); - GLuint loadShader(GLenum stage, char const* source); - void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message); - - /// Returns GlPixelFormat::Unknown if not an equivalent - static GlPixelFormat _getGlPixelFormat(Format format); - - static void APIENTRY staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); - static VertexAttributeFormat getVertexAttributeFormat(Format format); - - static void compileTimeAsserts(); - - HDC m_hdc; - HGLRC m_glContext; - float m_clearColor[4] = { 0, 0, 0, 0 }; - - RefPtr m_currentPipelineState; - RefPtr > m_weakRenderer; - - RefPtr m_boundDescriptorSets[kMaxDescriptorSetCount]; - - GLenum m_boundPrimitiveTopology = GL_TRIANGLES; - GLuint m_boundVertexStreamBuffers[kMaxVertexStreams]; - UInt m_boundVertexStreamStrides[kMaxVertexStreams]; - UInt m_boundVertexStreamOffsets[kMaxVertexStreams]; - - Desc m_desc; - - List m_features; - - // Declare a function pointer for each OpenGL - // extension function we need to load -#define DECLARE_GL_EXTENSION_FUNC(NAME, TYPE) TYPE NAME; - MAP_GL_EXTENSION_FUNCS(DECLARE_GL_EXTENSION_FUNC) -#undef DECLARE_GL_EXTENSION_FUNC - - static const GlPixelFormatInfo s_pixelFormatInfos[]; /// Maps GlPixelFormat to a format info -}; - -/* static */GLRenderer::GlPixelFormat GLRenderer::_getGlPixelFormat(Format format) -{ - switch (format) - { - case Format::RGBA_Unorm_UInt8: return GlPixelFormat::RGBA_Unorm_UInt8; - default: return GlPixelFormat::Unknown; - } -} - -/* static */ const GLRenderer::GlPixelFormatInfo GLRenderer::s_pixelFormatInfos[] = -{ - // internalType, format, formatType - { 0, 0, 0}, // GlPixelFormat::Unknown - { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, // GlPixelFormat::RGBA_Unorm_UInt8 -}; - -/* static */void GLRenderer::compileTimeAsserts() -{ - SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_pixelFormatInfos) == int(GlPixelFormat::CountOf)); -} - -Renderer* createGLRenderer() -{ - return new GLRenderer(); -} - -void GLRenderer::debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message) -{ - ::OutputDebugStringA("GL: "); - ::OutputDebugStringA(message); - ::OutputDebugStringA("\n"); - - switch (type) - { - case GL_DEBUG_TYPE_ERROR: - break; - default: - break; - } -} - - -GLRenderer::GLRenderer() -{ - m_weakRenderer = new WeakSink(this); -} - -GLRenderer::~GLRenderer() -{ - // We can destroy things whilst in this state - m_currentPipelineState.setNull(); - - // By resetting the weak pointer, other objects accessing through WeakSink will no longer - // be able to access this object which is entering a 'being destroyed' to 'destroyed' state - if (m_weakRenderer) - { - SLANG_ASSERT(m_weakRenderer->get() == this); - m_weakRenderer->detach(); - } -} - -/* static */void APIENTRY GLRenderer::staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) -{ - ((GLRenderer*)userParam)->debugCallback(source, type, id, severity, length, message); -} - -/* static */GLRenderer::VertexAttributeFormat GLRenderer::getVertexAttributeFormat(Format format) -{ - switch (format) - { - default: assert(!"unexpected"); return VertexAttributeFormat(); - -#define CASE(NAME, COUNT, TYPE, NORMALIZED) \ - case Format::NAME: do { VertexAttributeFormat result = {COUNT, TYPE, NORMALIZED}; return result; } while (0) - - CASE(RGBA_Float32, 4, GL_FLOAT, GL_FALSE); - CASE(RGB_Float32, 3, GL_FLOAT, GL_FALSE); - CASE(RG_Float32, 2, GL_FLOAT, GL_FALSE); - CASE(R_Float32, 1, GL_FLOAT, GL_FALSE); -#undef CASE - } -} - -void GLRenderer::bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) -{ - for (UInt ii = 0; ii < slotCount; ++ii) - { - UInt slot = startSlot + ii; - - BufferResourceImpl* buffer = static_cast(buffers[ii]); - GLuint bufferID = buffer ? buffer->m_handle : 0; - - assert(!offsets || !offsets[ii]); - - glBindBufferBase(target, (GLuint)slot, bufferID); - } -} - -void GLRenderer::flushStateForDraw() -{ - auto inputLayout = m_currentPipelineState->m_inputLayout.Ptr(); - auto attrCount = Index(inputLayout->m_attributeCount); - for (Index ii = 0; ii < attrCount; ++ii) - { - auto& attr = inputLayout->m_attributes[ii]; - - auto streamIndex = attr.streamIndex; - - glBindBuffer(GL_ARRAY_BUFFER, m_boundVertexStreamBuffers[streamIndex]); - - glVertexAttribPointer( - (GLuint)ii, - attr.format.componentCount, - attr.format.componentType, - attr.format.normalized, - (GLsizei)m_boundVertexStreamStrides[streamIndex], - (GLvoid*)(attr.offset + m_boundVertexStreamOffsets[streamIndex])); - - glEnableVertexAttribArray((GLuint)ii); - } - for (Index ii = attrCount; ii < kMaxVertexStreams; ++ii) - { - glDisableVertexAttribArray((GLuint)ii); - } - - // Next bind the descriptor sets as required by the layout - auto pipelineLayout = m_currentPipelineState->m_pipelineLayout; - auto descriptorSetCount = pipelineLayout->m_sets.getCount(); - for(Index ii = 0; ii < descriptorSetCount; ++ii) - { - auto descriptorSet = m_boundDescriptorSets[ii]; - auto descriptorSetInfo = pipelineLayout->m_sets[ii]; - auto descriptorSetLayout = descriptorSetInfo.layout; - - // TODO: need to validate that `descriptorSet->m_layout` matches - // `descriptorSetLayout`. - - { - // First we will bind any uniform buffers that were specified. - - auto slotTypeIndex = int(GLDescriptorSlotType::ConstantBuffer); - auto count = descriptorSetLayout->m_counts[slotTypeIndex]; - auto baseIndex = descriptorSetInfo.baseArrayIndex[slotTypeIndex]; - - for(Int ii = 0; ii < count; ++ii) - { - auto bufferImpl = descriptorSet->m_constantBuffers[ii]; - glBindBufferBase(GL_UNIFORM_BUFFER, GLuint(ii), bufferImpl->m_handle); - } - } - - - { - // Next we will bind any combined texture/sampler slots. - - auto slotTypeIndex = int(GLDescriptorSlotType::CombinedTextureSampler); - auto count = descriptorSetLayout->m_counts[slotTypeIndex]; - auto baseIndex = descriptorSetInfo.baseArrayIndex[slotTypeIndex]; - - // TODO: We should be able to use a single call to glBindTextures here, - // rather than a loop. This would also eliminate the need to retain - // the appropriate target (e.g., `GL_TEXTURE_2D` for binding). - - for(Int ii = 0; ii < count; ++ii) - { - auto textureViewImpl = descriptorSet->m_textures[ii]; - auto samplerImpl = descriptorSet->m_samplers[ii]; - - glActiveTexture(GLuint(GL_TEXTURE0 + ii)); - glBindTexture(GL_TEXTURE_2D, textureViewImpl->m_textureID); - - glBindSampler(GLuint(baseIndex + ii), samplerImpl->m_samplerID); - } - } - } -} - -GLuint GLRenderer::loadShader(GLenum stage, const char* source) -{ - // GLSL is monumentally stupid. It officially requires the `#version` directive - // to be the first thing in the file, which wouldn't be so bad but the API - // doesn't provide a way to pass a `#define` into your shader other than by - // prepending it to the whole thing. - // - // We are going to solve this problem by doing some surgery on the source - // that was passed in. - - const char* sourceBegin = source; - const char* sourceEnd = source + strlen(source); - - // Look for a version directive in the user-provided source. - const char* versionBegin = strstr(source, "#version"); - const char* versionEnd = nullptr; - if (versionBegin) - { - // If we found a directive, then scan for the end-of-line - // after it, and use that to specify the slice. - versionEnd = strchr(versionBegin, '\n'); - if (!versionEnd) - { - versionEnd = sourceEnd; - } - else - { - versionEnd = versionEnd + 1; - } - } - else - { - // If we didn't find a directive, then treat it as being - // a zero-byte slice at the start of the string - versionBegin = sourceBegin; - versionEnd = sourceBegin; - } - - enum { kMaxSourceStringCount = 16 }; - const GLchar* sourceStrings[kMaxSourceStringCount]; - GLint sourceStringLengths[kMaxSourceStringCount]; - - int sourceStringCount = 0; - - const char* stagePrelude = "\n"; - switch (stage) - { -#define CASE(NAME) case GL_##NAME##_SHADER: stagePrelude = "#define __GLSL_" #NAME "__ 1\n"; break - - CASE(VERTEX); - CASE(TESS_CONTROL); - CASE(TESS_EVALUATION); - CASE(GEOMETRY); - CASE(FRAGMENT); - CASE(COMPUTE); - -#undef CASE - } - - const char* prelude = - "#define __GLSL__ 1\n" - ; - -#define ADD_SOURCE_STRING_SPAN(BEGIN, END) \ - sourceStrings[sourceStringCount] = BEGIN; \ - sourceStringLengths[sourceStringCount++] = GLint(END - BEGIN) \ - /* end */ - -#define ADD_SOURCE_STRING(BEGIN) \ - sourceStrings[sourceStringCount] = BEGIN; \ - sourceStringLengths[sourceStringCount++] = GLint(strlen(BEGIN)) \ - /* end */ - - ADD_SOURCE_STRING_SPAN(versionBegin, versionEnd); - ADD_SOURCE_STRING(stagePrelude); - ADD_SOURCE_STRING(prelude); - ADD_SOURCE_STRING_SPAN(sourceBegin, versionBegin); - ADD_SOURCE_STRING_SPAN(versionEnd, sourceEnd); - - auto shaderID = glCreateShader(stage); - glShaderSource( - shaderID, - sourceStringCount, - &sourceStrings[0], - &sourceStringLengths[0]); - glCompileShader(shaderID); - - GLint success = GL_FALSE; - glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); - if (!success) - { - int maxSize = 0; - glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxSize); - - auto infoBuffer = (char*)malloc(maxSize); - - int infoSize = 0; - glGetShaderInfoLog(shaderID, maxSize, &infoSize, infoBuffer); - if (infoSize > 0) - { - fprintf(stderr, "%s", infoBuffer); - ::OutputDebugStringA(infoBuffer); - } - - glDeleteShader(shaderID); - return 0; - } - - return shaderID; -} - -#if 0 -void GLRenderer::destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details) -{ - const auto& bindings = desc.m_bindings; - const int numBindings = int(bindings.Count()); - for (int i = 0; i < numBindings; ++i) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - if (binding.bindingType == BindingType::Sampler && detail.m_samplerHandle != 0) - { - glDeleteSamplers(1, &detail.m_samplerHandle); - } - } -} -#endif - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! - -SlangResult GLRenderer::initialize(const Desc& desc, void* inWindowHandle) -{ - auto windowHandle = (HWND)inWindowHandle; - m_desc = desc; - - m_hdc = ::GetDC(windowHandle); - - PIXELFORMATDESCRIPTOR pixelFormatDesc = { sizeof(PIXELFORMATDESCRIPTOR) }; - pixelFormatDesc.nVersion = 1; - pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pixelFormatDesc.iPixelType = PFD_TYPE_RGBA; - pixelFormatDesc.cColorBits = 32; - pixelFormatDesc.cDepthBits = 24; - pixelFormatDesc.cStencilBits = 8; - pixelFormatDesc.iLayerType = PFD_MAIN_PLANE; - - int pixelFormatIndex = ChoosePixelFormat(m_hdc, &pixelFormatDesc); - SetPixelFormat(m_hdc, pixelFormatIndex, &pixelFormatDesc); - - m_glContext = wglCreateContext(m_hdc); - wglMakeCurrent(m_hdc, m_glContext); - - auto renderer = glGetString(GL_RENDERER); - - if (renderer && desc.adapter.getLength() > 0) - { - String lowerAdapter = desc.adapter.toLower(); - String lowerRenderer = String((const char*)renderer).toLower(); - - // The adapter is not available - if (lowerRenderer.indexOf(lowerAdapter) == Index(-1)) - { - return SLANG_E_NOT_AVAILABLE; - } - } - - auto extensions = glGetString(GL_EXTENSIONS); - - // Load each of our extension functions by name - -#define LOAD_GL_EXTENSION_FUNC(NAME, TYPE) NAME = (TYPE) wglGetProcAddress(#NAME); - MAP_GL_EXTENSION_FUNCS(LOAD_GL_EXTENSION_FUNC) -#undef LOAD_GL_EXTENSION_FUNC - - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - - glViewport(0, 0, desc.width, desc.height); - - if (glDebugMessageCallback) - { - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(staticDebugCallback, this); - } - - return SLANG_OK; -} - -void GLRenderer::setClearColor(const float color[4]) -{ - glClearColor(color[0], color[1], color[2], color[3]); -} - -void GLRenderer::clearFrame() -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); -} - -void GLRenderer::presentFrame() -{ - glFlush(); - ::SwapBuffers(m_hdc); -} - -TextureResource::Desc GLRenderer::getSwapChainTextureDesc() -{ - TextureResource::Desc desc; - desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1); - return desc; -} - -SlangResult GLRenderer::captureScreenSurface(Surface& surfaceOut) -{ - SLANG_RETURN_ON_FAIL(surfaceOut.allocate(m_desc.width, m_desc.height, Format::RGBA_Unorm_UInt8, 1, SurfaceAllocator::getMallocAllocator())); - glReadPixels(0, 0, m_desc.width, m_desc.height, GL_RGBA, GL_UNSIGNED_BYTE, surfaceOut.m_data); - surfaceOut.flipInplaceVertically(); - return SLANG_OK; -} - -Result GLRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) -{ - TextureResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - GlPixelFormat pixelFormat = _getGlPixelFormat(srcDesc.format); - if (pixelFormat == GlPixelFormat::Unknown) - { - return SLANG_FAIL; - } - - const GlPixelFormatInfo& info = s_pixelFormatInfos[int(pixelFormat)]; - - const GLint internalFormat = info.internalFormat; - const GLenum format = info.format; - const GLenum formatType = info.formatType; - - RefPtr texture(new TextureResourceImpl(initialUsage, srcDesc, m_weakRenderer)); - - GLenum target = 0; - GLuint handle = 0; - glGenTextures(1, &handle); - - const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); - - assert(initData); - assert(initData->numSubResources == srcDesc.numMipLevels * srcDesc.size.depth * effectiveArraySize); - - // Set on texture so will be freed if failure - texture->m_handle = handle; - const void*const*const data = initData->subResources; - - switch (srcDesc.type) - { - case Resource::Type::Texture1D: - { - if (srcDesc.arraySize > 0) - { - target = GL_TEXTURE_1D_ARRAY; - glBindTexture(target, handle); - - int slice = 0; - for (int i = 0; i < effectiveArraySize; i++) - { - for (int j = 0; j < srcDesc.numMipLevels; j++) - { - glTexImage2D(target, j, internalFormat, srcDesc.size.width, i, 0, format, formatType, data[slice++]); - } - } - } - else - { - target = GL_TEXTURE_1D; - glBindTexture(target, handle); - for (int i = 0; i < srcDesc.numMipLevels; i++) - { - glTexImage1D(target, i, internalFormat, srcDesc.size.width, 0, format, formatType, data[i]); - } - } - break; - } - case Resource::Type::TextureCube: - case Resource::Type::Texture2D: - { - if (srcDesc.arraySize > 0) - { - if (srcDesc.type == Resource::Type::TextureCube) - { - target = GL_TEXTURE_CUBE_MAP_ARRAY; - } - else - { - target = GL_TEXTURE_2D_ARRAY; - } - - glBindTexture(target, handle); - - int slice = 0; - for (int i = 0; i < effectiveArraySize; i++) - { - for (int j = 0; j < srcDesc.numMipLevels; j++) - { - glTexImage3D(target, j, internalFormat, srcDesc.size.width, srcDesc.size.height, slice, 0, format, formatType, data[slice++]); - } - } - } - else - { - if (srcDesc.type == Resource::Type::TextureCube) - { - target = GL_TEXTURE_CUBE_MAP; - glBindTexture(target, handle); - - int slice = 0; - for (int j = 0; j < 6; j++) - { - for (int i = 0; i < srcDesc.numMipLevels; i++) - { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[slice++]); - } - } - } - else - { - target = GL_TEXTURE_2D; - glBindTexture(target, handle); - for (int i = 0; i < srcDesc.numMipLevels; i++) - { - glTexImage2D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[i]); - } - } - } - break; - } - case Resource::Type::Texture3D: - { - target = GL_TEXTURE_3D; - glBindTexture(target, handle); - for (int i = 0; i < srcDesc.numMipLevels; i++) - { - glTexImage3D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, srcDesc.size.depth, 0, format, formatType, data[i]); - } - break; - } - default: - return SLANG_FAIL; - } - - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); - - // Assume regular sampling (might be superseded - if a combined sampler wanted) - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); - - texture->m_target = target; - - *outResource = texture.detach(); - return SLANG_OK; -} - -static GLenum _calcUsage(Resource::Usage usage) -{ - typedef Resource::Usage Usage; - switch (usage) - { - case Usage::ConstantBuffer: return GL_DYNAMIC_DRAW; - default: return GL_STATIC_READ; - } -} - -static GLenum _calcTarget(Resource::Usage usage) -{ - typedef Resource::Usage Usage; - switch (usage) - { - case Usage::ConstantBuffer: return GL_UNIFORM_BUFFER; - default: return GL_SHADER_STORAGE_BUFFER; - } -} - -Result GLRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) -{ - BufferResource::Desc desc(descIn); - desc.setDefaults(initialUsage); - - const GLenum target = _calcTarget(initialUsage); - // TODO: should derive from desc... - const GLenum usage = _calcUsage(initialUsage); - - GLuint bufferID = 0; - glGenBuffers(1, &bufferID); - glBindBuffer(target, bufferID); - - glBufferData(target, descIn.sizeInBytes, initData, usage); - - RefPtr resourceImpl = new BufferResourceImpl(initialUsage, desc, m_weakRenderer, bufferID, target); - *outResource = resourceImpl.detach(); - return SLANG_OK; -} - -Result GLRenderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) -{ - GLuint samplerID; - glCreateSamplers(1, &samplerID); - - RefPtr samplerImpl = new SamplerStateImpl(); - samplerImpl->m_samplerID = samplerID; - *outSampler = samplerImpl.detach(); - return SLANG_OK; -} - -Result GLRenderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) -{ - auto resourceImpl = (TextureResourceImpl*) texture; - - // TODO: actually do something? - - RefPtr viewImpl = new TextureViewImpl(); - viewImpl->m_resource = resourceImpl; - viewImpl->m_textureID = resourceImpl->m_handle; - *outView = viewImpl; - return SLANG_OK; -} - -Result GLRenderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) -{ - auto resourceImpl = (BufferResourceImpl*) buffer; - - // TODO: actually do something? - - RefPtr viewImpl = new BufferViewImpl(); - viewImpl->m_resource = resourceImpl; - viewImpl->m_bufferID = resourceImpl->m_handle; - *outView = viewImpl.detach(); - return SLANG_OK; -} - -Result GLRenderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) -{ - RefPtr inputLayout = new InputLayoutImpl; - - inputLayout->m_attributeCount = inputElementCount; - for (UInt ii = 0; ii < inputElementCount; ++ii) - { - auto& inputAttr = inputElements[ii]; - auto& glAttr = inputLayout->m_attributes[ii]; - - glAttr.streamIndex = 0; - glAttr.format = getVertexAttributeFormat(inputAttr.format); - glAttr.offset = (GLsizei)inputAttr.offset; - } - - *outLayout = inputLayout.detach(); - return SLANG_OK; -} - -void* GLRenderer::map(BufferResource* bufferIn, MapFlavor flavor) -{ - BufferResourceImpl* buffer = static_cast(bufferIn); - - //GLenum target = GL_UNIFORM_BUFFER; - - GLuint access = 0; - switch (flavor) - { - case MapFlavor::WriteDiscard: - case MapFlavor::HostWrite: - access = GL_WRITE_ONLY; - break; - case MapFlavor::HostRead: - access = GL_READ_ONLY; - break; - } - - glBindBuffer(buffer->m_target, buffer->m_handle); - - return glMapBuffer(buffer->m_target, access); -} - -void GLRenderer::unmap(BufferResource* bufferIn) -{ - BufferResourceImpl* buffer = static_cast(bufferIn); - glUnmapBuffer(buffer->m_target); -} - -void GLRenderer::setPrimitiveTopology(PrimitiveTopology topology) -{ - GLenum glTopology = 0; - switch (topology) - { -#define CASE(NAME, VALUE) case PrimitiveTopology::NAME: glTopology = VALUE; break - - CASE(TriangleList, GL_TRIANGLES); - -#undef CASE - } - m_boundPrimitiveTopology = glTopology; -} - -void GLRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) -{ - for (UInt ii = 0; ii < slotCount; ++ii) - { - UInt slot = startSlot + ii; - - BufferResourceImpl* buffer = static_cast(buffers[ii]); - GLuint bufferID = buffer ? buffer->m_handle : 0; - - m_boundVertexStreamBuffers[slot] = bufferID; - m_boundVertexStreamStrides[slot] = strides[ii]; - m_boundVertexStreamOffsets[slot] = offsets[ii]; - } -} - -void GLRenderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) -{ -} - -void GLRenderer::setDepthStencilTarget(ResourceView* depthStencilView) -{ -} - -void GLRenderer::setViewports(UInt count, Viewport const* viewports) -{ - assert(count == 1); - auto viewport = viewports[0]; - glViewport( - (GLint) viewport.originX, - (GLint) viewport.originY, - (GLsizei) viewport.extentX, - (GLsizei) viewport.extentY); - glDepthRange(viewport.minZ, viewport.maxZ); -} - -void GLRenderer::setScissorRects(UInt count, ScissorRect const* rects) -{ - assert(count <= 1); - if( count ) - { - // TODO: this isn't goign to be quite right because of the - // flipped coordinate system in GL. - // - // The best way around this is probably to *always* render - // things internally into textures with "flipped" conventions, - // and then only deal with the flipping as part of a final - // "present" step that copies to the primary back-buffer. - // - auto rect = rects[0]; - glScissor( - GLint(rect.minX), - GLint(rect.minY), - GLsizei(rect.maxX - rect.minX), - GLsizei(rect.maxY - rect.minY)); - - glEnable(GL_SCISSOR_TEST); - } - else - { - glDisable(GL_SCISSOR_TEST); - } -} - -void GLRenderer::setPipelineState(PipelineType pipelineType, PipelineState* state) -{ - auto pipelineStateImpl = (PipelineStateImpl*) state; - - m_currentPipelineState = pipelineStateImpl; - - auto program = pipelineStateImpl->m_program; - GLuint programID = program ? program->m_id : 0; - glUseProgram(programID); -} - -void GLRenderer::draw(UInt vertexCount, UInt startVertex = 0) -{ - flushStateForDraw(); - - glDrawArrays(m_boundPrimitiveTopology, (GLint)startVertex, (GLsizei)vertexCount); -} - -void GLRenderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) -{ - assert(!"unimplemented"); -} - -void GLRenderer::dispatchCompute(int x, int y, int z) -{ - glDispatchCompute(x, y, z); -} - -#if 0 -BindingState* GLRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc, this)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - auto& dstDetail = dstDetails[i]; - const auto& srcBinding = srcBindings[i]; - - - switch (srcBinding.bindingType) - { - case BindingType::Texture: - case BindingType::Buffer: - { - break; - } - case BindingType::CombinedTextureSampler: - { - assert(srcBinding.resource && srcBinding.resource->isTexture()); - TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; - - if (samplerDesc.isCompareSampler) - { - auto target = texture->m_target; - - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - } - break; - } - case BindingType::Sampler: - { - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; - - GLuint handle; - - glCreateSamplers(1, &handle); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, GL_REPEAT); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, GL_REPEAT); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, GL_REPEAT); - - if (samplerDesc.isCompareSampler) - { - glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - } - else - { - glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); - } - - dstDetail.m_samplerHandle = handle; - break; - } - } - } - - return bindingState.detach(); -} - -void GLRenderer::setBindingState(BindingState* stateIn) -{ - BindingStateImpl* state = static_cast(stateIn); - - const auto& bindingDesc = state->getDesc(); - - const auto& details = state->m_bindingDetails; - const auto& bindings = bindingDesc.m_bindings; - const int numBindings = int(bindings.Count()); - - for (int i = 0; i < numBindings; ++i) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - switch (binding.bindingType) - { - case BindingType::Buffer: - { - const int bindingIndex = binding.registerRange.getSingleIndex(); - - BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); - glBindBufferBase(buffer->m_target, bindingIndex, buffer->m_handle); - break; - } - case BindingType::Sampler: - { - for (int index = binding.registerRange.index; index < binding.registerRange.index + binding.registerRange.size; ++index) - { - glBindSampler(index, detail.m_samplerHandle); - } - break; - } - case BindingType::Texture: - case BindingType::CombinedTextureSampler: - { - BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); - - const int bindingIndex = binding.registerRange.getSingleIndex(); - - glActiveTexture(GL_TEXTURE0 + bindingIndex); - glBindTexture(buffer->m_target, buffer->m_handle); - break; - } - } - } -} -#endif - -void GLRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) -{ - auto resourceImpl = (BufferResourceImpl*) buffer; - - auto layout = m_layout; - auto rangeInfo = layout->m_ranges[range]; - auto arrayIndex = rangeInfo.arrayIndex + index; - - m_constantBuffers[arrayIndex] = resourceImpl; -} - -void GLRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) -{ - auto viewImpl = (ResourceViewImpl*) view; - - auto layout = m_layout; - auto rangeInfo = layout->m_ranges[range]; - auto arrayIndex = rangeInfo.arrayIndex + index; - - assert(!"unimplemented"); -} - -void GLRenderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) -{ - assert(!"unsupported"); -} - -void GLRenderer::DescriptorSetImpl::setCombinedTextureSampler( - UInt range, - UInt index, - ResourceView* textureView, - SamplerState* sampler) -{ - auto viewImpl = (TextureViewImpl*) textureView; - auto samplerImpl = (SamplerStateImpl*) sampler; - - auto layout = m_layout; - auto rangeInfo = layout->m_ranges[range]; - auto arrayIndex = rangeInfo.arrayIndex + index; - - m_textures[arrayIndex] = viewImpl; - m_samplers[arrayIndex] = samplerImpl; -} - -void GLRenderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) -{ - auto descriptorSetImpl = (DescriptorSetImpl*)descriptorSet; - - // TODO: can we just bind things immediately here, rather than shadowing the state? - - m_boundDescriptorSets[index] = descriptorSetImpl; -} - -Result GLRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) -{ - RefPtr layoutImpl = new DescriptorSetLayoutImpl(); - - Int counts[int(GLDescriptorSlotType::CountOf)] = { 0, }; - - Int rangeCount = desc.slotRangeCount; - for(Int rr = 0; rr < rangeCount; ++rr) - { - auto rangeDesc = desc.slotRanges[rr]; - DescriptorSetLayoutImpl::RangeInfo rangeInfo; - - GLDescriptorSlotType glSlotType; - switch( rangeDesc.type ) - { - default: - assert(!"unsupported"); - break; - - // TODO: There are many other slot types we could support here, - // in particular including storage buffers. - - case DescriptorSlotType::CombinedImageSampler: - glSlotType = GLDescriptorSlotType::CombinedTextureSampler; - break; - - case DescriptorSlotType::UniformBuffer: - case DescriptorSlotType::DynamicUniformBuffer: - glSlotType = GLDescriptorSlotType::ConstantBuffer; - break; - } - - rangeInfo.type = glSlotType; - rangeInfo.arrayIndex = counts[int(glSlotType)]; - counts[int(glSlotType)] += rangeDesc.count; - - layoutImpl->m_ranges.add(rangeInfo); - } - - for( Int ii = 0; ii < int(GLDescriptorSlotType::CountOf); ++ii ) - { - layoutImpl->m_counts[ii] = counts[ii]; - } - - *outLayout = layoutImpl.detach(); - return SLANG_OK; -} - -Result GLRenderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) -{ - RefPtr layoutImpl = new PipelineLayoutImpl(); - - static const int kSlotTypeCount = int(GLDescriptorSlotType::CountOf); - Int counts[kSlotTypeCount] = { 0, }; - - Int setCount = desc.descriptorSetCount; - for( Int ii = 0; ii < setCount; ++ii ) - { - auto setLayout = (DescriptorSetLayoutImpl*) desc.descriptorSets[ii].layout; - - PipelineLayoutImpl::DescriptorSetInfo setInfo; - setInfo.layout = setLayout; - - for( Int ii = 0; ii < int(GLDescriptorSlotType::CountOf); ++ii ) - { - setInfo.baseArrayIndex[ii] = counts[ii]; - counts[ii] += setLayout->m_counts[ii]; - } - - layoutImpl->m_sets.add(setInfo); - } - - *outLayout = layoutImpl.detach(); - return SLANG_OK; -} - -Result GLRenderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) -{ - auto layoutImpl = (DescriptorSetLayoutImpl*) layout; - - RefPtr descriptorSetImpl = new DescriptorSetImpl(); - - descriptorSetImpl->m_layout = layoutImpl; - - // TODO: storage for the arrays of bound objects could be tail allocated - // as part of the descriptor set, with offsets pre-computed in the - // descriptor set layout. - - { - auto slotTypeIndex = int(GLDescriptorSlotType::ConstantBuffer); - auto slotCount = layoutImpl->m_counts[slotTypeIndex]; - descriptorSetImpl->m_constantBuffers.setCount(slotCount); - } - - { - auto slotTypeIndex = int(GLDescriptorSlotType::CombinedTextureSampler); - auto slotCount = layoutImpl->m_counts[slotTypeIndex]; - - descriptorSetImpl->m_textures.setCount(slotCount); - descriptorSetImpl->m_samplers.setCount(slotCount); - } - - *outDescriptorSet = descriptorSetImpl.detach(); - return SLANG_OK; -} - -Result GLRenderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) -{ - auto programID = glCreateProgram(); - if(desc.pipelineType == PipelineType::Compute ) - { - auto computeKernel = desc.findKernel(StageType::Compute); - auto computeShaderID = loadShader(GL_COMPUTE_SHADER, (char const*) computeKernel->codeBegin); - glAttachShader(programID, computeShaderID); - glLinkProgram(programID); - glDeleteShader(computeShaderID); - } - else - { - auto vertexKernel = desc.findKernel(StageType::Vertex); - auto fragmentKernel = desc.findKernel(StageType::Fragment); - - auto vertexShaderID = loadShader(GL_VERTEX_SHADER, (char const*) vertexKernel->codeBegin); - auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, (char const*) fragmentKernel->codeBegin); - - glAttachShader(programID, vertexShaderID); - glAttachShader(programID, fragmentShaderID); - - - glLinkProgram(programID); - - glDeleteShader(vertexShaderID); - glDeleteShader(fragmentShaderID); - } - GLint success = GL_FALSE; - glGetProgramiv(programID, GL_LINK_STATUS, &success); - if (!success) - { - int maxSize = 0; - glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxSize); - - auto infoBuffer = (char*)::malloc(maxSize); - - int infoSize = 0; - glGetProgramInfoLog(programID, maxSize, &infoSize, infoBuffer); - if (infoSize > 0) - { - fprintf(stderr, "%s", infoBuffer); - OutputDebugStringA(infoBuffer); - } - - ::free(infoBuffer); - - glDeleteProgram(programID); - return SLANG_FAIL; - } - - *outProgram = new ShaderProgramImpl(m_weakRenderer, programID); - return SLANG_OK; -} - -Result GLRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) -{ - auto programImpl = (ShaderProgramImpl*) desc.program; - auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; - auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; - - RefPtr pipelineStateImpl = new PipelineStateImpl(); - pipelineStateImpl->m_program = programImpl; - pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; - pipelineStateImpl->m_inputLayout = inputLayoutImpl; - *outState = pipelineStateImpl.detach(); - return SLANG_OK; -} - -Result GLRenderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) -{ - auto programImpl = (ShaderProgramImpl*) desc.program; - auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; - - RefPtr pipelineStateImpl = new PipelineStateImpl(); - pipelineStateImpl->m_program = programImpl; - pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; - *outState = pipelineStateImpl.detach(); - return SLANG_OK; -} - - -} // renderer_test diff --git a/tools/gfx/render-gl.h b/tools/gfx/render-gl.h deleted file mode 100644 index 055031d38..000000000 --- a/tools/gfx/render-gl.h +++ /dev/null @@ -1,10 +0,0 @@ -// render-d3d11.h -#pragma once - -namespace gfx { - -class Renderer; - -Renderer* createGLRenderer(); - -} // gfx diff --git a/tools/gfx/render-vk.cpp b/tools/gfx/render-vk.cpp deleted file mode 100644 index 77b593565..000000000 --- a/tools/gfx/render-vk.cpp +++ /dev/null @@ -1,2793 +0,0 @@ -// render-vk.cpp -#include "render-vk.h" - -//WORKING:#include "options.h" -#include "render.h" - -#include "../../source/core/slang-smart-pointer.h" - -#include "vk-api.h" -#include "vk-util.h" -#include "vk-device-queue.h" -#include "vk-swap-chain.h" - -#include "surface.h" - -// Vulkan has a different coordinate system to ogl -// http://anki3d.org/vulkan-coordinate-system/ - -#define ENABLE_VALIDATION_LAYER 1 - -#ifdef _MSC_VER -# include -# pragma warning(disable: 4996) -# if (_MSC_VER < 1900) -# define snprintf sprintf_s -# endif -#endif - -namespace gfx { -using namespace Slang; - -class VKRenderer : public Renderer -{ -public: - enum - { - kMaxRenderTargets = 8, - kMaxAttachments = kMaxRenderTargets + 1, - - kMaxDescriptorSets = 4, - }; - - // Renderer implementation - virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; - virtual const List& getFeatures() override { return m_features; } - virtual void setClearColor(const float color[4]) override; - virtual void clearFrame() override; - virtual void presentFrame() override; - TextureResource::Desc getSwapChainTextureDesc() override; - - Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; - Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; - Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; - - Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; - Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; - - Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; - - Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; - Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; - Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; - - Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; - Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; - Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; - - virtual SlangResult captureScreenSurface(Surface& surface) override; - - virtual void* map(BufferResource* buffer, MapFlavor flavor) override; - virtual void unmap(BufferResource* buffer) override; - virtual void setPrimitiveTopology(PrimitiveTopology topology) override; - - virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; - - virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; - virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; - virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; - void setViewports(UInt count, Viewport const* viewports) override; - void setScissorRects(UInt count, ScissorRect const* rects) override; - virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; - virtual void draw(UInt vertexCount, UInt startVertex) override; - virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; - virtual void dispatchCompute(int x, int y, int z) override; - virtual void submitGpuWork() override; - virtual void waitForGpu() override; - virtual RendererType getRendererType() const override { return RendererType::Vulkan; } - - /// Dtor - ~VKRenderer(); - - protected: - - class Buffer - { - public: - /// Initialize a buffer with specified size, and memory props - Result init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties); - - /// Returns true if has been initialized - bool isInitialized() const { return m_api != nullptr; } - - // Default Ctor - Buffer(): - m_api(nullptr) - {} - - /// Dtor - ~Buffer() - { - if (m_api) - { - m_api->vkDestroyBuffer(m_api->m_device, m_buffer, nullptr); - m_api->vkFreeMemory(m_api->m_device, m_memory, nullptr); - } - } - - VkBuffer m_buffer; - VkDeviceMemory m_memory; - const VulkanApi* m_api; - }; - - class InputLayoutImpl : public InputLayout - { - public: - List m_vertexDescs; - int m_vertexSize; - }; - - class BufferResourceImpl: public BufferResource - { - public: - typedef BufferResource Parent; - - BufferResourceImpl(Resource::Usage initialUsage, const BufferResource::Desc& desc, VKRenderer* renderer): - Parent(desc), - m_renderer(renderer), - m_initialUsage(initialUsage) - { - assert(renderer); - } - - Resource::Usage m_initialUsage; - VKRenderer* m_renderer; - Buffer m_buffer; - Buffer m_uploadBuffer; - List m_readBuffer; ///< Stores the contents when a map read is performed - - MapFlavor m_mapFlavor = MapFlavor::Unknown; ///< If resource is mapped, records what kind of mapping else Unknown (if not mapped) - }; - - class TextureResourceImpl : public TextureResource - { - public: - typedef TextureResource Parent; - - TextureResourceImpl(const Desc& desc, Usage initialUsage, const VulkanApi* api) : - Parent(desc), - m_initialUsage(initialUsage), - m_api(api) - { - } - ~TextureResourceImpl() - { - if (m_api) - { - if (m_imageMemory != VK_NULL_HANDLE) - { - m_api->vkFreeMemory(m_api->m_device, m_imageMemory, nullptr); - } - if (m_image != VK_NULL_HANDLE) - { - m_api->vkDestroyImage(m_api->m_device, m_image, nullptr); - } - } - } - - Usage m_initialUsage; - - VkImage m_image = VK_NULL_HANDLE; - VkDeviceMemory m_imageMemory = VK_NULL_HANDLE; - - const VulkanApi* m_api; - }; - - class SamplerStateImpl : public SamplerState - { - public: - VkSampler m_sampler; - }; - - class ResourceViewImpl : public ResourceView - { - public: - enum class ViewType - { - Texture, - TexelBuffer, - PlainBuffer, - }; - ViewType m_type; - }; - - class TextureResourceViewImpl : public ResourceViewImpl - { - public: - TextureResourceViewImpl() - { - m_type = ViewType::Texture; - } - - RefPtr m_texture; - VkImageView m_view; - VkImageLayout m_layout; - }; - - class TexelBufferResourceViewImpl : public ResourceViewImpl - { - public: - TexelBufferResourceViewImpl() - { - m_type = ViewType::TexelBuffer; - } - - RefPtr m_buffer; - VkBufferView m_view; - }; - - class PlainBufferResourceViewImpl : public ResourceViewImpl - { - public: - PlainBufferResourceViewImpl() - { - m_type = ViewType::PlainBuffer; - } - - RefPtr m_buffer; - VkDeviceSize offset; - VkDeviceSize size; - }; - - class ShaderProgramImpl: public ShaderProgram - { - public: - - ShaderProgramImpl(PipelineType pipelineType): - m_pipelineType(pipelineType) - {} - - PipelineType m_pipelineType; - - VkPipelineShaderStageCreateInfo m_compute; - VkPipelineShaderStageCreateInfo m_vertex; - VkPipelineShaderStageCreateInfo m_fragment; - - List m_buffers[2]; //< To keep storage of code in scope - }; - - class DescriptorSetLayoutImpl : public DescriptorSetLayout - { - public: - DescriptorSetLayoutImpl(const VulkanApi& api) - : m_api(&api) - { - } - - ~DescriptorSetLayoutImpl() - { - if(m_descriptorSetLayout != VK_NULL_HANDLE) - { - m_api->vkDestroyDescriptorSetLayout(m_api->m_device, m_descriptorSetLayout, nullptr); - } - if (m_descriptorPool != VK_NULL_HANDLE) - { - m_api->vkDestroyDescriptorPool(m_api->m_device, m_descriptorPool, nullptr); - } - } - - VulkanApi const* m_api; - VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE; - VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE; - - struct RangeInfo - { - VkDescriptorType descriptorType; - }; - List m_ranges; - }; - - class PipelineLayoutImpl : public PipelineLayout - { - public: - PipelineLayoutImpl(const VulkanApi& api) - : m_api(&api) - { - } - - ~PipelineLayoutImpl() - { - if (m_pipelineLayout != VK_NULL_HANDLE) - { - m_api->vkDestroyPipelineLayout(m_api->m_device, m_pipelineLayout, nullptr); - } - } - - VulkanApi const* m_api; - VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; - UInt m_descriptorSetCount = 0; - }; - - class DescriptorSetImpl : public DescriptorSet - { - public: - // Record the view binding - struct Binding - { - enum class Type : uint8_t - { - Unknown, - ResourceView, - SamplerState, - BufferResource, - CountOf, - }; - Type type; - uint32_t range; - uint32_t index; - RefPtr obj; - }; - - DescriptorSetImpl(VKRenderer* renderer) - : m_renderer(renderer) - { - } - - ~DescriptorSetImpl() - { - } - - virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; - virtual void setResource(UInt range, UInt index, ResourceView* view) override; - virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; - virtual void setCombinedTextureSampler( - UInt range, - UInt index, - ResourceView* textureView, - SamplerState* sampler) override; - - static Binding::Type _getBindingType(RefObject* ptr); - void _setBinding(Binding::Type type, UInt range, UInt index, RefObject* ptr); - - VKRenderer* m_renderer = nullptr; ///< Weak pointer, can't be strong, because if set will become circular reference - RefPtr m_layout; - VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE; - - List m_bindings; ///< Records entities are bound to this descriptor set, and keeps the associated resources/views/state in scope - }; - -#if 0 - struct BindingDetail - { - VkImageView m_srv = VK_NULL_HANDLE; - VkBufferView m_uav = VK_NULL_HANDLE; - VkSampler m_sampler = VK_NULL_HANDLE; - }; - - class BindingStateImpl: public BindingState - { - public: - typedef BindingState Parent; - - BindingStateImpl(const Desc& desc, const VulkanApi* api): - Parent(desc), - m_api(api) - { - } - ~BindingStateImpl() - { - for (int i = 0; i < int(m_bindingDetails.Count()); ++i) - { - BindingDetail& detail = m_bindingDetails[i]; - if (detail.m_sampler != VK_NULL_HANDLE) - { - m_api->vkDestroySampler(m_api->m_device, detail.m_sampler, nullptr); - } - if (detail.m_srv != VK_NULL_HANDLE) - { - m_api->vkDestroyImageView(m_api->m_device, detail.m_srv, nullptr); - } - if (detail.m_uav != VK_NULL_HANDLE) - { - m_api->vkDestroyBufferView(m_api->m_device, detail.m_uav, nullptr); - } - } - } - - const VulkanApi* m_api; - List m_bindingDetails; - }; -#endif - - struct BoundVertexBuffer - { - RefPtr m_buffer; - int m_stride; - int m_offset; - }; - - class PipelineStateImpl : public PipelineState - { - public: - PipelineStateImpl(const VulkanApi& api): - m_api(&api) - { - } - ~PipelineStateImpl() - { - if (m_pipeline != VK_NULL_HANDLE) - { - m_api->vkDestroyPipeline(m_api->m_device, m_pipeline, nullptr); - } - } - - const VulkanApi* m_api; - -// VkPrimitiveTopology m_primitiveTopology; - - RefPtr m_pipelineLayout; - -// RefPtr m_inputLayout; - RefPtr m_shaderProgram; - - VkPipeline m_pipeline = VK_NULL_HANDLE; - }; - - VkBool32 handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, - size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg); - - VkPipelineShaderStageCreateInfo compileEntryPoint( - ShaderProgram::KernelDesc const& kernelDesc, - VkShaderStageFlagBits stage, - List& bufferOut); - - static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, - size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData); - - /// Returns true if m_currentPipeline matches the current configuration -// Pipeline* _getPipeline(); -// bool _isEqual(const Pipeline& pipeline) const; -// Slang::Result _createPipeline(RefPtr& pipelineOut); - void _beginRender(); - void _endRender(); - - Slang::Result _beginPass(); - void _endPass(); - void _transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout); - - VkDebugReportCallbackEXT m_debugReportCallback; - -// RefPtr m_currentInputLayout; - -// RefPtr m_currentBindingState; - RefPtr m_currentPipelineLayout; - - RefPtr m_currentDescriptorSetImpls [kMaxDescriptorSets]; - VkDescriptorSet m_currentDescriptorSets [kMaxDescriptorSets]; - -// RefPtr m_currentProgram; - -// List > m_pipelineCache; - RefPtr m_currentPipeline; - - List m_boundVertexBuffers; - - VkPrimitiveTopology m_primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - VkDevice m_device = VK_NULL_HANDLE; - - VulkanModule m_module; - VulkanApi m_api; - - VulkanDeviceQueue m_deviceQueue; - VulkanSwapChain m_swapChain; - - VkRenderPass m_renderPass = VK_NULL_HANDLE; - - int m_swapChainImageIndex = -1; - - float m_clearColor[4] = { 0, 0, 0, 0 }; - - Desc m_desc; - List m_features; -}; - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer::Buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -Result VKRenderer::Buffer::init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties) -{ - assert(!isInitialized()); - - m_api = &api; - m_memory = VK_NULL_HANDLE; - m_buffer = VK_NULL_HANDLE; - - VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - bufferCreateInfo.size = bufferSize; - bufferCreateInfo.usage = usage; - - SLANG_VK_CHECK(api.vkCreateBuffer(api.m_device, &bufferCreateInfo, nullptr, &m_buffer)); - - VkMemoryRequirements memoryReqs = {}; - api.vkGetBufferMemoryRequirements(api.m_device, m_buffer, &memoryReqs); - - int memoryTypeIndex = api.findMemoryTypeIndex(memoryReqs.memoryTypeBits, reqMemoryProperties); - assert(memoryTypeIndex >= 0); - - VkMemoryPropertyFlags actualMemoryProperites = api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; - - VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - allocateInfo.allocationSize = memoryReqs.size; - allocateInfo.memoryTypeIndex = memoryTypeIndex; - - SLANG_VK_CHECK(api.vkAllocateMemory(api.m_device, &allocateInfo, nullptr, &m_memory)); - SLANG_VK_CHECK(api.vkBindBufferMemory(api.m_device, m_buffer, m_memory, 0)); - - return SLANG_OK; -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -#if 0 -bool VKRenderer::_isEqual(const Pipeline& pipeline) const -{ - return - pipeline.m_pipelineLayout == m_currentPipelineLayout && - pipeline.m_primitiveTopology == m_primitiveTopology && - pipeline.m_inputLayout == m_currentInputLayout && - pipeline.m_shaderProgram == m_currentProgram; -} - -VKRenderer::Pipeline* VKRenderer::_getPipeline() -{ - if (m_currentPipeline && _isEqual(*m_currentPipeline)) - { - return m_currentPipeline; - } - - // Look for a match in the cache - for (int i = 0; i < int(m_pipelineCache.Count()); ++i) - { - Pipeline* pipeline = m_pipelineCache[i]; - if (_isEqual(*pipeline)) - { - m_currentPipeline = pipeline; - return pipeline; - } - } - - RefPtr pipeline; - SLANG_RETURN_NULL_ON_FAIL(_createPipeline(pipeline)); - m_pipelineCache.Add(pipeline); - m_currentPipeline = pipeline; - return pipeline; -} - -Slang::Result VKRenderer::_createPipeline(RefPtr& pipelineOut) -{ - RefPtr pipeline(new Pipeline(m_api)); - - // Initialize the state - pipeline->m_primitiveTopology = m_primitiveTopology; - pipeline->m_pipelineLayout = m_currentPipelineLayout; - pipeline->m_shaderProgram = m_currentProgram; - pipeline->m_inputLayout = m_currentInputLayout; - - // Must be equal at this point if all the items are correctly set in pipeline - assert(_isEqual(*pipeline)); - - VkPipelineCache pipelineCache = VK_NULL_HANDLE; - - if (m_currentProgram->m_pipelineType == PipelineType::Compute) - { - // Then create a pipeline to use that layout - - VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; - computePipelineInfo.stage = m_currentProgram->m_compute; - computePipelineInfo.layout = pipeline->m_pipelineLayout->m_pipelineLayout; - - SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline->m_pipeline)); - } - else if (m_currentProgram->m_pipelineType == PipelineType::Graphics) - { - // Create the graphics pipeline - - const int width = m_swapChain.getWidth(); - const int height = m_swapChain.getHeight(); - - VkPipelineShaderStageCreateInfo shaderStages[] = { m_currentProgram->m_vertex, m_currentProgram->m_fragment }; - - // VertexBuffer/s - // Currently only handles one - - VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.vertexAttributeDescriptionCount = 0; - - VkVertexInputBindingDescription vertexInputBindingDescription; - - if (m_currentInputLayout) - { - vertexInputBindingDescription.binding = 0; - vertexInputBindingDescription.stride = m_currentInputLayout->m_vertexSize; - vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - const auto& srcAttributeDescs = m_currentInputLayout->m_vertexDescs; - - vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription; - - vertexInputInfo.vertexAttributeDescriptionCount = static_cast(srcAttributeDescs.Count()); - vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.getBuffer(); - } - - // - - VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = (float)width; - viewport.height = (float)height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset = { 0, 0 }; - scissor.extent = { uint32_t(width), uint32_t(height) }; - - VkPipelineViewportStateCreateInfo viewportState = {}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; - viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; - - VkPipelineRasterizationStateCreateInfo rasterizer = {}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_NONE; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - - VkPipelineMultisampleStateCreateInfo multisampling = {}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo colorBlending = {}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; - - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = shaderStages; - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.layout = pipeline->m_pipelineLayout->m_pipelineLayout; - pipelineInfo.renderPass = m_renderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - - SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline->m_pipeline)); - } - else - { - assert(!"Unhandled program type"); - return SLANG_FAIL; - } - - pipelineOut = pipeline; - return SLANG_OK; -} -#endif - -Result VKRenderer::_beginPass() -{ - if (m_swapChainImageIndex < 0) - { - return SLANG_FAIL; - } - - const int numRenderTargets = 1; - - const VulkanSwapChain::Image& image = m_swapChain.getImages()[m_swapChainImageIndex]; - - int numAttachments = 0; - - // Start render pass - VkClearValue clearValues[kMaxAttachments]; - clearValues[numAttachments++] = VkClearValue{ m_clearColor[0], m_clearColor[1], m_clearColor[2], m_clearColor[3] }; - - bool hasDepthBuffer = false; - if (hasDepthBuffer) - { - VkClearValue& clearValue = clearValues[numAttachments++]; - - clearValue.depthStencil.depth = 1.0f; - clearValue.depthStencil.stencil = 0; - } - - const int width = m_swapChain.getWidth(); - const int height = m_swapChain.getHeight(); - - VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); - - VkRenderPassBeginInfo renderPassBegin = {}; - renderPassBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassBegin.renderPass = m_renderPass; - renderPassBegin.framebuffer = image.m_frameBuffer; - renderPassBegin.renderArea.offset.x = 0; - renderPassBegin.renderArea.offset.y = 0; - renderPassBegin.renderArea.extent.width = width; - renderPassBegin.renderArea.extent.height = height; - renderPassBegin.clearValueCount = numAttachments; - renderPassBegin.pClearValues = clearValues; - - m_api.vkCmdBeginRenderPass(cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE); - - // Set up scissor and viewport - { - VkRect2D rects[kMaxRenderTargets] = {}; - VkViewport viewports[kMaxRenderTargets] = {}; - for (int i = 0; i < numRenderTargets; ++i) - { - rects[i] = VkRect2D{ 0, 0, uint32_t(width), uint32_t(height) }; - - VkViewport& dstViewport = viewports[i]; - - dstViewport.x = 0.0f; - dstViewport.y = 0.0f; - dstViewport.width = float(width); - dstViewport.height = float(height); - dstViewport.minDepth = 0.0f; - dstViewport.maxDepth = 1.0f; - } - - m_api.vkCmdSetScissor(cmdBuffer, 0, numRenderTargets, rects); - m_api.vkCmdSetViewport(cmdBuffer, 0, numRenderTargets, viewports); - } - - return SLANG_OK; -} - -void VKRenderer::_endPass() -{ - VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); - m_api.vkCmdEndRenderPass(cmdBuffer); -} - -void VKRenderer::_beginRender() -{ - m_swapChainImageIndex = m_swapChain.nextFrontImageIndex(); - - if (m_swapChainImageIndex < 0) - { - return; - } -} - -void VKRenderer::_endRender() -{ - m_deviceQueue.flush(); -} - -Renderer* createVKRenderer() -{ - return new VKRenderer; -} - -VKRenderer::~VKRenderer() -{ - if (m_renderPass != VK_NULL_HANDLE) - { - m_api.vkDestroyRenderPass(m_device, m_renderPass, nullptr); - m_renderPass = VK_NULL_HANDLE; - } -} - - -VkBool32 VKRenderer::handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, - size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg) -{ - char const* severity = "message"; - if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) - severity = "warning"; - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) - severity = "error"; - - // pMsg can be really big (it can be assembler dump for example) - // Use a dynamic buffer to store - size_t bufferSize = strlen(pMsg) + 1 + 1024; - List bufferArray; - bufferArray.setCount(bufferSize); - char* buffer = bufferArray.getBuffer(); - - sprintf_s(buffer, - bufferSize, - "%s: %s %d: %s\n", - pLayerPrefix, - severity, - msgCode, - pMsg); - - fprintf(stderr, "%s", buffer); - fflush(stderr); - - OutputDebugStringA(buffer); - - return VK_FALSE; -} - -/* static */VKAPI_ATTR VkBool32 VKAPI_CALL VKRenderer::debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, - size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData) -{ - return ((VKRenderer*)pUserData)->handleDebugMessage(flags, objType, srcObject, location, msgCode, pLayerPrefix, pMsg); -} - -VkPipelineShaderStageCreateInfo VKRenderer::compileEntryPoint( - ShaderProgram::KernelDesc const& kernelDesc, - VkShaderStageFlagBits stage, - List& bufferOut) -{ - char const* dataBegin = (char const*) kernelDesc.codeBegin; - char const* dataEnd = (char const*) kernelDesc.codeEnd; - - // We need to make a copy of the code, since the Slang compiler - // will free the memory after a compile request is closed. - size_t codeSize = dataEnd - dataBegin; - - bufferOut.insertRange(0, dataBegin, codeSize); - - char* codeBegin = bufferOut.getBuffer(); - - VkShaderModuleCreateInfo moduleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; - moduleCreateInfo.pCode = (uint32_t*)codeBegin; - moduleCreateInfo.codeSize = codeSize; - - VkShaderModule module; - SLANG_VK_CHECK(m_api.vkCreateShaderModule(m_device, &moduleCreateInfo, nullptr, &module)); - - VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; - shaderStageCreateInfo.stage = stage; - - shaderStageCreateInfo.module = module; - shaderStageCreateInfo.pName = "main"; - - return shaderStageCreateInfo; -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! - -SlangResult VKRenderer::initialize(const Desc& desc, void* inWindowHandle) -{ - SLANG_RETURN_ON_FAIL(m_module.init()); - SLANG_RETURN_ON_FAIL(m_api.initGlobalProcs(m_module)); - - m_desc = desc; - - VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; - applicationInfo.pApplicationName = "slang-render-test"; - applicationInfo.pEngineName = "slang-render-test"; - applicationInfo.apiVersion = VK_API_VERSION_1_0; - - char const* instanceExtensions[] = - { - VK_KHR_SURFACE_EXTENSION_NAME, - - VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, - -#if SLANG_WINDOWS_FAMILY - VK_KHR_WIN32_SURFACE_EXTENSION_NAME, -#else - VK_KHR_XLIB_SURFACE_EXTENSION_NAME -#endif - -#if ENABLE_VALIDATION_LAYER - VK_EXT_DEBUG_REPORT_EXTENSION_NAME, -#endif - }; - - VkInstance instance = VK_NULL_HANDLE; - - VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; - instanceCreateInfo.pApplicationInfo = &applicationInfo; - - instanceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(instanceExtensions); - instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0]; - -#if ENABLE_VALIDATION_LAYER - const char* layerNames[] = { "VK_LAYER_LUNARG_standard_validation" }; - instanceCreateInfo.enabledLayerCount = SLANG_COUNT_OF(layerNames); - instanceCreateInfo.ppEnabledLayerNames = layerNames; -#endif - - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); - SLANG_RETURN_ON_FAIL(m_api.initInstanceProcs(instance)); - -#if ENABLE_VALIDATION_LAYER - VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - - VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; - debugCreateInfo.pfnCallback = &debugMessageCallback; - debugCreateInfo.pUserData = this; - debugCreateInfo.flags = debugFlags; - - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDebugReportCallbackEXT(instance, &debugCreateInfo, nullptr, &m_debugReportCallback)); -#endif - - uint32_t numPhysicalDevices = 0; - SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, nullptr)); - - List physicalDevices; - physicalDevices.setCount(numPhysicalDevices); - SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, physicalDevices.getBuffer())); - - Index selectedDeviceIndex = 0; - - if (desc.adapter.getLength()) - { - selectedDeviceIndex = -1; - - String lowerAdapter = desc.adapter.toLower(); - - for (Index i = 0; i < physicalDevices.getCount(); ++i) - { - auto physicalDevice = physicalDevices[i]; - - VkPhysicalDeviceProperties basicProps = {}; - m_api.vkGetPhysicalDeviceProperties(physicalDevice, &basicProps); - - String lowerName = String(basicProps.deviceName).toLower(); - - if (lowerName.indexOf(lowerAdapter) != Index(-1)) - { - selectedDeviceIndex = i; - break; - } - } - if (selectedDeviceIndex < 0) - { - // Device not found - return SLANG_FAIL; - } - } - - SLANG_RETURN_ON_FAIL(m_api.initPhysicalDevice(physicalDevices[selectedDeviceIndex])); - - List deviceExtensions; - deviceExtensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - - VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; - deviceCreateInfo.queueCreateInfoCount = 1; - - deviceCreateInfo.pEnabledFeatures = &m_api.m_deviceFeatures; - - // Get the device features (doesn't use, but useful when debugging) - if (m_api.vkGetPhysicalDeviceFeatures2) - { - VkPhysicalDeviceFeatures2 deviceFeatures2 = {}; - deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - m_api.vkGetPhysicalDeviceFeatures2(m_api.m_physicalDevice, &deviceFeatures2); - } - - VkPhysicalDeviceProperties basicProps = {}; - m_api.vkGetPhysicalDeviceProperties(m_api.m_physicalDevice, &basicProps); - - // Get the API version - const uint32_t majorVersion = VK_VERSION_MAJOR(basicProps.apiVersion); - const uint32_t minorVersion = VK_VERSION_MINOR(basicProps.apiVersion); - - // Float16 features - // Need in this scope because it will be linked into the device creation (if it is available) - VkPhysicalDeviceFloat16Int8FeaturesKHR float16Features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR }; - - // API version check, can't use vkGetPhysicalDeviceProperties2 yet since this device might not support it - if (VK_MAKE_VERSION(majorVersion, minorVersion, 0) >= VK_API_VERSION_1_1 && - m_api.vkGetPhysicalDeviceProperties2 && - m_api.vkGetPhysicalDeviceFeatures2) - { - VkPhysicalDeviceProperties2 physicalDeviceProps2; - - physicalDeviceProps2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - physicalDeviceProps2.pNext = nullptr; - physicalDeviceProps2.properties = {}; - - m_api.vkGetPhysicalDeviceProperties2(m_api.m_physicalDevice, &physicalDeviceProps2); - - // Get device features - VkPhysicalDeviceFeatures2 deviceFeatures2 = {}; - deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - - // Link together for lookup - float16Features.pNext = deviceFeatures2.pNext; - deviceFeatures2.pNext = &float16Features; - - m_api.vkGetPhysicalDeviceFeatures2(m_api.m_physicalDevice, &deviceFeatures2); - - // If we have float16 features then enable - if (float16Features.shaderFloat16) - { - // Link into the creation features - float16Features.pNext = (void*)deviceCreateInfo.pNext; - deviceCreateInfo.pNext = &float16Features; - - // Add the Float16 extension - deviceExtensions.add(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); - - // We have half support - m_features.add("half"); - } - } - - int queueFamilyIndex = m_api.findQueue(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); - assert(queueFamilyIndex >= 0); - - float queuePriority = 0.0f; - VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; - queueCreateInfo.queueFamilyIndex = queueFamilyIndex; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &queuePriority; - - deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; - - deviceCreateInfo.enabledExtensionCount = uint32_t(deviceExtensions.getCount()); - deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.getBuffer(); - - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDevice(m_api.m_physicalDevice, &deviceCreateInfo, nullptr, &m_device)); - SLANG_RETURN_ON_FAIL(m_api.initDeviceProcs(m_device)); - - { - VkQueue queue; - m_api.vkGetDeviceQueue(m_device, queueFamilyIndex, 0, &queue); - SLANG_RETURN_ON_FAIL(m_deviceQueue.init(m_api, queue, queueFamilyIndex)); - } - - // set up swap chain - - { - VulkanSwapChain::Desc desc; - VulkanSwapChain::PlatformDesc* platformDesc = nullptr; - - desc.init(); - desc.m_format = Format::RGBA_Unorm_UInt8; - -#if SLANG_WINDOWS_FAMILY - VulkanSwapChain::WinPlatformDesc winPlatformDesc; - winPlatformDesc.m_hinstance = ::GetModuleHandle(nullptr); - winPlatformDesc.m_hwnd = (HWND)inWindowHandle; - platformDesc = &winPlatformDesc; -#endif - - SLANG_RETURN_ON_FAIL(m_swapChain.init(&m_deviceQueue, desc, platformDesc)); - } - - // depth/stencil? - - // render pass? - - { - const int numRenderTargets = 1; - bool shouldClear = true; - bool shouldClearDepth = false; - bool shouldClearStencil = false; - bool hasDepthBuffer = false; - - Format depthFormat = Format::Unknown; - VkFormat colorFormat = m_swapChain.getVkFormat(); - - int numAttachments = 0; - // We need extra space if we have depth buffer - VkAttachmentDescription attachmentDesc[kMaxRenderTargets + 1] = {}; - for (int i = 0; i < numRenderTargets; ++i) - { - VkAttachmentDescription& dst = attachmentDesc[numAttachments ++]; - - dst.flags = 0; - dst.format = colorFormat; - dst.samples = VK_SAMPLE_COUNT_1_BIT; - dst.loadOp = shouldClear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - dst.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - dst.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - dst.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - } - if (hasDepthBuffer) - { - VkAttachmentDescription& dst = attachmentDesc[numAttachments++]; - - dst.flags = 0; - dst.format = VulkanUtil::getVkFormat(depthFormat); - dst.samples = VK_SAMPLE_COUNT_1_BIT; - dst.loadOp = shouldClearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - dst.stencilLoadOp = shouldClearStencil ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - dst.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - dst.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } - - VkAttachmentReference colorAttachments[kMaxRenderTargets] = {}; - for (int i = 0; i < numRenderTargets; ++i) - { - VkAttachmentReference& dst = colorAttachments[i]; - dst.attachment = i; - dst.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } - - VkAttachmentReference depthAttachment = {}; - depthAttachment.attachment = numRenderTargets; - depthAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpassDesc = {}; - subpassDesc.flags = 0; - subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDesc.inputAttachmentCount = 0u; - subpassDesc.pInputAttachments = nullptr; - subpassDesc.colorAttachmentCount = numRenderTargets; - subpassDesc.pColorAttachments = colorAttachments; - subpassDesc.pResolveAttachments = nullptr; - subpassDesc.pDepthStencilAttachment = hasDepthBuffer ? &depthAttachment : nullptr; - subpassDesc.preserveAttachmentCount = 0u; - subpassDesc.pPreserveAttachments = nullptr; - - VkRenderPassCreateInfo renderPassCreateInfo = {}; - renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCreateInfo.attachmentCount = numAttachments; - renderPassCreateInfo.pAttachments = attachmentDesc; - renderPassCreateInfo.subpassCount = 1; - renderPassCreateInfo.pSubpasses = &subpassDesc; - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &m_renderPass)); - } - - // frame buffer - SLANG_RETURN_ON_FAIL(m_swapChain.createFrameBuffers(m_renderPass)); - - _beginRender(); - - return SLANG_OK; -} - -void VKRenderer::submitGpuWork() -{ - m_deviceQueue.flush(); -} - -void VKRenderer::waitForGpu() -{ - m_deviceQueue.flushAndWait(); -} - -void VKRenderer::setClearColor(const float color[4]) -{ - for (int ii = 0; ii < 4; ++ii) - m_clearColor[ii] = color[ii]; -} - -void VKRenderer::clearFrame() -{ -} - -void VKRenderer::presentFrame() -{ - _endRender(); - - const bool vsync = true; - m_swapChain.present(vsync); - - _beginRender(); -} - -TextureResource::Desc VKRenderer::getSwapChainTextureDesc() -{ - TextureResource::Desc desc; - desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1); - return desc; -} - -SlangResult VKRenderer::captureScreenSurface(Surface& surfaceOut) -{ - return SLANG_FAIL; -} - -static VkBufferUsageFlagBits _calcBufferUsageFlags(Resource::BindFlag::Enum bind) -{ - typedef Resource::BindFlag BindFlag; - - switch (bind) - { - case BindFlag::VertexBuffer: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - case BindFlag::IndexBuffer: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - case BindFlag::ConstantBuffer: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - case BindFlag::StreamOutput: - case BindFlag::RenderTarget: - case BindFlag::DepthStencil: - { - assert(!"Not supported yet"); - return VkBufferUsageFlagBits(0); - } - case BindFlag::UnorderedAccess: return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; - case BindFlag::PixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - case BindFlag::NonPixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - default: return VkBufferUsageFlagBits(0); - } -} - -static VkBufferUsageFlagBits _calcBufferUsageFlags(int bindFlags) -{ - int dstFlags = 0; - while (bindFlags) - { - int lsb = bindFlags & -bindFlags; - dstFlags |= _calcBufferUsageFlags(Resource::BindFlag::Enum(lsb)); - bindFlags &= ~lsb; - } - return VkBufferUsageFlagBits(dstFlags); -} - -static VkBufferUsageFlags _calcBufferUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) -{ - VkBufferUsageFlags usage = _calcBufferUsageFlags(bindFlags); - - if (cpuAccessFlags & Resource::AccessFlag::Read) - { - // If it can be read from, set this - usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - } - if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData) - { - usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; - } - - return usage; -} - -static VkImageUsageFlagBits _calcImageUsageFlags(Resource::BindFlag::Enum bind) -{ - typedef Resource::BindFlag BindFlag; - - switch (bind) - { - case BindFlag::RenderTarget: return VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - case BindFlag::DepthStencil: return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - case BindFlag::NonPixelShaderResource: - case BindFlag::PixelShaderResource: - { - // Ignore - return VkImageUsageFlagBits(0); - } - default: - { - assert(!"Unsupported"); - return VkImageUsageFlagBits(0); - } - } -} - -static VkImageUsageFlagBits _calcImageUsageFlags(int bindFlags) -{ - int dstFlags = 0; - while (bindFlags) - { - int lsb = bindFlags & -bindFlags; - dstFlags |= _calcImageUsageFlags(Resource::BindFlag::Enum(lsb)); - bindFlags &= ~lsb; - } - return VkImageUsageFlagBits(dstFlags); -} - -static VkImageUsageFlags _calcImageUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) -{ - VkImageUsageFlags usage = _calcImageUsageFlags(bindFlags); - - usage |= VK_IMAGE_USAGE_SAMPLED_BIT; - - if (cpuAccessFlags & Resource::AccessFlag::Read) - { - // If it can be read from, set this - usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - } - if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData) - { - usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - - return usage; -} - -void VKRenderer::_transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout) -{ - VkImageMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = oldLayout; - barrier.newLayout = newLayout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = desc.numMipLevels; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - - VkPipelineStageFlags sourceStage; - VkPipelineStageFlags destinationStage; - - if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) - { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - - sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - { - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - else - { - assert(!"unsupported layout transition!"); - return; - } - - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - m_api.vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); -} - -Result VKRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) -{ - TextureResource::Desc desc(descIn); - desc.setDefaults(initialUsage); - - const VkFormat format = VulkanUtil::getVkFormat(desc.format); - if (format == VK_FORMAT_UNDEFINED) - { - assert(!"Unhandled image format"); - return SLANG_FAIL; - } - - const int arraySize = desc.calcEffectiveArraySize(); - - RefPtr texture(new TextureResourceImpl(desc, initialUsage, &m_api)); - - // Create the image - { - VkImageCreateInfo imageInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - - switch (desc.type) - { - case Resource::Type::Texture1D: - { - imageInfo.imageType = VK_IMAGE_TYPE_1D; - imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), 1, 1 }; - break; - } - case Resource::Type::Texture2D: - { - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 }; - break; - } - case Resource::Type::TextureCube: - { - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 }; - break; - } - case Resource::Type::Texture3D: - { - // Can't have an array and 3d texture - assert(desc.arraySize <= 1); - - imageInfo.imageType = VK_IMAGE_TYPE_3D; - imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), uint32_t(descIn.size.depth) }; - break; - } - default: - { - assert(!"Unhandled type"); - return SLANG_FAIL; - } - } - - imageInfo.mipLevels = desc.numMipLevels; - imageInfo.arrayLayers = arraySize; - - imageInfo.format = format; - - imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageInfo.usage = _calcImageUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData); - imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.flags = 0; // Optional - - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateImage(m_device, &imageInfo, nullptr, &texture->m_image)); - } - - VkMemoryRequirements memRequirements; - m_api.vkGetImageMemoryRequirements(m_device, texture->m_image, &memRequirements); - - // Allocate the memory - { - VkMemoryPropertyFlags reqMemoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - - VkMemoryAllocateInfo allocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; - - int memoryTypeIndex = m_api.findMemoryTypeIndex(memRequirements.memoryTypeBits, reqMemoryProperties); - assert(memoryTypeIndex >= 0); - - VkMemoryPropertyFlags actualMemoryProperites = m_api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; - - allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = memoryTypeIndex; - - SLANG_VK_RETURN_ON_FAIL(m_api.vkAllocateMemory(m_device, &allocInfo, nullptr, &texture->m_imageMemory)); - } - - // Bind the memory to the image - m_api.vkBindImageMemory(m_device, texture->m_image, texture->m_imageMemory, 0); - - if (initData) - { - List mipSizes; - - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - const int numMipMaps = desc.numMipLevels; - assert(initData->numMips == numMipMaps); - - // Calculate how large the buffer has to be - size_t bufferSize = 0; - // Calculate how large an array entry is - for (int j = 0; j < numMipMaps; ++j) - { - const TextureResource::Size mipSize = desc.size.calcMipSize(j); - - const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); - const int numRows = Surface::calcNumRows(desc.format, mipSize.height); - - mipSizes.add(mipSize); - - bufferSize += (rowSizeInBytes * numRows) * mipSize.depth; - } - - - // Calculate the total size taking into account the array - bufferSize *= arraySize; - - Buffer uploadBuffer; - SLANG_RETURN_ON_FAIL(uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); - - assert(mipSizes.getCount() == numMipMaps); - - // Copy into upload buffer - { - int subResourceIndex = 0; - - uint8_t* dstData; - m_api.vkMapMemory(m_device, uploadBuffer.m_memory, 0, bufferSize, 0, (void**)&dstData); - - for (int i = 0; i < arraySize; ++i) - { - for (Index j = 0; j < mipSizes.getCount(); ++j) - { - const auto& mipSize = mipSizes[j]; - - const ptrdiff_t srcRowStride = initData->mipRowStrides[j]; - const int dstRowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); - const int numRows = Surface::calcNumRows(desc.format, mipSize.height); - - for (int k = 0; k < mipSize.depth; k++) - { - const uint8_t* srcData = (const uint8_t*)(initData->subResources[subResourceIndex]); - - for (int l = 0; l < numRows; l++) - { - ::memcpy(dstData, srcData, dstRowSizeInBytes); - - dstData += dstRowSizeInBytes; - srcData += srcRowStride; - } - - subResourceIndex++; - } - } - } - - m_api.vkUnmapMemory(m_device, uploadBuffer.m_memory); - } - - _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - { - size_t srcOffset = 0; - for (int i = 0; i < arraySize; ++i) - { - for (Index j = 0; j < mipSizes.getCount(); ++j) - { - const auto& mipSize = mipSizes[j]; - - const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); - const int numRows = Surface::calcNumRows(desc.format, mipSize.height); - - // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkBufferImageCopy.html - // bufferRowLength and bufferImageHeight specify the data in buffer memory as a subregion of a larger two- or three-dimensional image, - // and control the addressing calculations of data in buffer memory. If either of these values is zero, that aspect of the buffer memory - // is considered to be tightly packed according to the imageExtent. - - VkBufferImageCopy region = {}; - - region.bufferOffset = srcOffset; - region.bufferRowLength = 0; //rowSizeInBytes; - region.bufferImageHeight = 0; - - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = uint32_t(j); - region.imageSubresource.baseArrayLayer = i; - region.imageSubresource.layerCount = 1; - region.imageOffset = { 0, 0, 0 }; - region.imageExtent = { uint32_t(mipSize.width), uint32_t(mipSize.height), uint32_t(mipSize.depth) }; - - // Do the copy (do all depths in a single go) - m_api.vkCmdCopyBufferToImage(commandBuffer, uploadBuffer.m_buffer, texture->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - - // Next - srcOffset += rowSizeInBytes * numRows * mipSize.depth; - } - } - } - - _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - m_deviceQueue.flushAndWait(); - } - - *outResource = texture.detach(); - return SLANG_OK; -} - -Result VKRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) -{ - BufferResource::Desc desc(descIn); - desc.setDefaults(initialUsage); - - const size_t bufferSize = desc.sizeInBytes; - - VkMemoryPropertyFlags reqMemoryProperties = 0; - - VkBufferUsageFlags usage = _calcBufferUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData); - - switch (initialUsage) - { - case Resource::Usage::ConstantBuffer: - { - reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - break; - } - default: break; - } - - RefPtr buffer(new BufferResourceImpl(initialUsage, desc, this)); - SLANG_RETURN_ON_FAIL(buffer->m_buffer.init(m_api, desc.sizeInBytes, usage, reqMemoryProperties)); - - if ((desc.cpuAccessFlags & Resource::AccessFlag::Write) || initData) - { - SLANG_RETURN_ON_FAIL(buffer->m_uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); - } - - if (initData) - { - // TODO: only create staging buffer if the memory type - // used for the buffer doesn't let us fill things in - // directly. - // Copy into staging buffer - void* mappedData = nullptr; - SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); - ::memcpy(mappedData, initData, bufferSize); - m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); - - // Copy from staging buffer to real buffer - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - VkBufferCopy copyInfo = {}; - copyInfo.size = bufferSize; - m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); - - //flushCommandBuffer(commandBuffer); - } - - *outResource = buffer.detach(); - return SLANG_OK; -} - - -VkFilter translateFilterMode(TextureFilteringMode mode) -{ - switch (mode) - { - default: - return VkFilter(0); - -#define CASE(SRC, DST) \ - case TextureFilteringMode::SRC: return VK_FILTER_##DST - - CASE(Point, NEAREST); - CASE(Linear, LINEAR); - -#undef CASE - } -} - -VkSamplerMipmapMode translateMipFilterMode(TextureFilteringMode mode) -{ - switch (mode) - { - default: - return VkSamplerMipmapMode(0); - -#define CASE(SRC, DST) \ - case TextureFilteringMode::SRC: return VK_SAMPLER_MIPMAP_MODE_##DST - - CASE(Point, NEAREST); - CASE(Linear, LINEAR); - -#undef CASE - } -} - -VkSamplerAddressMode translateAddressingMode(TextureAddressingMode mode) -{ - switch (mode) - { - default: - return VkSamplerAddressMode(0); - -#define CASE(SRC, DST) \ - case TextureAddressingMode::SRC: return VK_SAMPLER_ADDRESS_MODE_##DST - - CASE(Wrap, REPEAT); - CASE(ClampToEdge, CLAMP_TO_EDGE); - CASE(ClampToBorder, CLAMP_TO_BORDER); - CASE(MirrorRepeat, MIRRORED_REPEAT); - CASE(MirrorOnce, MIRROR_CLAMP_TO_EDGE); - -#undef CASE - } -} - -static VkCompareOp translateComparisonFunc(ComparisonFunc func) -{ - switch (func) - { - default: - // TODO: need to report failures - return VK_COMPARE_OP_ALWAYS; - -#define CASE(FROM, TO) \ - case ComparisonFunc::FROM: return VK_COMPARE_OP_##TO - - CASE(Never, NEVER); - CASE(Less, LESS); - CASE(Equal, EQUAL); - CASE(LessEqual, LESS_OR_EQUAL); - CASE(Greater, GREATER); - CASE(NotEqual, NOT_EQUAL); - CASE(GreaterEqual, GREATER_OR_EQUAL); - CASE(Always, ALWAYS); -#undef CASE - } -} - -Result VKRenderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) -{ - VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - - samplerInfo.magFilter = translateFilterMode(desc.minFilter); - samplerInfo.minFilter = translateFilterMode(desc.magFilter); - - samplerInfo.addressModeU = translateAddressingMode(desc.addressU); - samplerInfo.addressModeV = translateAddressingMode(desc.addressV); - samplerInfo.addressModeW = translateAddressingMode(desc.addressW); - - samplerInfo.anisotropyEnable = desc.maxAnisotropy > 1; - samplerInfo.maxAnisotropy = (float) desc.maxAnisotropy; - - // TODO: support translation of border color... - samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - - samplerInfo.unnormalizedCoordinates = VK_FALSE; - samplerInfo.compareEnable = desc.reductionOp == TextureReductionOp::Comparison; - samplerInfo.compareOp = translateComparisonFunc(desc.comparisonFunc); - samplerInfo.mipmapMode = translateMipFilterMode(desc.mipFilter); - - VkSampler sampler; - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &sampler)); - - RefPtr samplerImpl = new SamplerStateImpl(); - samplerImpl->m_sampler = sampler; - *outSampler = samplerImpl.detach(); - return SLANG_OK; -} - -Result VKRenderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) -{ - assert(!"unimplemented"); - return SLANG_FAIL; -} - -Result VKRenderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) -{ - auto resourceImpl = (BufferResourceImpl*) buffer; - - // TODO: These should come from the `ResourceView::Desc` - VkDeviceSize offset = 0; - VkDeviceSize size = resourceImpl->getDesc().sizeInBytes; - - // There are two different cases we need to think about for buffers. - // - // One is when we have a "uniform texel buffer" or "storage texel buffer," - // in which case we need to construct a `VkBufferView` to represent the - // formatting that is applied to the buffer. This case would correspond - // to a `textureBuffer` or `imageBuffer` in GLSL, and more or less to - // `Buffer<..>` or `RWBuffer<...>` in HLSL. - // - // The other case is a `storage buffer` which is the catch-all for any - // non-formatted R/W access to a buffer. In GLSL this is a `buffer { ... }` - // declaration, while in HLSL it covers a bunch of different `RW*Buffer` - // cases. In these cases we do *not* need a `VkBufferView`, but in - // order to be compatible with other APIs that require views for any - // potentially writable access, we will have to create one anyway. - // - // We will distinguish the two cases by looking at whether the view - // is being requested with a format or not. - // - - switch(desc.type) - { - default: - assert(!"unhandled"); - return SLANG_FAIL; - - case ResourceView::Type::UnorderedAccess: - // Is this a formatted view? - // - if(desc.format == Format::Unknown) - { - // Buffer usage that doesn't involve formatting doesn't - // require a view in Vulkan. - RefPtr viewImpl = new PlainBufferResourceViewImpl(); - viewImpl->m_buffer = resourceImpl; - viewImpl->offset = 0; - viewImpl->size = size; - *outView = viewImpl.detach(); - return SLANG_OK; - } - // - // If the view is formatted, then we need to handle - // it just like we would for a "sampled" buffer: - // - // FALLTHROUGH - case ResourceView::Type::ShaderResource: - { - VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO }; - - info.format = VulkanUtil::getVkFormat(desc.format); - info.buffer = resourceImpl->m_buffer.m_buffer; - info.offset = offset; - info.range = size; - - VkBufferView view; - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &view)); - - RefPtr viewImpl = new TexelBufferResourceViewImpl(); - viewImpl->m_buffer = resourceImpl; - viewImpl->m_view = view; - *outView = viewImpl.detach(); - return SLANG_OK; - } - break; - } -} - -Result VKRenderer::createInputLayout(const InputElementDesc* elements, UInt numElements, InputLayout** outLayout) -{ - RefPtr layout(new InputLayoutImpl); - - List& dstVertexDescs = layout->m_vertexDescs; - - size_t vertexSize = 0; - dstVertexDescs.setCount(numElements); - - for (UInt i = 0; i < numElements; ++i) - { - const InputElementDesc& srcDesc = elements[i]; - VkVertexInputAttributeDescription& dstDesc = dstVertexDescs[i]; - - dstDesc.location = uint32_t(i); - dstDesc.binding = 0; - dstDesc.format = VulkanUtil::getVkFormat(srcDesc.format); - if (dstDesc.format == VK_FORMAT_UNDEFINED) - { - return SLANG_FAIL; - } - - dstDesc.offset = uint32_t(srcDesc.offset); - - const size_t elementSize = RendererUtil::getFormatSize(srcDesc.format); - assert(elementSize > 0); - const size_t endElement = srcDesc.offset + elementSize; - - vertexSize = (vertexSize < endElement) ? endElement : vertexSize; - } - - // Work out the overall size - layout->m_vertexSize = int(vertexSize); - *outLayout = layout.detach(); - return SLANG_OK; -} - -void* VKRenderer::map(BufferResource* bufferIn, MapFlavor flavor) -{ - BufferResourceImpl* buffer = static_cast(bufferIn); - assert(buffer->m_mapFlavor == MapFlavor::Unknown); - - // Make sure everything has completed before reading... - m_deviceQueue.flushAndWait(); - - const size_t bufferSize = buffer->getDesc().sizeInBytes; - - switch (flavor) - { - case MapFlavor::WriteDiscard: - case MapFlavor::HostWrite: - { - if (!buffer->m_uploadBuffer.isInitialized()) - { - return nullptr; - } - - void* mappedData = nullptr; - SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); - buffer->m_mapFlavor = flavor; - return mappedData; - } - case MapFlavor::HostRead: - { - // Make sure there is space in the read buffer - buffer->m_readBuffer.setCount(bufferSize); - - // create staging buffer - Buffer staging; - - SLANG_RETURN_NULL_ON_FAIL(staging.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); - - // Copy from real buffer to staging buffer - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - VkBufferCopy copyInfo = {}; - copyInfo.size = bufferSize; - m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_buffer.m_buffer, staging.m_buffer, 1, ©Info); - - m_deviceQueue.flushAndWait(); - - // Write out the data from the buffer - void* mappedData = nullptr; - SLANG_VK_CHECK(m_api.vkMapMemory(m_device, staging.m_memory, 0, bufferSize, 0, &mappedData)); - - ::memcpy(buffer->m_readBuffer.getBuffer(), mappedData, bufferSize); - m_api.vkUnmapMemory(m_device, staging.m_memory); - - buffer->m_mapFlavor = flavor; - - return buffer->m_readBuffer.getBuffer(); - } - default: - return nullptr; - } -} - -void VKRenderer::unmap(BufferResource* bufferIn) -{ - BufferResourceImpl* buffer = static_cast(bufferIn); - assert(buffer->m_mapFlavor != MapFlavor::Unknown); - - const size_t bufferSize = buffer->getDesc().sizeInBytes; - - switch (buffer->m_mapFlavor) - { - case MapFlavor::WriteDiscard: - case MapFlavor::HostWrite: - { - m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); - - // Copy from staging buffer to real buffer - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - VkBufferCopy copyInfo = {}; - copyInfo.size = bufferSize; - m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); - - // TODO: is this necessary? - //m_deviceQueue.flushAndWait(); - break; - } - default: break; - } - - // Mark as no longer mapped - buffer->m_mapFlavor = MapFlavor::Unknown; -} - -void VKRenderer::setPrimitiveTopology(PrimitiveTopology topology) -{ - m_primitiveTopology = VulkanUtil::getVkPrimitiveTopology(topology); -} - -void VKRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) -{ - { - const Index num = Index(startSlot + slotCount); - if (num > m_boundVertexBuffers.getCount()) - { - m_boundVertexBuffers.setCount(num); - } - } - - for (Index i = 0; i < Index(slotCount); i++) - { - BufferResourceImpl* buffer = static_cast(buffers[i]); - if (buffer) - { - assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer); - } - - BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i]; - boundBuffer.m_buffer = buffer; - boundBuffer.m_stride = int(strides[i]); - boundBuffer.m_offset = int(offsets[i]); - } -} - -void VKRenderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) -{ -} - -void VKRenderer::setDepthStencilTarget(ResourceView* depthStencilView) -{ -} - -void VKRenderer::setViewports(UInt count, Viewport const* viewports) -{ - static const int kMaxViewports = 8; // TODO: base on device caps - assert(count <= kMaxViewports); - - VkViewport vkViewports[kMaxViewports]; - for(UInt ii = 0; ii < count; ++ii) - { - auto& inViewport = viewports[ii]; - auto& vkViewport = vkViewports[ii]; - - vkViewport.x = inViewport.originX; - vkViewport.y = inViewport.originY; - vkViewport.width = inViewport.extentX; - vkViewport.height = inViewport.extentY; - vkViewport.minDepth = inViewport.minZ; - vkViewport.maxDepth = inViewport.maxZ; - } - - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - m_api.vkCmdSetViewport(commandBuffer, 0, uint32_t(count), vkViewports); -} - -void VKRenderer::setScissorRects(UInt count, ScissorRect const* rects) -{ - static const int kMaxScissorRects = 8; // TODO: base on device caps - assert(count <= kMaxScissorRects); - - VkRect2D vkRects[kMaxScissorRects]; - for(UInt ii = 0; ii < count; ++ii) - { - auto& inRect = rects[ii]; - auto& vkRect = vkRects[ii]; - - vkRect.offset.x = int32_t(inRect.minX); - vkRect.offset.y = int32_t(inRect.minY); - vkRect.extent.width = uint32_t(inRect.maxX - inRect.minX); - vkRect.extent.height = uint32_t(inRect.maxY - inRect.minY); - } - - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - m_api.vkCmdSetScissor(commandBuffer, 0, uint32_t(count), vkRects); -} - -void VKRenderer::setPipelineState(PipelineType pipelineType, PipelineState* state) -{ - m_currentPipeline = (PipelineStateImpl*)state; -} - -void VKRenderer::draw(UInt vertexCount, UInt startVertex = 0) -{ - auto pipeline = m_currentPipeline; - if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Graphics) - { - assert(!"Invalid render pipeline"); - return; - } - - SLANG_RETURN_VOID_ON_FAIL(_beginPass()); - - // Also create descriptor sets based on the given pipeline layout - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->m_pipeline); - - auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr(); - m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayoutImpl->m_pipelineLayout, - 0, uint32_t(pipelineLayoutImpl->m_descriptorSetCount), - &m_currentDescriptorSets[0], - 0, nullptr); - - // Bind the vertex buffer - if (m_boundVertexBuffers.getCount() > 0 && m_boundVertexBuffers[0].m_buffer) - { - const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[0]; - - VkBuffer vertexBuffers[] = { boundVertexBuffer.m_buffer->m_buffer.m_buffer }; - VkDeviceSize offsets[] = { VkDeviceSize(boundVertexBuffer.m_offset) }; - - m_api.vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); - } - - m_api.vkCmdDraw(commandBuffer, static_cast(vertexCount), 1, 0, 0); - - _endPass(); -} - -void VKRenderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) -{ -} - -void VKRenderer::dispatchCompute(int x, int y, int z) -{ - auto pipeline = m_currentPipeline; - if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Compute) - { - assert(!"Invalid compute pipeline"); - return; - } - - // Also create descriptor sets based on the given pipeline layout - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->m_pipeline); - - auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr(); - m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayoutImpl->m_pipelineLayout, - 0, uint32_t(pipelineLayoutImpl->m_descriptorSetCount), - &m_currentDescriptorSets[0], - 0, nullptr); - - m_api.vkCmdDispatch(commandBuffer, x, y, z); -} - -static VkImageViewType _calcImageViewType(TextureResource::Type type, const TextureResource::Desc& desc) -{ - switch (type) - { - case Resource::Type::Texture1D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D; - case Resource::Type::Texture2D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; - case Resource::Type::TextureCube: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE; - case Resource::Type::Texture3D: - { - // Can't have an array and 3d texture - assert(desc.arraySize <= 1); - if (desc.arraySize <= 1) - { - return VK_IMAGE_VIEW_TYPE_3D; - } - break; - } - default: break; - } - - return VK_IMAGE_VIEW_TYPE_MAX_ENUM; -} - -#if 0 -BindingState* VKRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc, &m_api)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - auto& dstDetail = dstDetails[i]; - const auto& srcBinding = srcBindings[i]; - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - if (!srcBinding.resource || !srcBinding.resource->isBuffer()) - { - assert(!"Needs to have a buffer resource set"); - return nullptr; - } - - BufferResourceImpl* bufferResource = static_cast(srcBinding.resource.Ptr()); - const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); - - if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - // VkBufferView uav - - VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO }; - - info.format = VK_FORMAT_R32_SFLOAT; - // TODO: - // Not sure how to handle typeless? - if (bufferResourceDesc.elementSize == 0) - { - info.format = VK_FORMAT_R32_SFLOAT; // DXGI_FORMAT_R32_TYPELESS ? - } - - info.buffer = bufferResource->m_buffer.m_buffer; - info.offset = 0; - info.range = bufferResourceDesc.sizeInBytes; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &dstDetail.m_uav)); - } - - // TODO: Setup views. - // VkImageView srv - - - break; - } - case BindingType::Sampler: - { - VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - - samplerInfo.magFilter = VK_FILTER_LINEAR; - samplerInfo.minFilter = VK_FILTER_LINEAR; - - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - - samplerInfo.anisotropyEnable = VK_FALSE; - samplerInfo.maxAnisotropy = 1; - - samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - samplerInfo.unnormalizedCoordinates = VK_FALSE; - samplerInfo.compareEnable = VK_FALSE; - samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &dstDetail.m_sampler)); - - break; - } - case BindingType::Texture: - { - if (!srcBinding.resource || !srcBinding.resource->isTexture()) - { - assert(!"Needs to have a texture resource set"); - return nullptr; - } - - TextureResourceImpl* textureResource = static_cast(srcBinding.resource.Ptr()); - const TextureResource::Desc& texDesc = textureResource->getDesc(); - - VkImageViewType imageViewType = _calcImageViewType(textureResource->getType(), texDesc); - if (imageViewType == VK_IMAGE_VIEW_TYPE_MAX_ENUM) - { - assert(!"Invalid view type"); - return nullptr; - } - const VkFormat format = VulkanUtil::getVkFormat(texDesc.format); - if (format == VK_FORMAT_UNDEFINED) - { - assert(!"Unhandled image format"); - return nullptr; - } - - // Create the image view - - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.image = textureResource->m_image; - viewInfo.viewType = imageViewType; - viewInfo.format = format; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateImageView(m_device, &viewInfo, nullptr, &dstDetail.m_srv)); - - break; - } - case BindingType::CombinedTextureSampler: - { - assert(!"not implemented"); - return nullptr; - } - } - } - - return bindingState.detach();; -} -#endif - -static VkDescriptorType translateDescriptorType(DescriptorSlotType type) -{ - switch(type) - { - default: - return VK_DESCRIPTOR_TYPE_MAX_ENUM; - -#define CASE(SRC, DST) \ - case DescriptorSlotType::SRC: return VK_DESCRIPTOR_TYPE_##DST - - CASE(Sampler, SAMPLER); - CASE(CombinedImageSampler, COMBINED_IMAGE_SAMPLER); - CASE(SampledImage, SAMPLED_IMAGE); - CASE(StorageImage, STORAGE_IMAGE); - CASE(UniformTexelBuffer, UNIFORM_TEXEL_BUFFER); - CASE(StorageTexelBuffer, STORAGE_TEXEL_BUFFER); - CASE(UniformBuffer, UNIFORM_BUFFER); - CASE(StorageBuffer, STORAGE_BUFFER); - CASE(DynamicUniformBuffer, UNIFORM_BUFFER_DYNAMIC); - CASE(DynamicStorageBuffer, STORAGE_BUFFER_DYNAMIC); - CASE(InputAttachment, INPUT_ATTACHMENT); - -#undef CASE - } -} - -Result VKRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) -{ - RefPtr descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(m_api); - - Slang::List dstBindings; - - uint32_t descriptorCountForTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE] = { 0, }; - - UInt rangeCount = desc.slotRangeCount; - for(UInt rr = 0; rr < rangeCount; ++rr) - { - auto& srcRange = desc.slotRanges[rr]; - - VkDescriptorType dstDescriptorType = translateDescriptorType(srcRange.type); - - VkDescriptorSetLayoutBinding dstBinding; - dstBinding.binding = uint32_t(rr); - dstBinding.descriptorType = dstDescriptorType; - dstBinding.descriptorCount = uint32_t(srcRange.count); - dstBinding.stageFlags = VK_SHADER_STAGE_ALL; - dstBinding.pImmutableSamplers = nullptr; - - descriptorCountForTypes[dstDescriptorType] += uint32_t(srcRange.count); - - dstBindings.add(dstBinding); - - DescriptorSetLayoutImpl::RangeInfo rangeInfo; - rangeInfo.descriptorType = dstDescriptorType; - descriptorSetLayoutImpl->m_ranges.add(rangeInfo); - } - - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; - descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.getCount()); - descriptorSetLayoutInfo.pBindings = dstBindings.getBuffer(); - - VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; - SLANG_VK_CHECK(m_api.vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout)); - - // Create a pool while we are at it, to allocate descriptor sets of this type. - - VkDescriptorPoolSize poolSizes[VK_DESCRIPTOR_TYPE_RANGE_SIZE]; - uint32_t poolSizeCount = 0; - for (int ii = 0; ii < SLANG_COUNT_OF(descriptorCountForTypes); ++ii) - { - auto descriptorCount = descriptorCountForTypes[ii]; - if (descriptorCount > 0) - { - poolSizes[poolSizeCount].type = VkDescriptorType(ii); - poolSizes[poolSizeCount].descriptorCount = descriptorCount; - poolSizeCount++; - } - } - - VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size. - descriptorPoolInfo.poolSizeCount = poolSizeCount; - descriptorPoolInfo.pPoolSizes = &poolSizes[0]; - - VkDescriptorPool descriptorPool = VK_NULL_HANDLE; - SLANG_VK_CHECK(m_api.vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &descriptorPool)); - - descriptorSetLayoutImpl->m_descriptorSetLayout = descriptorSetLayout; - descriptorSetLayoutImpl->m_descriptorPool = descriptorPool; - - *outLayout = descriptorSetLayoutImpl.detach(); - return SLANG_OK; -} - -Result VKRenderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) -{ - UInt descriptorSetCount = desc.descriptorSetCount; - - VkDescriptorSetLayout descriptorSetLayouts[kMaxDescriptorSets]; - for(UInt ii = 0; ii < descriptorSetCount; ++ii) - { - descriptorSetLayouts[ii] = ((DescriptorSetLayoutImpl*) desc.descriptorSets[ii].layout)->m_descriptorSetLayout; - } - - VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; - pipelineLayoutInfo.setLayoutCount = uint32_t(desc.descriptorSetCount); - pipelineLayoutInfo.pSetLayouts = &descriptorSetLayouts[0]; - - VkPipelineLayout pipelineLayout; - SLANG_VK_CHECK(m_api.vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); - - RefPtr pipelineLayoutImpl = new PipelineLayoutImpl(m_api); - pipelineLayoutImpl->m_pipelineLayout = pipelineLayout; - pipelineLayoutImpl->m_descriptorSetCount = descriptorSetCount; - - *outLayout = pipelineLayoutImpl.detach(); - return SLANG_OK; -} - -Result VKRenderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) -{ - auto layoutImpl = (DescriptorSetLayoutImpl*)layout; - - VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; - descriptorSetAllocInfo.descriptorPool = layoutImpl->m_descriptorPool; - descriptorSetAllocInfo.descriptorSetCount = 1; - descriptorSetAllocInfo.pSetLayouts = &layoutImpl->m_descriptorSetLayout; - - VkDescriptorSet descriptorSet; - SLANG_VK_CHECK(m_api.vkAllocateDescriptorSets(m_device, &descriptorSetAllocInfo, &descriptorSet)); - - RefPtr descriptorSetImpl = new DescriptorSetImpl(this); - descriptorSetImpl->m_layout = layoutImpl; - descriptorSetImpl->m_descriptorSet = descriptorSet; - *outDescriptorSet = descriptorSetImpl.detach(); - return SLANG_OK; -} - -/* static */VKRenderer::DescriptorSetImpl::Binding::Type VKRenderer::DescriptorSetImpl::_getBindingType(RefObject* ptr) -{ - typedef Binding::Type Type; - - if (ptr) - { - if (dynamic_cast(ptr)) - { - return Type::ResourceView; - } - else if (dynamic_cast(ptr)) - { - return Type::BufferResource; - } - else if (dynamic_cast(ptr)) - { - return Type::SamplerState; - } - } - return Type::Unknown; -} - -void VKRenderer::DescriptorSetImpl::_setBinding(Binding::Type type, UInt range, UInt index, RefObject* ptr) -{ - SLANG_ASSERT(ptr == nullptr || _getBindingType(ptr) == type); - - const Index numBindings = m_bindings.getCount(); - for (Index i = 0; i < numBindings; ++i) - { - Binding& binding = m_bindings[i]; - - if (binding.type == type && binding.range == uint32_t(range) && binding.index == uint32_t(index)) - { - if (ptr) - { - binding.obj = ptr; - } - else - { - m_bindings.removeAt(i); - } - - return; - } - } - - // If an entry is not found, and we have a pointer, create an entry - if (ptr) - { - Binding binding; - binding.type = type; - binding.range = uint32_t(range); - binding.index = uint32_t(index); - binding.obj = ptr; - - m_bindings.add(binding); - } -} - -void VKRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) -{ - auto bufferImpl = (BufferResourceImpl*)buffer; - - VkDescriptorBufferInfo bufferInfo = {}; - bufferInfo.buffer = bufferImpl->m_buffer.m_buffer; - bufferInfo.offset = 0; - bufferInfo.range = bufferImpl->getDesc().sizeInBytes; - - VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - writeInfo.dstSet = m_descriptorSet; - writeInfo.dstBinding = uint32_t(range); - writeInfo.dstArrayElement = uint32_t(index); - writeInfo.descriptorCount = 1; - writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; - writeInfo.pBufferInfo = &bufferInfo; - - m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); - - _setBinding(Binding::Type::BufferResource, range, index, buffer); -} - -void VKRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) -{ - auto viewImpl = (ResourceViewImpl*)view; - switch (viewImpl->m_type) - { - case ResourceViewImpl::ViewType::Texture: - { - auto textureViewImpl = (TextureResourceViewImpl*)viewImpl; - VkDescriptorImageInfo imageInfo = {}; - imageInfo.imageView = textureViewImpl->m_view; - imageInfo.imageLayout = textureViewImpl->m_layout; - // imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - writeInfo.dstSet = m_descriptorSet; - writeInfo.dstBinding = uint32_t(range); - writeInfo.dstArrayElement = uint32_t(index); - writeInfo.descriptorCount = 1; - writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; - writeInfo.pImageInfo = &imageInfo; - - m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); - } - break; - - case ResourceViewImpl::ViewType::TexelBuffer: - { - auto bufferViewImpl = (TexelBufferResourceViewImpl*)viewImpl; - - VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - writeInfo.dstSet = m_descriptorSet; - writeInfo.dstBinding = uint32_t(range); - writeInfo.dstArrayElement = uint32_t(index); - writeInfo.descriptorCount = 1; - writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; - writeInfo.pTexelBufferView = &bufferViewImpl->m_view; - - m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); - } - break; - - case ResourceViewImpl::ViewType::PlainBuffer: - { - auto bufferViewImpl = (PlainBufferResourceViewImpl*) viewImpl; - - VkDescriptorBufferInfo bufferInfo = {}; - bufferInfo.buffer = bufferViewImpl->m_buffer->m_buffer.m_buffer; - bufferInfo.offset = bufferViewImpl->offset; - bufferInfo.range = bufferViewImpl->size; - - VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - writeInfo.dstSet = m_descriptorSet; - writeInfo.dstBinding = uint32_t(range); - writeInfo.dstArrayElement = uint32_t(index); - writeInfo.descriptorCount = 1; - writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; - writeInfo.pBufferInfo = &bufferInfo; - - m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); - } - break; - - } - - _setBinding(Binding::Type::ResourceView, range, index, view); -} - -void VKRenderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) -{ - - _setBinding(Binding::Type::SamplerState, range, index, sampler); -} - -void VKRenderer::DescriptorSetImpl::setCombinedTextureSampler( - UInt range, - UInt index, - ResourceView* textureView, - SamplerState* sampler) -{ - - _setBinding(Binding::Type::SamplerState, range, index, sampler); - _setBinding(Binding::Type::ResourceView, range, index, textureView); -} - -void VKRenderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) -{ - // Ideally this should eventually be as simple as: - // - // m_api.vkCmdBindDescriptorSets( - // commandBuffer, - // translatePipelineBindPoint(pipelineType), - // layout->m_pipelineLayout, - // index, - // 1, - // ((DescriptorSetImpl*) descriptorSet)->m_descriptorSet, - // 0, - // nullptr); - // - // For now we are lazily flushing state right before drawing, so - // we will hang onto the parameters that were passed in and then - // use them later. - // - - auto descriptorSetImpl = (DescriptorSetImpl*)descriptorSet; - m_currentDescriptorSetImpls[index] = descriptorSetImpl; - m_currentDescriptorSets[index] = descriptorSetImpl->m_descriptorSet; -} - -Result VKRenderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) -{ - RefPtr impl = new ShaderProgramImpl(desc.pipelineType); - if( desc.pipelineType == PipelineType::Compute) - { - auto computeKernel = desc.findKernel(StageType::Compute); - impl->m_compute = compileEntryPoint(*computeKernel, VK_SHADER_STAGE_COMPUTE_BIT, impl->m_buffers[0]); - } - else - { - auto vertexKernel = desc.findKernel(StageType::Vertex); - auto fragmentKernel = desc.findKernel(StageType::Fragment); - - impl->m_vertex = compileEntryPoint(*vertexKernel, VK_SHADER_STAGE_VERTEX_BIT, impl->m_buffers[0]); - impl->m_fragment = compileEntryPoint(*fragmentKernel, VK_SHADER_STAGE_FRAGMENT_BIT, impl->m_buffers[1]); - } - *outProgram = impl.detach(); - return SLANG_OK; -} - -Result VKRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) -{ - VkPipelineCache pipelineCache = VK_NULL_HANDLE; - - auto programImpl = (ShaderProgramImpl*) desc.program; - auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; - auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; - - const int width = int(desc.framebufferWidth); - const int height = int(desc.framebufferHeight); - - // Shader Stages - // - // Currently only handles vertex/fragment. - - static const uint32_t kMaxShaderStages = 2; - VkPipelineShaderStageCreateInfo shaderStages[kMaxShaderStages]; - - uint32_t shaderStageCount = 0; - shaderStages[shaderStageCount++] = programImpl->m_vertex; - shaderStages[shaderStageCount++] = programImpl->m_fragment; - - // VertexBuffer/s - // Currently only handles one - - VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.vertexAttributeDescriptionCount = 0; - - VkVertexInputBindingDescription vertexInputBindingDescription; - - if (inputLayoutImpl) - { - vertexInputBindingDescription.binding = 0; - vertexInputBindingDescription.stride = inputLayoutImpl->m_vertexSize; - vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - const auto& srcAttributeDescs = inputLayoutImpl->m_vertexDescs; - - vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription; - - vertexInputInfo.vertexAttributeDescriptionCount = static_cast(srcAttributeDescs.getCount()); - vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.getBuffer(); - } - - VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = (float)width; - viewport.height = (float)height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset = { 0, 0 }; - scissor.extent = { uint32_t(width), uint32_t(height) }; - - VkPipelineViewportStateCreateInfo viewportState = {}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; - viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; - - VkPipelineRasterizationStateCreateInfo rasterizer = {}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_NONE; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - - VkPipelineMultisampleStateCreateInfo multisampling = {}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo colorBlending = {}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; - - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = shaderStages; - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout; - pipelineInfo.renderPass = m_renderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - - VkPipeline pipeline = VK_NULL_HANDLE; - SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline)); - - RefPtr pipelineStateImpl = new PipelineStateImpl(m_api); - pipelineStateImpl->m_pipeline = pipeline; - pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; - pipelineStateImpl->m_shaderProgram = programImpl; - *outState = pipelineStateImpl.detach(); - return SLANG_OK; -} - -Result VKRenderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) -{ - VkPipelineCache pipelineCache = VK_NULL_HANDLE; - - auto programImpl = (ShaderProgramImpl*) desc.program; - auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; - - VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; - computePipelineInfo.stage = programImpl->m_compute; - computePipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout; - - VkPipeline pipeline = VK_NULL_HANDLE; - SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline)); - - RefPtr pipelineStateImpl = new PipelineStateImpl(m_api); - pipelineStateImpl->m_pipeline = pipeline; - pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; - pipelineStateImpl->m_shaderProgram = programImpl; - *outState = pipelineStateImpl.detach(); - return SLANG_OK; -} - - -#if 0 - else if (m_currentProgram->m_pipelineType == PipelineType::Graphics) - { - // Create the graphics pipeline - - const int width = m_swapChain.getWidth(); - const int height = m_swapChain.getHeight(); - - - - - - // - - - } - else - { - assert(!"Unhandled program type"); - return SLANG_FAIL; - } - - pipelineOut = pipeline; - return SLANG_OK; - - -#endif - -} // renderer_test diff --git a/tools/gfx/render-vk.h b/tools/gfx/render-vk.h deleted file mode 100644 index 14a8e403a..000000000 --- a/tools/gfx/render-vk.h +++ /dev/null @@ -1,10 +0,0 @@ -// render-vk.h -#pragma once - -namespace gfx { - -class Renderer; - -Renderer* createVKRenderer(); - -} // gfx diff --git a/tools/gfx/render.cpp b/tools/gfx/render.cpp index 44bb4fb04..785343927 100644 --- a/tools/gfx/render.cpp +++ b/tools/gfx/render.cpp @@ -3,6 +3,11 @@ #include "../../source/core/slang-math.h" +#include "d3d11/render-d3d11.h" +#include "d3d12/render-d3d12.h" +#include "open-gl/render-gl.h" +#include "vulkan/render-vk.h" + namespace gfx { using namespace Slang; @@ -404,4 +409,32 @@ ProjectionStyle RendererUtil::getProjectionStyle(RendererType type) } } +/* static */ RendererUtil::CreateFunc RendererUtil::getCreateFunc(RendererType type) +{ + switch (type) + { +#if SLANG_WINDOWS_FAMILY + case RendererType::DirectX11: + { + return &createD3D11Renderer; + } + case RendererType::DirectX12: + { + return &createD3D12Renderer; + } + case RendererType::OpenGl: + { + return &createGLRenderer; + } + case RendererType::Vulkan: + { + return &createVKRenderer; + } +#endif + + default: return nullptr; + } +} + + } // renderer_test diff --git a/tools/gfx/render.h b/tools/gfx/render.h index 4a8458e96..42cf5fe60 100644 --- a/tools/gfx/render.h +++ b/tools/gfx/render.h @@ -972,6 +972,8 @@ inline void Renderer::setVertexBuffer(UInt slot, BufferResource* buffer, UInt st /// Functions that are around Renderer and it's types struct RendererUtil { + typedef Renderer* (*CreateFunc)(); + /// Gets the size in bytes of a Format type. Returns 0 if a size is not defined/invalid SLANG_FORCE_INLINE static size_t getFormatSize(Format format) { return s_formatSize[int(format)]; } /// Given a renderer type, gets a projection style @@ -986,6 +988,9 @@ struct RendererUtil /// Get as text static Slang::UnownedStringSlice toText(RendererType type); + /// Given a type returns a function that can construct it, or nullptr if there isn't one + static CreateFunc getCreateFunc(RendererType type); + private: static void compileTimeAsserts(); static const uint8_t s_formatSize[]; // Maps Format::XXX to a size in bytes; diff --git a/tools/gfx/resource-d3d12.cpp b/tools/gfx/resource-d3d12.cpp deleted file mode 100644 index 27de868b6..000000000 --- a/tools/gfx/resource-d3d12.cpp +++ /dev/null @@ -1,214 +0,0 @@ -// resource-d3d12.cpp -#include "resource-d3d12.h" - -namespace gfx { -using namespace Slang; - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12BarrierSubmitter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -void D3D12BarrierSubmitter::_flush() -{ - assert(m_numBarriers > 0); - - if (m_commandList) - { - m_commandList->ResourceBarrier(UINT(m_numBarriers), m_barriers); - } - m_numBarriers = 0; -} - -D3D12_RESOURCE_BARRIER& D3D12BarrierSubmitter::_expandOne() -{ - _flush(); - return m_barriers[m_numBarriers++]; -} - -void D3D12BarrierSubmitter::transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState) -{ - if (nextState != prevState) - { - D3D12_RESOURCE_BARRIER& barrier = expandOne(); - - const UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - const D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - - ::memset(&barrier, 0, sizeof(barrier)); - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = flags; - barrier.Transition.pResource = resource; - barrier.Transition.StateBefore = prevState; - barrier.Transition.StateAfter = nextState; - barrier.Transition.Subresource = subresource; - } - else - { - if (nextState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS) - { - D3D12_RESOURCE_BARRIER& barrier = expandOne(); - - ::memset(&barrier, 0, sizeof(barrier)); - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; - barrier.UAV.pResource = resource; - } - } -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12ResourceBase !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -/* static */DXGI_FORMAT D3D12ResourceBase::calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource) -{ - return resource ? D3DUtil::calcFormat(usage, resource->GetDesc().Format) : DXGI_FORMAT_UNKNOWN; -} - -void D3D12ResourceBase::transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter) -{ - // Transition only if there is a resource - if (m_resource) - { - submitter.transition(m_resource, m_state, nextState); - m_state = nextState; - } -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12CounterFence !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -D3D12CounterFence::~D3D12CounterFence() -{ - if (m_event) - { - CloseHandle(m_event); - } -} - -Result D3D12CounterFence::init(ID3D12Device* device, uint64_t initialValue) -{ - m_currentValue = initialValue; - - SLANG_RETURN_ON_FAIL(device->CreateFence(m_currentValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef()))); - // Create an event handle to use for frame synchronization. - m_event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (m_event == nullptr) - { - Result res = HRESULT_FROM_WIN32(GetLastError()); - return SLANG_FAILED(res) ? res : SLANG_FAIL; - } - return SLANG_OK; -} - -UInt64 D3D12CounterFence::nextSignal(ID3D12CommandQueue* commandQueue) -{ - // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >= - m_currentValue++; - // Schedule a Signal command in the queue. - Result res = commandQueue->Signal(m_fence, m_currentValue); - if (SLANG_FAILED(res)) - { - assert(!"Signal failed"); - } - return m_currentValue; -} - -void D3D12CounterFence::waitUntilCompleted(uint64_t completedValue) -{ - // You can only wait for a value that is less than or equal to the current value - assert(completedValue <= m_currentValue); - - // Wait until the previous frame is finished. - while (m_fence->GetCompletedValue() < completedValue) - { - // Make it signal with the current value - SLANG_ASSERT_VOID_ON_FAIL(m_fence->SetEventOnCompletion(completedValue, m_event)); - WaitForSingleObject(m_event, INFINITE); - } -} - -void D3D12CounterFence::nextSignalAndWait(ID3D12CommandQueue* commandQueue) -{ - waitUntilCompleted(nextSignal(commandQueue)); -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!! D3D12Resource !!!!!!!!!!!!!!!!!!!!!!!! */ - -/* static */void D3D12Resource::setDebugName(ID3D12Resource* resource, const char* name) -{ - if (resource) - { - size_t len = ::strlen(name); - List buf; - buf.setCount(len + 1); - - D3DUtil::appendWideChars(name, buf); - resource->SetName(buf.begin()); - } -} - -void D3D12Resource::setDebugName(const char* name) -{ - setDebugName(m_resource, name); -} - -void D3D12Resource::setDebugName(const wchar_t* name) -{ - if (m_resource) - { - m_resource->SetName(name); - } -} - -void D3D12Resource::setResource(ID3D12Resource* resource, D3D12_RESOURCE_STATES initialState) -{ - if (resource != m_resource) - { - if (resource) - { - resource->AddRef(); - } - if (m_resource) - { - m_resource->Release(); - } - m_resource = resource; - } - m_prevState = initialState; - m_state = initialState; -} - -void D3D12Resource::setResourceNull() -{ - if (m_resource) - { - m_resource->Release(); - m_resource = nullptr; - } -} - -Result D3D12Resource::initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue) -{ - setResourceNull(); - ComPtr resource; - SLANG_RETURN_ON_FAIL(device->CreateCommittedResource(&heapProps, heapFlags, &resourceDesc, initState, clearValue, IID_PPV_ARGS(resource.writeRef()))); - setResource(resource, initState); - return SLANG_OK; -} - -ID3D12Resource* D3D12Resource::detach() -{ - ID3D12Resource* resource = m_resource; - m_resource = nullptr; - return resource; -} - -void D3D12Resource::swap(ComPtr& resourceInOut) -{ - ID3D12Resource* tmp = m_resource; - m_resource = resourceInOut.detach(); - resourceInOut.attach(tmp); -} - -void D3D12Resource::setState(D3D12_RESOURCE_STATES state) -{ - m_prevState = state; - m_state = state; -} - -} // renderer_test diff --git a/tools/gfx/resource-d3d12.h b/tools/gfx/resource-d3d12.h deleted file mode 100644 index 1764adf9d..000000000 --- a/tools/gfx/resource-d3d12.h +++ /dev/null @@ -1,178 +0,0 @@ -// resource-d3d12.h -#pragma once - -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#include -#include - -#include "../../slang-com-ptr.h" -#include "d3d-util.h" - -namespace gfx { - -// Enables more conservative barriers - restoring the state of resources after they are used. -// Should not need to be enabled in normal builds, as the barriers should correctly sync resources -// If enabling fixes an issue it implies regular barriers are not correctly used. -#define SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS 0 - -struct D3D12BarrierSubmitter -{ - enum { MAX_BARRIERS = 8 }; - - /// Expand one space to hold a barrier - SLANG_FORCE_INLINE D3D12_RESOURCE_BARRIER& expandOne() { return (m_numBarriers < MAX_BARRIERS) ? m_barriers[m_numBarriers++] : _expandOne(); } - /// Flush barriers to command list - SLANG_FORCE_INLINE void flush() { if (m_numBarriers > 0) _flush(); } - - /// Transition resource from prevState to nextState - void transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState); - - /// Ctor - SLANG_FORCE_INLINE D3D12BarrierSubmitter(ID3D12GraphicsCommandList* commandList) : m_numBarriers(0), m_commandList(commandList) { } - /// Dtor - SLANG_FORCE_INLINE ~D3D12BarrierSubmitter() { flush(); } - -protected: - D3D12_RESOURCE_BARRIER& _expandOne(); - void _flush(); - - ID3D12GraphicsCommandList* m_commandList; - int m_numBarriers; - D3D12_RESOURCE_BARRIER m_barriers[MAX_BARRIERS]; -}; - -/*! \brief A class to simplify using Dx12 fences. - -A fence is a mechanism to track GPU work. This is achieved by having a counter that the CPU holds -called the current value. Calling nextSignal will increase the CPU counter, and add a fence -with that value to the commandQueue. When the GPU has completed all the work before the fence it will -update the completed value. This is typically used when -the CPU needs to know the GPU has finished some piece of work has completed. To do this the CPU -can check the completed value, and when it is greater or equal to the value returned by nextSignal the -CPU will know that all the work prior to when the nextSignal was added to the queue will have completed. - -NOTE! This cannot be used across threads, as for amongst other reasons SetEventOnCompletion -only works with a single value. - -Signal on the CommandQueue updates the fence on the GPU side. Signal on the fence object changes -the value on the CPU side (not used here). - -Useful article describing how Dx12 synchronization works: -https://msdn.microsoft.com/en-us/library/windows/desktop/dn899217%28v=vs.85%29.aspx -*/ -class D3D12CounterFence -{ -public: - /// Must be called before used - SlangResult init(ID3D12Device* device, uint64_t initialValue = 0); - /// Increases the counter, signals the queue and waits for the signal to be hit - void nextSignalAndWait(ID3D12CommandQueue* queue); - /// Signals with next counter value. Returns the value the signal was called on - uint64_t nextSignal(ID3D12CommandQueue* commandQueue); - /// Get the current value - SLANG_FORCE_INLINE uint64_t getCurrentValue() const { return m_currentValue; } - /// Get the completed value - SLANG_FORCE_INLINE uint64_t getCompletedValue() const { return m_fence->GetCompletedValue(); } - - /// Waits for the the specified value - void waitUntilCompleted(uint64_t completedValue); - - /// Ctor - D3D12CounterFence() :m_event(nullptr), m_currentValue(0) {} - /// Dtor - ~D3D12CounterFence(); - -protected: - HANDLE m_event; - Slang::ComPtr m_fence; - UINT64 m_currentValue; -}; - -/** The base class for resource types allows for tracking of state. It does not allow for setting of the resource though, such that -an interface can return a D3D12ResourceBase, and a client cant manipulate it's state, but it cannot replace/change the actual resource */ -struct D3D12ResourceBase -{ - /// Add a transition if necessary to the list - void transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter); - /// Get the current state - SLANG_FORCE_INLINE D3D12_RESOURCE_STATES getState() const { return m_state; } - - /// Get the associated resource - SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_resource; } - - /// True if a resource is set - SLANG_FORCE_INLINE bool isSet() const { return m_resource != nullptr; } - - /// Coercible into ID3D12Resource - SLANG_FORCE_INLINE operator ID3D12Resource*() const { return m_resource; } - - /// restore previous state -#if SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS - SLANG_FORCE_INLINE Void restore(D3D12BarrierSubmitter& submitter) { transition(m_prevState, submitter); } -#else - SLANG_FORCE_INLINE void restore(D3D12BarrierSubmitter& submitter) { SLANG_UNUSED(submitter) } -#endif - - /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible - static DXGI_FORMAT calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource); - - /// Ctor - SLANG_FORCE_INLINE D3D12ResourceBase() : - m_state(D3D12_RESOURCE_STATE_COMMON), - m_prevState(D3D12_RESOURCE_STATE_COMMON), - m_resource(nullptr) - {} - -protected: - /// This is protected so as clients cannot slice the class, and so state tracking is lost - ~D3D12ResourceBase() {} - - ID3D12Resource* m_resource; ///< The resource (ref counted) - D3D12_RESOURCE_STATES m_state; ///< The current tracked expected state, if all associated transitions have completed on ID3D12CommandList - D3D12_RESOURCE_STATES m_prevState; ///< The previous state -}; - -struct D3D12Resource : public D3D12ResourceBase -{ - - /// Dtor - ~D3D12Resource() - { - if (m_resource) - { - m_resource->Release(); - } - } - - /// Initialize as committed resource - Slang::Result initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue); - - /// Set a resource with an initial state - void setResource(ID3D12Resource* resource, D3D12_RESOURCE_STATES initialState); - /// Make the resource null - void setResourceNull(); - /// Returns the attached resource (with any ref counts) and sets to nullptr on this. - ID3D12Resource* detach(); - - /// Swaps the resource contents with the contents of the smart pointer - void swap(Slang::ComPtr& resourceInOut); - - /// Sets the current state of the resource (the current state is taken to be the future state once the command list has executed) - /// NOTE! This must be used with care, otherwise state tracking can be made incorrect. - void setState(D3D12_RESOURCE_STATES state); - - /// Set the debug name on a resource - static void setDebugName(ID3D12Resource* resource, const char* name); - - /// Set the the debug name on the resource - void setDebugName(const wchar_t* name); - /// Set the debug name - void setDebugName(const char* name); -}; - -} // renderer_test diff --git a/tools/gfx/vk-api.cpp b/tools/gfx/vk-api.cpp deleted file mode 100644 index 50f80aa26..000000000 --- a/tools/gfx/vk-api.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// vk-api.cpp -#include "vk-api.h" - -#include "../../source/core/slang-list.h" - -namespace gfx { -using namespace Slang; - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanApi !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -#define VK_API_CHECK_FUNCTION(x) && (x != nullptr) -#define VK_API_CHECK_FUNCTIONS(FUNCTION_LIST) true FUNCTION_LIST(VK_API_CHECK_FUNCTION) - -bool VulkanApi::areDefined(ProcType type) const -{ - switch (type) - { - case ProcType::Global: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_GLOBAL_PROCS); - case ProcType::Instance: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_INSTANCE_PROCS); - case ProcType::Device: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_DEVICE_PROCS); - default: - { - assert(!"Unhandled type"); - return false; - } - } -} - -Slang::Result VulkanApi::initGlobalProcs(const VulkanModule& module) -{ -#define VK_API_GET_GLOBAL_PROC(x) x = (PFN_##x)module.getFunction(#x); - - // Initialize all the global functions - VK_API_ALL_GLOBAL_PROCS(VK_API_GET_GLOBAL_PROC) - - if (!areDefined(ProcType::Global)) - { - return SLANG_FAIL; - } - m_module = &module; - return SLANG_OK; -} - -Slang::Result VulkanApi::initInstanceProcs(VkInstance instance) -{ - assert(instance && vkGetInstanceProcAddr != nullptr); - -#define VK_API_GET_INSTANCE_PROC(x) x = (PFN_##x)vkGetInstanceProcAddr(instance, #x); - - VK_API_ALL_INSTANCE_PROCS(VK_API_GET_INSTANCE_PROC) - - // Get optional - VK_API_INSTANCE_PROCS_OPT(VK_API_GET_INSTANCE_PROC) - - if (!areDefined(ProcType::Instance)) - { - return SLANG_FAIL; - } - - - m_instance = instance; - return SLANG_OK; -} - -Slang::Result VulkanApi::initPhysicalDevice(VkPhysicalDevice physicalDevice) -{ - assert(m_physicalDevice == VK_NULL_HANDLE); - m_physicalDevice = physicalDevice; - - vkGetPhysicalDeviceProperties(m_physicalDevice, &m_deviceProperties); - vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_deviceFeatures); - vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_deviceMemoryProperties); - - return SLANG_OK; -} - -Slang::Result VulkanApi::initDeviceProcs(VkDevice device) -{ - assert(m_instance && device && vkGetDeviceProcAddr != nullptr); - -#define VK_API_GET_DEVICE_PROC(x) x = (PFN_##x)vkGetDeviceProcAddr(device, #x); - - VK_API_ALL_DEVICE_PROCS(VK_API_GET_DEVICE_PROC) - - if (!areDefined(ProcType::Device)) - { - return SLANG_FAIL; - } - - m_device = device; - return SLANG_OK; -} - -int VulkanApi::findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const -{ - assert(typeBits); - - const int numMemoryTypes = int(m_deviceMemoryProperties.memoryTypeCount); - - // bit holds current test bit against typeBits. Ie bit == 1 << typeBits - - uint32_t bit = 1; - for (int i = 0; i < numMemoryTypes; ++i, bit += bit) - { - auto const& memoryType = m_deviceMemoryProperties.memoryTypes[i]; - if ((typeBits & bit) && (memoryType.propertyFlags & properties) == properties) - { - return i; - } - } - - //assert(!"failed to find a usable memory type"); - return -1; -} - -int VulkanApi::findQueue(VkQueueFlags reqFlags) const -{ - assert(m_physicalDevice != VK_NULL_HANDLE); - - uint32_t numQueueFamilies = 0; - vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, nullptr); - - Slang::List queueFamilies; - queueFamilies.setCount(numQueueFamilies); - vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, queueFamilies.getBuffer()); - - // Find a queue that can service our needs - //VkQueueFlags reqQueueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; - - int queueFamilyIndex = -1; - for (int i = 0; i < int(numQueueFamilies); ++i) - { - if ((queueFamilies[i].queueFlags & reqFlags) == reqFlags) - { - return i; - } - } - - return -1; -} - -} // renderer_test diff --git a/tools/gfx/vk-api.h b/tools/gfx/vk-api.h deleted file mode 100644 index 001f44d19..000000000 --- a/tools/gfx/vk-api.h +++ /dev/null @@ -1,203 +0,0 @@ -// vk-api.h -#pragma once - -#include "vk-module.h" - -namespace gfx { - -#define VK_API_GLOBAL_PROCS(x) \ - x(vkGetInstanceProcAddr) \ - x(vkCreateInstance) \ - /* */ - -#define VK_API_INSTANCE_PROCS_OPT(x) \ - x(vkGetPhysicalDeviceFeatures2) \ - x(vkGetPhysicalDeviceProperties2) \ - /* */ - -#define VK_API_INSTANCE_PROCS(x) \ - x(vkCreateDevice) \ - x(vkCreateDebugReportCallbackEXT) \ - x(vkDestroyDebugReportCallbackEXT) \ - x(vkDebugReportMessageEXT) \ - x(vkEnumeratePhysicalDevices) \ - x(vkGetPhysicalDeviceProperties) \ - x(vkGetPhysicalDeviceFeatures) \ - x(vkGetPhysicalDeviceMemoryProperties) \ - x(vkGetPhysicalDeviceQueueFamilyProperties) \ - x(vkGetPhysicalDeviceFormatProperties) \ - x(vkGetDeviceProcAddr) \ - /* */ - -#define VK_API_DEVICE_PROCS(x) \ - x(vkCreateDescriptorPool) \ - x(vkDestroyDescriptorPool) \ - x(vkGetDeviceQueue) \ - x(vkQueueSubmit) \ - x(vkQueueWaitIdle) \ - x(vkCreateBuffer) \ - x(vkAllocateMemory) \ - x(vkMapMemory) \ - x(vkUnmapMemory) \ - x(vkCmdCopyBuffer) \ - x(vkDestroyBuffer) \ - x(vkFreeMemory) \ - x(vkCreateDescriptorSetLayout) \ - x(vkDestroyDescriptorSetLayout) \ - x(vkAllocateDescriptorSets) \ - x(vkUpdateDescriptorSets) \ - x(vkCreatePipelineLayout) \ - x(vkDestroyPipelineLayout) \ - x(vkCreateComputePipelines) \ - x(vkCreateGraphicsPipelines) \ - x(vkDestroyPipeline) \ - x(vkCreateShaderModule) \ - x(vkDestroyShaderModule) \ - x(vkCreateFramebuffer) \ - x(vkDestroyFramebuffer) \ - x(vkCreateImage) \ - x(vkDestroyImage) \ - x(vkCreateImageView) \ - x(vkDestroyImageView) \ - x(vkCreateRenderPass) \ - x(vkDestroyRenderPass) \ - x(vkCreateCommandPool) \ - x(vkDestroyCommandPool) \ - x(vkCreateSampler) \ - x(vkDestroySampler) \ - x(vkCreateBufferView) \ - x(vkDestroyBufferView) \ - \ - x(vkGetBufferMemoryRequirements) \ - x(vkGetImageMemoryRequirements) \ - \ - x(vkCmdBindPipeline) \ - x(vkCmdBindDescriptorSets) \ - x(vkCmdDispatch) \ - x(vkCmdDraw) \ - x(vkCmdSetScissor) \ - x(vkCmdSetViewport) \ - x(vkCmdBindVertexBuffers) \ - x(vkCmdBindIndexBuffer) \ - x(vkCmdBeginRenderPass) \ - x(vkCmdEndRenderPass) \ - x(vkCmdPipelineBarrier) \ - x(vkCmdCopyBufferToImage)\ - \ - x(vkCreateFence) \ - x(vkDestroyFence) \ - x(vkResetFences) \ - x(vkGetFenceStatus) \ - x(vkWaitForFences) \ - \ - x(vkCreateSemaphore) \ - x(vkDestroySemaphore) \ - \ - x(vkCreateEvent) \ - x(vkDestroyEvent) \ - x(vkGetEventStatus) \ - x(vkSetEvent) \ - x(vkResetEvent) \ - \ - x(vkFreeCommandBuffers) \ - x(vkAllocateCommandBuffers) \ - x(vkBeginCommandBuffer) \ - x(vkEndCommandBuffer) \ - x(vkResetCommandBuffer) \ - \ - x(vkBindImageMemory) \ - x(vkBindBufferMemory) \ - /* */ - -#if SLANG_WINDOWS_FAMILY -# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ - x(vkCreateWin32SurfaceKHR) \ - /* */ -#else -# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ - x(vkCreateXlibSurfaceKHR) \ - /* */ -#endif - -#define VK_API_INSTANCE_KHR_PROCS(x) \ - VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ - x(vkGetPhysicalDeviceSurfaceSupportKHR) \ - x(vkGetPhysicalDeviceSurfaceFormatsKHR) \ - x(vkGetPhysicalDeviceSurfacePresentModesKHR) \ - x(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ - x(vkDestroySurfaceKHR) \ - /* */ - -#define VK_API_DEVICE_KHR_PROCS(x) \ - x(vkQueuePresentKHR) \ - x(vkCreateSwapchainKHR) \ - x(vkGetSwapchainImagesKHR) \ - x(vkDestroySwapchainKHR) \ - x(vkAcquireNextImageKHR) \ - /* */ - -#define VK_API_ALL_GLOBAL_PROCS(x) \ - VK_API_GLOBAL_PROCS(x) - -#define VK_API_ALL_INSTANCE_PROCS(x) \ - VK_API_INSTANCE_PROCS(x) \ - VK_API_INSTANCE_KHR_PROCS(x) - -#define VK_API_ALL_DEVICE_PROCS(x) \ - VK_API_DEVICE_PROCS(x) \ - VK_API_DEVICE_KHR_PROCS(x) - -#define VK_API_ALL_PROCS(x) \ - VK_API_ALL_GLOBAL_PROCS(x) \ - VK_API_ALL_INSTANCE_PROCS(x) \ - VK_API_ALL_DEVICE_PROCS(x) \ - \ - VK_API_INSTANCE_PROCS_OPT(x) \ - /* */ - -#define VK_API_DECLARE_PROC(NAME) PFN_##NAME NAME = nullptr; - -struct VulkanApi -{ - VK_API_ALL_PROCS(VK_API_DECLARE_PROC) - - enum class ProcType - { - Global, - Instance, - Device, - }; - - /// Returns true if all the functions in the class are defined - bool areDefined(ProcType type) const; - - /// Sets up global parameters - Slang::Result initGlobalProcs(const VulkanModule& module); - /// Initialize the instance functions - Slang::Result initInstanceProcs(VkInstance instance); - - /// Called before initDevice - Slang::Result initPhysicalDevice(VkPhysicalDevice physicalDevice); - - /// Initialize the device functions - Slang::Result initDeviceProcs(VkDevice device); - - /// Type bits control which indices are tested against bit 0 for testing at index 0 - /// properties - a memory type must have all the bits set as passed in - /// Returns -1 if couldn't find an appropriate memory type index - int findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const; - - /// Given queue required flags, finds a queue - int findQueue(VkQueueFlags reqFlags) const; - - const VulkanModule* m_module = nullptr; ///< Module this was all loaded from - VkInstance m_instance = VK_NULL_HANDLE; - VkDevice m_device = VK_NULL_HANDLE; - VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; - - VkPhysicalDeviceProperties m_deviceProperties; - VkPhysicalDeviceFeatures m_deviceFeatures; - VkPhysicalDeviceMemoryProperties m_deviceMemoryProperties; -}; - -} // renderer_test diff --git a/tools/gfx/vk-device-queue.cpp b/tools/gfx/vk-device-queue.cpp deleted file mode 100644 index 10a3d0e3b..000000000 --- a/tools/gfx/vk-device-queue.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// vk-device-queue.cpp -#include "vk-device-queue.h" - -#include -#include -#include - -namespace gfx { -using namespace Slang; - -VulkanDeviceQueue::~VulkanDeviceQueue() -{ - for (int i = 0; i < int(EventType::CountOf); ++i) - { - m_api->vkDestroySemaphore(m_api->m_device, m_semaphores[i], nullptr); - } - - for (int i = 0; i < m_numCommandBuffers; i++) - { - m_api->vkFreeCommandBuffers(m_api->m_device, m_commandPool, 1, &m_commandBuffers[i]); - m_api->vkDestroyFence(m_api->m_device, m_fences[i].fence, nullptr); - } - m_api->vkDestroyCommandPool(m_api->m_device, m_commandPool, nullptr); -} - -SlangResult VulkanDeviceQueue::init(const VulkanApi& api, VkQueue queue, int queueIndex) -{ - assert(m_api == nullptr); - m_api = &api; - - for (int i = 0; i < int(EventType::CountOf); ++i) - { - m_semaphores[i] = VK_NULL_HANDLE; - m_currentSemaphores[i] = VK_NULL_HANDLE; - } - - m_numCommandBuffers = kMaxCommandBuffers; - m_queueIndex = queueIndex; - - m_queue = queue; - - VkCommandPoolCreateInfo poolCreateInfo = {}; - poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - - poolCreateInfo.queueFamilyIndex = queueIndex; - - api.vkCreateCommandPool(api.m_device, &poolCreateInfo, nullptr, &m_commandPool); - - VkCommandBufferAllocateInfo commandInfo = {}; - commandInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - commandInfo.commandPool = m_commandPool; - commandInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - commandInfo.commandBufferCount = 1; - - VkFenceCreateInfo fenceCreateInfo = {}; - fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceCreateInfo.flags = 0; // VK_FENCE_CREATE_SIGNALED_BIT; - - for (int i = 0; i < m_numCommandBuffers; i++) - { - Fence& fence = m_fences[i]; - - api.vkAllocateCommandBuffers(api.m_device, &commandInfo, &m_commandBuffers[i]); - - api.vkCreateFence(api.m_device, &fenceCreateInfo, nullptr, &fence.fence); - fence.active = false; - fence.value = 0; - } - - VkSemaphoreCreateInfo semaphoreCreateInfo = {}; - semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - for (int i = 0; i < int(EventType::CountOf); ++i) - { - api.vkCreateSemaphore(api.m_device, &semaphoreCreateInfo, nullptr, &m_semaphores[i]); - } - - // Second step of flush to prime command buffer - flushStepB(); - - return SLANG_OK; -} - -void VulkanDeviceQueue::flushStepA() -{ - m_api->vkEndCommandBuffer(m_commandBuffer); - - VkPipelineStageFlags stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - // Wait semaphores - if (isCurrent(EventType::BeginFrame)) - { - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &m_currentSemaphores[int(EventType::BeginFrame)]; - } - - submitInfo.pWaitDstStageMask = &stageFlags; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &m_commandBuffer; - - // Signal semaphores - if (isCurrent(EventType::EndFrame)) - { - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &m_currentSemaphores[int(EventType::EndFrame)]; - } - - Fence& fence = m_fences[m_commandBufferIndex]; - - m_api->vkQueueSubmit(m_queue, 1, &submitInfo, fence.fence); - - // mark signaled fence value - fence.value = m_nextFenceValue; - fence.active = true; - - // increment fence value - m_nextFenceValue++; - - // No longer waiting on this semaphore - makeCompleted(EventType::BeginFrame); -} - -void VulkanDeviceQueue::_updateFenceAtIndex( int fenceIndex, bool blocking) -{ - Fence& fence = m_fences[fenceIndex]; - - if (fence.active) - { - uint64_t timeout = blocking ? ~uint64_t(0) : 0; - - if (VK_SUCCESS == m_api->vkWaitForFences(m_api->m_device, 1, &fence.fence, VK_TRUE, timeout)) - { - m_api->vkResetFences(m_api->m_device, 1, &fence.fence); - - fence.active = false; - - if (fence.value > m_lastFenceCompleted) - { - m_lastFenceCompleted = fence.value; - } - } - } -} - -void VulkanDeviceQueue::flushStepB() -{ - m_commandBufferIndex = (m_commandBufferIndex + 1) % m_numCommandBuffers; - m_commandBuffer = m_commandBuffers[m_commandBufferIndex]; - - // non-blocking update of fence values - for (int i = 0; i < m_numCommandBuffers; ++i) - { - _updateFenceAtIndex(i, false); - } - - // blocking update of fence values - _updateFenceAtIndex(m_commandBufferIndex, true); - - m_api->vkResetCommandBuffer(m_commandBuffer, 0); - - //m_api.vkResetCommandPool(m_api->m_device, m_commandPool, 0); - - VkCommandBufferBeginInfo beginInfo = {}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - m_api->vkBeginCommandBuffer(m_commandBuffer, &beginInfo); -} - -void VulkanDeviceQueue::flush() -{ - flushStepA(); - flushStepB(); -} - -void VulkanDeviceQueue::flushAndWait() -{ - flush(); - waitForIdle(); -} - -VkSemaphore VulkanDeviceQueue::makeCurrent(EventType eventType) -{ - assert(!isCurrent(eventType)); - VkSemaphore semaphore = m_semaphores[int(eventType)]; - m_currentSemaphores[int(eventType)] = semaphore; - return semaphore; -} - -void VulkanDeviceQueue::makeCompleted(EventType eventType) -{ - m_currentSemaphores[int(eventType)] = VK_NULL_HANDLE; -} - -} // renderer_test diff --git a/tools/gfx/vk-device-queue.h b/tools/gfx/vk-device-queue.h deleted file mode 100644 index d57483ec0..000000000 --- a/tools/gfx/vk-device-queue.h +++ /dev/null @@ -1,94 +0,0 @@ -// vk-swap-chain.h -#pragma once - -#include "vk-api.h" - -namespace gfx { - -struct VulkanDeviceQueue -{ - enum - { - kMaxCommandBuffers = 8, - }; - - enum class EventType - { - BeginFrame, - EndFrame, - CountOf, - }; - - /// Initialize - must be called before anything else can be done - SlangResult init(const VulkanApi& api, VkQueue queue, int queueIndex); - - /// Flushes the current command list, and steps to next (internally this is equivalent to a stepA followed by stepB) - void flush(); - /// Performs a full flush, and then waits for idle. - void flushAndWait(); - - /// Blocks until all work submitted to GPU has completed - void waitForIdle() { m_api->vkQueueWaitIdle(m_queue); } - - /// Get the graphics queue index (as set on init) - int getQueueIndex() const { return m_queueIndex; } - - /// Make the specified event 'current' - meaning it's semaphore must be waited on - VkSemaphore makeCurrent(EventType eventType); - /// Makes the event no longer required to be waited on - void makeCompleted(EventType eventType); - /// Returns true if the event is already current - SLANG_FORCE_INLINE bool isCurrent(EventType eventType) const { return m_currentSemaphores[int(eventType)] != VK_NULL_HANDLE; } - - /// Get the command buffer - VkCommandBuffer getCommandBuffer() const { return m_commandBuffer; } - - /// Get the queue - VkQueue getQueue() const { return m_queue; } - - /// Get the API - const VulkanApi* getApi() const { return m_api; } - - /// Flushes the current command list - void flushStepA(); - /// Steps to next command buffer and opens. May block if command buffer is still in use - void flushStepB(); - - /// Dtor - ~VulkanDeviceQueue(); - - protected: - - struct Fence - { - VkFence fence; - bool active; - uint64_t value; - }; - - void _updateFenceAtIndex(int fenceIndex, bool blocking); - - VkQueue m_queue = VK_NULL_HANDLE; - - VkCommandPool m_commandPool = VK_NULL_HANDLE; - int m_numCommandBuffers = 0; - int m_commandBufferIndex = 0; - // There are the same amount of command buffers as fences - VkCommandBuffer m_commandBuffers[kMaxCommandBuffers] = { VK_NULL_HANDLE }; - - Fence m_fences[kMaxCommandBuffers] = { {VK_NULL_HANDLE, 0, 0u} }; - - VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE; - - VkSemaphore m_semaphores[int(EventType::CountOf)]; - VkSemaphore m_currentSemaphores[int(EventType::CountOf)]; - - uint64_t m_lastFenceCompleted = 1; - uint64_t m_nextFenceValue = 2; - - int m_queueIndex = 0; - - const VulkanApi* m_api = nullptr; -}; - -} // renderer_test diff --git a/tools/gfx/vk-module.cpp b/tools/gfx/vk-module.cpp deleted file mode 100644 index 4e92a3d2c..000000000 --- a/tools/gfx/vk-module.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// module.cpp -#include "vk-module.h" - -#include -#include -#include - -#if SLANG_WINDOWS_FAMILY -# include -#else -# include -#endif - -namespace gfx { -using namespace Slang; - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanModule !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -Slang::Result VulkanModule::init() -{ - if (isInitialized()) - { - destroy(); - return SLANG_OK; - } - - const char* dynamicLibraryName = "Unknown"; - -#if SLANG_WINDOWS_FAMILY - dynamicLibraryName = "vulkan-1.dll"; - HMODULE module = ::LoadLibraryA(dynamicLibraryName); - m_module = (void*)module; -#else - dynamicLibraryName = "libvulkan.so.1"; - m_module = dlopen(dynamicLibraryName, RTLD_NOW); -#endif - - if (!m_module) - { - fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); - return SLANG_FAIL; - } - - return SLANG_OK; -} - -PFN_vkVoidFunction VulkanModule::getFunction(const char* name) const -{ - assert(m_module); - if (!m_module) - { - return nullptr; - } -#if SLANG_WINDOWS_FAMILY - return (PFN_vkVoidFunction)::GetProcAddress((HMODULE)m_module, name); -#else - return (PFN_vkVoidFunction)dlsym(m_module, name); -#endif -} - -void VulkanModule::destroy() -{ - if (!isInitialized()) - { - return; - } - -#if SLANG_WINDOWS_FAMILY - ::FreeLibrary((HMODULE)m_module); -#else - dlclose(m_module); -#endif - m_module = nullptr; -} - -} // renderer_test diff --git a/tools/gfx/vk-module.h b/tools/gfx/vk-module.h deleted file mode 100644 index 4d18823ca..000000000 --- a/tools/gfx/vk-module.h +++ /dev/null @@ -1,40 +0,0 @@ -// vk-module.h -#pragma once - -#include "../../slang.h" - -#include "../../slang-com-helper.h" - -#if SLANG_WINDOWS_FAMILY -# define VK_USE_PLATFORM_WIN32_KHR 1 -#else -# define VK_USE_PLATFORM_XLIB_KHR 1 -#endif - -#define VK_NO_PROTOTYPES - -#include - -namespace gfx { - -struct VulkanModule -{ - /// true if has been initialized - SLANG_FORCE_INLINE bool isInitialized() const { return m_module != nullptr; } - - /// Get a function by name - PFN_vkVoidFunction getFunction(const char* name) const; - - /// Initialize - Slang::Result init(); - /// Destroy - void destroy(); - - /// Dtor - ~VulkanModule() { destroy(); } - - protected: - void* m_module = nullptr; -}; - -} // renderer_test diff --git a/tools/gfx/vk-swap-chain.cpp b/tools/gfx/vk-swap-chain.cpp deleted file mode 100644 index 5cf2e96ae..000000000 --- a/tools/gfx/vk-swap-chain.cpp +++ /dev/null @@ -1,421 +0,0 @@ -// vk-swap-chain.cpp -#include "vk-swap-chain.h" - -#include "vk-util.h" - -#include "../../source/core/slang-list.h" - -#include -#include - -namespace gfx { -using namespace Slang; - -static Index _indexOfFormat(List& formatsIn, VkFormat format) -{ - const Index numFormats = formatsIn.getCount(); - const VkSurfaceFormatKHR* formats = formatsIn.getBuffer(); - - for (Index i = 0; i < numFormats; ++i) - { - if (formats[i].format == format) - { - return i; - } - } - return -1; -} - -SlangResult VulkanSwapChain::init(VulkanDeviceQueue* deviceQueue, const Desc& descIn, const PlatformDesc* platformDescIn) -{ - assert(platformDescIn); - - m_deviceQueue = deviceQueue; - m_api = deviceQueue->getApi(); - - // Make sure it's not set initially - m_format = VK_FORMAT_UNDEFINED; - - Desc desc(descIn); - -#if SLANG_WINDOWS_FAMILY - const WinPlatformDesc* platformDesc = static_cast(platformDescIn); - _setPlatformDesc(*platformDesc); - - VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.hinstance = platformDesc->m_hinstance; - surfaceCreateInfo.hwnd = platformDesc->m_hwnd; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateWin32SurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); -#else - const XPlatformDesc* platformDesc = static_cast(platformDescIn); - _setPlatformDesc(*platformDesc); - - VkXlibSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.dpy = platformDesc->m_display; - surfaceCreateInfo.window = platformDesc->m_window; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateXlibSurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); -#endif - - VkBool32 supported = false; - m_api->vkGetPhysicalDeviceSurfaceSupportKHR(m_api->m_physicalDevice, deviceQueue->getQueueIndex(), m_surface, &supported); - - uint32_t numSurfaceFormats = 0; - List surfaceFormats; - m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, nullptr); - surfaceFormats.setCount(int(numSurfaceFormats)); - m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, surfaceFormats.getBuffer()); - - // Look for a suitable format - List formats; - formats.add(VulkanUtil::getVkFormat(desc.m_format)); - // HACK! To check for a different format if couldn't be found - if (descIn.m_format == Format::RGBA_Unorm_UInt8) - { - formats.add(VK_FORMAT_B8G8R8A8_UNORM); - } - - for(Index i = 0; i < formats.getCount(); ++i) - { - VkFormat format = formats[i]; - if (_indexOfFormat(surfaceFormats, format) >= 0) - { - m_format = format; - } - } - - if (m_format == VK_FORMAT_UNDEFINED) - { - return SLANG_FAIL; - } - - // Save the desc - m_desc = desc; - - SLANG_RETURN_ON_FAIL(_createSwapChain()); - - m_desc = desc; - return SLANG_OK; -} - -void VulkanSwapChain::getWindowSize(int* widthOut, int* heightOut) const -{ -#if SLANG_WINDOWS_FAMILY - auto platformDesc = _getPlatformDesc(); - - RECT rc; - ::GetClientRect(platformDesc->m_hwnd, &rc); - *widthOut = rc.right - rc.left; - *heightOut = rc.bottom - rc.top; -#else - auto platformDesc = _getPlatformDesc(); - - XWindowAttributes winAttr = {}; - XGetWindowAttributes(platformDesc->m_display, platformDesc->m_window, &winAttr); - - *widthOut = winAttr.width; - *heightOut = winAttr.height; -#endif -} - -SlangResult VulkanSwapChain::_createFrameBuffers(VkRenderPass renderPass) -{ - assert(renderPass != VK_NULL_HANDLE); - - for (Index i = 0; i < m_images.getCount(); ++i) - { - Image& image = m_images[i]; - VkImageView attachments[] = - { - image.m_imageView - }; - - VkFramebufferCreateInfo framebufferInfo = {}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = m_width; - framebufferInfo.height = m_height; - framebufferInfo.layers = 1; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateFramebuffer(m_api->m_device, &framebufferInfo, nullptr, &image.m_frameBuffer)); - } - - return SLANG_OK; -} - -void VulkanSwapChain::_destroyFrameBuffers() -{ - for (Index i = 0; i < m_images.getCount(); ++i) - { - Image& image = m_images[i]; - if (image.m_frameBuffer != VK_NULL_HANDLE) - { - m_api->vkDestroyFramebuffer(m_api->m_device, image.m_frameBuffer, nullptr); - image.m_frameBuffer = VK_NULL_HANDLE; - } - } -} - -SlangResult VulkanSwapChain::createFrameBuffers(VkRenderPass renderPass) -{ - if (m_renderPass != VK_NULL_HANDLE) - { - _destroyFrameBuffers(); - m_renderPass = VK_NULL_HANDLE; - } - if (renderPass != VK_NULL_HANDLE) - { - SLANG_RETURN_ON_FAIL(_createFrameBuffers(renderPass)); - } - m_renderPass = renderPass; - return SLANG_OK; -} - -SlangResult VulkanSwapChain::_createSwapChain() -{ - if (hasValidSwapChain()) - { - return SLANG_OK; - } - - int width, height; - getWindowSize(&width, &height); - - VkExtent2D imageExtent = {}; - imageExtent.width = width; - imageExtent.height = height; - - m_width = width; - m_height = height; - - // catch this before throwing error - if (m_width == 0 || m_height == 0) - { - return SLANG_FAIL; - } - - // It is necessary to query the caps -> otherwise the LunarG verification layer will issue an error - { - VkSurfaceCapabilitiesKHR surfaceCaps; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_api->m_physicalDevice, m_surface, &surfaceCaps)); - } - - List presentModes; - uint32_t numPresentModes = 0; - m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, nullptr); - presentModes.setCount(numPresentModes); - m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, presentModes.getBuffer()); - - { - int numCheckPresentOptions = 3; - VkPresentModeKHR presentOptions[] = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR }; - if (m_vsync) - { - presentOptions[0] = VK_PRESENT_MODE_FIFO_KHR; - presentOptions[1] = VK_PRESENT_MODE_IMMEDIATE_KHR; - presentOptions[2] = VK_PRESENT_MODE_MAILBOX_KHR; - } - - m_presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; // Invalid - - // Find the first option that's available on the device - for (int j = 0; j < numCheckPresentOptions; j++) - { - if (presentModes.indexOf(presentOptions[j]) != Index(-1)) - { - m_presentMode = presentOptions[j]; - break; - } - } - - if (m_presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR) - { - return SLANG_FAIL; - } - } - - VkSwapchainKHR oldSwapchain = VK_NULL_HANDLE; - - VkSwapchainCreateInfoKHR swapchainDesc = {}; - swapchainDesc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swapchainDesc.surface = m_surface; - swapchainDesc.minImageCount = 3; - swapchainDesc.imageFormat = m_format; - swapchainDesc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; - swapchainDesc.imageExtent = imageExtent; - swapchainDesc.imageArrayLayers = 1; - swapchainDesc.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - swapchainDesc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swapchainDesc.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - swapchainDesc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swapchainDesc.presentMode = m_presentMode; - swapchainDesc.clipped = VK_TRUE; - swapchainDesc.oldSwapchain = oldSwapchain; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateSwapchainKHR(m_api->m_device, &swapchainDesc, nullptr, &m_swapChain)); - - uint32_t numSwapChainImages = 0; - m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, nullptr); - - { - List images; - images.setCount(numSwapChainImages); - - m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, images.getBuffer()); - - m_images.setCount(numSwapChainImages); - for (int i = 0; i < int(numSwapChainImages); ++i) - { - Image& dstImage = m_images[i]; - dstImage.m_image = images[i]; - - } - } - - { - VkImageViewCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = m_format; - - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - for (int i = 0; i < int(numSwapChainImages); ++i) - { - Image& image = m_images[i]; - - createInfo.image = image.m_image; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateImageView(m_api->m_device, &createInfo, nullptr, &image.m_imageView)); - } - } - - if (m_renderPass != VK_NULL_HANDLE) - { - _createFrameBuffers(m_renderPass); - } - - return SLANG_OK; -} - -void VulkanSwapChain::_destroySwapChain() -{ - if (!hasValidSwapChain()) - { - return; - } - - m_deviceQueue->waitForIdle(); - - if (m_renderPass != VK_NULL_HANDLE) - { - _destroyFrameBuffers(); - } - - for (Index i = 0; i < m_images.getCount(); ++i) - { - Image& image = m_images[i]; - - if (image.m_imageView != VK_NULL_HANDLE) - { - m_api->vkDestroyImageView(m_api->m_device, image.m_imageView, nullptr); - } - } - - if (m_swapChain != VK_NULL_HANDLE) - { - m_api->vkDestroySwapchainKHR(m_api->m_device, m_swapChain, nullptr); - m_swapChain = VK_NULL_HANDLE; - } - - // Mark that it is no longer used - m_images.clear(); -} - -VulkanSwapChain::~VulkanSwapChain() -{ - _destroySwapChain(); - - if (m_surface) - { - m_api->vkDestroySurfaceKHR(m_api->m_instance, m_surface, nullptr); - m_surface = VK_NULL_HANDLE; - } -} - -int VulkanSwapChain::nextFrontImageIndex() -{ - if (!hasValidSwapChain()) - { - if (SLANG_FAILED(_createSwapChain())) - { - return -1; - } - } - - VkSemaphore beginFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::BeginFrame); - - uint32_t swapChainIndex = 0; - VkResult result = m_api->vkAcquireNextImageKHR(m_api->m_device, m_swapChain, UINT64_MAX, beginFrameSemaphore, VK_NULL_HANDLE, &swapChainIndex); - - if (result != VK_SUCCESS) - { - _destroySwapChain(); - return -1; - } - m_currentSwapChainIndex = int(swapChainIndex); - return swapChainIndex; -} - -void VulkanSwapChain::present(bool vsync) -{ - if (!hasValidSwapChain()) - { - m_deviceQueue->flush(); - return; - } - - VkSemaphore endFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::EndFrame); - - m_deviceQueue->flushStepA(); - - uint32_t swapChainIndices[] = { uint32_t(m_currentSwapChainIndex) }; - - VkPresentInfoKHR presentInfo = {}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &m_swapChain; - presentInfo.pImageIndices = swapChainIndices; - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = &endFrameSemaphore; - - VkResult result = m_api->vkQueuePresentKHR(m_deviceQueue->getQueue(), &presentInfo); - - m_deviceQueue->makeCompleted(VulkanDeviceQueue::EventType::EndFrame); - - m_deviceQueue->flushStepB(); - - if (result != VK_SUCCESS || m_vsync != vsync) - { - m_vsync = vsync; - _destroySwapChain(); - } -} - -} // renderer_test diff --git a/tools/gfx/vk-swap-chain.h b/tools/gfx/vk-swap-chain.h deleted file mode 100644 index f8ad98a83..000000000 --- a/tools/gfx/vk-swap-chain.h +++ /dev/null @@ -1,141 +0,0 @@ -// vk-swap-chain.h -#pragma once - -#include "vk-api.h" -#include "vk-device-queue.h" - -#include "render.h" - -#include "../../source/core/slang-list.h" - -namespace gfx { - -struct VulkanSwapChain -{ - /* enum - { - kMaxImages = 8, - }; */ - - /// Base class for platform specific information - struct PlatformDesc - { - }; - -#if SLANG_WINDOWS_FAMILY - struct WinPlatformDesc: public PlatformDesc - { - HINSTANCE m_hinstance; - HWND m_hwnd; - }; -#else - struct XPlatformDesc : public PlatformDesc - { - Display* m_display; - Window m_window; - }; -#endif - - struct Desc - { - void init() - { - m_format = Format::Unknown; - m_depthFormatTypeless = Format::Unknown; - m_depthFormat = Format::Unknown; - m_textureDepthFormat = Format::Unknown; - } - - Format m_format; - //bool m_enableFormat; - Format m_depthFormatTypeless; - Format m_depthFormat; - Format m_textureDepthFormat; - }; - - struct Image - { - VkImage m_image = VK_NULL_HANDLE; - VkImageView m_imageView = VK_NULL_HANDLE; - VkFramebuffer m_frameBuffer = VK_NULL_HANDLE; - }; - - - /// Must be called before the swap chain can be used - SlangResult init(VulkanDeviceQueue* deviceQueue, const Desc& desc, const PlatformDesc* platformDesc); - - /// Create the frame buffers (they must be compatible with the supplied renderPass) - SlangResult createFrameBuffers(VkRenderPass renderPass); - - /// Returned the desc used to construct the swap chain. - /// Is invalid if init hasn't returned with successful result. - const Desc& getDesc() const { return m_desc; } - - /// True if the swap chain is available - bool hasValidSwapChain() const { return m_images.getCount() > 0; } - - /// Present to the display - void present(bool vsync); - - /// Get the current size of the window (in pixels written to widthOut, heightOut) - void getWindowSize(int* widthOut, int* heightOut) const; - - /// Get the VkFormat for the back buffer - VkFormat getVkFormat() const { return m_format; } - - /// Get width of the back buffers - int getWidth() const { return m_width; } - /// Get the height of the back buffer - int getHeight() const { return m_height; } - - /// Get the detail about the images - const Slang::List& getImages() const { return m_images; } - - /// Get the next front render image index. Returns -1, if image couldn't be found - int nextFrontImageIndex(); - - /// Dtor - ~VulkanSwapChain(); - - protected: - - - template - void _setPlatformDesc(const T& desc) - { - const PlatformDesc* check = &desc; - int size = (sizeof(T) + sizeof(void*) - 1) / sizeof(void*); - m_platformDescBuffer.setCount(size); - *(T*)m_platformDescBuffer.getBuffer() = desc; - } - template - const T* _getPlatformDesc() const { return static_cast((const PlatformDesc*)m_platformDescBuffer.getBuffer()); } - SlangResult _createSwapChain(); - void _destroySwapChain(); - SlangResult _createFrameBuffers(VkRenderPass renderPass); - void _destroyFrameBuffers(); - - bool m_vsync = true; - int m_width = 0; - int m_height = 0; - - VkPresentModeKHR m_presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; - VkFormat m_format = VK_FORMAT_UNDEFINED; ///< The format used for backbuffer. Valid after successful init. - - VkSurfaceKHR m_surface = VK_NULL_HANDLE; - VkSwapchainKHR m_swapChain = VK_NULL_HANDLE; - - VkRenderPass m_renderPass = VK_NULL_HANDLE; //< Not owned - - int m_currentSwapChainIndex = 0; - - Slang::List m_images; - - VulkanDeviceQueue* m_deviceQueue = nullptr; - const VulkanApi* m_api = nullptr; - - Desc m_desc; ///< The desc used to init this swap chain - Slang::List m_platformDescBuffer; ///< Buffer to hold the platform specific description parameters (as passed in platformDesc) -}; - -} // renderer_test diff --git a/tools/gfx/vk-util.cpp b/tools/gfx/vk-util.cpp deleted file mode 100644 index e8940d1b2..000000000 --- a/tools/gfx/vk-util.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// vk-util.cpp -#include "vk-util.h" - -#include -#include - -namespace gfx { - -/* static */VkFormat VulkanUtil::getVkFormat(Format format) -{ - switch (format) - { - case Format::RGBA_Float32: return VK_FORMAT_R32G32B32A32_SFLOAT; - case Format::RGB_Float32: return VK_FORMAT_R32G32B32_SFLOAT; - case Format::RG_Float32: return VK_FORMAT_R32G32_SFLOAT; - case Format::R_Float32: return VK_FORMAT_R32_SFLOAT; - case Format::RGBA_Unorm_UInt8: return VK_FORMAT_R8G8B8A8_UNORM; - case Format::R_UInt32: return VK_FORMAT_R32_UINT; - - case Format::D_Float32: return VK_FORMAT_D32_SFLOAT; - case Format::D_Unorm24_S8: return VK_FORMAT_D24_UNORM_S8_UINT; - - default: return VK_FORMAT_UNDEFINED; - } -} - -/* static */SlangResult VulkanUtil::toSlangResult(VkResult res) -{ - return (res == VK_SUCCESS) ? SLANG_OK : SLANG_FAIL; -} - -/* static */Slang::Result VulkanUtil::handleFail(VkResult res) -{ - if (res != VK_SUCCESS) - { - assert(!"Vulkan returned a failure"); - } - return toSlangResult(res); -} - -/* static */void VulkanUtil::checkFail(VkResult res) -{ - assert(res != VK_SUCCESS); - assert(!"Vulkan check failed"); - -} - -/* static */VkPrimitiveTopology VulkanUtil::getVkPrimitiveTopology(PrimitiveTopology topology) -{ - switch (topology) - { - case PrimitiveTopology::TriangleList: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - default: break; - } - assert(!"Unknown topology"); - return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; -} - -} // renderer_test diff --git a/tools/gfx/vk-util.h b/tools/gfx/vk-util.h deleted file mode 100644 index edba3a7d2..000000000 --- a/tools/gfx/vk-util.h +++ /dev/null @@ -1,41 +0,0 @@ -// vk-util.h -#pragma once - -#include "vk-api.h" -#include "render.h" - -// Macros to make testing vulkan return codes simpler - -/// SLANG_VK_RETURN_ON_FAIL can be used in a similar way to SLANG_RETURN_ON_FAIL macro, except it will turn a vulkan failure into Slang::Result in the process -/// Calls handleFail which on debug builds asserts -#define SLANG_VK_RETURN_ON_FAIL(x) { VkResult _res = x; if (_res != VK_SUCCESS) { return VulkanUtil::handleFail(_res); } } - -#define SLANG_VK_RETURN_NULL_ON_FAIL(x) { VkResult _res = x; if (_res != VK_SUCCESS) { VulkanUtil::handleFail(_res); return nullptr; } } - -/// Is similar to SLANG_VK_RETURN_ON_FAIL, but does not return. Will call checkFail on failure - which asserts on debug builds. -#define SLANG_VK_CHECK(x) { VkResult _res = x; if (_res != VK_SUCCESS) { VulkanUtil::checkFail(_res); } } - -namespace gfx { - -// Utility functions for Vulkan -struct VulkanUtil -{ - /// Get the equivalent VkFormat from the format - /// Returns VK_FORMAT_UNDEFINED if a match is not found - static VkFormat getVkFormat(Format format); - - /// Called by SLANG_VK_RETURN_FAIL if a res is a failure. - /// On debug builds this will cause an assertion on failure. - static Slang::Result handleFail(VkResult res); - /// Called when a failure has occurred with SLANG_VK_CHECK - will typically assert. - static void checkFail(VkResult res); - - /// Get the VkPrimitiveTopology for the given topology. - /// Returns VK_PRIMITIVE_TOPOLOGY_MAX_ENUM on failure - static VkPrimitiveTopology getVkPrimitiveTopology(PrimitiveTopology topology); - - /// Returns Slang::Result equivalent of a VkResult - static Slang::Result toSlangResult(VkResult res); -}; - -} // renderer_test diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp new file mode 100644 index 000000000..2221187ed --- /dev/null +++ b/tools/gfx/vulkan/render-vk.cpp @@ -0,0 +1,2793 @@ +// render-vk.cpp +#include "render-vk.h" + +//WORKING:#include "options.h" +#include "../render.h" + +#include "../../source/core/slang-smart-pointer.h" + +#include "vk-api.h" +#include "vk-util.h" +#include "vk-device-queue.h" +#include "vk-swap-chain.h" + +#include "../surface.h" + +// Vulkan has a different coordinate system to ogl +// http://anki3d.org/vulkan-coordinate-system/ + +#define ENABLE_VALIDATION_LAYER 1 + +#ifdef _MSC_VER +# include +# pragma warning(disable: 4996) +# if (_MSC_VER < 1900) +# define snprintf sprintf_s +# endif +#endif + +namespace gfx { +using namespace Slang; + +class VKRenderer : public Renderer +{ +public: + enum + { + kMaxRenderTargets = 8, + kMaxAttachments = kMaxRenderTargets + 1, + + kMaxDescriptorSets = 4, + }; + + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual const List& getFeatures() override { return m_features; } + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + TextureResource::Desc getSwapChainTextureDesc() override; + + Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; + Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; + Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; + + Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; + Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; + + Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; + + Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; + Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; + Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; + + Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; + Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; + Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; + + virtual SlangResult captureScreenSurface(Surface& surface) override; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; + virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + void setViewports(UInt count, Viewport const* viewports) override; + void setScissorRects(UInt count, ScissorRect const* rects) override; + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override; + virtual void waitForGpu() override; + virtual RendererType getRendererType() const override { return RendererType::Vulkan; } + + /// Dtor + ~VKRenderer(); + + protected: + + class Buffer + { + public: + /// Initialize a buffer with specified size, and memory props + Result init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties); + + /// Returns true if has been initialized + bool isInitialized() const { return m_api != nullptr; } + + // Default Ctor + Buffer(): + m_api(nullptr) + {} + + /// Dtor + ~Buffer() + { + if (m_api) + { + m_api->vkDestroyBuffer(m_api->m_device, m_buffer, nullptr); + m_api->vkFreeMemory(m_api->m_device, m_memory, nullptr); + } + } + + VkBuffer m_buffer; + VkDeviceMemory m_memory; + const VulkanApi* m_api; + }; + + class InputLayoutImpl : public InputLayout + { + public: + List m_vertexDescs; + int m_vertexSize; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + BufferResourceImpl(Resource::Usage initialUsage, const BufferResource::Desc& desc, VKRenderer* renderer): + Parent(desc), + m_renderer(renderer), + m_initialUsage(initialUsage) + { + assert(renderer); + } + + Resource::Usage m_initialUsage; + VKRenderer* m_renderer; + Buffer m_buffer; + Buffer m_uploadBuffer; + List m_readBuffer; ///< Stores the contents when a map read is performed + + MapFlavor m_mapFlavor = MapFlavor::Unknown; ///< If resource is mapped, records what kind of mapping else Unknown (if not mapped) + }; + + class TextureResourceImpl : public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(const Desc& desc, Usage initialUsage, const VulkanApi* api) : + Parent(desc), + m_initialUsage(initialUsage), + m_api(api) + { + } + ~TextureResourceImpl() + { + if (m_api) + { + if (m_imageMemory != VK_NULL_HANDLE) + { + m_api->vkFreeMemory(m_api->m_device, m_imageMemory, nullptr); + } + if (m_image != VK_NULL_HANDLE) + { + m_api->vkDestroyImage(m_api->m_device, m_image, nullptr); + } + } + } + + Usage m_initialUsage; + + VkImage m_image = VK_NULL_HANDLE; + VkDeviceMemory m_imageMemory = VK_NULL_HANDLE; + + const VulkanApi* m_api; + }; + + class SamplerStateImpl : public SamplerState + { + public: + VkSampler m_sampler; + }; + + class ResourceViewImpl : public ResourceView + { + public: + enum class ViewType + { + Texture, + TexelBuffer, + PlainBuffer, + }; + ViewType m_type; + }; + + class TextureResourceViewImpl : public ResourceViewImpl + { + public: + TextureResourceViewImpl() + { + m_type = ViewType::Texture; + } + + RefPtr m_texture; + VkImageView m_view; + VkImageLayout m_layout; + }; + + class TexelBufferResourceViewImpl : public ResourceViewImpl + { + public: + TexelBufferResourceViewImpl() + { + m_type = ViewType::TexelBuffer; + } + + RefPtr m_buffer; + VkBufferView m_view; + }; + + class PlainBufferResourceViewImpl : public ResourceViewImpl + { + public: + PlainBufferResourceViewImpl() + { + m_type = ViewType::PlainBuffer; + } + + RefPtr m_buffer; + VkDeviceSize offset; + VkDeviceSize size; + }; + + class ShaderProgramImpl: public ShaderProgram + { + public: + + ShaderProgramImpl(PipelineType pipelineType): + m_pipelineType(pipelineType) + {} + + PipelineType m_pipelineType; + + VkPipelineShaderStageCreateInfo m_compute; + VkPipelineShaderStageCreateInfo m_vertex; + VkPipelineShaderStageCreateInfo m_fragment; + + List m_buffers[2]; //< To keep storage of code in scope + }; + + class DescriptorSetLayoutImpl : public DescriptorSetLayout + { + public: + DescriptorSetLayoutImpl(const VulkanApi& api) + : m_api(&api) + { + } + + ~DescriptorSetLayoutImpl() + { + if(m_descriptorSetLayout != VK_NULL_HANDLE) + { + m_api->vkDestroyDescriptorSetLayout(m_api->m_device, m_descriptorSetLayout, nullptr); + } + if (m_descriptorPool != VK_NULL_HANDLE) + { + m_api->vkDestroyDescriptorPool(m_api->m_device, m_descriptorPool, nullptr); + } + } + + VulkanApi const* m_api; + VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE; + VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE; + + struct RangeInfo + { + VkDescriptorType descriptorType; + }; + List m_ranges; + }; + + class PipelineLayoutImpl : public PipelineLayout + { + public: + PipelineLayoutImpl(const VulkanApi& api) + : m_api(&api) + { + } + + ~PipelineLayoutImpl() + { + if (m_pipelineLayout != VK_NULL_HANDLE) + { + m_api->vkDestroyPipelineLayout(m_api->m_device, m_pipelineLayout, nullptr); + } + } + + VulkanApi const* m_api; + VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; + UInt m_descriptorSetCount = 0; + }; + + class DescriptorSetImpl : public DescriptorSet + { + public: + // Record the view binding + struct Binding + { + enum class Type : uint8_t + { + Unknown, + ResourceView, + SamplerState, + BufferResource, + CountOf, + }; + Type type; + uint32_t range; + uint32_t index; + RefPtr obj; + }; + + DescriptorSetImpl(VKRenderer* renderer) + : m_renderer(renderer) + { + } + + ~DescriptorSetImpl() + { + } + + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; + virtual void setResource(UInt range, UInt index, ResourceView* view) override; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) override; + + static Binding::Type _getBindingType(RefObject* ptr); + void _setBinding(Binding::Type type, UInt range, UInt index, RefObject* ptr); + + VKRenderer* m_renderer = nullptr; ///< Weak pointer, can't be strong, because if set will become circular reference + RefPtr m_layout; + VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE; + + List m_bindings; ///< Records entities are bound to this descriptor set, and keeps the associated resources/views/state in scope + }; + +#if 0 + struct BindingDetail + { + VkImageView m_srv = VK_NULL_HANDLE; + VkBufferView m_uav = VK_NULL_HANDLE; + VkSampler m_sampler = VK_NULL_HANDLE; + }; + + class BindingStateImpl: public BindingState + { + public: + typedef BindingState Parent; + + BindingStateImpl(const Desc& desc, const VulkanApi* api): + Parent(desc), + m_api(api) + { + } + ~BindingStateImpl() + { + for (int i = 0; i < int(m_bindingDetails.Count()); ++i) + { + BindingDetail& detail = m_bindingDetails[i]; + if (detail.m_sampler != VK_NULL_HANDLE) + { + m_api->vkDestroySampler(m_api->m_device, detail.m_sampler, nullptr); + } + if (detail.m_srv != VK_NULL_HANDLE) + { + m_api->vkDestroyImageView(m_api->m_device, detail.m_srv, nullptr); + } + if (detail.m_uav != VK_NULL_HANDLE) + { + m_api->vkDestroyBufferView(m_api->m_device, detail.m_uav, nullptr); + } + } + } + + const VulkanApi* m_api; + List m_bindingDetails; + }; +#endif + + struct BoundVertexBuffer + { + RefPtr m_buffer; + int m_stride; + int m_offset; + }; + + class PipelineStateImpl : public PipelineState + { + public: + PipelineStateImpl(const VulkanApi& api): + m_api(&api) + { + } + ~PipelineStateImpl() + { + if (m_pipeline != VK_NULL_HANDLE) + { + m_api->vkDestroyPipeline(m_api->m_device, m_pipeline, nullptr); + } + } + + const VulkanApi* m_api; + +// VkPrimitiveTopology m_primitiveTopology; + + RefPtr m_pipelineLayout; + +// RefPtr m_inputLayout; + RefPtr m_shaderProgram; + + VkPipeline m_pipeline = VK_NULL_HANDLE; + }; + + VkBool32 handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, + size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg); + + VkPipelineShaderStageCreateInfo compileEntryPoint( + ShaderProgram::KernelDesc const& kernelDesc, + VkShaderStageFlagBits stage, + List& bufferOut); + + static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, + size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData); + + /// Returns true if m_currentPipeline matches the current configuration +// Pipeline* _getPipeline(); +// bool _isEqual(const Pipeline& pipeline) const; +// Slang::Result _createPipeline(RefPtr& pipelineOut); + void _beginRender(); + void _endRender(); + + Slang::Result _beginPass(); + void _endPass(); + void _transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout); + + VkDebugReportCallbackEXT m_debugReportCallback; + +// RefPtr m_currentInputLayout; + +// RefPtr m_currentBindingState; + RefPtr m_currentPipelineLayout; + + RefPtr m_currentDescriptorSetImpls [kMaxDescriptorSets]; + VkDescriptorSet m_currentDescriptorSets [kMaxDescriptorSets]; + +// RefPtr m_currentProgram; + +// List > m_pipelineCache; + RefPtr m_currentPipeline; + + List m_boundVertexBuffers; + + VkPrimitiveTopology m_primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkDevice m_device = VK_NULL_HANDLE; + + VulkanModule m_module; + VulkanApi m_api; + + VulkanDeviceQueue m_deviceQueue; + VulkanSwapChain m_swapChain; + + VkRenderPass m_renderPass = VK_NULL_HANDLE; + + int m_swapChainImageIndex = -1; + + float m_clearColor[4] = { 0, 0, 0, 0 }; + + Desc m_desc; + List m_features; +}; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer::Buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +Result VKRenderer::Buffer::init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties) +{ + assert(!isInitialized()); + + m_api = &api; + m_memory = VK_NULL_HANDLE; + m_buffer = VK_NULL_HANDLE; + + VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufferCreateInfo.size = bufferSize; + bufferCreateInfo.usage = usage; + + SLANG_VK_CHECK(api.vkCreateBuffer(api.m_device, &bufferCreateInfo, nullptr, &m_buffer)); + + VkMemoryRequirements memoryReqs = {}; + api.vkGetBufferMemoryRequirements(api.m_device, m_buffer, &memoryReqs); + + int memoryTypeIndex = api.findMemoryTypeIndex(memoryReqs.memoryTypeBits, reqMemoryProperties); + assert(memoryTypeIndex >= 0); + + VkMemoryPropertyFlags actualMemoryProperites = api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; + + VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocateInfo.allocationSize = memoryReqs.size; + allocateInfo.memoryTypeIndex = memoryTypeIndex; + + SLANG_VK_CHECK(api.vkAllocateMemory(api.m_device, &allocateInfo, nullptr, &m_memory)); + SLANG_VK_CHECK(api.vkBindBufferMemory(api.m_device, m_buffer, m_memory, 0)); + + return SLANG_OK; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +#if 0 +bool VKRenderer::_isEqual(const Pipeline& pipeline) const +{ + return + pipeline.m_pipelineLayout == m_currentPipelineLayout && + pipeline.m_primitiveTopology == m_primitiveTopology && + pipeline.m_inputLayout == m_currentInputLayout && + pipeline.m_shaderProgram == m_currentProgram; +} + +VKRenderer::Pipeline* VKRenderer::_getPipeline() +{ + if (m_currentPipeline && _isEqual(*m_currentPipeline)) + { + return m_currentPipeline; + } + + // Look for a match in the cache + for (int i = 0; i < int(m_pipelineCache.Count()); ++i) + { + Pipeline* pipeline = m_pipelineCache[i]; + if (_isEqual(*pipeline)) + { + m_currentPipeline = pipeline; + return pipeline; + } + } + + RefPtr pipeline; + SLANG_RETURN_NULL_ON_FAIL(_createPipeline(pipeline)); + m_pipelineCache.Add(pipeline); + m_currentPipeline = pipeline; + return pipeline; +} + +Slang::Result VKRenderer::_createPipeline(RefPtr& pipelineOut) +{ + RefPtr pipeline(new Pipeline(m_api)); + + // Initialize the state + pipeline->m_primitiveTopology = m_primitiveTopology; + pipeline->m_pipelineLayout = m_currentPipelineLayout; + pipeline->m_shaderProgram = m_currentProgram; + pipeline->m_inputLayout = m_currentInputLayout; + + // Must be equal at this point if all the items are correctly set in pipeline + assert(_isEqual(*pipeline)); + + VkPipelineCache pipelineCache = VK_NULL_HANDLE; + + if (m_currentProgram->m_pipelineType == PipelineType::Compute) + { + // Then create a pipeline to use that layout + + VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; + computePipelineInfo.stage = m_currentProgram->m_compute; + computePipelineInfo.layout = pipeline->m_pipelineLayout->m_pipelineLayout; + + SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline->m_pipeline)); + } + else if (m_currentProgram->m_pipelineType == PipelineType::Graphics) + { + // Create the graphics pipeline + + const int width = m_swapChain.getWidth(); + const int height = m_swapChain.getHeight(); + + VkPipelineShaderStageCreateInfo shaderStages[] = { m_currentProgram->m_vertex, m_currentProgram->m_fragment }; + + // VertexBuffer/s + // Currently only handles one + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + + VkVertexInputBindingDescription vertexInputBindingDescription; + + if (m_currentInputLayout) + { + vertexInputBindingDescription.binding = 0; + vertexInputBindingDescription.stride = m_currentInputLayout->m_vertexSize; + vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + const auto& srcAttributeDescs = m_currentInputLayout->m_vertexDescs; + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription; + + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(srcAttributeDescs.Count()); + vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.getBuffer(); + } + + // + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)width; + viewport.height = (float)height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = { uint32_t(width), uint32_t(height) }; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = pipeline->m_pipelineLayout->m_pipelineLayout; + pipelineInfo.renderPass = m_renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + + SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline->m_pipeline)); + } + else + { + assert(!"Unhandled program type"); + return SLANG_FAIL; + } + + pipelineOut = pipeline; + return SLANG_OK; +} +#endif + +Result VKRenderer::_beginPass() +{ + if (m_swapChainImageIndex < 0) + { + return SLANG_FAIL; + } + + const int numRenderTargets = 1; + + const VulkanSwapChain::Image& image = m_swapChain.getImages()[m_swapChainImageIndex]; + + int numAttachments = 0; + + // Start render pass + VkClearValue clearValues[kMaxAttachments]; + clearValues[numAttachments++] = VkClearValue{ m_clearColor[0], m_clearColor[1], m_clearColor[2], m_clearColor[3] }; + + bool hasDepthBuffer = false; + if (hasDepthBuffer) + { + VkClearValue& clearValue = clearValues[numAttachments++]; + + clearValue.depthStencil.depth = 1.0f; + clearValue.depthStencil.stencil = 0; + } + + const int width = m_swapChain.getWidth(); + const int height = m_swapChain.getHeight(); + + VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); + + VkRenderPassBeginInfo renderPassBegin = {}; + renderPassBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassBegin.renderPass = m_renderPass; + renderPassBegin.framebuffer = image.m_frameBuffer; + renderPassBegin.renderArea.offset.x = 0; + renderPassBegin.renderArea.offset.y = 0; + renderPassBegin.renderArea.extent.width = width; + renderPassBegin.renderArea.extent.height = height; + renderPassBegin.clearValueCount = numAttachments; + renderPassBegin.pClearValues = clearValues; + + m_api.vkCmdBeginRenderPass(cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE); + + // Set up scissor and viewport + { + VkRect2D rects[kMaxRenderTargets] = {}; + VkViewport viewports[kMaxRenderTargets] = {}; + for (int i = 0; i < numRenderTargets; ++i) + { + rects[i] = VkRect2D{ 0, 0, uint32_t(width), uint32_t(height) }; + + VkViewport& dstViewport = viewports[i]; + + dstViewport.x = 0.0f; + dstViewport.y = 0.0f; + dstViewport.width = float(width); + dstViewport.height = float(height); + dstViewport.minDepth = 0.0f; + dstViewport.maxDepth = 1.0f; + } + + m_api.vkCmdSetScissor(cmdBuffer, 0, numRenderTargets, rects); + m_api.vkCmdSetViewport(cmdBuffer, 0, numRenderTargets, viewports); + } + + return SLANG_OK; +} + +void VKRenderer::_endPass() +{ + VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); + m_api.vkCmdEndRenderPass(cmdBuffer); +} + +void VKRenderer::_beginRender() +{ + m_swapChainImageIndex = m_swapChain.nextFrontImageIndex(); + + if (m_swapChainImageIndex < 0) + { + return; + } +} + +void VKRenderer::_endRender() +{ + m_deviceQueue.flush(); +} + +Renderer* createVKRenderer() +{ + return new VKRenderer; +} + +VKRenderer::~VKRenderer() +{ + if (m_renderPass != VK_NULL_HANDLE) + { + m_api.vkDestroyRenderPass(m_device, m_renderPass, nullptr); + m_renderPass = VK_NULL_HANDLE; + } +} + + +VkBool32 VKRenderer::handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, + size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg) +{ + char const* severity = "message"; + if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) + severity = "warning"; + if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) + severity = "error"; + + // pMsg can be really big (it can be assembler dump for example) + // Use a dynamic buffer to store + size_t bufferSize = strlen(pMsg) + 1 + 1024; + List bufferArray; + bufferArray.setCount(bufferSize); + char* buffer = bufferArray.getBuffer(); + + sprintf_s(buffer, + bufferSize, + "%s: %s %d: %s\n", + pLayerPrefix, + severity, + msgCode, + pMsg); + + fprintf(stderr, "%s", buffer); + fflush(stderr); + + OutputDebugStringA(buffer); + + return VK_FALSE; +} + +/* static */VKAPI_ATTR VkBool32 VKAPI_CALL VKRenderer::debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, + size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData) +{ + return ((VKRenderer*)pUserData)->handleDebugMessage(flags, objType, srcObject, location, msgCode, pLayerPrefix, pMsg); +} + +VkPipelineShaderStageCreateInfo VKRenderer::compileEntryPoint( + ShaderProgram::KernelDesc const& kernelDesc, + VkShaderStageFlagBits stage, + List& bufferOut) +{ + char const* dataBegin = (char const*) kernelDesc.codeBegin; + char const* dataEnd = (char const*) kernelDesc.codeEnd; + + // We need to make a copy of the code, since the Slang compiler + // will free the memory after a compile request is closed. + size_t codeSize = dataEnd - dataBegin; + + bufferOut.insertRange(0, dataBegin, codeSize); + + char* codeBegin = bufferOut.getBuffer(); + + VkShaderModuleCreateInfo moduleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; + moduleCreateInfo.pCode = (uint32_t*)codeBegin; + moduleCreateInfo.codeSize = codeSize; + + VkShaderModule module; + SLANG_VK_CHECK(m_api.vkCreateShaderModule(m_device, &moduleCreateInfo, nullptr, &module)); + + VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; + shaderStageCreateInfo.stage = stage; + + shaderStageCreateInfo.module = module; + shaderStageCreateInfo.pName = "main"; + + return shaderStageCreateInfo; +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult VKRenderer::initialize(const Desc& desc, void* inWindowHandle) +{ + SLANG_RETURN_ON_FAIL(m_module.init()); + SLANG_RETURN_ON_FAIL(m_api.initGlobalProcs(m_module)); + + m_desc = desc; + + VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + applicationInfo.pApplicationName = "slang-render-test"; + applicationInfo.pEngineName = "slang-render-test"; + applicationInfo.apiVersion = VK_API_VERSION_1_0; + + char const* instanceExtensions[] = + { + VK_KHR_SURFACE_EXTENSION_NAME, + + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, + +#if SLANG_WINDOWS_FAMILY + VK_KHR_WIN32_SURFACE_EXTENSION_NAME, +#else + VK_KHR_XLIB_SURFACE_EXTENSION_NAME +#endif + +#if ENABLE_VALIDATION_LAYER + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, +#endif + }; + + VkInstance instance = VK_NULL_HANDLE; + + VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + instanceCreateInfo.pApplicationInfo = &applicationInfo; + + instanceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(instanceExtensions); + instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0]; + +#if ENABLE_VALIDATION_LAYER + const char* layerNames[] = { "VK_LAYER_LUNARG_standard_validation" }; + instanceCreateInfo.enabledLayerCount = SLANG_COUNT_OF(layerNames); + instanceCreateInfo.ppEnabledLayerNames = layerNames; +#endif + + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); + SLANG_RETURN_ON_FAIL(m_api.initInstanceProcs(instance)); + +#if ENABLE_VALIDATION_LAYER + VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + + VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; + debugCreateInfo.pfnCallback = &debugMessageCallback; + debugCreateInfo.pUserData = this; + debugCreateInfo.flags = debugFlags; + + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDebugReportCallbackEXT(instance, &debugCreateInfo, nullptr, &m_debugReportCallback)); +#endif + + uint32_t numPhysicalDevices = 0; + SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, nullptr)); + + List physicalDevices; + physicalDevices.setCount(numPhysicalDevices); + SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, physicalDevices.getBuffer())); + + Index selectedDeviceIndex = 0; + + if (desc.adapter.getLength()) + { + selectedDeviceIndex = -1; + + String lowerAdapter = desc.adapter.toLower(); + + for (Index i = 0; i < physicalDevices.getCount(); ++i) + { + auto physicalDevice = physicalDevices[i]; + + VkPhysicalDeviceProperties basicProps = {}; + m_api.vkGetPhysicalDeviceProperties(physicalDevice, &basicProps); + + String lowerName = String(basicProps.deviceName).toLower(); + + if (lowerName.indexOf(lowerAdapter) != Index(-1)) + { + selectedDeviceIndex = i; + break; + } + } + if (selectedDeviceIndex < 0) + { + // Device not found + return SLANG_FAIL; + } + } + + SLANG_RETURN_ON_FAIL(m_api.initPhysicalDevice(physicalDevices[selectedDeviceIndex])); + + List deviceExtensions; + deviceExtensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + deviceCreateInfo.queueCreateInfoCount = 1; + + deviceCreateInfo.pEnabledFeatures = &m_api.m_deviceFeatures; + + // Get the device features (doesn't use, but useful when debugging) + if (m_api.vkGetPhysicalDeviceFeatures2) + { + VkPhysicalDeviceFeatures2 deviceFeatures2 = {}; + deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + m_api.vkGetPhysicalDeviceFeatures2(m_api.m_physicalDevice, &deviceFeatures2); + } + + VkPhysicalDeviceProperties basicProps = {}; + m_api.vkGetPhysicalDeviceProperties(m_api.m_physicalDevice, &basicProps); + + // Get the API version + const uint32_t majorVersion = VK_VERSION_MAJOR(basicProps.apiVersion); + const uint32_t minorVersion = VK_VERSION_MINOR(basicProps.apiVersion); + + // Float16 features + // Need in this scope because it will be linked into the device creation (if it is available) + VkPhysicalDeviceFloat16Int8FeaturesKHR float16Features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR }; + + // API version check, can't use vkGetPhysicalDeviceProperties2 yet since this device might not support it + if (VK_MAKE_VERSION(majorVersion, minorVersion, 0) >= VK_API_VERSION_1_1 && + m_api.vkGetPhysicalDeviceProperties2 && + m_api.vkGetPhysicalDeviceFeatures2) + { + VkPhysicalDeviceProperties2 physicalDeviceProps2; + + physicalDeviceProps2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + physicalDeviceProps2.pNext = nullptr; + physicalDeviceProps2.properties = {}; + + m_api.vkGetPhysicalDeviceProperties2(m_api.m_physicalDevice, &physicalDeviceProps2); + + // Get device features + VkPhysicalDeviceFeatures2 deviceFeatures2 = {}; + deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + + // Link together for lookup + float16Features.pNext = deviceFeatures2.pNext; + deviceFeatures2.pNext = &float16Features; + + m_api.vkGetPhysicalDeviceFeatures2(m_api.m_physicalDevice, &deviceFeatures2); + + // If we have float16 features then enable + if (float16Features.shaderFloat16) + { + // Link into the creation features + float16Features.pNext = (void*)deviceCreateInfo.pNext; + deviceCreateInfo.pNext = &float16Features; + + // Add the Float16 extension + deviceExtensions.add(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); + + // We have half support + m_features.add("half"); + } + } + + int queueFamilyIndex = m_api.findQueue(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); + assert(queueFamilyIndex >= 0); + + float queuePriority = 0.0f; + VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; + queueCreateInfo.queueFamilyIndex = queueFamilyIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + + deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; + + deviceCreateInfo.enabledExtensionCount = uint32_t(deviceExtensions.getCount()); + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.getBuffer(); + + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDevice(m_api.m_physicalDevice, &deviceCreateInfo, nullptr, &m_device)); + SLANG_RETURN_ON_FAIL(m_api.initDeviceProcs(m_device)); + + { + VkQueue queue; + m_api.vkGetDeviceQueue(m_device, queueFamilyIndex, 0, &queue); + SLANG_RETURN_ON_FAIL(m_deviceQueue.init(m_api, queue, queueFamilyIndex)); + } + + // set up swap chain + + { + VulkanSwapChain::Desc desc; + VulkanSwapChain::PlatformDesc* platformDesc = nullptr; + + desc.init(); + desc.m_format = Format::RGBA_Unorm_UInt8; + +#if SLANG_WINDOWS_FAMILY + VulkanSwapChain::WinPlatformDesc winPlatformDesc; + winPlatformDesc.m_hinstance = ::GetModuleHandle(nullptr); + winPlatformDesc.m_hwnd = (HWND)inWindowHandle; + platformDesc = &winPlatformDesc; +#endif + + SLANG_RETURN_ON_FAIL(m_swapChain.init(&m_deviceQueue, desc, platformDesc)); + } + + // depth/stencil? + + // render pass? + + { + const int numRenderTargets = 1; + bool shouldClear = true; + bool shouldClearDepth = false; + bool shouldClearStencil = false; + bool hasDepthBuffer = false; + + Format depthFormat = Format::Unknown; + VkFormat colorFormat = m_swapChain.getVkFormat(); + + int numAttachments = 0; + // We need extra space if we have depth buffer + VkAttachmentDescription attachmentDesc[kMaxRenderTargets + 1] = {}; + for (int i = 0; i < numRenderTargets; ++i) + { + VkAttachmentDescription& dst = attachmentDesc[numAttachments ++]; + + dst.flags = 0; + dst.format = colorFormat; + dst.samples = VK_SAMPLE_COUNT_1_BIT; + dst.loadOp = shouldClear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + dst.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + dst.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + dst.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + if (hasDepthBuffer) + { + VkAttachmentDescription& dst = attachmentDesc[numAttachments++]; + + dst.flags = 0; + dst.format = VulkanUtil::getVkFormat(depthFormat); + dst.samples = VK_SAMPLE_COUNT_1_BIT; + dst.loadOp = shouldClearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + dst.stencilLoadOp = shouldClearStencil ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + dst.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + dst.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + VkAttachmentReference colorAttachments[kMaxRenderTargets] = {}; + for (int i = 0; i < numRenderTargets; ++i) + { + VkAttachmentReference& dst = colorAttachments[i]; + dst.attachment = i; + dst.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + + VkAttachmentReference depthAttachment = {}; + depthAttachment.attachment = numRenderTargets; + depthAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpassDesc = {}; + subpassDesc.flags = 0; + subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDesc.inputAttachmentCount = 0u; + subpassDesc.pInputAttachments = nullptr; + subpassDesc.colorAttachmentCount = numRenderTargets; + subpassDesc.pColorAttachments = colorAttachments; + subpassDesc.pResolveAttachments = nullptr; + subpassDesc.pDepthStencilAttachment = hasDepthBuffer ? &depthAttachment : nullptr; + subpassDesc.preserveAttachmentCount = 0u; + subpassDesc.pPreserveAttachments = nullptr; + + VkRenderPassCreateInfo renderPassCreateInfo = {}; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.attachmentCount = numAttachments; + renderPassCreateInfo.pAttachments = attachmentDesc; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subpassDesc; + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &m_renderPass)); + } + + // frame buffer + SLANG_RETURN_ON_FAIL(m_swapChain.createFrameBuffers(m_renderPass)); + + _beginRender(); + + return SLANG_OK; +} + +void VKRenderer::submitGpuWork() +{ + m_deviceQueue.flush(); +} + +void VKRenderer::waitForGpu() +{ + m_deviceQueue.flushAndWait(); +} + +void VKRenderer::setClearColor(const float color[4]) +{ + for (int ii = 0; ii < 4; ++ii) + m_clearColor[ii] = color[ii]; +} + +void VKRenderer::clearFrame() +{ +} + +void VKRenderer::presentFrame() +{ + _endRender(); + + const bool vsync = true; + m_swapChain.present(vsync); + + _beginRender(); +} + +TextureResource::Desc VKRenderer::getSwapChainTextureDesc() +{ + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1); + return desc; +} + +SlangResult VKRenderer::captureScreenSurface(Surface& surfaceOut) +{ + return SLANG_FAIL; +} + +static VkBufferUsageFlagBits _calcBufferUsageFlags(Resource::BindFlag::Enum bind) +{ + typedef Resource::BindFlag BindFlag; + + switch (bind) + { + case BindFlag::VertexBuffer: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + case BindFlag::IndexBuffer: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + case BindFlag::ConstantBuffer: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + case BindFlag::StreamOutput: + case BindFlag::RenderTarget: + case BindFlag::DepthStencil: + { + assert(!"Not supported yet"); + return VkBufferUsageFlagBits(0); + } + case BindFlag::UnorderedAccess: return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; + case BindFlag::PixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + case BindFlag::NonPixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + default: return VkBufferUsageFlagBits(0); + } +} + +static VkBufferUsageFlagBits _calcBufferUsageFlags(int bindFlags) +{ + int dstFlags = 0; + while (bindFlags) + { + int lsb = bindFlags & -bindFlags; + dstFlags |= _calcBufferUsageFlags(Resource::BindFlag::Enum(lsb)); + bindFlags &= ~lsb; + } + return VkBufferUsageFlagBits(dstFlags); +} + +static VkBufferUsageFlags _calcBufferUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) +{ + VkBufferUsageFlags usage = _calcBufferUsageFlags(bindFlags); + + if (cpuAccessFlags & Resource::AccessFlag::Read) + { + // If it can be read from, set this + usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + } + if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData) + { + usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + } + + return usage; +} + +static VkImageUsageFlagBits _calcImageUsageFlags(Resource::BindFlag::Enum bind) +{ + typedef Resource::BindFlag BindFlag; + + switch (bind) + { + case BindFlag::RenderTarget: return VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + case BindFlag::DepthStencil: return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + case BindFlag::NonPixelShaderResource: + case BindFlag::PixelShaderResource: + { + // Ignore + return VkImageUsageFlagBits(0); + } + default: + { + assert(!"Unsupported"); + return VkImageUsageFlagBits(0); + } + } +} + +static VkImageUsageFlagBits _calcImageUsageFlags(int bindFlags) +{ + int dstFlags = 0; + while (bindFlags) + { + int lsb = bindFlags & -bindFlags; + dstFlags |= _calcImageUsageFlags(Resource::BindFlag::Enum(lsb)); + bindFlags &= ~lsb; + } + return VkImageUsageFlagBits(dstFlags); +} + +static VkImageUsageFlags _calcImageUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) +{ + VkImageUsageFlags usage = _calcImageUsageFlags(bindFlags); + + usage |= VK_IMAGE_USAGE_SAMPLED_BIT; + + if (cpuAccessFlags & Resource::AccessFlag::Read) + { + // If it can be read from, set this + usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData) + { + usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + + return usage; +} + +void VKRenderer::_transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout) +{ + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = desc.numMipLevels; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + else + { + assert(!"unsupported layout transition!"); + return; + } + + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + m_api.vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); +} + +Result VKRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) +{ + TextureResource::Desc desc(descIn); + desc.setDefaults(initialUsage); + + const VkFormat format = VulkanUtil::getVkFormat(desc.format); + if (format == VK_FORMAT_UNDEFINED) + { + assert(!"Unhandled image format"); + return SLANG_FAIL; + } + + const int arraySize = desc.calcEffectiveArraySize(); + + RefPtr texture(new TextureResourceImpl(desc, initialUsage, &m_api)); + + // Create the image + { + VkImageCreateInfo imageInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; + + switch (desc.type) + { + case Resource::Type::Texture1D: + { + imageInfo.imageType = VK_IMAGE_TYPE_1D; + imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), 1, 1 }; + break; + } + case Resource::Type::Texture2D: + { + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 }; + break; + } + case Resource::Type::TextureCube: + { + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 }; + break; + } + case Resource::Type::Texture3D: + { + // Can't have an array and 3d texture + assert(desc.arraySize <= 1); + + imageInfo.imageType = VK_IMAGE_TYPE_3D; + imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), uint32_t(descIn.size.depth) }; + break; + } + default: + { + assert(!"Unhandled type"); + return SLANG_FAIL; + } + } + + imageInfo.mipLevels = desc.numMipLevels; + imageInfo.arrayLayers = arraySize; + + imageInfo.format = format; + + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.usage = _calcImageUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData); + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.flags = 0; // Optional + + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateImage(m_device, &imageInfo, nullptr, &texture->m_image)); + } + + VkMemoryRequirements memRequirements; + m_api.vkGetImageMemoryRequirements(m_device, texture->m_image, &memRequirements); + + // Allocate the memory + { + VkMemoryPropertyFlags reqMemoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + + VkMemoryAllocateInfo allocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; + + int memoryTypeIndex = m_api.findMemoryTypeIndex(memRequirements.memoryTypeBits, reqMemoryProperties); + assert(memoryTypeIndex >= 0); + + VkMemoryPropertyFlags actualMemoryProperites = m_api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; + + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = memoryTypeIndex; + + SLANG_VK_RETURN_ON_FAIL(m_api.vkAllocateMemory(m_device, &allocInfo, nullptr, &texture->m_imageMemory)); + } + + // Bind the memory to the image + m_api.vkBindImageMemory(m_device, texture->m_image, texture->m_imageMemory, 0); + + if (initData) + { + List mipSizes; + + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + const int numMipMaps = desc.numMipLevels; + assert(initData->numMips == numMipMaps); + + // Calculate how large the buffer has to be + size_t bufferSize = 0; + // Calculate how large an array entry is + for (int j = 0; j < numMipMaps; ++j) + { + const TextureResource::Size mipSize = desc.size.calcMipSize(j); + + const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); + const int numRows = Surface::calcNumRows(desc.format, mipSize.height); + + mipSizes.add(mipSize); + + bufferSize += (rowSizeInBytes * numRows) * mipSize.depth; + } + + + // Calculate the total size taking into account the array + bufferSize *= arraySize; + + Buffer uploadBuffer; + SLANG_RETURN_ON_FAIL(uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); + + assert(mipSizes.getCount() == numMipMaps); + + // Copy into upload buffer + { + int subResourceIndex = 0; + + uint8_t* dstData; + m_api.vkMapMemory(m_device, uploadBuffer.m_memory, 0, bufferSize, 0, (void**)&dstData); + + for (int i = 0; i < arraySize; ++i) + { + for (Index j = 0; j < mipSizes.getCount(); ++j) + { + const auto& mipSize = mipSizes[j]; + + const ptrdiff_t srcRowStride = initData->mipRowStrides[j]; + const int dstRowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); + const int numRows = Surface::calcNumRows(desc.format, mipSize.height); + + for (int k = 0; k < mipSize.depth; k++) + { + const uint8_t* srcData = (const uint8_t*)(initData->subResources[subResourceIndex]); + + for (int l = 0; l < numRows; l++) + { + ::memcpy(dstData, srcData, dstRowSizeInBytes); + + dstData += dstRowSizeInBytes; + srcData += srcRowStride; + } + + subResourceIndex++; + } + } + } + + m_api.vkUnmapMemory(m_device, uploadBuffer.m_memory); + } + + _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + { + size_t srcOffset = 0; + for (int i = 0; i < arraySize; ++i) + { + for (Index j = 0; j < mipSizes.getCount(); ++j) + { + const auto& mipSize = mipSizes[j]; + + const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); + const int numRows = Surface::calcNumRows(desc.format, mipSize.height); + + // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkBufferImageCopy.html + // bufferRowLength and bufferImageHeight specify the data in buffer memory as a subregion of a larger two- or three-dimensional image, + // and control the addressing calculations of data in buffer memory. If either of these values is zero, that aspect of the buffer memory + // is considered to be tightly packed according to the imageExtent. + + VkBufferImageCopy region = {}; + + region.bufferOffset = srcOffset; + region.bufferRowLength = 0; //rowSizeInBytes; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = uint32_t(j); + region.imageSubresource.baseArrayLayer = i; + region.imageSubresource.layerCount = 1; + region.imageOffset = { 0, 0, 0 }; + region.imageExtent = { uint32_t(mipSize.width), uint32_t(mipSize.height), uint32_t(mipSize.depth) }; + + // Do the copy (do all depths in a single go) + m_api.vkCmdCopyBufferToImage(commandBuffer, uploadBuffer.m_buffer, texture->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + // Next + srcOffset += rowSizeInBytes * numRows * mipSize.depth; + } + } + } + + _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + m_deviceQueue.flushAndWait(); + } + + *outResource = texture.detach(); + return SLANG_OK; +} + +Result VKRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) +{ + BufferResource::Desc desc(descIn); + desc.setDefaults(initialUsage); + + const size_t bufferSize = desc.sizeInBytes; + + VkMemoryPropertyFlags reqMemoryProperties = 0; + + VkBufferUsageFlags usage = _calcBufferUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData); + + switch (initialUsage) + { + case Resource::Usage::ConstantBuffer: + { + reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + } + default: break; + } + + RefPtr buffer(new BufferResourceImpl(initialUsage, desc, this)); + SLANG_RETURN_ON_FAIL(buffer->m_buffer.init(m_api, desc.sizeInBytes, usage, reqMemoryProperties)); + + if ((desc.cpuAccessFlags & Resource::AccessFlag::Write) || initData) + { + SLANG_RETURN_ON_FAIL(buffer->m_uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); + } + + if (initData) + { + // TODO: only create staging buffer if the memory type + // used for the buffer doesn't let us fill things in + // directly. + // Copy into staging buffer + void* mappedData = nullptr; + SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); + ::memcpy(mappedData, initData, bufferSize); + m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); + + // Copy from staging buffer to real buffer + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); + + //flushCommandBuffer(commandBuffer); + } + + *outResource = buffer.detach(); + return SLANG_OK; +} + + +VkFilter translateFilterMode(TextureFilteringMode mode) +{ + switch (mode) + { + default: + return VkFilter(0); + +#define CASE(SRC, DST) \ + case TextureFilteringMode::SRC: return VK_FILTER_##DST + + CASE(Point, NEAREST); + CASE(Linear, LINEAR); + +#undef CASE + } +} + +VkSamplerMipmapMode translateMipFilterMode(TextureFilteringMode mode) +{ + switch (mode) + { + default: + return VkSamplerMipmapMode(0); + +#define CASE(SRC, DST) \ + case TextureFilteringMode::SRC: return VK_SAMPLER_MIPMAP_MODE_##DST + + CASE(Point, NEAREST); + CASE(Linear, LINEAR); + +#undef CASE + } +} + +VkSamplerAddressMode translateAddressingMode(TextureAddressingMode mode) +{ + switch (mode) + { + default: + return VkSamplerAddressMode(0); + +#define CASE(SRC, DST) \ + case TextureAddressingMode::SRC: return VK_SAMPLER_ADDRESS_MODE_##DST + + CASE(Wrap, REPEAT); + CASE(ClampToEdge, CLAMP_TO_EDGE); + CASE(ClampToBorder, CLAMP_TO_BORDER); + CASE(MirrorRepeat, MIRRORED_REPEAT); + CASE(MirrorOnce, MIRROR_CLAMP_TO_EDGE); + +#undef CASE + } +} + +static VkCompareOp translateComparisonFunc(ComparisonFunc func) +{ + switch (func) + { + default: + // TODO: need to report failures + return VK_COMPARE_OP_ALWAYS; + +#define CASE(FROM, TO) \ + case ComparisonFunc::FROM: return VK_COMPARE_OP_##TO + + CASE(Never, NEVER); + CASE(Less, LESS); + CASE(Equal, EQUAL); + CASE(LessEqual, LESS_OR_EQUAL); + CASE(Greater, GREATER); + CASE(NotEqual, NOT_EQUAL); + CASE(GreaterEqual, GREATER_OR_EQUAL); + CASE(Always, ALWAYS); +#undef CASE + } +} + +Result VKRenderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) +{ + VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + + samplerInfo.magFilter = translateFilterMode(desc.minFilter); + samplerInfo.minFilter = translateFilterMode(desc.magFilter); + + samplerInfo.addressModeU = translateAddressingMode(desc.addressU); + samplerInfo.addressModeV = translateAddressingMode(desc.addressV); + samplerInfo.addressModeW = translateAddressingMode(desc.addressW); + + samplerInfo.anisotropyEnable = desc.maxAnisotropy > 1; + samplerInfo.maxAnisotropy = (float) desc.maxAnisotropy; + + // TODO: support translation of border color... + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = desc.reductionOp == TextureReductionOp::Comparison; + samplerInfo.compareOp = translateComparisonFunc(desc.comparisonFunc); + samplerInfo.mipmapMode = translateMipFilterMode(desc.mipFilter); + + VkSampler sampler; + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &sampler)); + + RefPtr samplerImpl = new SamplerStateImpl(); + samplerImpl->m_sampler = sampler; + *outSampler = samplerImpl.detach(); + return SLANG_OK; +} + +Result VKRenderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) +{ + assert(!"unimplemented"); + return SLANG_FAIL; +} + +Result VKRenderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + + // TODO: These should come from the `ResourceView::Desc` + VkDeviceSize offset = 0; + VkDeviceSize size = resourceImpl->getDesc().sizeInBytes; + + // There are two different cases we need to think about for buffers. + // + // One is when we have a "uniform texel buffer" or "storage texel buffer," + // in which case we need to construct a `VkBufferView` to represent the + // formatting that is applied to the buffer. This case would correspond + // to a `textureBuffer` or `imageBuffer` in GLSL, and more or less to + // `Buffer<..>` or `RWBuffer<...>` in HLSL. + // + // The other case is a `storage buffer` which is the catch-all for any + // non-formatted R/W access to a buffer. In GLSL this is a `buffer { ... }` + // declaration, while in HLSL it covers a bunch of different `RW*Buffer` + // cases. In these cases we do *not* need a `VkBufferView`, but in + // order to be compatible with other APIs that require views for any + // potentially writable access, we will have to create one anyway. + // + // We will distinguish the two cases by looking at whether the view + // is being requested with a format or not. + // + + switch(desc.type) + { + default: + assert(!"unhandled"); + return SLANG_FAIL; + + case ResourceView::Type::UnorderedAccess: + // Is this a formatted view? + // + if(desc.format == Format::Unknown) + { + // Buffer usage that doesn't involve formatting doesn't + // require a view in Vulkan. + RefPtr viewImpl = new PlainBufferResourceViewImpl(); + viewImpl->m_buffer = resourceImpl; + viewImpl->offset = 0; + viewImpl->size = size; + *outView = viewImpl.detach(); + return SLANG_OK; + } + // + // If the view is formatted, then we need to handle + // it just like we would for a "sampled" buffer: + // + // FALLTHROUGH + case ResourceView::Type::ShaderResource: + { + VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO }; + + info.format = VulkanUtil::getVkFormat(desc.format); + info.buffer = resourceImpl->m_buffer.m_buffer; + info.offset = offset; + info.range = size; + + VkBufferView view; + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &view)); + + RefPtr viewImpl = new TexelBufferResourceViewImpl(); + viewImpl->m_buffer = resourceImpl; + viewImpl->m_view = view; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + } +} + +Result VKRenderer::createInputLayout(const InputElementDesc* elements, UInt numElements, InputLayout** outLayout) +{ + RefPtr layout(new InputLayoutImpl); + + List& dstVertexDescs = layout->m_vertexDescs; + + size_t vertexSize = 0; + dstVertexDescs.setCount(numElements); + + for (UInt i = 0; i < numElements; ++i) + { + const InputElementDesc& srcDesc = elements[i]; + VkVertexInputAttributeDescription& dstDesc = dstVertexDescs[i]; + + dstDesc.location = uint32_t(i); + dstDesc.binding = 0; + dstDesc.format = VulkanUtil::getVkFormat(srcDesc.format); + if (dstDesc.format == VK_FORMAT_UNDEFINED) + { + return SLANG_FAIL; + } + + dstDesc.offset = uint32_t(srcDesc.offset); + + const size_t elementSize = RendererUtil::getFormatSize(srcDesc.format); + assert(elementSize > 0); + const size_t endElement = srcDesc.offset + elementSize; + + vertexSize = (vertexSize < endElement) ? endElement : vertexSize; + } + + // Work out the overall size + layout->m_vertexSize = int(vertexSize); + *outLayout = layout.detach(); + return SLANG_OK; +} + +void* VKRenderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + BufferResourceImpl* buffer = static_cast(bufferIn); + assert(buffer->m_mapFlavor == MapFlavor::Unknown); + + // Make sure everything has completed before reading... + m_deviceQueue.flushAndWait(); + + const size_t bufferSize = buffer->getDesc().sizeInBytes; + + switch (flavor) + { + case MapFlavor::WriteDiscard: + case MapFlavor::HostWrite: + { + if (!buffer->m_uploadBuffer.isInitialized()) + { + return nullptr; + } + + void* mappedData = nullptr; + SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); + buffer->m_mapFlavor = flavor; + return mappedData; + } + case MapFlavor::HostRead: + { + // Make sure there is space in the read buffer + buffer->m_readBuffer.setCount(bufferSize); + + // create staging buffer + Buffer staging; + + SLANG_RETURN_NULL_ON_FAIL(staging.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); + + // Copy from real buffer to staging buffer + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_buffer.m_buffer, staging.m_buffer, 1, ©Info); + + m_deviceQueue.flushAndWait(); + + // Write out the data from the buffer + void* mappedData = nullptr; + SLANG_VK_CHECK(m_api.vkMapMemory(m_device, staging.m_memory, 0, bufferSize, 0, &mappedData)); + + ::memcpy(buffer->m_readBuffer.getBuffer(), mappedData, bufferSize); + m_api.vkUnmapMemory(m_device, staging.m_memory); + + buffer->m_mapFlavor = flavor; + + return buffer->m_readBuffer.getBuffer(); + } + default: + return nullptr; + } +} + +void VKRenderer::unmap(BufferResource* bufferIn) +{ + BufferResourceImpl* buffer = static_cast(bufferIn); + assert(buffer->m_mapFlavor != MapFlavor::Unknown); + + const size_t bufferSize = buffer->getDesc().sizeInBytes; + + switch (buffer->m_mapFlavor) + { + case MapFlavor::WriteDiscard: + case MapFlavor::HostWrite: + { + m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); + + // Copy from staging buffer to real buffer + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); + + // TODO: is this necessary? + //m_deviceQueue.flushAndWait(); + break; + } + default: break; + } + + // Mark as no longer mapped + buffer->m_mapFlavor = MapFlavor::Unknown; +} + +void VKRenderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + m_primitiveTopology = VulkanUtil::getVkPrimitiveTopology(topology); +} + +void VKRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) +{ + { + const Index num = Index(startSlot + slotCount); + if (num > m_boundVertexBuffers.getCount()) + { + m_boundVertexBuffers.setCount(num); + } + } + + for (Index i = 0; i < Index(slotCount); i++) + { + BufferResourceImpl* buffer = static_cast(buffers[i]); + if (buffer) + { + assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer); + } + + BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i]; + boundBuffer.m_buffer = buffer; + boundBuffer.m_stride = int(strides[i]); + boundBuffer.m_offset = int(offsets[i]); + } +} + +void VKRenderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) +{ +} + +void VKRenderer::setDepthStencilTarget(ResourceView* depthStencilView) +{ +} + +void VKRenderer::setViewports(UInt count, Viewport const* viewports) +{ + static const int kMaxViewports = 8; // TODO: base on device caps + assert(count <= kMaxViewports); + + VkViewport vkViewports[kMaxViewports]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inViewport = viewports[ii]; + auto& vkViewport = vkViewports[ii]; + + vkViewport.x = inViewport.originX; + vkViewport.y = inViewport.originY; + vkViewport.width = inViewport.extentX; + vkViewport.height = inViewport.extentY; + vkViewport.minDepth = inViewport.minZ; + vkViewport.maxDepth = inViewport.maxZ; + } + + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + m_api.vkCmdSetViewport(commandBuffer, 0, uint32_t(count), vkViewports); +} + +void VKRenderer::setScissorRects(UInt count, ScissorRect const* rects) +{ + static const int kMaxScissorRects = 8; // TODO: base on device caps + assert(count <= kMaxScissorRects); + + VkRect2D vkRects[kMaxScissorRects]; + for(UInt ii = 0; ii < count; ++ii) + { + auto& inRect = rects[ii]; + auto& vkRect = vkRects[ii]; + + vkRect.offset.x = int32_t(inRect.minX); + vkRect.offset.y = int32_t(inRect.minY); + vkRect.extent.width = uint32_t(inRect.maxX - inRect.minX); + vkRect.extent.height = uint32_t(inRect.maxY - inRect.minY); + } + + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + m_api.vkCmdSetScissor(commandBuffer, 0, uint32_t(count), vkRects); +} + +void VKRenderer::setPipelineState(PipelineType pipelineType, PipelineState* state) +{ + m_currentPipeline = (PipelineStateImpl*)state; +} + +void VKRenderer::draw(UInt vertexCount, UInt startVertex = 0) +{ + auto pipeline = m_currentPipeline; + if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Graphics) + { + assert(!"Invalid render pipeline"); + return; + } + + SLANG_RETURN_VOID_ON_FAIL(_beginPass()); + + // Also create descriptor sets based on the given pipeline layout + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->m_pipeline); + + auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr(); + m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayoutImpl->m_pipelineLayout, + 0, uint32_t(pipelineLayoutImpl->m_descriptorSetCount), + &m_currentDescriptorSets[0], + 0, nullptr); + + // Bind the vertex buffer + if (m_boundVertexBuffers.getCount() > 0 && m_boundVertexBuffers[0].m_buffer) + { + const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[0]; + + VkBuffer vertexBuffers[] = { boundVertexBuffer.m_buffer->m_buffer.m_buffer }; + VkDeviceSize offsets[] = { VkDeviceSize(boundVertexBuffer.m_offset) }; + + m_api.vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + } + + m_api.vkCmdDraw(commandBuffer, static_cast(vertexCount), 1, 0, 0); + + _endPass(); +} + +void VKRenderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) +{ +} + +void VKRenderer::dispatchCompute(int x, int y, int z) +{ + auto pipeline = m_currentPipeline; + if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Compute) + { + assert(!"Invalid compute pipeline"); + return; + } + + // Also create descriptor sets based on the given pipeline layout + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->m_pipeline); + + auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr(); + m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayoutImpl->m_pipelineLayout, + 0, uint32_t(pipelineLayoutImpl->m_descriptorSetCount), + &m_currentDescriptorSets[0], + 0, nullptr); + + m_api.vkCmdDispatch(commandBuffer, x, y, z); +} + +static VkImageViewType _calcImageViewType(TextureResource::Type type, const TextureResource::Desc& desc) +{ + switch (type) + { + case Resource::Type::Texture1D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D; + case Resource::Type::Texture2D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; + case Resource::Type::TextureCube: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE; + case Resource::Type::Texture3D: + { + // Can't have an array and 3d texture + assert(desc.arraySize <= 1); + if (desc.arraySize <= 1) + { + return VK_IMAGE_VIEW_TYPE_3D; + } + break; + } + default: break; + } + + return VK_IMAGE_VIEW_TYPE_MAX_ENUM; +} + +#if 0 +BindingState* VKRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr bindingState(new BindingStateImpl(bindingStateDesc, &m_api)); + + const auto& srcBindings = bindingStateDesc.m_bindings; + const int numBindings = int(srcBindings.Count()); + + auto& dstDetails = bindingState->m_bindingDetails; + dstDetails.SetSize(numBindings); + + for (int i = 0; i < numBindings; ++i) + { + auto& dstDetail = dstDetails[i]; + const auto& srcBinding = srcBindings[i]; + + switch (srcBinding.bindingType) + { + case BindingType::Buffer: + { + if (!srcBinding.resource || !srcBinding.resource->isBuffer()) + { + assert(!"Needs to have a buffer resource set"); + return nullptr; + } + + BufferResourceImpl* bufferResource = static_cast(srcBinding.resource.Ptr()); + const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); + + if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) + { + // VkBufferView uav + + VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO }; + + info.format = VK_FORMAT_R32_SFLOAT; + // TODO: + // Not sure how to handle typeless? + if (bufferResourceDesc.elementSize == 0) + { + info.format = VK_FORMAT_R32_SFLOAT; // DXGI_FORMAT_R32_TYPELESS ? + } + + info.buffer = bufferResource->m_buffer.m_buffer; + info.offset = 0; + info.range = bufferResourceDesc.sizeInBytes; + + SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &dstDetail.m_uav)); + } + + // TODO: Setup views. + // VkImageView srv + + + break; + } + case BindingType::Sampler: + { + VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.maxAnisotropy = 1; + + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + + SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &dstDetail.m_sampler)); + + break; + } + case BindingType::Texture: + { + if (!srcBinding.resource || !srcBinding.resource->isTexture()) + { + assert(!"Needs to have a texture resource set"); + return nullptr; + } + + TextureResourceImpl* textureResource = static_cast(srcBinding.resource.Ptr()); + const TextureResource::Desc& texDesc = textureResource->getDesc(); + + VkImageViewType imageViewType = _calcImageViewType(textureResource->getType(), texDesc); + if (imageViewType == VK_IMAGE_VIEW_TYPE_MAX_ENUM) + { + assert(!"Invalid view type"); + return nullptr; + } + const VkFormat format = VulkanUtil::getVkFormat(texDesc.format); + if (format == VK_FORMAT_UNDEFINED) + { + assert(!"Unhandled image format"); + return nullptr; + } + + // Create the image view + + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = textureResource->m_image; + viewInfo.viewType = imageViewType; + viewInfo.format = format; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateImageView(m_device, &viewInfo, nullptr, &dstDetail.m_srv)); + + break; + } + case BindingType::CombinedTextureSampler: + { + assert(!"not implemented"); + return nullptr; + } + } + } + + return bindingState.detach();; +} +#endif + +static VkDescriptorType translateDescriptorType(DescriptorSlotType type) +{ + switch(type) + { + default: + return VK_DESCRIPTOR_TYPE_MAX_ENUM; + +#define CASE(SRC, DST) \ + case DescriptorSlotType::SRC: return VK_DESCRIPTOR_TYPE_##DST + + CASE(Sampler, SAMPLER); + CASE(CombinedImageSampler, COMBINED_IMAGE_SAMPLER); + CASE(SampledImage, SAMPLED_IMAGE); + CASE(StorageImage, STORAGE_IMAGE); + CASE(UniformTexelBuffer, UNIFORM_TEXEL_BUFFER); + CASE(StorageTexelBuffer, STORAGE_TEXEL_BUFFER); + CASE(UniformBuffer, UNIFORM_BUFFER); + CASE(StorageBuffer, STORAGE_BUFFER); + CASE(DynamicUniformBuffer, UNIFORM_BUFFER_DYNAMIC); + CASE(DynamicStorageBuffer, STORAGE_BUFFER_DYNAMIC); + CASE(InputAttachment, INPUT_ATTACHMENT); + +#undef CASE + } +} + +Result VKRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) +{ + RefPtr descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(m_api); + + Slang::List dstBindings; + + uint32_t descriptorCountForTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE] = { 0, }; + + UInt rangeCount = desc.slotRangeCount; + for(UInt rr = 0; rr < rangeCount; ++rr) + { + auto& srcRange = desc.slotRanges[rr]; + + VkDescriptorType dstDescriptorType = translateDescriptorType(srcRange.type); + + VkDescriptorSetLayoutBinding dstBinding; + dstBinding.binding = uint32_t(rr); + dstBinding.descriptorType = dstDescriptorType; + dstBinding.descriptorCount = uint32_t(srcRange.count); + dstBinding.stageFlags = VK_SHADER_STAGE_ALL; + dstBinding.pImmutableSamplers = nullptr; + + descriptorCountForTypes[dstDescriptorType] += uint32_t(srcRange.count); + + dstBindings.add(dstBinding); + + DescriptorSetLayoutImpl::RangeInfo rangeInfo; + rangeInfo.descriptorType = dstDescriptorType; + descriptorSetLayoutImpl->m_ranges.add(rangeInfo); + } + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.getCount()); + descriptorSetLayoutInfo.pBindings = dstBindings.getBuffer(); + + VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; + SLANG_VK_CHECK(m_api.vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout)); + + // Create a pool while we are at it, to allocate descriptor sets of this type. + + VkDescriptorPoolSize poolSizes[VK_DESCRIPTOR_TYPE_RANGE_SIZE]; + uint32_t poolSizeCount = 0; + for (int ii = 0; ii < SLANG_COUNT_OF(descriptorCountForTypes); ++ii) + { + auto descriptorCount = descriptorCountForTypes[ii]; + if (descriptorCount > 0) + { + poolSizes[poolSizeCount].type = VkDescriptorType(ii); + poolSizes[poolSizeCount].descriptorCount = descriptorCount; + poolSizeCount++; + } + } + + VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size. + descriptorPoolInfo.poolSizeCount = poolSizeCount; + descriptorPoolInfo.pPoolSizes = &poolSizes[0]; + + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; + SLANG_VK_CHECK(m_api.vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + descriptorSetLayoutImpl->m_descriptorSetLayout = descriptorSetLayout; + descriptorSetLayoutImpl->m_descriptorPool = descriptorPool; + + *outLayout = descriptorSetLayoutImpl.detach(); + return SLANG_OK; +} + +Result VKRenderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) +{ + UInt descriptorSetCount = desc.descriptorSetCount; + + VkDescriptorSetLayout descriptorSetLayouts[kMaxDescriptorSets]; + for(UInt ii = 0; ii < descriptorSetCount; ++ii) + { + descriptorSetLayouts[ii] = ((DescriptorSetLayoutImpl*) desc.descriptorSets[ii].layout)->m_descriptorSetLayout; + } + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + pipelineLayoutInfo.setLayoutCount = uint32_t(desc.descriptorSetCount); + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayouts[0]; + + VkPipelineLayout pipelineLayout; + SLANG_VK_CHECK(m_api.vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); + + RefPtr pipelineLayoutImpl = new PipelineLayoutImpl(m_api); + pipelineLayoutImpl->m_pipelineLayout = pipelineLayout; + pipelineLayoutImpl->m_descriptorSetCount = descriptorSetCount; + + *outLayout = pipelineLayoutImpl.detach(); + return SLANG_OK; +} + +Result VKRenderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) +{ + auto layoutImpl = (DescriptorSetLayoutImpl*)layout; + + VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + descriptorSetAllocInfo.descriptorPool = layoutImpl->m_descriptorPool; + descriptorSetAllocInfo.descriptorSetCount = 1; + descriptorSetAllocInfo.pSetLayouts = &layoutImpl->m_descriptorSetLayout; + + VkDescriptorSet descriptorSet; + SLANG_VK_CHECK(m_api.vkAllocateDescriptorSets(m_device, &descriptorSetAllocInfo, &descriptorSet)); + + RefPtr descriptorSetImpl = new DescriptorSetImpl(this); + descriptorSetImpl->m_layout = layoutImpl; + descriptorSetImpl->m_descriptorSet = descriptorSet; + *outDescriptorSet = descriptorSetImpl.detach(); + return SLANG_OK; +} + +/* static */VKRenderer::DescriptorSetImpl::Binding::Type VKRenderer::DescriptorSetImpl::_getBindingType(RefObject* ptr) +{ + typedef Binding::Type Type; + + if (ptr) + { + if (dynamic_cast(ptr)) + { + return Type::ResourceView; + } + else if (dynamic_cast(ptr)) + { + return Type::BufferResource; + } + else if (dynamic_cast(ptr)) + { + return Type::SamplerState; + } + } + return Type::Unknown; +} + +void VKRenderer::DescriptorSetImpl::_setBinding(Binding::Type type, UInt range, UInt index, RefObject* ptr) +{ + SLANG_ASSERT(ptr == nullptr || _getBindingType(ptr) == type); + + const Index numBindings = m_bindings.getCount(); + for (Index i = 0; i < numBindings; ++i) + { + Binding& binding = m_bindings[i]; + + if (binding.type == type && binding.range == uint32_t(range) && binding.index == uint32_t(index)) + { + if (ptr) + { + binding.obj = ptr; + } + else + { + m_bindings.removeAt(i); + } + + return; + } + } + + // If an entry is not found, and we have a pointer, create an entry + if (ptr) + { + Binding binding; + binding.type = type; + binding.range = uint32_t(range); + binding.index = uint32_t(index); + binding.obj = ptr; + + m_bindings.add(binding); + } +} + +void VKRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) +{ + auto bufferImpl = (BufferResourceImpl*)buffer; + + VkDescriptorBufferInfo bufferInfo = {}; + bufferInfo.buffer = bufferImpl->m_buffer.m_buffer; + bufferInfo.offset = 0; + bufferInfo.range = bufferImpl->getDesc().sizeInBytes; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.dstSet = m_descriptorSet; + writeInfo.dstBinding = uint32_t(range); + writeInfo.dstArrayElement = uint32_t(index); + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; + writeInfo.pBufferInfo = &bufferInfo; + + m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); + + _setBinding(Binding::Type::BufferResource, range, index, buffer); +} + +void VKRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) +{ + auto viewImpl = (ResourceViewImpl*)view; + switch (viewImpl->m_type) + { + case ResourceViewImpl::ViewType::Texture: + { + auto textureViewImpl = (TextureResourceViewImpl*)viewImpl; + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageView = textureViewImpl->m_view; + imageInfo.imageLayout = textureViewImpl->m_layout; + // imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.dstSet = m_descriptorSet; + writeInfo.dstBinding = uint32_t(range); + writeInfo.dstArrayElement = uint32_t(index); + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; + writeInfo.pImageInfo = &imageInfo; + + m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); + } + break; + + case ResourceViewImpl::ViewType::TexelBuffer: + { + auto bufferViewImpl = (TexelBufferResourceViewImpl*)viewImpl; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.dstSet = m_descriptorSet; + writeInfo.dstBinding = uint32_t(range); + writeInfo.dstArrayElement = uint32_t(index); + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; + writeInfo.pTexelBufferView = &bufferViewImpl->m_view; + + m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); + } + break; + + case ResourceViewImpl::ViewType::PlainBuffer: + { + auto bufferViewImpl = (PlainBufferResourceViewImpl*) viewImpl; + + VkDescriptorBufferInfo bufferInfo = {}; + bufferInfo.buffer = bufferViewImpl->m_buffer->m_buffer.m_buffer; + bufferInfo.offset = bufferViewImpl->offset; + bufferInfo.range = bufferViewImpl->size; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.dstSet = m_descriptorSet; + writeInfo.dstBinding = uint32_t(range); + writeInfo.dstArrayElement = uint32_t(index); + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; + writeInfo.pBufferInfo = &bufferInfo; + + m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); + } + break; + + } + + _setBinding(Binding::Type::ResourceView, range, index, view); +} + +void VKRenderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) +{ + + _setBinding(Binding::Type::SamplerState, range, index, sampler); +} + +void VKRenderer::DescriptorSetImpl::setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) +{ + + _setBinding(Binding::Type::SamplerState, range, index, sampler); + _setBinding(Binding::Type::ResourceView, range, index, textureView); +} + +void VKRenderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) +{ + // Ideally this should eventually be as simple as: + // + // m_api.vkCmdBindDescriptorSets( + // commandBuffer, + // translatePipelineBindPoint(pipelineType), + // layout->m_pipelineLayout, + // index, + // 1, + // ((DescriptorSetImpl*) descriptorSet)->m_descriptorSet, + // 0, + // nullptr); + // + // For now we are lazily flushing state right before drawing, so + // we will hang onto the parameters that were passed in and then + // use them later. + // + + auto descriptorSetImpl = (DescriptorSetImpl*)descriptorSet; + m_currentDescriptorSetImpls[index] = descriptorSetImpl; + m_currentDescriptorSets[index] = descriptorSetImpl->m_descriptorSet; +} + +Result VKRenderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) +{ + RefPtr impl = new ShaderProgramImpl(desc.pipelineType); + if( desc.pipelineType == PipelineType::Compute) + { + auto computeKernel = desc.findKernel(StageType::Compute); + impl->m_compute = compileEntryPoint(*computeKernel, VK_SHADER_STAGE_COMPUTE_BIT, impl->m_buffers[0]); + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + impl->m_vertex = compileEntryPoint(*vertexKernel, VK_SHADER_STAGE_VERTEX_BIT, impl->m_buffers[0]); + impl->m_fragment = compileEntryPoint(*fragmentKernel, VK_SHADER_STAGE_FRAGMENT_BIT, impl->m_buffers[1]); + } + *outProgram = impl.detach(); + return SLANG_OK; +} + +Result VKRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +{ + VkPipelineCache pipelineCache = VK_NULL_HANDLE; + + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; + + const int width = int(desc.framebufferWidth); + const int height = int(desc.framebufferHeight); + + // Shader Stages + // + // Currently only handles vertex/fragment. + + static const uint32_t kMaxShaderStages = 2; + VkPipelineShaderStageCreateInfo shaderStages[kMaxShaderStages]; + + uint32_t shaderStageCount = 0; + shaderStages[shaderStageCount++] = programImpl->m_vertex; + shaderStages[shaderStageCount++] = programImpl->m_fragment; + + // VertexBuffer/s + // Currently only handles one + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + + VkVertexInputBindingDescription vertexInputBindingDescription; + + if (inputLayoutImpl) + { + vertexInputBindingDescription.binding = 0; + vertexInputBindingDescription.stride = inputLayoutImpl->m_vertexSize; + vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + const auto& srcAttributeDescs = inputLayoutImpl->m_vertexDescs; + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription; + + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(srcAttributeDescs.getCount()); + vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.getBuffer(); + } + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)width; + viewport.height = (float)height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = { uint32_t(width), uint32_t(height) }; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout; + pipelineInfo.renderPass = m_renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + + VkPipeline pipeline = VK_NULL_HANDLE; + SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline)); + + RefPtr pipelineStateImpl = new PipelineStateImpl(m_api); + pipelineStateImpl->m_pipeline = pipeline; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_shaderProgram = programImpl; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + +Result VKRenderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +{ + VkPipelineCache pipelineCache = VK_NULL_HANDLE; + + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + + VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; + computePipelineInfo.stage = programImpl->m_compute; + computePipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout; + + VkPipeline pipeline = VK_NULL_HANDLE; + SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline)); + + RefPtr pipelineStateImpl = new PipelineStateImpl(m_api); + pipelineStateImpl->m_pipeline = pipeline; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_shaderProgram = programImpl; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + + +#if 0 + else if (m_currentProgram->m_pipelineType == PipelineType::Graphics) + { + // Create the graphics pipeline + + const int width = m_swapChain.getWidth(); + const int height = m_swapChain.getHeight(); + + + + + + // + + + } + else + { + assert(!"Unhandled program type"); + return SLANG_FAIL; + } + + pipelineOut = pipeline; + return SLANG_OK; + + +#endif + +} // renderer_test diff --git a/tools/gfx/vulkan/render-vk.h b/tools/gfx/vulkan/render-vk.h new file mode 100644 index 000000000..14a8e403a --- /dev/null +++ b/tools/gfx/vulkan/render-vk.h @@ -0,0 +1,10 @@ +// render-vk.h +#pragma once + +namespace gfx { + +class Renderer; + +Renderer* createVKRenderer(); + +} // gfx diff --git a/tools/gfx/vulkan/vk-api.cpp b/tools/gfx/vulkan/vk-api.cpp new file mode 100644 index 000000000..50f80aa26 --- /dev/null +++ b/tools/gfx/vulkan/vk-api.cpp @@ -0,0 +1,142 @@ +// vk-api.cpp +#include "vk-api.h" + +#include "../../source/core/slang-list.h" + +namespace gfx { +using namespace Slang; + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanApi !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +#define VK_API_CHECK_FUNCTION(x) && (x != nullptr) +#define VK_API_CHECK_FUNCTIONS(FUNCTION_LIST) true FUNCTION_LIST(VK_API_CHECK_FUNCTION) + +bool VulkanApi::areDefined(ProcType type) const +{ + switch (type) + { + case ProcType::Global: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_GLOBAL_PROCS); + case ProcType::Instance: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_INSTANCE_PROCS); + case ProcType::Device: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_DEVICE_PROCS); + default: + { + assert(!"Unhandled type"); + return false; + } + } +} + +Slang::Result VulkanApi::initGlobalProcs(const VulkanModule& module) +{ +#define VK_API_GET_GLOBAL_PROC(x) x = (PFN_##x)module.getFunction(#x); + + // Initialize all the global functions + VK_API_ALL_GLOBAL_PROCS(VK_API_GET_GLOBAL_PROC) + + if (!areDefined(ProcType::Global)) + { + return SLANG_FAIL; + } + m_module = &module; + return SLANG_OK; +} + +Slang::Result VulkanApi::initInstanceProcs(VkInstance instance) +{ + assert(instance && vkGetInstanceProcAddr != nullptr); + +#define VK_API_GET_INSTANCE_PROC(x) x = (PFN_##x)vkGetInstanceProcAddr(instance, #x); + + VK_API_ALL_INSTANCE_PROCS(VK_API_GET_INSTANCE_PROC) + + // Get optional + VK_API_INSTANCE_PROCS_OPT(VK_API_GET_INSTANCE_PROC) + + if (!areDefined(ProcType::Instance)) + { + return SLANG_FAIL; + } + + + m_instance = instance; + return SLANG_OK; +} + +Slang::Result VulkanApi::initPhysicalDevice(VkPhysicalDevice physicalDevice) +{ + assert(m_physicalDevice == VK_NULL_HANDLE); + m_physicalDevice = physicalDevice; + + vkGetPhysicalDeviceProperties(m_physicalDevice, &m_deviceProperties); + vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_deviceFeatures); + vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_deviceMemoryProperties); + + return SLANG_OK; +} + +Slang::Result VulkanApi::initDeviceProcs(VkDevice device) +{ + assert(m_instance && device && vkGetDeviceProcAddr != nullptr); + +#define VK_API_GET_DEVICE_PROC(x) x = (PFN_##x)vkGetDeviceProcAddr(device, #x); + + VK_API_ALL_DEVICE_PROCS(VK_API_GET_DEVICE_PROC) + + if (!areDefined(ProcType::Device)) + { + return SLANG_FAIL; + } + + m_device = device; + return SLANG_OK; +} + +int VulkanApi::findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const +{ + assert(typeBits); + + const int numMemoryTypes = int(m_deviceMemoryProperties.memoryTypeCount); + + // bit holds current test bit against typeBits. Ie bit == 1 << typeBits + + uint32_t bit = 1; + for (int i = 0; i < numMemoryTypes; ++i, bit += bit) + { + auto const& memoryType = m_deviceMemoryProperties.memoryTypes[i]; + if ((typeBits & bit) && (memoryType.propertyFlags & properties) == properties) + { + return i; + } + } + + //assert(!"failed to find a usable memory type"); + return -1; +} + +int VulkanApi::findQueue(VkQueueFlags reqFlags) const +{ + assert(m_physicalDevice != VK_NULL_HANDLE); + + uint32_t numQueueFamilies = 0; + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, nullptr); + + Slang::List queueFamilies; + queueFamilies.setCount(numQueueFamilies); + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, queueFamilies.getBuffer()); + + // Find a queue that can service our needs + //VkQueueFlags reqQueueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; + + int queueFamilyIndex = -1; + for (int i = 0; i < int(numQueueFamilies); ++i) + { + if ((queueFamilies[i].queueFlags & reqFlags) == reqFlags) + { + return i; + } + } + + return -1; +} + +} // renderer_test diff --git a/tools/gfx/vulkan/vk-api.h b/tools/gfx/vulkan/vk-api.h new file mode 100644 index 000000000..001f44d19 --- /dev/null +++ b/tools/gfx/vulkan/vk-api.h @@ -0,0 +1,203 @@ +// vk-api.h +#pragma once + +#include "vk-module.h" + +namespace gfx { + +#define VK_API_GLOBAL_PROCS(x) \ + x(vkGetInstanceProcAddr) \ + x(vkCreateInstance) \ + /* */ + +#define VK_API_INSTANCE_PROCS_OPT(x) \ + x(vkGetPhysicalDeviceFeatures2) \ + x(vkGetPhysicalDeviceProperties2) \ + /* */ + +#define VK_API_INSTANCE_PROCS(x) \ + x(vkCreateDevice) \ + x(vkCreateDebugReportCallbackEXT) \ + x(vkDestroyDebugReportCallbackEXT) \ + x(vkDebugReportMessageEXT) \ + x(vkEnumeratePhysicalDevices) \ + x(vkGetPhysicalDeviceProperties) \ + x(vkGetPhysicalDeviceFeatures) \ + x(vkGetPhysicalDeviceMemoryProperties) \ + x(vkGetPhysicalDeviceQueueFamilyProperties) \ + x(vkGetPhysicalDeviceFormatProperties) \ + x(vkGetDeviceProcAddr) \ + /* */ + +#define VK_API_DEVICE_PROCS(x) \ + x(vkCreateDescriptorPool) \ + x(vkDestroyDescriptorPool) \ + x(vkGetDeviceQueue) \ + x(vkQueueSubmit) \ + x(vkQueueWaitIdle) \ + x(vkCreateBuffer) \ + x(vkAllocateMemory) \ + x(vkMapMemory) \ + x(vkUnmapMemory) \ + x(vkCmdCopyBuffer) \ + x(vkDestroyBuffer) \ + x(vkFreeMemory) \ + x(vkCreateDescriptorSetLayout) \ + x(vkDestroyDescriptorSetLayout) \ + x(vkAllocateDescriptorSets) \ + x(vkUpdateDescriptorSets) \ + x(vkCreatePipelineLayout) \ + x(vkDestroyPipelineLayout) \ + x(vkCreateComputePipelines) \ + x(vkCreateGraphicsPipelines) \ + x(vkDestroyPipeline) \ + x(vkCreateShaderModule) \ + x(vkDestroyShaderModule) \ + x(vkCreateFramebuffer) \ + x(vkDestroyFramebuffer) \ + x(vkCreateImage) \ + x(vkDestroyImage) \ + x(vkCreateImageView) \ + x(vkDestroyImageView) \ + x(vkCreateRenderPass) \ + x(vkDestroyRenderPass) \ + x(vkCreateCommandPool) \ + x(vkDestroyCommandPool) \ + x(vkCreateSampler) \ + x(vkDestroySampler) \ + x(vkCreateBufferView) \ + x(vkDestroyBufferView) \ + \ + x(vkGetBufferMemoryRequirements) \ + x(vkGetImageMemoryRequirements) \ + \ + x(vkCmdBindPipeline) \ + x(vkCmdBindDescriptorSets) \ + x(vkCmdDispatch) \ + x(vkCmdDraw) \ + x(vkCmdSetScissor) \ + x(vkCmdSetViewport) \ + x(vkCmdBindVertexBuffers) \ + x(vkCmdBindIndexBuffer) \ + x(vkCmdBeginRenderPass) \ + x(vkCmdEndRenderPass) \ + x(vkCmdPipelineBarrier) \ + x(vkCmdCopyBufferToImage)\ + \ + x(vkCreateFence) \ + x(vkDestroyFence) \ + x(vkResetFences) \ + x(vkGetFenceStatus) \ + x(vkWaitForFences) \ + \ + x(vkCreateSemaphore) \ + x(vkDestroySemaphore) \ + \ + x(vkCreateEvent) \ + x(vkDestroyEvent) \ + x(vkGetEventStatus) \ + x(vkSetEvent) \ + x(vkResetEvent) \ + \ + x(vkFreeCommandBuffers) \ + x(vkAllocateCommandBuffers) \ + x(vkBeginCommandBuffer) \ + x(vkEndCommandBuffer) \ + x(vkResetCommandBuffer) \ + \ + x(vkBindImageMemory) \ + x(vkBindBufferMemory) \ + /* */ + +#if SLANG_WINDOWS_FAMILY +# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ + x(vkCreateWin32SurfaceKHR) \ + /* */ +#else +# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ + x(vkCreateXlibSurfaceKHR) \ + /* */ +#endif + +#define VK_API_INSTANCE_KHR_PROCS(x) \ + VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ + x(vkGetPhysicalDeviceSurfaceSupportKHR) \ + x(vkGetPhysicalDeviceSurfaceFormatsKHR) \ + x(vkGetPhysicalDeviceSurfacePresentModesKHR) \ + x(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ + x(vkDestroySurfaceKHR) \ + /* */ + +#define VK_API_DEVICE_KHR_PROCS(x) \ + x(vkQueuePresentKHR) \ + x(vkCreateSwapchainKHR) \ + x(vkGetSwapchainImagesKHR) \ + x(vkDestroySwapchainKHR) \ + x(vkAcquireNextImageKHR) \ + /* */ + +#define VK_API_ALL_GLOBAL_PROCS(x) \ + VK_API_GLOBAL_PROCS(x) + +#define VK_API_ALL_INSTANCE_PROCS(x) \ + VK_API_INSTANCE_PROCS(x) \ + VK_API_INSTANCE_KHR_PROCS(x) + +#define VK_API_ALL_DEVICE_PROCS(x) \ + VK_API_DEVICE_PROCS(x) \ + VK_API_DEVICE_KHR_PROCS(x) + +#define VK_API_ALL_PROCS(x) \ + VK_API_ALL_GLOBAL_PROCS(x) \ + VK_API_ALL_INSTANCE_PROCS(x) \ + VK_API_ALL_DEVICE_PROCS(x) \ + \ + VK_API_INSTANCE_PROCS_OPT(x) \ + /* */ + +#define VK_API_DECLARE_PROC(NAME) PFN_##NAME NAME = nullptr; + +struct VulkanApi +{ + VK_API_ALL_PROCS(VK_API_DECLARE_PROC) + + enum class ProcType + { + Global, + Instance, + Device, + }; + + /// Returns true if all the functions in the class are defined + bool areDefined(ProcType type) const; + + /// Sets up global parameters + Slang::Result initGlobalProcs(const VulkanModule& module); + /// Initialize the instance functions + Slang::Result initInstanceProcs(VkInstance instance); + + /// Called before initDevice + Slang::Result initPhysicalDevice(VkPhysicalDevice physicalDevice); + + /// Initialize the device functions + Slang::Result initDeviceProcs(VkDevice device); + + /// Type bits control which indices are tested against bit 0 for testing at index 0 + /// properties - a memory type must have all the bits set as passed in + /// Returns -1 if couldn't find an appropriate memory type index + int findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const; + + /// Given queue required flags, finds a queue + int findQueue(VkQueueFlags reqFlags) const; + + const VulkanModule* m_module = nullptr; ///< Module this was all loaded from + VkInstance m_instance = VK_NULL_HANDLE; + VkDevice m_device = VK_NULL_HANDLE; + VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; + + VkPhysicalDeviceProperties m_deviceProperties; + VkPhysicalDeviceFeatures m_deviceFeatures; + VkPhysicalDeviceMemoryProperties m_deviceMemoryProperties; +}; + +} // renderer_test diff --git a/tools/gfx/vulkan/vk-device-queue.cpp b/tools/gfx/vulkan/vk-device-queue.cpp new file mode 100644 index 000000000..10a3d0e3b --- /dev/null +++ b/tools/gfx/vulkan/vk-device-queue.cpp @@ -0,0 +1,199 @@ +// vk-device-queue.cpp +#include "vk-device-queue.h" + +#include +#include +#include + +namespace gfx { +using namespace Slang; + +VulkanDeviceQueue::~VulkanDeviceQueue() +{ + for (int i = 0; i < int(EventType::CountOf); ++i) + { + m_api->vkDestroySemaphore(m_api->m_device, m_semaphores[i], nullptr); + } + + for (int i = 0; i < m_numCommandBuffers; i++) + { + m_api->vkFreeCommandBuffers(m_api->m_device, m_commandPool, 1, &m_commandBuffers[i]); + m_api->vkDestroyFence(m_api->m_device, m_fences[i].fence, nullptr); + } + m_api->vkDestroyCommandPool(m_api->m_device, m_commandPool, nullptr); +} + +SlangResult VulkanDeviceQueue::init(const VulkanApi& api, VkQueue queue, int queueIndex) +{ + assert(m_api == nullptr); + m_api = &api; + + for (int i = 0; i < int(EventType::CountOf); ++i) + { + m_semaphores[i] = VK_NULL_HANDLE; + m_currentSemaphores[i] = VK_NULL_HANDLE; + } + + m_numCommandBuffers = kMaxCommandBuffers; + m_queueIndex = queueIndex; + + m_queue = queue; + + VkCommandPoolCreateInfo poolCreateInfo = {}; + poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + poolCreateInfo.queueFamilyIndex = queueIndex; + + api.vkCreateCommandPool(api.m_device, &poolCreateInfo, nullptr, &m_commandPool); + + VkCommandBufferAllocateInfo commandInfo = {}; + commandInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandInfo.commandPool = m_commandPool; + commandInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandInfo.commandBufferCount = 1; + + VkFenceCreateInfo fenceCreateInfo = {}; + fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceCreateInfo.flags = 0; // VK_FENCE_CREATE_SIGNALED_BIT; + + for (int i = 0; i < m_numCommandBuffers; i++) + { + Fence& fence = m_fences[i]; + + api.vkAllocateCommandBuffers(api.m_device, &commandInfo, &m_commandBuffers[i]); + + api.vkCreateFence(api.m_device, &fenceCreateInfo, nullptr, &fence.fence); + fence.active = false; + fence.value = 0; + } + + VkSemaphoreCreateInfo semaphoreCreateInfo = {}; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + for (int i = 0; i < int(EventType::CountOf); ++i) + { + api.vkCreateSemaphore(api.m_device, &semaphoreCreateInfo, nullptr, &m_semaphores[i]); + } + + // Second step of flush to prime command buffer + flushStepB(); + + return SLANG_OK; +} + +void VulkanDeviceQueue::flushStepA() +{ + m_api->vkEndCommandBuffer(m_commandBuffer); + + VkPipelineStageFlags stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + // Wait semaphores + if (isCurrent(EventType::BeginFrame)) + { + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &m_currentSemaphores[int(EventType::BeginFrame)]; + } + + submitInfo.pWaitDstStageMask = &stageFlags; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &m_commandBuffer; + + // Signal semaphores + if (isCurrent(EventType::EndFrame)) + { + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &m_currentSemaphores[int(EventType::EndFrame)]; + } + + Fence& fence = m_fences[m_commandBufferIndex]; + + m_api->vkQueueSubmit(m_queue, 1, &submitInfo, fence.fence); + + // mark signaled fence value + fence.value = m_nextFenceValue; + fence.active = true; + + // increment fence value + m_nextFenceValue++; + + // No longer waiting on this semaphore + makeCompleted(EventType::BeginFrame); +} + +void VulkanDeviceQueue::_updateFenceAtIndex( int fenceIndex, bool blocking) +{ + Fence& fence = m_fences[fenceIndex]; + + if (fence.active) + { + uint64_t timeout = blocking ? ~uint64_t(0) : 0; + + if (VK_SUCCESS == m_api->vkWaitForFences(m_api->m_device, 1, &fence.fence, VK_TRUE, timeout)) + { + m_api->vkResetFences(m_api->m_device, 1, &fence.fence); + + fence.active = false; + + if (fence.value > m_lastFenceCompleted) + { + m_lastFenceCompleted = fence.value; + } + } + } +} + +void VulkanDeviceQueue::flushStepB() +{ + m_commandBufferIndex = (m_commandBufferIndex + 1) % m_numCommandBuffers; + m_commandBuffer = m_commandBuffers[m_commandBufferIndex]; + + // non-blocking update of fence values + for (int i = 0; i < m_numCommandBuffers; ++i) + { + _updateFenceAtIndex(i, false); + } + + // blocking update of fence values + _updateFenceAtIndex(m_commandBufferIndex, true); + + m_api->vkResetCommandBuffer(m_commandBuffer, 0); + + //m_api.vkResetCommandPool(m_api->m_device, m_commandPool, 0); + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + m_api->vkBeginCommandBuffer(m_commandBuffer, &beginInfo); +} + +void VulkanDeviceQueue::flush() +{ + flushStepA(); + flushStepB(); +} + +void VulkanDeviceQueue::flushAndWait() +{ + flush(); + waitForIdle(); +} + +VkSemaphore VulkanDeviceQueue::makeCurrent(EventType eventType) +{ + assert(!isCurrent(eventType)); + VkSemaphore semaphore = m_semaphores[int(eventType)]; + m_currentSemaphores[int(eventType)] = semaphore; + return semaphore; +} + +void VulkanDeviceQueue::makeCompleted(EventType eventType) +{ + m_currentSemaphores[int(eventType)] = VK_NULL_HANDLE; +} + +} // renderer_test diff --git a/tools/gfx/vulkan/vk-device-queue.h b/tools/gfx/vulkan/vk-device-queue.h new file mode 100644 index 000000000..d57483ec0 --- /dev/null +++ b/tools/gfx/vulkan/vk-device-queue.h @@ -0,0 +1,94 @@ +// vk-swap-chain.h +#pragma once + +#include "vk-api.h" + +namespace gfx { + +struct VulkanDeviceQueue +{ + enum + { + kMaxCommandBuffers = 8, + }; + + enum class EventType + { + BeginFrame, + EndFrame, + CountOf, + }; + + /// Initialize - must be called before anything else can be done + SlangResult init(const VulkanApi& api, VkQueue queue, int queueIndex); + + /// Flushes the current command list, and steps to next (internally this is equivalent to a stepA followed by stepB) + void flush(); + /// Performs a full flush, and then waits for idle. + void flushAndWait(); + + /// Blocks until all work submitted to GPU has completed + void waitForIdle() { m_api->vkQueueWaitIdle(m_queue); } + + /// Get the graphics queue index (as set on init) + int getQueueIndex() const { return m_queueIndex; } + + /// Make the specified event 'current' - meaning it's semaphore must be waited on + VkSemaphore makeCurrent(EventType eventType); + /// Makes the event no longer required to be waited on + void makeCompleted(EventType eventType); + /// Returns true if the event is already current + SLANG_FORCE_INLINE bool isCurrent(EventType eventType) const { return m_currentSemaphores[int(eventType)] != VK_NULL_HANDLE; } + + /// Get the command buffer + VkCommandBuffer getCommandBuffer() const { return m_commandBuffer; } + + /// Get the queue + VkQueue getQueue() const { return m_queue; } + + /// Get the API + const VulkanApi* getApi() const { return m_api; } + + /// Flushes the current command list + void flushStepA(); + /// Steps to next command buffer and opens. May block if command buffer is still in use + void flushStepB(); + + /// Dtor + ~VulkanDeviceQueue(); + + protected: + + struct Fence + { + VkFence fence; + bool active; + uint64_t value; + }; + + void _updateFenceAtIndex(int fenceIndex, bool blocking); + + VkQueue m_queue = VK_NULL_HANDLE; + + VkCommandPool m_commandPool = VK_NULL_HANDLE; + int m_numCommandBuffers = 0; + int m_commandBufferIndex = 0; + // There are the same amount of command buffers as fences + VkCommandBuffer m_commandBuffers[kMaxCommandBuffers] = { VK_NULL_HANDLE }; + + Fence m_fences[kMaxCommandBuffers] = { {VK_NULL_HANDLE, 0, 0u} }; + + VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE; + + VkSemaphore m_semaphores[int(EventType::CountOf)]; + VkSemaphore m_currentSemaphores[int(EventType::CountOf)]; + + uint64_t m_lastFenceCompleted = 1; + uint64_t m_nextFenceValue = 2; + + int m_queueIndex = 0; + + const VulkanApi* m_api = nullptr; +}; + +} // renderer_test diff --git a/tools/gfx/vulkan/vk-module.cpp b/tools/gfx/vulkan/vk-module.cpp new file mode 100644 index 000000000..4e92a3d2c --- /dev/null +++ b/tools/gfx/vulkan/vk-module.cpp @@ -0,0 +1,76 @@ +// module.cpp +#include "vk-module.h" + +#include +#include +#include + +#if SLANG_WINDOWS_FAMILY +# include +#else +# include +#endif + +namespace gfx { +using namespace Slang; + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanModule !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +Slang::Result VulkanModule::init() +{ + if (isInitialized()) + { + destroy(); + return SLANG_OK; + } + + const char* dynamicLibraryName = "Unknown"; + +#if SLANG_WINDOWS_FAMILY + dynamicLibraryName = "vulkan-1.dll"; + HMODULE module = ::LoadLibraryA(dynamicLibraryName); + m_module = (void*)module; +#else + dynamicLibraryName = "libvulkan.so.1"; + m_module = dlopen(dynamicLibraryName, RTLD_NOW); +#endif + + if (!m_module) + { + fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); + return SLANG_FAIL; + } + + return SLANG_OK; +} + +PFN_vkVoidFunction VulkanModule::getFunction(const char* name) const +{ + assert(m_module); + if (!m_module) + { + return nullptr; + } +#if SLANG_WINDOWS_FAMILY + return (PFN_vkVoidFunction)::GetProcAddress((HMODULE)m_module, name); +#else + return (PFN_vkVoidFunction)dlsym(m_module, name); +#endif +} + +void VulkanModule::destroy() +{ + if (!isInitialized()) + { + return; + } + +#if SLANG_WINDOWS_FAMILY + ::FreeLibrary((HMODULE)m_module); +#else + dlclose(m_module); +#endif + m_module = nullptr; +} + +} // renderer_test diff --git a/tools/gfx/vulkan/vk-module.h b/tools/gfx/vulkan/vk-module.h new file mode 100644 index 000000000..4d18823ca --- /dev/null +++ b/tools/gfx/vulkan/vk-module.h @@ -0,0 +1,40 @@ +// vk-module.h +#pragma once + +#include "../../slang.h" + +#include "../../slang-com-helper.h" + +#if SLANG_WINDOWS_FAMILY +# define VK_USE_PLATFORM_WIN32_KHR 1 +#else +# define VK_USE_PLATFORM_XLIB_KHR 1 +#endif + +#define VK_NO_PROTOTYPES + +#include + +namespace gfx { + +struct VulkanModule +{ + /// true if has been initialized + SLANG_FORCE_INLINE bool isInitialized() const { return m_module != nullptr; } + + /// Get a function by name + PFN_vkVoidFunction getFunction(const char* name) const; + + /// Initialize + Slang::Result init(); + /// Destroy + void destroy(); + + /// Dtor + ~VulkanModule() { destroy(); } + + protected: + void* m_module = nullptr; +}; + +} // renderer_test diff --git a/tools/gfx/vulkan/vk-swap-chain.cpp b/tools/gfx/vulkan/vk-swap-chain.cpp new file mode 100644 index 000000000..5cf2e96ae --- /dev/null +++ b/tools/gfx/vulkan/vk-swap-chain.cpp @@ -0,0 +1,421 @@ +// vk-swap-chain.cpp +#include "vk-swap-chain.h" + +#include "vk-util.h" + +#include "../../source/core/slang-list.h" + +#include +#include + +namespace gfx { +using namespace Slang; + +static Index _indexOfFormat(List& formatsIn, VkFormat format) +{ + const Index numFormats = formatsIn.getCount(); + const VkSurfaceFormatKHR* formats = formatsIn.getBuffer(); + + for (Index i = 0; i < numFormats; ++i) + { + if (formats[i].format == format) + { + return i; + } + } + return -1; +} + +SlangResult VulkanSwapChain::init(VulkanDeviceQueue* deviceQueue, const Desc& descIn, const PlatformDesc* platformDescIn) +{ + assert(platformDescIn); + + m_deviceQueue = deviceQueue; + m_api = deviceQueue->getApi(); + + // Make sure it's not set initially + m_format = VK_FORMAT_UNDEFINED; + + Desc desc(descIn); + +#if SLANG_WINDOWS_FAMILY + const WinPlatformDesc* platformDesc = static_cast(platformDescIn); + _setPlatformDesc(*platformDesc); + + VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.hinstance = platformDesc->m_hinstance; + surfaceCreateInfo.hwnd = platformDesc->m_hwnd; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateWin32SurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); +#else + const XPlatformDesc* platformDesc = static_cast(platformDescIn); + _setPlatformDesc(*platformDesc); + + VkXlibSurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.dpy = platformDesc->m_display; + surfaceCreateInfo.window = platformDesc->m_window; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateXlibSurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); +#endif + + VkBool32 supported = false; + m_api->vkGetPhysicalDeviceSurfaceSupportKHR(m_api->m_physicalDevice, deviceQueue->getQueueIndex(), m_surface, &supported); + + uint32_t numSurfaceFormats = 0; + List surfaceFormats; + m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, nullptr); + surfaceFormats.setCount(int(numSurfaceFormats)); + m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, surfaceFormats.getBuffer()); + + // Look for a suitable format + List formats; + formats.add(VulkanUtil::getVkFormat(desc.m_format)); + // HACK! To check for a different format if couldn't be found + if (descIn.m_format == Format::RGBA_Unorm_UInt8) + { + formats.add(VK_FORMAT_B8G8R8A8_UNORM); + } + + for(Index i = 0; i < formats.getCount(); ++i) + { + VkFormat format = formats[i]; + if (_indexOfFormat(surfaceFormats, format) >= 0) + { + m_format = format; + } + } + + if (m_format == VK_FORMAT_UNDEFINED) + { + return SLANG_FAIL; + } + + // Save the desc + m_desc = desc; + + SLANG_RETURN_ON_FAIL(_createSwapChain()); + + m_desc = desc; + return SLANG_OK; +} + +void VulkanSwapChain::getWindowSize(int* widthOut, int* heightOut) const +{ +#if SLANG_WINDOWS_FAMILY + auto platformDesc = _getPlatformDesc(); + + RECT rc; + ::GetClientRect(platformDesc->m_hwnd, &rc); + *widthOut = rc.right - rc.left; + *heightOut = rc.bottom - rc.top; +#else + auto platformDesc = _getPlatformDesc(); + + XWindowAttributes winAttr = {}; + XGetWindowAttributes(platformDesc->m_display, platformDesc->m_window, &winAttr); + + *widthOut = winAttr.width; + *heightOut = winAttr.height; +#endif +} + +SlangResult VulkanSwapChain::_createFrameBuffers(VkRenderPass renderPass) +{ + assert(renderPass != VK_NULL_HANDLE); + + for (Index i = 0; i < m_images.getCount(); ++i) + { + Image& image = m_images[i]; + VkImageView attachments[] = + { + image.m_imageView + }; + + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = m_width; + framebufferInfo.height = m_height; + framebufferInfo.layers = 1; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateFramebuffer(m_api->m_device, &framebufferInfo, nullptr, &image.m_frameBuffer)); + } + + return SLANG_OK; +} + +void VulkanSwapChain::_destroyFrameBuffers() +{ + for (Index i = 0; i < m_images.getCount(); ++i) + { + Image& image = m_images[i]; + if (image.m_frameBuffer != VK_NULL_HANDLE) + { + m_api->vkDestroyFramebuffer(m_api->m_device, image.m_frameBuffer, nullptr); + image.m_frameBuffer = VK_NULL_HANDLE; + } + } +} + +SlangResult VulkanSwapChain::createFrameBuffers(VkRenderPass renderPass) +{ + if (m_renderPass != VK_NULL_HANDLE) + { + _destroyFrameBuffers(); + m_renderPass = VK_NULL_HANDLE; + } + if (renderPass != VK_NULL_HANDLE) + { + SLANG_RETURN_ON_FAIL(_createFrameBuffers(renderPass)); + } + m_renderPass = renderPass; + return SLANG_OK; +} + +SlangResult VulkanSwapChain::_createSwapChain() +{ + if (hasValidSwapChain()) + { + return SLANG_OK; + } + + int width, height; + getWindowSize(&width, &height); + + VkExtent2D imageExtent = {}; + imageExtent.width = width; + imageExtent.height = height; + + m_width = width; + m_height = height; + + // catch this before throwing error + if (m_width == 0 || m_height == 0) + { + return SLANG_FAIL; + } + + // It is necessary to query the caps -> otherwise the LunarG verification layer will issue an error + { + VkSurfaceCapabilitiesKHR surfaceCaps; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_api->m_physicalDevice, m_surface, &surfaceCaps)); + } + + List presentModes; + uint32_t numPresentModes = 0; + m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, nullptr); + presentModes.setCount(numPresentModes); + m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, presentModes.getBuffer()); + + { + int numCheckPresentOptions = 3; + VkPresentModeKHR presentOptions[] = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR }; + if (m_vsync) + { + presentOptions[0] = VK_PRESENT_MODE_FIFO_KHR; + presentOptions[1] = VK_PRESENT_MODE_IMMEDIATE_KHR; + presentOptions[2] = VK_PRESENT_MODE_MAILBOX_KHR; + } + + m_presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; // Invalid + + // Find the first option that's available on the device + for (int j = 0; j < numCheckPresentOptions; j++) + { + if (presentModes.indexOf(presentOptions[j]) != Index(-1)) + { + m_presentMode = presentOptions[j]; + break; + } + } + + if (m_presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR) + { + return SLANG_FAIL; + } + } + + VkSwapchainKHR oldSwapchain = VK_NULL_HANDLE; + + VkSwapchainCreateInfoKHR swapchainDesc = {}; + swapchainDesc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainDesc.surface = m_surface; + swapchainDesc.minImageCount = 3; + swapchainDesc.imageFormat = m_format; + swapchainDesc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + swapchainDesc.imageExtent = imageExtent; + swapchainDesc.imageArrayLayers = 1; + swapchainDesc.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swapchainDesc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainDesc.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + swapchainDesc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchainDesc.presentMode = m_presentMode; + swapchainDesc.clipped = VK_TRUE; + swapchainDesc.oldSwapchain = oldSwapchain; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateSwapchainKHR(m_api->m_device, &swapchainDesc, nullptr, &m_swapChain)); + + uint32_t numSwapChainImages = 0; + m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, nullptr); + + { + List images; + images.setCount(numSwapChainImages); + + m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, images.getBuffer()); + + m_images.setCount(numSwapChainImages); + for (int i = 0; i < int(numSwapChainImages); ++i) + { + Image& dstImage = m_images[i]; + dstImage.m_image = images[i]; + + } + } + + { + VkImageViewCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_format; + + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + for (int i = 0; i < int(numSwapChainImages); ++i) + { + Image& image = m_images[i]; + + createInfo.image = image.m_image; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateImageView(m_api->m_device, &createInfo, nullptr, &image.m_imageView)); + } + } + + if (m_renderPass != VK_NULL_HANDLE) + { + _createFrameBuffers(m_renderPass); + } + + return SLANG_OK; +} + +void VulkanSwapChain::_destroySwapChain() +{ + if (!hasValidSwapChain()) + { + return; + } + + m_deviceQueue->waitForIdle(); + + if (m_renderPass != VK_NULL_HANDLE) + { + _destroyFrameBuffers(); + } + + for (Index i = 0; i < m_images.getCount(); ++i) + { + Image& image = m_images[i]; + + if (image.m_imageView != VK_NULL_HANDLE) + { + m_api->vkDestroyImageView(m_api->m_device, image.m_imageView, nullptr); + } + } + + if (m_swapChain != VK_NULL_HANDLE) + { + m_api->vkDestroySwapchainKHR(m_api->m_device, m_swapChain, nullptr); + m_swapChain = VK_NULL_HANDLE; + } + + // Mark that it is no longer used + m_images.clear(); +} + +VulkanSwapChain::~VulkanSwapChain() +{ + _destroySwapChain(); + + if (m_surface) + { + m_api->vkDestroySurfaceKHR(m_api->m_instance, m_surface, nullptr); + m_surface = VK_NULL_HANDLE; + } +} + +int VulkanSwapChain::nextFrontImageIndex() +{ + if (!hasValidSwapChain()) + { + if (SLANG_FAILED(_createSwapChain())) + { + return -1; + } + } + + VkSemaphore beginFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::BeginFrame); + + uint32_t swapChainIndex = 0; + VkResult result = m_api->vkAcquireNextImageKHR(m_api->m_device, m_swapChain, UINT64_MAX, beginFrameSemaphore, VK_NULL_HANDLE, &swapChainIndex); + + if (result != VK_SUCCESS) + { + _destroySwapChain(); + return -1; + } + m_currentSwapChainIndex = int(swapChainIndex); + return swapChainIndex; +} + +void VulkanSwapChain::present(bool vsync) +{ + if (!hasValidSwapChain()) + { + m_deviceQueue->flush(); + return; + } + + VkSemaphore endFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::EndFrame); + + m_deviceQueue->flushStepA(); + + uint32_t swapChainIndices[] = { uint32_t(m_currentSwapChainIndex) }; + + VkPresentInfoKHR presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &m_swapChain; + presentInfo.pImageIndices = swapChainIndices; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = &endFrameSemaphore; + + VkResult result = m_api->vkQueuePresentKHR(m_deviceQueue->getQueue(), &presentInfo); + + m_deviceQueue->makeCompleted(VulkanDeviceQueue::EventType::EndFrame); + + m_deviceQueue->flushStepB(); + + if (result != VK_SUCCESS || m_vsync != vsync) + { + m_vsync = vsync; + _destroySwapChain(); + } +} + +} // renderer_test diff --git a/tools/gfx/vulkan/vk-swap-chain.h b/tools/gfx/vulkan/vk-swap-chain.h new file mode 100644 index 000000000..57d1173b8 --- /dev/null +++ b/tools/gfx/vulkan/vk-swap-chain.h @@ -0,0 +1,141 @@ +// vk-swap-chain.h +#pragma once + +#include "vk-api.h" +#include "vk-device-queue.h" + +#include "../render.h" + +#include "../../source/core/slang-list.h" + +namespace gfx { + +struct VulkanSwapChain +{ + /* enum + { + kMaxImages = 8, + }; */ + + /// Base class for platform specific information + struct PlatformDesc + { + }; + +#if SLANG_WINDOWS_FAMILY + struct WinPlatformDesc: public PlatformDesc + { + HINSTANCE m_hinstance; + HWND m_hwnd; + }; +#else + struct XPlatformDesc : public PlatformDesc + { + Display* m_display; + Window m_window; + }; +#endif + + struct Desc + { + void init() + { + m_format = Format::Unknown; + m_depthFormatTypeless = Format::Unknown; + m_depthFormat = Format::Unknown; + m_textureDepthFormat = Format::Unknown; + } + + Format m_format; + //bool m_enableFormat; + Format m_depthFormatTypeless; + Format m_depthFormat; + Format m_textureDepthFormat; + }; + + struct Image + { + VkImage m_image = VK_NULL_HANDLE; + VkImageView m_imageView = VK_NULL_HANDLE; + VkFramebuffer m_frameBuffer = VK_NULL_HANDLE; + }; + + + /// Must be called before the swap chain can be used + SlangResult init(VulkanDeviceQueue* deviceQueue, const Desc& desc, const PlatformDesc* platformDesc); + + /// Create the frame buffers (they must be compatible with the supplied renderPass) + SlangResult createFrameBuffers(VkRenderPass renderPass); + + /// Returned the desc used to construct the swap chain. + /// Is invalid if init hasn't returned with successful result. + const Desc& getDesc() const { return m_desc; } + + /// True if the swap chain is available + bool hasValidSwapChain() const { return m_images.getCount() > 0; } + + /// Present to the display + void present(bool vsync); + + /// Get the current size of the window (in pixels written to widthOut, heightOut) + void getWindowSize(int* widthOut, int* heightOut) const; + + /// Get the VkFormat for the back buffer + VkFormat getVkFormat() const { return m_format; } + + /// Get width of the back buffers + int getWidth() const { return m_width; } + /// Get the height of the back buffer + int getHeight() const { return m_height; } + + /// Get the detail about the images + const Slang::List& getImages() const { return m_images; } + + /// Get the next front render image index. Returns -1, if image couldn't be found + int nextFrontImageIndex(); + + /// Dtor + ~VulkanSwapChain(); + + protected: + + + template + void _setPlatformDesc(const T& desc) + { + const PlatformDesc* check = &desc; + int size = (sizeof(T) + sizeof(void*) - 1) / sizeof(void*); + m_platformDescBuffer.setCount(size); + *(T*)m_platformDescBuffer.getBuffer() = desc; + } + template + const T* _getPlatformDesc() const { return static_cast((const PlatformDesc*)m_platformDescBuffer.getBuffer()); } + SlangResult _createSwapChain(); + void _destroySwapChain(); + SlangResult _createFrameBuffers(VkRenderPass renderPass); + void _destroyFrameBuffers(); + + bool m_vsync = true; + int m_width = 0; + int m_height = 0; + + VkPresentModeKHR m_presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + VkFormat m_format = VK_FORMAT_UNDEFINED; ///< The format used for backbuffer. Valid after successful init. + + VkSurfaceKHR m_surface = VK_NULL_HANDLE; + VkSwapchainKHR m_swapChain = VK_NULL_HANDLE; + + VkRenderPass m_renderPass = VK_NULL_HANDLE; //< Not owned + + int m_currentSwapChainIndex = 0; + + Slang::List m_images; + + VulkanDeviceQueue* m_deviceQueue = nullptr; + const VulkanApi* m_api = nullptr; + + Desc m_desc; ///< The desc used to init this swap chain + Slang::List m_platformDescBuffer; ///< Buffer to hold the platform specific description parameters (as passed in platformDesc) +}; + +} // renderer_test diff --git a/tools/gfx/vulkan/vk-util.cpp b/tools/gfx/vulkan/vk-util.cpp new file mode 100644 index 000000000..e8940d1b2 --- /dev/null +++ b/tools/gfx/vulkan/vk-util.cpp @@ -0,0 +1,59 @@ +// vk-util.cpp +#include "vk-util.h" + +#include +#include + +namespace gfx { + +/* static */VkFormat VulkanUtil::getVkFormat(Format format) +{ + switch (format) + { + case Format::RGBA_Float32: return VK_FORMAT_R32G32B32A32_SFLOAT; + case Format::RGB_Float32: return VK_FORMAT_R32G32B32_SFLOAT; + case Format::RG_Float32: return VK_FORMAT_R32G32_SFLOAT; + case Format::R_Float32: return VK_FORMAT_R32_SFLOAT; + case Format::RGBA_Unorm_UInt8: return VK_FORMAT_R8G8B8A8_UNORM; + case Format::R_UInt32: return VK_FORMAT_R32_UINT; + + case Format::D_Float32: return VK_FORMAT_D32_SFLOAT; + case Format::D_Unorm24_S8: return VK_FORMAT_D24_UNORM_S8_UINT; + + default: return VK_FORMAT_UNDEFINED; + } +} + +/* static */SlangResult VulkanUtil::toSlangResult(VkResult res) +{ + return (res == VK_SUCCESS) ? SLANG_OK : SLANG_FAIL; +} + +/* static */Slang::Result VulkanUtil::handleFail(VkResult res) +{ + if (res != VK_SUCCESS) + { + assert(!"Vulkan returned a failure"); + } + return toSlangResult(res); +} + +/* static */void VulkanUtil::checkFail(VkResult res) +{ + assert(res != VK_SUCCESS); + assert(!"Vulkan check failed"); + +} + +/* static */VkPrimitiveTopology VulkanUtil::getVkPrimitiveTopology(PrimitiveTopology topology) +{ + switch (topology) + { + case PrimitiveTopology::TriangleList: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + default: break; + } + assert(!"Unknown topology"); + return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; +} + +} // renderer_test diff --git a/tools/gfx/vulkan/vk-util.h b/tools/gfx/vulkan/vk-util.h new file mode 100644 index 000000000..c8194789d --- /dev/null +++ b/tools/gfx/vulkan/vk-util.h @@ -0,0 +1,41 @@ +// vk-util.h +#pragma once + +#include "vk-api.h" +#include "../render.h" + +// Macros to make testing vulkan return codes simpler + +/// SLANG_VK_RETURN_ON_FAIL can be used in a similar way to SLANG_RETURN_ON_FAIL macro, except it will turn a vulkan failure into Slang::Result in the process +/// Calls handleFail which on debug builds asserts +#define SLANG_VK_RETURN_ON_FAIL(x) { VkResult _res = x; if (_res != VK_SUCCESS) { return VulkanUtil::handleFail(_res); } } + +#define SLANG_VK_RETURN_NULL_ON_FAIL(x) { VkResult _res = x; if (_res != VK_SUCCESS) { VulkanUtil::handleFail(_res); return nullptr; } } + +/// Is similar to SLANG_VK_RETURN_ON_FAIL, but does not return. Will call checkFail on failure - which asserts on debug builds. +#define SLANG_VK_CHECK(x) { VkResult _res = x; if (_res != VK_SUCCESS) { VulkanUtil::checkFail(_res); } } + +namespace gfx { + +// Utility functions for Vulkan +struct VulkanUtil +{ + /// Get the equivalent VkFormat from the format + /// Returns VK_FORMAT_UNDEFINED if a match is not found + static VkFormat getVkFormat(Format format); + + /// Called by SLANG_VK_RETURN_FAIL if a res is a failure. + /// On debug builds this will cause an assertion on failure. + static Slang::Result handleFail(VkResult res); + /// Called when a failure has occurred with SLANG_VK_CHECK - will typically assert. + static void checkFail(VkResult res); + + /// Get the VkPrimitiveTopology for the given topology. + /// Returns VK_PRIMITIVE_TOPOLOGY_MAX_ENUM on failure + static VkPrimitiveTopology getVkPrimitiveTopology(PrimitiveTopology topology); + + /// Returns Slang::Result equivalent of a VkResult + static Slang::Result toSlangResult(VkResult res); +}; + +} // renderer_test diff --git a/tools/gfx/window.cpp b/tools/gfx/window.cpp deleted file mode 100644 index 02a083ef2..000000000 --- a/tools/gfx/window.cpp +++ /dev/null @@ -1,399 +0,0 @@ -// window.cpp -#include "window.h" - -#include - -#ifdef _MSC_VER -#include -#if (_MSC_VER < 1900) -#define snprintf sprintf_s -#endif -#endif - -#include - - -#if _WIN32 -#include -#include -#else -#error "The slang-graphics library currently only supports Windows platforms" -#endif - -namespace gfx { - -#if _WIN32 - -struct OSString -{ - OSString(char const* begin, char const* end) - { - _initialize(begin, end - begin); - } - - OSString(char const* begin) - { - _initialize(begin, strlen(begin)); - } - - ~OSString() - { - free(mBegin); - } - - operator WCHAR const*() - { - return mBegin; - } - -private: - WCHAR* mBegin; - WCHAR* mEnd; - - void _initialize(char const* input, size_t inputSize) - { - const DWORD dwFlags = 0; - int outputCodeUnitCount = ::MultiByteToWideChar(CP_UTF8, dwFlags, input, int(inputSize), nullptr, 0); - - WCHAR* buffer = (WCHAR*)malloc(sizeof(WCHAR) * (outputCodeUnitCount + 1)); - - ::MultiByteToWideChar(CP_UTF8, dwFlags, input, int(inputSize), buffer, outputCodeUnitCount); - buffer[outputCodeUnitCount] = 0; - - mBegin = buffer; - mEnd = buffer + outputCodeUnitCount; - } -}; - -struct ApplicationContext -{ - HINSTANCE instance; - int showCommand = SW_SHOWDEFAULT; - int resultCode = 0; -}; - -static uint64_t gTimerFrequency; - - -static void initApplication(ApplicationContext* context) -{ - LARGE_INTEGER timerFrequency; - QueryPerformanceFrequency(&timerFrequency); - gTimerFrequency = timerFrequency.QuadPart; -} - -/// Run an application given the specified callback and command-line arguments. -int runApplication( - ApplicationFunc func, - int argc, - char const* const* argv) -{ - ApplicationContext context; - context.instance = (HINSTANCE) GetModuleHandle(0); - initApplication(&context); - func(&context); - return context.resultCode; -} - -int runWindowsApplication( - ApplicationFunc func, - void* instance, - int showCommand) -{ - ApplicationContext context; - context.instance = (HINSTANCE) instance; - context.showCommand = showCommand; - initApplication(&context); - func(&context); - return context.resultCode; -} - -struct Window -{ - HWND handle; - WNDPROC nativeHook; - EventHandler eventHandler; - void* userData; -}; - -void setNativeWindowHook(Window* window, WNDPROC proc) -{ - window->nativeHook = proc; -} - -static KeyCode translateKeyCode(int vkey) -{ - switch( vkey ) - { - default: - return KeyCode::Unknown; - -#define CASE(FROM, TO) case FROM: return KeyCode::TO; - CASE('A', A); CASE('B', B); CASE('C', C); CASE('D', D); CASE('E', E); - CASE('F', F); CASE('G', G); CASE('H', H); CASE('I', I); CASE('J', J); - CASE('K', K); CASE('L', M); CASE('M', M); CASE('N', N); CASE('O', O); - CASE('P', P); CASE('Q', Q); CASE('R', R); CASE('S', S); CASE('T', T); - CASE('U', U); CASE('V', V); CASE('W', W); CASE('X', X); CASE('Y', Y); - CASE('Z', Z); -#undef CASE - -#define CASE(FROM, TO) case VK_##FROM: return KeyCode::TO; - CASE(SPACE, Space); -#undef CASE - } -} - -static LRESULT CALLBACK windowProc( - HWND windowHandle, - UINT message, - WPARAM wParam, - LPARAM lParam) -{ - Window* window = (Window*) GetWindowLongPtrW(windowHandle, GWLP_USERDATA); - - // Give the installed filter a chance to intercept messages. - // (This is used for ImGui) - if( window ) - { - if(auto nativeHook = window->nativeHook) - { - auto result = nativeHook(windowHandle, message, wParam, lParam); - if(result) - return result; - } - } - - auto eventHandler = window ? window->eventHandler : nullptr; - - switch (message) - { - case WM_CREATE: - { - auto createInfo = (CREATESTRUCTW*) lParam; - window = (Window*) createInfo->lpCreateParams; - window->handle = windowHandle; - - SetWindowLongPtrW(windowHandle, GWLP_USERDATA, (LONG_PTR) window); - } - break; - - case WM_KEYDOWN: - case WM_KEYUP: - { - int virtualKey = (int) wParam; - auto keyCode = translateKeyCode(virtualKey); - if(eventHandler) - { - Event event; - event.window = window; - event.code = message == WM_KEYDOWN ? EventCode::KeyDown : EventCode::KeyUp; - event.u.key = keyCode; - eventHandler(event); - } - } - break; - - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - { - if(eventHandler) - { - Event event; - event.window = window; - event.code = message == WM_LBUTTONDOWN ? EventCode::MouseDown : EventCode::MouseUp; - event.u.mouse.x = (float) GET_X_LPARAM(lParam); - event.u.mouse.y = (float) GET_Y_LPARAM(lParam); - eventHandler(event); - } - } - break; - - case WM_MOUSEMOVE: - { - if(eventHandler) - { - Event event; - event.window = window; - event.code = EventCode::MouseMoved; - event.u.mouse.x = (float) GET_X_LPARAM(lParam); - event.u.mouse.y = (float) GET_Y_LPARAM(lParam); - eventHandler(event); - } - } - break; - - case WM_CLOSE: - PostQuitMessage(0); - return 0; - } - - - return DefWindowProcW(windowHandle, message, wParam, lParam); -} - - -static ATOM createWindowClassAtom() -{ - WNDCLASSEXW windowClassDesc; - windowClassDesc.cbSize = sizeof(windowClassDesc); - windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; - windowClassDesc.lpfnWndProc = &windowProc; - windowClassDesc.cbClsExtra = 0; - windowClassDesc.cbWndExtra = 0; - windowClassDesc.hInstance = (HINSTANCE) GetModuleHandle(0); - windowClassDesc.hIcon = 0; - windowClassDesc.hCursor = 0; - windowClassDesc.hbrBackground = 0; - windowClassDesc.lpszMenuName = 0; - windowClassDesc.lpszClassName = L"SlangGraphicsWindow"; - windowClassDesc.hIconSm = 0; - ATOM windowClassAtom = RegisterClassExW(&windowClassDesc); - return windowClassAtom; -} - -static ATOM getWindowClassAtom() -{ - static ATOM windowClassAtom = createWindowClassAtom(); - return windowClassAtom; -} - -Window* createWindow(WindowDesc const& desc) -{ - Window* window = new Window(); - window->handle = nullptr; - window->nativeHook = nullptr; - window->eventHandler = desc.eventHandler; - window->userData = desc.userData; - - OSString windowTitle(desc.title); - - DWORD windowExtendedStyle = 0; - DWORD windowStyle = 0; - - HINSTANCE instance = (HINSTANCE) GetModuleHandle(0); - - HWND windowHandle = CreateWindowExW( - windowExtendedStyle, - (LPWSTR) getWindowClassAtom(), - windowTitle, - windowStyle, - 0, 0, // x, y - desc.width, desc.height, - NULL, // parent - NULL, // menu - instance, - window); - - if(!windowHandle) - { - delete window; - return nullptr; - } - - window->handle = windowHandle; - return window; -} - -void showWindow(Window* window) -{ - ShowWindow(window->handle, SW_SHOW); -} - -void* getPlatformWindowHandle(Window* window) -{ - return window->handle; -} - -void* getUserData(Window* window) -{ - return window->userData; -} - -bool dispatchEvents(ApplicationContext* context) -{ - for(;;) - { - MSG message; - - int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); - if (result != 0) - { - if (message.message == WM_QUIT) - { - context->resultCode = (int)message.wParam; - return false; - } - - TranslateMessage(&message); - DispatchMessageW(&message); - } - else - { - return true; - } - } - -} - -void exitApplication(ApplicationContext* context, int resultCode) -{ - ExitProcess(resultCode); -} - -void log(char const* message, ...) -{ - va_list args; - va_start(args, message); - - static const int kBufferSize = 1024; - char messageBuffer[kBufferSize]; - vsnprintf(messageBuffer, kBufferSize - 1, message, args); - messageBuffer[kBufferSize - 1] = 0; - - va_end(args); - - fputs(messageBuffer, stderr); - - OSString wideMessageBuffer(messageBuffer); - OutputDebugStringW(wideMessageBuffer); -} - -int reportError(char const* message, ...) -{ - va_list args; - va_start(args, message); - - static const int kBufferSize = 1024; - char messageBuffer[kBufferSize]; - vsnprintf(messageBuffer, kBufferSize - 1, message, args); - messageBuffer[kBufferSize - 1] = 0; - - va_end(args); - - fputs(messageBuffer, stderr); - - OSString wideMessageBuffer(messageBuffer); - OutputDebugStringW(wideMessageBuffer); - - return 1; -} - -uint64_t getCurrentTime() -{ - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - return counter.QuadPart; -} - -uint64_t getTimerFrequency() -{ - return gTimerFrequency; -} - -#else - -// TODO: put an SDL version here - -#endif - -} // gfx diff --git a/tools/gfx/windows/win-window.cpp b/tools/gfx/windows/win-window.cpp new file mode 100644 index 000000000..45315a27a --- /dev/null +++ b/tools/gfx/windows/win-window.cpp @@ -0,0 +1,398 @@ +// win-window.cpp +#include "../window.h" + +#include + +#ifdef _MSC_VER +#include +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif + +#include + +#if _WIN32 +#include +#include +#else +#error "The slang-graphics library currently only supports Windows platforms" +#endif + +namespace gfx { + +#if _WIN32 + +struct OSString +{ + OSString(char const* begin, char const* end) + { + _initialize(begin, end - begin); + } + + OSString(char const* begin) + { + _initialize(begin, strlen(begin)); + } + + ~OSString() + { + free(mBegin); + } + + operator WCHAR const*() + { + return mBegin; + } + +private: + WCHAR* mBegin; + WCHAR* mEnd; + + void _initialize(char const* input, size_t inputSize) + { + const DWORD dwFlags = 0; + int outputCodeUnitCount = ::MultiByteToWideChar(CP_UTF8, dwFlags, input, int(inputSize), nullptr, 0); + + WCHAR* buffer = (WCHAR*)malloc(sizeof(WCHAR) * (outputCodeUnitCount + 1)); + + ::MultiByteToWideChar(CP_UTF8, dwFlags, input, int(inputSize), buffer, outputCodeUnitCount); + buffer[outputCodeUnitCount] = 0; + + mBegin = buffer; + mEnd = buffer + outputCodeUnitCount; + } +}; + +struct ApplicationContext +{ + HINSTANCE instance; + int showCommand = SW_SHOWDEFAULT; + int resultCode = 0; +}; + +static uint64_t gTimerFrequency; + + +static void initApplication(ApplicationContext* context) +{ + LARGE_INTEGER timerFrequency; + QueryPerformanceFrequency(&timerFrequency); + gTimerFrequency = timerFrequency.QuadPart; +} + +/// Run an application given the specified callback and command-line arguments. +int runApplication( + ApplicationFunc func, + int argc, + char const* const* argv) +{ + ApplicationContext context; + context.instance = (HINSTANCE) GetModuleHandle(0); + initApplication(&context); + func(&context); + return context.resultCode; +} + +int runWindowsApplication( + ApplicationFunc func, + void* instance, + int showCommand) +{ + ApplicationContext context; + context.instance = (HINSTANCE) instance; + context.showCommand = showCommand; + initApplication(&context); + func(&context); + return context.resultCode; +} + +struct Window +{ + HWND handle; + WNDPROC nativeHook; + EventHandler eventHandler; + void* userData; +}; + +void setNativeWindowHook(Window* window, WNDPROC proc) +{ + window->nativeHook = proc; +} + +static KeyCode translateKeyCode(int vkey) +{ + switch( vkey ) + { + default: + return KeyCode::Unknown; + +#define CASE(FROM, TO) case FROM: return KeyCode::TO; + CASE('A', A); CASE('B', B); CASE('C', C); CASE('D', D); CASE('E', E); + CASE('F', F); CASE('G', G); CASE('H', H); CASE('I', I); CASE('J', J); + CASE('K', K); CASE('L', M); CASE('M', M); CASE('N', N); CASE('O', O); + CASE('P', P); CASE('Q', Q); CASE('R', R); CASE('S', S); CASE('T', T); + CASE('U', U); CASE('V', V); CASE('W', W); CASE('X', X); CASE('Y', Y); + CASE('Z', Z); +#undef CASE + +#define CASE(FROM, TO) case VK_##FROM: return KeyCode::TO; + CASE(SPACE, Space); +#undef CASE + } +} + +static LRESULT CALLBACK windowProc( + HWND windowHandle, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + Window* window = (Window*) GetWindowLongPtrW(windowHandle, GWLP_USERDATA); + + // Give the installed filter a chance to intercept messages. + // (This is used for ImGui) + if( window ) + { + if(auto nativeHook = window->nativeHook) + { + auto result = nativeHook(windowHandle, message, wParam, lParam); + if(result) + return result; + } + } + + auto eventHandler = window ? window->eventHandler : nullptr; + + switch (message) + { + case WM_CREATE: + { + auto createInfo = (CREATESTRUCTW*) lParam; + window = (Window*) createInfo->lpCreateParams; + window->handle = windowHandle; + + SetWindowLongPtrW(windowHandle, GWLP_USERDATA, (LONG_PTR) window); + } + break; + + case WM_KEYDOWN: + case WM_KEYUP: + { + int virtualKey = (int) wParam; + auto keyCode = translateKeyCode(virtualKey); + if(eventHandler) + { + Event event; + event.window = window; + event.code = message == WM_KEYDOWN ? EventCode::KeyDown : EventCode::KeyUp; + event.u.key = keyCode; + eventHandler(event); + } + } + break; + + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + { + if(eventHandler) + { + Event event; + event.window = window; + event.code = message == WM_LBUTTONDOWN ? EventCode::MouseDown : EventCode::MouseUp; + event.u.mouse.x = (float) GET_X_LPARAM(lParam); + event.u.mouse.y = (float) GET_Y_LPARAM(lParam); + eventHandler(event); + } + } + break; + + case WM_MOUSEMOVE: + { + if(eventHandler) + { + Event event; + event.window = window; + event.code = EventCode::MouseMoved; + event.u.mouse.x = (float) GET_X_LPARAM(lParam); + event.u.mouse.y = (float) GET_Y_LPARAM(lParam); + eventHandler(event); + } + } + break; + + case WM_CLOSE: + PostQuitMessage(0); + return 0; + } + + + return DefWindowProcW(windowHandle, message, wParam, lParam); +} + + +static ATOM createWindowClassAtom() +{ + WNDCLASSEXW windowClassDesc; + windowClassDesc.cbSize = sizeof(windowClassDesc); + windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + windowClassDesc.lpfnWndProc = &windowProc; + windowClassDesc.cbClsExtra = 0; + windowClassDesc.cbWndExtra = 0; + windowClassDesc.hInstance = (HINSTANCE) GetModuleHandle(0); + windowClassDesc.hIcon = 0; + windowClassDesc.hCursor = 0; + windowClassDesc.hbrBackground = 0; + windowClassDesc.lpszMenuName = 0; + windowClassDesc.lpszClassName = L"SlangGraphicsWindow"; + windowClassDesc.hIconSm = 0; + ATOM windowClassAtom = RegisterClassExW(&windowClassDesc); + return windowClassAtom; +} + +static ATOM getWindowClassAtom() +{ + static ATOM windowClassAtom = createWindowClassAtom(); + return windowClassAtom; +} + +Window* createWindow(WindowDesc const& desc) +{ + Window* window = new Window(); + window->handle = nullptr; + window->nativeHook = nullptr; + window->eventHandler = desc.eventHandler; + window->userData = desc.userData; + + OSString windowTitle(desc.title); + + DWORD windowExtendedStyle = 0; + DWORD windowStyle = 0; + + HINSTANCE instance = (HINSTANCE) GetModuleHandle(0); + + HWND windowHandle = CreateWindowExW( + windowExtendedStyle, + (LPWSTR) getWindowClassAtom(), + windowTitle, + windowStyle, + 0, 0, // x, y + desc.width, desc.height, + NULL, // parent + NULL, // menu + instance, + window); + + if(!windowHandle) + { + delete window; + return nullptr; + } + + window->handle = windowHandle; + return window; +} + +void showWindow(Window* window) +{ + ShowWindow(window->handle, SW_SHOW); +} + +void* getPlatformWindowHandle(Window* window) +{ + return window->handle; +} + +void* getUserData(Window* window) +{ + return window->userData; +} + +bool dispatchEvents(ApplicationContext* context) +{ + for(;;) + { + MSG message; + + int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); + if (result != 0) + { + if (message.message == WM_QUIT) + { + context->resultCode = (int)message.wParam; + return false; + } + + TranslateMessage(&message); + DispatchMessageW(&message); + } + else + { + return true; + } + } + +} + +void exitApplication(ApplicationContext* context, int resultCode) +{ + ExitProcess(resultCode); +} + +void log(char const* message, ...) +{ + va_list args; + va_start(args, message); + + static const int kBufferSize = 1024; + char messageBuffer[kBufferSize]; + vsnprintf(messageBuffer, kBufferSize - 1, message, args); + messageBuffer[kBufferSize - 1] = 0; + + va_end(args); + + fputs(messageBuffer, stderr); + + OSString wideMessageBuffer(messageBuffer); + OutputDebugStringW(wideMessageBuffer); +} + +int reportError(char const* message, ...) +{ + va_list args; + va_start(args, message); + + static const int kBufferSize = 1024; + char messageBuffer[kBufferSize]; + vsnprintf(messageBuffer, kBufferSize - 1, message, args); + messageBuffer[kBufferSize - 1] = 0; + + va_end(args); + + fputs(messageBuffer, stderr); + + OSString wideMessageBuffer(messageBuffer); + OutputDebugStringW(wideMessageBuffer); + + return 1; +} + +uint64_t getCurrentTime() +{ + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return counter.QuadPart; +} + +uint64_t getTimerFrequency() +{ + return gTimerFrequency; +} + +#else + +// TODO: put an SDL version here + +#endif + +} // gfx diff --git a/tools/render-test/cpu-memory-binding.cpp b/tools/render-test/cpu-memory-binding.cpp index c2d853aa8..3bf1f1bbd 100644 --- a/tools/render-test/cpu-memory-binding.cpp +++ b/tools/render-test/cpu-memory-binding.cpp @@ -91,7 +91,7 @@ SlangResult CPUMemoryBinding::init(slang::ShaderReflection* reflection, int entr } { - auto entryPointCount = reflection->getEntryPointCount(); + auto entryPointCount = int(reflection->getEntryPointCount()); if (entryPointIndex < 0 || entryPointIndex >= entryPointCount) { SLANG_ASSERT(!"Entry point index out of range"); diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index 5b9621142..d2ef2a746 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -1,11 +1,9 @@ // render-test-main.cpp +#define _CRT_SECURE_NO_WARNINGS 1 + #include "options.h" #include "render.h" -#include "render-d3d11.h" -#include "render-d3d12.h" -#include "render-gl.h" -#include "render-vk.h" #include "slang-support.h" #include "surface.h" @@ -21,20 +19,12 @@ #include #include +#include "window.h" + #include "../../source/core/slang-test-tool-util.h" #include "cpu-compute-util.h" -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#ifdef _MSC_VER -#pragma warning(disable: 4996) -#endif - namespace renderer_test { using Slang::Result; @@ -42,141 +32,6 @@ using Slang::Result; int gWindowWidth = 1024; int gWindowHeight = 768; -class Window: public RefObject -{ -public: - SlangResult initialize(int width, int height); - - void show(); - - void* getHandle() const { return m_hwnd; } - - Window() {} - ~Window(); - - static LRESULT CALLBACK windowProc(HWND windowHandle, - UINT message, - WPARAM wParam, - LPARAM lParam); - -protected: - - HINSTANCE m_hinst = nullptr; - HWND m_hwnd = nullptr; -}; - -// -// We use a bare-minimum window procedure to get things up and running. -// - -/* static */LRESULT CALLBACK Window::windowProc( - HWND windowHandle, - UINT message, - WPARAM wParam, - LPARAM lParam) -{ - switch (message) - { - case WM_CLOSE: - PostQuitMessage(0); - return 0; - } - - return DefWindowProcW(windowHandle, message, wParam, lParam); -} - -static ATOM _getWindowClassAtom(HINSTANCE hinst) -{ - static ATOM s_windowClassAtom; - - if (s_windowClassAtom) - { - return s_windowClassAtom; - } - WNDCLASSEXW windowClassDesc; - windowClassDesc.cbSize = sizeof(windowClassDesc); - windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; - windowClassDesc.lpfnWndProc = &Window::windowProc; - windowClassDesc.cbClsExtra = 0; - windowClassDesc.cbWndExtra = 0; - windowClassDesc.hInstance = hinst; - windowClassDesc.hIcon = 0; - windowClassDesc.hCursor = 0; - windowClassDesc.hbrBackground = 0; - windowClassDesc.lpszMenuName = 0; - windowClassDesc.lpszClassName = L"SlangRenderTest"; - windowClassDesc.hIconSm = 0; - s_windowClassAtom = RegisterClassExW(&windowClassDesc); - - return s_windowClassAtom; -} - -SlangResult Window::initialize(int widthIn, int heightIn) -{ - // Do initial window-creation stuff here, rather than in the renderer-specific files - - m_hinst = GetModuleHandleA(0); - - // First we register a window class. - ATOM windowClassAtom = _getWindowClassAtom(m_hinst); - if (!windowClassAtom) - { - fprintf(stderr, "error: failed to register window class\n"); - return SLANG_FAIL; - } - - // Next, we create a window using that window class. - - // We will create a borderless window since our screen-capture logic in GL - // seems to get thrown off by having to deal with a window frame. - DWORD windowStyle = WS_POPUP; - DWORD windowExtendedStyle = 0; - - RECT windowRect = { 0, 0, widthIn, heightIn }; - AdjustWindowRectEx(&windowRect, windowStyle, /*hasMenu=*/false, windowExtendedStyle); - - { - auto width = windowRect.right - windowRect.left; - auto height = windowRect.bottom - windowRect.top; - - LPWSTR windowName = L"Slang Render Test"; - m_hwnd = CreateWindowExW( - windowExtendedStyle, - (LPWSTR)windowClassAtom, - windowName, - windowStyle, - 0, 0, // x, y - width, height, - NULL, // parent - NULL, // menu - m_hinst, - NULL); - } - if (!m_hwnd) - { - fprintf(stderr, "error: failed to create window\n"); - return SLANG_FAIL; - } - - return SLANG_OK; -} - - -void Window::show() -{ - // Once initialization is all complete, we show the window... - int showCommand = SW_SHOW; - ShowWindow(m_hwnd, showCommand); -} - -Window::~Window() -{ - if (m_hwnd) - { - DestroyWindow(m_hwnd); - } -} - // // For the purposes of a small example, we will define the vertex data for a // single triangle directly in the source file. It should be easy to extend @@ -200,16 +55,19 @@ static const int kVertexCount = SLANG_COUNT_OF(kVertexData); using namespace Slang; -class RenderTestApp +class RenderTestApp : public WindowListener { public: - // At initialization time, we are going to load and compile our Slang shader - // code, and then create the API objects we need for rendering. - Result initialize(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input); - void runCompute(); - void renderFrame(); - void finalize(); + // WindowListener + virtual Result update(Window* window) SLANG_OVERRIDE; + + // At initialization time, we are going to load and compile our Slang shader + // code, and then create the API objects we need for rendering. + Result initialize(SlangSession* session, Renderer* renderer, const Options& options, const ShaderCompilerUtil::Input& input); + void runCompute(); + void renderFrame(); + void finalize(); BindingStateImpl* getBindingState() const { return m_bindingState; } @@ -235,11 +93,15 @@ class RenderTestApp ShaderInputLayout m_shaderInputLayout; ///< The binding layout int m_numAddedConstantBuffers; ///< Constant buffers can be added to the binding directly. Will be added at the end. + + Options m_options; }; -SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input) +SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, const Options& options, const ShaderCompilerUtil::Input& input) { - SLANG_RETURN_ON_FAIL(_initializeShaders(session, renderer, shaderType, input)); + m_options = options; + + SLANG_RETURN_ON_FAIL(_initializeShaders(session, renderer, options.shaderType, input)); m_numAddedConstantBuffers = 0; m_renderer = renderer; @@ -260,7 +122,7 @@ SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, // TODO: Should probably be more sophisticated than this - with 'dynamic' constant buffer/s binding always being specified // in the test file RefPtr addedConstantBuffer; - switch(shaderType) + switch(m_options.shaderType) { default: break; @@ -298,7 +160,7 @@ SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, return SLANG_FAIL; { - switch(shaderType) + switch(m_options.shaderType) { default: assert(!"unexpected test shader type"); @@ -435,6 +297,52 @@ Result RenderTestApp::writeScreen(const char* filename) return PngSerializeUtil::write(filename, surface); } +Result RenderTestApp::update(Window* window) +{ + // Whenever we don't have Windows events to process, we render a frame. + if (m_options.shaderType == Options::ShaderProgramType::Compute) + { + runCompute(); + } + else + { + static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; + m_renderer->setClearColor(kClearColor); + m_renderer->clearFrame(); + + renderFrame(); + } + + // If we are in a mode where output is requested, we need to snapshot the back buffer here + if (m_options.outputPath) + { + // Submit the work + m_renderer->submitGpuWork(); + // Wait until everything is complete + m_renderer->waitForGpu(); + + if (gOptions.shaderType == Options::ShaderProgramType::Compute || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) + { + SLANG_RETURN_ON_FAIL(writeBindingOutput(gOptions.outputPath)); + } + else + { + SlangResult res = writeScreen(gOptions.outputPath); + if (SLANG_FAILED(res)) + { + fprintf(stderr, "ERROR: failed to write screen capture to file\n"); + return res; + } + } + // We are done + window->postQuit(); + return SLANG_OK; + } + + m_renderer->presentFrame(); + return SLANG_OK; +} + } // namespace renderer_test SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSession* session, int argcIn, const char*const* argvIn) @@ -449,8 +357,6 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe // Declare window pointer before renderer, such that window is released after renderer RefPtr window; - // Renderer is constructed (later) using the window - Slang::RefPtr renderer; ShaderCompilerUtil::Input input; @@ -465,7 +371,6 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe switch (gOptions.rendererType) { case RendererType::DirectX11: - renderer = createD3D11Renderer(); input.target = SLANG_DXBC; input.profile = "sm_5_0"; nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; @@ -474,7 +379,6 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe break; case RendererType::DirectX12: - renderer = createD3D12Renderer(); input.target = SLANG_DXBC; input.profile = "sm_5_0"; nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; @@ -489,7 +393,6 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe break; case RendererType::OpenGl: - renderer = createGLRenderer(); input.target = SLANG_GLSL; input.profile = "glsl_430"; nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; @@ -497,7 +400,6 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe break; case RendererType::Vulkan: - renderer = createVKRenderer(); input.target = SLANG_SPIRV; input.profile = "glsl_430"; nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; @@ -540,18 +442,59 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe rendererName << "'" << gOptions.adapter << "'"; } - - if (renderer) + // If it's CPU testing we don't need a window or a renderer + if (gOptions.rendererType == RendererType::CPU) { + if (gOptions.onlyStartup) + { + // Need generic C/C++ + if (SLANG_FAILED(spSessionCheckPassThroughSupport(session, SLANG_PASS_THROUGH_GENERIC_C_CPP))) + { + return SLANG_FAIL; + } + // Should work ... + return SLANG_OK; + } + + ShaderCompilerUtil::OutputAndLayout compilationAndLayout; + SLANG_RETURN_ON_FAIL(ShaderCompilerUtil::compileWithLayout(session, gOptions.sourcePath, gOptions.shaderType, input, compilationAndLayout)); + + CPUComputeUtil::Context context; + SLANG_RETURN_ON_FAIL(CPUComputeUtil::calcBindings(compilationAndLayout, context)); + SLANG_RETURN_ON_FAIL(CPUComputeUtil::execute(compilationAndLayout, context)); + + // Dump everything out that was written + return CPUComputeUtil::writeBindings(compilationAndLayout.layout, context.buffers, gOptions.outputPath); + } + + // Renderer is constructed (later) using the window + Slang::RefPtr renderer; + + { + RendererUtil::CreateFunc createFunc = RendererUtil::getCreateFunc(gOptions.rendererType); + if (createFunc) + { + renderer = createFunc(); + } + + if (!renderer) + { + if (!gOptions.onlyStartup) + { + fprintf(stderr, "Unable to create renderer %s\n", rendererName.getBuffer()); + } + return SLANG_FAIL; + } + Renderer::Desc desc; desc.width = gWindowWidth; desc.height = gWindowHeight; desc.adapter = gOptions.adapter; - window = new renderer_test::Window; + window = renderer_test::Window::create(); SLANG_RETURN_ON_FAIL(window->initialize(gWindowWidth, gWindowHeight)); - SlangResult res = renderer->initialize(desc, (HWND)window->getHandle()); + SlangResult res = renderer->initialize(desc, window->getHandle()); if (SLANG_FAILED(res)) { if (!gOptions.onlyStartup) @@ -570,105 +513,19 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe } } } - else - { - if (gOptions.rendererType != RendererType::CPU) - { - if (!gOptions.onlyStartup) - { - fprintf(stderr, "Unable to create renderer %s\n", rendererName.getBuffer()); - } - return SLANG_FAIL; - } - } - + // If the only test is we can startup, then we are done if (gOptions.onlyStartup) { return SLANG_OK; } - if (!renderer) - { - ShaderCompilerUtil::OutputAndLayout compilationAndLayout; - SLANG_RETURN_ON_FAIL(ShaderCompilerUtil::compileWithLayout(session, gOptions.sourcePath, gOptions.shaderType, input, compilationAndLayout)); - - CPUComputeUtil::Context context; - SLANG_RETURN_ON_FAIL(CPUComputeUtil::calcBindings(compilationAndLayout, context)); - SLANG_RETURN_ON_FAIL(CPUComputeUtil::execute(compilationAndLayout, context)); - - // Dump everything out that was written - return CPUComputeUtil::writeBindings(compilationAndLayout.layout, context.buffers, gOptions.outputPath); - } - { - RenderTestApp app; - SLANG_RETURN_ON_FAIL(app.initialize(session, renderer, gOptions.shaderType, input)); - + RefPtr app(new RenderTestApp); + SLANG_RETURN_ON_FAIL(app->initialize(session, renderer, gOptions, input)); window->show(); - - // ... and enter the event loop: - for (;;) - { - MSG message; - - int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); - if (result != 0) - { - if (message.message == WM_QUIT) - { - return (int)message.wParam; - } - - TranslateMessage(&message); - DispatchMessageW(&message); - } - else - { - // Whenever we don't have Windows events to process, we render a frame. - if (gOptions.shaderType == Options::ShaderProgramType::Compute) - { - app.runCompute(); - } - else - { - static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; - renderer->setClearColor(kClearColor); - renderer->clearFrame(); - - app.renderFrame(); - } - // If we are in a mode where output is requested, we need to snapshot the back buffer here - if (gOptions.outputPath) - { - // Submit the work - renderer->submitGpuWork(); - // Wait until everything is complete - renderer->waitForGpu(); - - if (gOptions.shaderType == Options::ShaderProgramType::Compute || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) - { - SLANG_RETURN_ON_FAIL(app.writeBindingOutput(gOptions.outputPath)); - } - else - { - SlangResult res = app.writeScreen(gOptions.outputPath); - - if (SLANG_FAILED(res)) - { - fprintf(stderr, "ERROR: failed to write screen capture to file\n"); - return res; - } - } - return SLANG_OK; - } - - renderer->presentFrame(); - } - } + return window->runLoop(app); } - - return SLANG_OK; } int main(int argc, char** argv) diff --git a/tools/render-test/render-test-tool.vcxproj b/tools/render-test/render-test-tool.vcxproj index 3e0c0ae56..3cc813300 100644 --- a/tools/render-test/render-test-tool.vcxproj +++ b/tools/render-test/render-test-tool.vcxproj @@ -186,6 +186,7 @@ + @@ -196,6 +197,8 @@ + + diff --git a/tools/render-test/render-test-tool.vcxproj.filters b/tools/render-test/render-test-tool.vcxproj.filters index 769d53aed..7091fce6b 100644 --- a/tools/render-test/render-test-tool.vcxproj.filters +++ b/tools/render-test/render-test-tool.vcxproj.filters @@ -30,6 +30,9 @@ Header Files + + Header Files + @@ -56,5 +59,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/tools/render-test/window.cpp b/tools/render-test/window.cpp new file mode 100644 index 000000000..0ab4ff412 --- /dev/null +++ b/tools/render-test/window.cpp @@ -0,0 +1,21 @@ +// window.cpp + +#include "window.h" + +namespace renderer_test { +using namespace Slang; + +#if SLANG_WINDOWS_FAMILY +extern Window* createWinWindow(); +#endif + +/* static */Window* Window::create() +{ +#if SLANG_WINDOWS_FAMILY + return createWinWindow(); +#else + return nullptr; +#endif +} + +} // renderer_test diff --git a/tools/render-test/window.h b/tools/render-test/window.h new file mode 100644 index 000000000..e647a602c --- /dev/null +++ b/tools/render-test/window.h @@ -0,0 +1,45 @@ +// window.h +#pragma once + +#include +#include "../../source/core/slang-smart-pointer.h" + +namespace renderer_test { + +class Window; + +class WindowListener : public Slang::RefObject +{ +public: + virtual SlangResult update(Window* window) = 0; +}; + +class Window : public Slang::RefObject +{ +public: + virtual SlangResult initialize(int width, int height) = 0; + + virtual void show() = 0; + virtual void* getHandle() const = 0; + virtual void postQuit() { m_isQuitting = true; } + + /// Run the event loop. Events will be sent to the WindowListener + virtual SlangResult runLoop(WindowListener* listener) = 0; + + bool isQuitting() const { return m_isQuitting; } + int getQuitValue() const { return m_quitValue; } + + static Window* create(); + + virtual ~Window() {} + +protected: + Window() {} + + bool m_isQuitting = false; + int m_quitValue = 0; +}; + +Window* createWindow(); + +} // renderer_test diff --git a/tools/render-test/windows/win-window.cpp b/tools/render-test/windows/win-window.cpp new file mode 100644 index 000000000..7f45f9789 --- /dev/null +++ b/tools/render-test/windows/win-window.cpp @@ -0,0 +1,185 @@ +// win-window.cpp + +#define _CRT_SECURE_NO_WARNINGS 1 + +#include +#include + +#include "../window.h" + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + +#include + +namespace renderer_test { + +class WinWindow : public Window +{ +public: + virtual SlangResult initialize(int width, int height) SLANG_OVERRIDE; + + virtual void show() SLANG_OVERRIDE; + virtual void* getHandle() const SLANG_OVERRIDE { return m_hwnd; } + virtual SlangResult runLoop(WindowListener* listener) SLANG_OVERRIDE; + + virtual ~WinWindow(); + + static LRESULT CALLBACK windowProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam); + +protected: + + HINSTANCE m_hinst = nullptr; + HWND m_hwnd = nullptr; +}; + +// +// We use a bare-minimum window procedure to get things up and running. +// + +/* static */LRESULT CALLBACK WinWindow::windowProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CLOSE: + PostQuitMessage(0); + return 0; + } + return DefWindowProcW(windowHandle, message, wParam, lParam); +} + +static ATOM _getWindowClassAtom(HINSTANCE hinst) +{ + static ATOM s_windowClassAtom; + + if (s_windowClassAtom) + { + return s_windowClassAtom; + } + WNDCLASSEXW windowClassDesc; + windowClassDesc.cbSize = sizeof(windowClassDesc); + windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + windowClassDesc.lpfnWndProc = &WinWindow::windowProc; + windowClassDesc.cbClsExtra = 0; + windowClassDesc.cbWndExtra = 0; + windowClassDesc.hInstance = hinst; + windowClassDesc.hIcon = 0; + windowClassDesc.hCursor = 0; + windowClassDesc.hbrBackground = 0; + windowClassDesc.lpszMenuName = 0; + windowClassDesc.lpszClassName = L"SlangRenderTest"; + windowClassDesc.hIconSm = 0; + s_windowClassAtom = RegisterClassExW(&windowClassDesc); + + return s_windowClassAtom; +} + +SlangResult WinWindow::initialize(int widthIn, int heightIn) +{ + // Do initial window-creation stuff here, rather than in the renderer-specific files + + m_hinst = GetModuleHandleA(0); + + // First we register a window class. + ATOM windowClassAtom = _getWindowClassAtom(m_hinst); + if (!windowClassAtom) + { + fprintf(stderr, "error: failed to register window class\n"); + return SLANG_FAIL; + } + + // Next, we create a window using that window class. + + // We will create a borderless window since our screen-capture logic in GL + // seems to get thrown off by having to deal with a window frame. + DWORD windowStyle = WS_POPUP; + DWORD windowExtendedStyle = 0; + + RECT windowRect = { 0, 0, widthIn, heightIn }; + AdjustWindowRectEx(&windowRect, windowStyle, /*hasMenu=*/false, windowExtendedStyle); + + { + auto width = windowRect.right - windowRect.left; + auto height = windowRect.bottom - windowRect.top; + + LPWSTR windowName = L"Slang Render Test"; + m_hwnd = CreateWindowExW( + windowExtendedStyle, + (LPWSTR)windowClassAtom, + windowName, + windowStyle, + 0, 0, // x, y + width, height, + NULL, // parent + NULL, // menu + m_hinst, + NULL); + } + if (!m_hwnd) + { + fprintf(stderr, "error: failed to create window\n"); + return SLANG_FAIL; + } + + return SLANG_OK; +} + + +void WinWindow::show() +{ + // Once initialization is all complete, we show the window... + int showCommand = SW_SHOW; + ShowWindow(m_hwnd, showCommand); +} + +SlangResult WinWindow::runLoop(WindowListener* listener) +{ + // ... and enter the event loop: + while (!m_isQuitting) + { + MSG message; + int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); + if (result != 0) + { + if (message.message == WM_QUIT) + { + m_quitValue = (int)message.wParam; + return SLANG_OK; + } + + TranslateMessage(&message); + DispatchMessageW(&message); + } + else + { + if (listener) + { + SLANG_RETURN_ON_FAIL(listener->update(this)); + } + } + } + + return SLANG_OK; +} + +WinWindow::~WinWindow() +{ + if (m_hwnd) + { + DestroyWindow(m_hwnd); + } +} + +Window* createWinWindow() +{ + return new WinWindow; +} + +} // namespace renderer_test diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index 42b9e9331..26d611181 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -751,26 +751,11 @@ static SlangResult _extractReflectionTestRequirements(const CommandLine& cmdLine return SLANG_OK; } -static SlangResult _tryUseCPURenderTest(TestContext* context, CommandLine& ioCmdLine) -{ - String exeName = Path::getFileNameWithoutExt(ioCmdLine.m_executable); - - if (exeName == "render-test") - { - bool useCPU = ioCmdLine.findArgIndex(UnownedStringSlice::fromLiteral("-cpu")) >= 0; - if (useCPU) - { - ioCmdLine.setExecutablePath(Path::combine(context->options.binDir, String("cpu-render-test") + ProcessUtil::getExecutableSuffix())); - } - } - return SLANG_OK; -} - static SlangResult _extractTestRequirements(const CommandLine& cmdLine, TestRequirements* ioInfo) { String exeName = Path::getFileNameWithoutExt(cmdLine.m_executable); - if (exeName == "render-test" || exeName == "cpu-render-test") + if (exeName == "render-test") { return _extractRenderTestRequirements(cmdLine, ioInfo); } @@ -830,8 +815,6 @@ static RenderApiFlags _getAvailableRenderApiFlags(TestContext* context) builder << "-" << RenderApiUtil::getApiName(apiType); cmdLine.addArg(builder); - _tryUseCPURenderTest(context, cmdLine); - // Run the render-test tool and see if the device could startup ExecuteResult exeRes; if (SLANG_SUCCEEDED(spawnAndWaitSharedLibrary(context, "device-startup", cmdLine, exeRes)) @@ -2071,8 +2054,6 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons auto actualOutputFile = outputStem + ".actual.txt"; cmdLine.addArg(actualOutputFile); - _tryUseCPURenderTest(context, cmdLine); - if (context->isExecuting()) { // clear the stale actual output file first. This will allow us to detect error if render-test fails and outputs nothing. @@ -2188,8 +2169,6 @@ TestResult doRenderComparisonTestRun(TestContext* context, TestInput& input, cha cmdLine.addArg("-o"); cmdLine.addArg(outputStem + outputKind + ".png"); - _tryUseCPURenderTest(context, cmdLine); - ExecuteResult exeRes; TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); -- cgit v1.2.3