summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlucy96chen <47800040+lucy96chen@users.noreply.github.com>2022-05-17 10:56:14 -0700
committerGitHub <noreply@github.com>2022-05-17 10:56:14 -0700
commit5a3aa6159e0ef0241b528812e1d138f0d7055f22 (patch)
tree71d286e06030ee73f0b739e071cd58dd05d507d1
parent716e75b9ed1acfaee3dc7f3bc347ad17fca65e05 (diff)
Split render-d3d12.h/cpp into a set of smaller files (#2231)
* Split render-d3d12 into numerous smaller files to make the code easier to parse * Added all new D3D12 files created from splitting render-d3d12 * Fixed several uses of attachment still floating around; Changed resource-d3d12 and descriptor-heap-d3d12 to match naming conventions of new d3d12 implementation header files * Readded files with name changes because changing them from inside VS apparently results in them being treated as new files * Merged in externals changes from master * Small cleanup changes * Rerun CI Co-authored-by: Theresa Foley <10618364+tangent-vector@users.noreply.github.com>
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj53
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj.filters143
m---------external/glslang0
m---------external/spirv-headers0
m---------external/spirv-tools0
-rw-r--r--tools/gfx/d3d12/d3d12-base.h73
-rw-r--r--tools/gfx/d3d12/d3d12-buffer.cpp89
-rw-r--r--tools/gfx/d3d12/d3d12-buffer.h44
-rw-r--r--tools/gfx/d3d12/d3d12-command-buffer.cpp110
-rw-r--r--tools/gfx/d3d12/d3d12-command-buffer.h84
-rw-r--r--tools/gfx/d3d12/d3d12-command-encoder.cpp1406
-rw-r--r--tools/gfx/d3d12/d3d12-command-encoder.h305
-rw-r--r--tools/gfx/d3d12/d3d12-command-queue.cpp110
-rw-r--r--tools/gfx/d3d12/d3d12-command-queue.h51
-rw-r--r--tools/gfx/d3d12/d3d12-descriptor-heap.cpp (renamed from tools/gfx/d3d12/descriptor-heap-d3d12.cpp)2
-rw-r--r--tools/gfx/d3d12/d3d12-descriptor-heap.h (renamed from tools/gfx/d3d12/descriptor-heap-d3d12.h)0
-rw-r--r--tools/gfx/d3d12/d3d12-device.cpp2036
-rw-r--r--tools/gfx/d3d12/d3d12-device.h253
-rw-r--r--tools/gfx/d3d12/d3d12-fence.cpp74
-rw-r--r--tools/gfx/d3d12/d3d12-fence.h37
-rw-r--r--tools/gfx/d3d12/d3d12-framebuffer.h38
-rw-r--r--tools/gfx/d3d12/d3d12-helper-functions.cpp590
-rw-r--r--tools/gfx/d3d12/d3d12-helper-functions.h81
-rw-r--r--tools/gfx/d3d12/d3d12-pipeline-state.cpp469
-rw-r--r--tools/gfx/d3d12/d3d12-pipeline-state.h41
-rw-r--r--tools/gfx/d3d12/d3d12-query.cpp207
-rw-r--r--tools/gfx/d3d12/d3d12-query.h64
-rw-r--r--tools/gfx/d3d12/d3d12-render-pass.cpp19
-rw-r--r--tools/gfx/d3d12/d3d12-render-pass.h22
-rw-r--r--tools/gfx/d3d12/d3d12-resource-views.cpp41
-rw-r--r--tools/gfx/d3d12/d3d12-resource-views.h52
-rw-r--r--tools/gfx/d3d12/d3d12-resource.cpp (renamed from tools/gfx/d3d12/resource-d3d12.cpp)4
-rw-r--r--tools/gfx/d3d12/d3d12-resource.h (renamed from tools/gfx/d3d12/resource-d3d12.h)2
-rw-r--r--tools/gfx/d3d12/d3d12-sampler.cpp21
-rw-r--r--tools/gfx/d3d12/d3d12-sampler.h23
-rw-r--r--tools/gfx/d3d12/d3d12-shader-object-layout.cpp1004
-rw-r--r--tools/gfx/d3d12/d3d12-shader-object-layout.h485
-rw-r--r--tools/gfx/d3d12/d3d12-shader-object.cpp1179
-rw-r--r--tools/gfx/d3d12/d3d12-shader-object.h294
-rw-r--r--tools/gfx/d3d12/d3d12-shader-program.cpp25
-rw-r--r--tools/gfx/d3d12/d3d12-shader-program.h33
-rw-r--r--tools/gfx/d3d12/d3d12-shader-table.cpp106
-rw-r--r--tools/gfx/d3d12/d3d12-shader-table.h29
-rw-r--r--tools/gfx/d3d12/d3d12-submitter.cpp100
-rw-r--r--tools/gfx/d3d12/d3d12-submitter.h75
-rw-r--r--tools/gfx/d3d12/d3d12-swap-chain.cpp101
-rw-r--r--tools/gfx/d3d12/d3d12-swap-chain.h38
-rw-r--r--tools/gfx/d3d12/d3d12-texture.cpp58
-rw-r--r--tools/gfx/d3d12/d3d12-texture.h34
-rw-r--r--tools/gfx/d3d12/d3d12-transient-heap.cpp222
-rw-r--r--tools/gfx/d3d12/d3d12-transient-heap.h94
-rw-r--r--tools/gfx/d3d12/d3d12-vertex-layout.h23
-rw-r--r--tools/gfx/d3d12/render-d3d12.cpp7667
-rw-r--r--tools/gfx/d3d12/render-d3d12.h1806
54 files changed, 10424 insertions, 9493 deletions
diff --git a/build/visual-studio/gfx/gfx.vcxproj b/build/visual-studio/gfx/gfx.vcxproj
index ca6b365f5..a17310173 100644
--- a/build/visual-studio/gfx/gfx.vcxproj
+++ b/build/visual-studio/gfx/gfx.vcxproj
@@ -304,12 +304,34 @@
<ClInclude Include="..\..\..\tools\gfx\command-writer.h" />
<ClInclude Include="..\..\..\tools\gfx\cpu\render-cpu.h" />
<ClInclude Include="..\..\..\tools\gfx\cuda\render-cuda.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-buffer.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-command-buffer.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-command-encoder.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-command-queue.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-device.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-fence.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-framebuffer.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-base.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-helper-functions.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-query.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-pipeline-state.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-render-pass.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-resource-views.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-sampler.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-shader-object-layout.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-shader-object.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-shader-program.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-shader-table.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-submitter.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-swap-chain.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-texture.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-transient-heap.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-vertex-layout.h" />
<ClInclude Include="..\..\..\tools\gfx\d3d\d3d-swapchain.h" />
<ClInclude Include="..\..\..\tools\gfx\d3d\d3d-util.h" />
<ClInclude Include="..\..\..\tools\gfx\d3d11\render-d3d11.h" />
- <ClInclude Include="..\..\..\tools\gfx\d3d12\descriptor-heap-d3d12.h" />
- <ClInclude Include="..\..\..\tools\gfx\d3d12\render-d3d12.h" />
- <ClInclude Include="..\..\..\tools\gfx\d3d12\resource-d3d12.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-descriptor-heap.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-resource.h" />
<ClInclude Include="..\..\..\tools\gfx\debug-layer.h" />
<ClInclude Include="..\..\..\tools\gfx\flag-combiner.h" />
<ClInclude Include="..\..\..\tools\gfx\immediate-renderer-base.h" />
@@ -333,12 +355,31 @@
<ItemGroup>
<ClCompile Include="..\..\..\tools\gfx\cpu\render-cpu.cpp" />
<ClCompile Include="..\..\..\tools\gfx\cuda\render-cuda.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-buffer.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-command-buffer.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-command-encoder.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-command-queue.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-device.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-fence.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-helper-functions.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-query.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-pipeline-state.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-render-pass.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-resource-views.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-sampler.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-shader-object-layout.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-shader-object.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-shader-program.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-shader-table.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-submitter.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-swap-chain.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-texture.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-transient-heap.cpp" />
<ClCompile Include="..\..\..\tools\gfx\d3d\d3d-swapchain.cpp" />
<ClCompile Include="..\..\..\tools\gfx\d3d\d3d-util.cpp" />
<ClCompile Include="..\..\..\tools\gfx\d3d11\render-d3d11.cpp" />
- <ClCompile Include="..\..\..\tools\gfx\d3d12\descriptor-heap-d3d12.cpp" />
- <ClCompile Include="..\..\..\tools\gfx\d3d12\render-d3d12.cpp" />
- <ClCompile Include="..\..\..\tools\gfx\d3d12\resource-d3d12.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-descriptor-heap.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-resource.cpp" />
<ClCompile Include="..\..\..\tools\gfx\debug-layer.cpp" />
<ClCompile Include="..\..\..\tools\gfx\flag-combiner.cpp" />
<ClCompile Include="..\..\..\tools\gfx\immediate-renderer-base.cpp" />
diff --git a/build/visual-studio/gfx/gfx.vcxproj.filters b/build/visual-studio/gfx/gfx.vcxproj.filters
index a6b9aab7f..e509a00a4 100644
--- a/build/visual-studio/gfx/gfx.vcxproj.filters
+++ b/build/visual-studio/gfx/gfx.vcxproj.filters
@@ -33,13 +33,10 @@
<ClInclude Include="..\..\..\tools\gfx\d3d11\render-d3d11.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\..\..\tools\gfx\d3d12\descriptor-heap-d3d12.h">
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-descriptor-heap.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\..\..\tools\gfx\d3d12\render-d3d12.h">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="..\..\..\tools\gfx\d3d12\resource-d3d12.h">
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\tools\gfx\debug-layer.h">
@@ -99,6 +96,75 @@
<ClInclude Include="..\..\..\tools\gfx\vulkan\vk-util.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-buffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-texture.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-sampler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-resource-views.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-framebuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-render-pass.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-vertex-layout.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-pipeline-state.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-device.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-query.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-transient-heap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-command-encoder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-submitter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-shader-object.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-shader-object-layout.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-shader-program.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-shader-table.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-command-buffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-fence.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-command-queue.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-swap-chain.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-helper-functions.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-base.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\tools\gfx\cpu\render-cpu.cpp">
@@ -116,13 +182,10 @@
<ClCompile Include="..\..\..\tools\gfx\d3d11\render-d3d11.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\tools\gfx\d3d12\descriptor-heap-d3d12.cpp">
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-descriptor-heap.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\tools\gfx\d3d12\render-d3d12.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="..\..\..\tools\gfx\d3d12\resource-d3d12.cpp">
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-resource.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\tools\gfx\debug-layer.cpp">
@@ -170,5 +233,65 @@
<ClCompile Include="..\..\..\tools\gfx\vulkan\vk-util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-buffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-texture.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-sampler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-resource-views.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-render-pass.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-pipeline-state.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-device.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-query.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-transient-heap.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-command-encoder.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-shader-object.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-shader-object-layout.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-shader-program.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-shader-table.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-command-buffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-fence.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-command-queue.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-swap-chain.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-helper-functions.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-submitter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/external/glslang b/external/glslang
-Subproject dc703d95289e5376de457af0ed9b3dd0f0f9756
+Subproject 418694e6cd2e2e84052f77bb8306d560c7d8fe3
diff --git a/external/spirv-headers b/external/spirv-headers
-Subproject b765c355f488837ca4c77980ba69484f3ff277f
+Subproject 1380cbbec10756b492e9397d03c4250887e1509
diff --git a/external/spirv-tools b/external/spirv-tools
-Subproject ffc8f2d45566329bb9e21170f9e2a34ed8a65e0
+Subproject d997c83b103ed1f3af09ed65e1cbf89fbc6d945
diff --git a/tools/gfx/d3d12/d3d12-base.h b/tools/gfx/d3d12/d3d12-base.h
new file mode 100644
index 000000000..2446151a2
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-base.h
@@ -0,0 +1,73 @@
+// d3d12-base.h
+// Shared header file for D3D12 implementation
+#pragma once
+
+#include "../command-encoder-com-forward.h"
+#include "../d3d/d3d-swapchain.h"
+#include "../mutable-shader-object.h"
+#include "../renderer-shared.h"
+#include "../simple-render-pass-layout.h"
+#include "../transient-resource-heap-base.h"
+#include "core/slang-basic.h"
+#include "core/slang-blob.h"
+#include "core/slang-chunked-list.h"
+#include "d3d12-descriptor-heap.h"
+#include "d3d12-resource.h"
+
+#define _CRT_SECURE_NO_WARNINGS
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <Windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#undef NOMINMAX
+
+#include <d3d12.h>
+#include <dxgi1_4.h>
+
+#ifndef __ID3D12GraphicsCommandList1_FWD_DEFINED__
+// If can't find a definition of CommandList1, just use an empty definition
+struct ID3D12GraphicsCommandList1
+{};
+#endif
+
+namespace gfx
+{
+namespace d3d12
+{
+ class DeviceImpl;
+ class BufferResourceImpl;
+ class TextureResourceImpl;
+ class CommandBufferImpl;
+ class PipelineCommandEncoder;
+ class ResourceCommandEncoderImpl;
+ class ComputeCommandEncoderImpl;
+ class RenderCommandEncoderImpl;
+ class CommandQueueImpl;
+ class FenceImpl;
+ class FramebufferLayoutImpl;
+ class FramebufferImpl;
+ class QueryPoolImpl;
+ class PlainBufferProxyQueryPoolImpl;
+ class PipelineStateImpl;
+ class RenderPassLayoutImpl;
+ class ResourceViewInternalImpl;
+ class ResourceViewImpl;
+ class AccelerationStructureImpl;
+ class SamplerStateImpl;
+ class ShaderObjectImpl;
+ class RootShaderObjectImpl;
+ class MutableRootShaderObjectImpl;
+ class ShaderObjectLayoutImpl;
+ class RootShaderObjectLayoutImpl;
+ class ShaderProgramImpl;
+ class ShaderTableImpl;
+ class SwapChainImpl;
+ class TransientResourceHeapImpl;
+ class InputLayoutImpl;
+
+#if SLANG_GFX_HAS_DXR_SUPPORT
+ class RayTracingCommandEncoderImpl;
+ class RayTracingPipelineStateImpl;
+#endif
+}
+}
diff --git a/tools/gfx/d3d12/d3d12-buffer.cpp b/tools/gfx/d3d12/d3d12-buffer.cpp
new file mode 100644
index 000000000..42babfae6
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-buffer.cpp
@@ -0,0 +1,89 @@
+// d3d12-buffer.cpp
+#include "d3d12-buffer.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+BufferResourceImpl::BufferResourceImpl(const Desc& desc)
+ : Parent(desc)
+ , m_defaultState(D3DUtil::getResourceState(desc.defaultState))
+{}
+
+BufferResourceImpl::~BufferResourceImpl()
+{
+ if (sharedHandle.handleValue != 0)
+ {
+ CloseHandle((HANDLE)sharedHandle.handleValue);
+ }
+}
+
+DeviceAddress BufferResourceImpl::getDeviceAddress()
+{
+ return (DeviceAddress)m_resource.getResource()->GetGPUVirtualAddress();
+}
+
+Result BufferResourceImpl::getNativeResourceHandle(InteropHandle* outHandle)
+{
+ outHandle->handleValue = (uint64_t)m_resource.getResource();
+ outHandle->api = InteropHandleAPI::D3D12;
+ return SLANG_OK;
+}
+
+Result BufferResourceImpl::getSharedHandle(InteropHandle* outHandle)
+{
+ // Check if a shared handle already exists for this resource.
+ if (sharedHandle.handleValue != 0)
+ {
+ *outHandle = sharedHandle;
+ return SLANG_OK;
+ }
+
+ // If a shared handle doesn't exist, create one and store it.
+ ComPtr<ID3D12Device> pDevice;
+ auto pResource = m_resource.getResource();
+ pResource->GetDevice(IID_PPV_ARGS(pDevice.writeRef()));
+ SLANG_RETURN_ON_FAIL(pDevice->CreateSharedHandle(
+ pResource, NULL, GENERIC_ALL, nullptr, (HANDLE*)&outHandle->handleValue));
+ outHandle->api = InteropHandleAPI::D3D12;
+ sharedHandle = *outHandle;
+ return SLANG_OK;
+}
+
+Result BufferResourceImpl::map(MemoryRange* rangeToRead, void** outPointer)
+{
+ D3D12_RANGE range = {};
+ if (rangeToRead)
+ {
+ range.Begin = (SIZE_T)rangeToRead->offset;
+ range.End = (SIZE_T)(rangeToRead->offset + rangeToRead->size);
+ }
+ SLANG_RETURN_ON_FAIL(
+ m_resource.getResource()->Map(0, rangeToRead ? &range : nullptr, outPointer));
+ return SLANG_OK;
+}
+
+Result BufferResourceImpl::unmap(MemoryRange* writtenRange)
+{
+ D3D12_RANGE range = {};
+ if (writtenRange)
+ {
+ range.Begin = (SIZE_T)writtenRange->offset;
+ range.End = (SIZE_T)(writtenRange->offset + writtenRange->size);
+ }
+ m_resource.getResource()->Unmap(0, writtenRange ? &range : nullptr);
+ return SLANG_OK;
+}
+
+Result BufferResourceImpl::setDebugName(const char* name)
+{
+ Parent::setDebugName(name);
+ m_resource.setDebugName(name);
+ return SLANG_OK;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-buffer.h b/tools/gfx/d3d12/d3d12-buffer.h
new file mode 100644
index 000000000..272969b07
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-buffer.h
@@ -0,0 +1,44 @@
+// d3d12-buffer.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class BufferResourceImpl : public gfx::BufferResource
+{
+public:
+ typedef BufferResource Parent;
+
+ BufferResourceImpl(const Desc& desc);
+
+ ~BufferResourceImpl();
+
+ D3D12Resource m_resource; ///< The resource in gpu memory, allocated on the correct heap
+ ///< relative to the cpu access flag
+
+ D3D12_RESOURCE_STATES m_defaultState;
+
+ virtual SLANG_NO_THROW DeviceAddress SLANG_MCALL getDeviceAddress() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getNativeResourceHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ map(MemoryRange* rangeToRead, void** outPointer) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL unmap(MemoryRange* writtenRange) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setDebugName(const char* name) override;
+};
+
+} // namespace d3d12
+} // namespace gfx
+
diff --git a/tools/gfx/d3d12/d3d12-command-buffer.cpp b/tools/gfx/d3d12/d3d12-command-buffer.cpp
new file mode 100644
index 000000000..3a09da037
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-command-buffer.cpp
@@ -0,0 +1,110 @@
+// d3d12-command-buffer.cpp
+#include "d3d12-command-buffer.h"
+
+#include "d3d12-transient-heap.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+// There are a pair of cyclic references between a `TransientResourceHeap` and
+// a `CommandBuffer` created from the heap. We need to break the cycle upon
+// the public reference count of a command buffer dropping to 0.
+
+ICommandBufferD3D12* CommandBufferImpl::getInterface(const Guid& guid)
+{
+ if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ICommandBuffer ||
+ guid == GfxGUID::IID_ICommandBufferD3D12)
+ return static_cast<ICommandBufferD3D12*>(this);
+ return nullptr;
+}
+
+Result CommandBufferImpl::getNativeHandle(InteropHandle* handle)
+{
+ handle->api = InteropHandleAPI::D3D12;
+ handle->handleValue = (uint64_t)m_cmdList.get();
+ return SLANG_OK;
+}
+
+void CommandBufferImpl::bindDescriptorHeaps()
+{
+ if (!m_descriptorHeapsBound)
+ {
+ ID3D12DescriptorHeap* heaps[] = {
+ m_transientHeap->getCurrentViewHeap().getHeap(),
+ m_transientHeap->getCurrentSamplerHeap().getHeap(),
+ };
+ m_cmdList->SetDescriptorHeaps(SLANG_COUNT_OF(heaps), heaps);
+ m_descriptorHeapsBound = true;
+ }
+}
+
+void CommandBufferImpl::reinit()
+{
+ invalidateDescriptorHeapBinding();
+ m_rootShaderObject.init(m_renderer);
+}
+
+void CommandBufferImpl::init(
+ DeviceImpl* renderer,
+ ID3D12GraphicsCommandList* d3dCommandList,
+ TransientResourceHeapImpl* transientHeap)
+{
+ m_transientHeap = transientHeap;
+ m_renderer = renderer;
+ m_cmdList = d3dCommandList;
+
+ reinit();
+
+#if SLANG_GFX_HAS_DXR_SUPPORT
+ m_cmdList->QueryInterface<ID3D12GraphicsCommandList4>(m_cmdList4.writeRef());
+ if (m_cmdList4)
+ {
+ m_cmdList1 = m_cmdList4;
+ return;
+ }
+#endif
+ m_cmdList->QueryInterface<ID3D12GraphicsCommandList1>(m_cmdList1.writeRef());
+}
+
+void CommandBufferImpl::encodeResourceCommands(IResourceCommandEncoder** outEncoder)
+{
+ m_resourceCommandEncoder.init(this);
+ *outEncoder = &m_resourceCommandEncoder;
+}
+
+void CommandBufferImpl::encodeRenderCommands(
+ IRenderPassLayout* renderPass, IFramebuffer* framebuffer, IRenderCommandEncoder** outEncoder)
+{
+ m_renderCommandEncoder.init(
+ m_renderer,
+ m_transientHeap,
+ this,
+ static_cast<RenderPassLayoutImpl*>(renderPass),
+ static_cast<FramebufferImpl*>(framebuffer));
+ *outEncoder = &m_renderCommandEncoder;
+}
+
+void CommandBufferImpl::encodeComputeCommands(IComputeCommandEncoder** outEncoder)
+{
+ m_computeCommandEncoder.init(m_renderer, m_transientHeap, this);
+ *outEncoder = &m_computeCommandEncoder;
+}
+
+void CommandBufferImpl::encodeRayTracingCommands(IRayTracingCommandEncoder** outEncoder)
+{
+#if SLANG_GFX_HAS_DXR_SUPPORT
+ m_rayTracingCommandEncoder.init(this);
+ *outEncoder = &m_rayTracingCommandEncoder;
+#else
+ * outEncoder = nullptr;
+#endif
+}
+
+void CommandBufferImpl::close() { m_cmdList->Close(); }
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-command-buffer.h b/tools/gfx/d3d12/d3d12-command-buffer.h
new file mode 100644
index 000000000..dbe019807
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-command-buffer.h
@@ -0,0 +1,84 @@
+// d3d12-command-buffer.h
+#pragma once
+
+#include "d3d12-base.h"
+#include "d3d12-shader-object.h"
+#include "d3d12-command-encoder.h"
+
+#ifndef __ID3D12GraphicsCommandList1_FWD_DEFINED__
+// If can't find a definition of CommandList1, just use an empty definition
+struct ID3D12GraphicsCommandList1
+{};
+#endif
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class CommandBufferImpl
+ : public ICommandBufferD3D12
+ , public ComObject
+{
+public:
+ // There are a pair of cyclic references between a `TransientResourceHeap` and
+ // a `CommandBuffer` created from the heap. We need to break the cycle upon
+ // the public reference count of a command buffer dropping to 0.
+ SLANG_COM_OBJECT_IUNKNOWN_ALL
+
+ ICommandBufferD3D12* getInterface(const Guid& guid);
+ virtual void comFree() override { m_transientHeap.breakStrongReference(); }
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* handle) override;
+
+public:
+ ComPtr<ID3D12GraphicsCommandList> m_cmdList;
+ ComPtr<ID3D12GraphicsCommandList1> m_cmdList1;
+ ComPtr<ID3D12GraphicsCommandList4> m_cmdList4;
+
+ BreakableReference<TransientResourceHeapImpl> m_transientHeap;
+ // Weak reference is fine here since `m_transientHeap` already holds strong reference to
+ // device.
+ DeviceImpl* m_renderer;
+ RootShaderObjectImpl m_rootShaderObject;
+ RefPtr<MutableRootShaderObjectImpl> m_mutableRootShaderObject;
+ bool m_descriptorHeapsBound = false;
+
+ void bindDescriptorHeaps();
+
+ virtual SLANG_NO_THROW void SLANG_MCALL invalidateDescriptorHeapBinding() override { m_descriptorHeapsBound = false; }
+
+ void reinit();
+
+ void init(
+ DeviceImpl* renderer,
+ ID3D12GraphicsCommandList* d3dCommandList,
+ TransientResourceHeapImpl* transientHeap);
+
+ ResourceCommandEncoderImpl m_resourceCommandEncoder;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ encodeResourceCommands(IResourceCommandEncoder** outEncoder) override;
+
+ RenderCommandEncoderImpl m_renderCommandEncoder;
+ virtual SLANG_NO_THROW void SLANG_MCALL encodeRenderCommands(
+ IRenderPassLayout* renderPass,
+ IFramebuffer* framebuffer,
+ IRenderCommandEncoder** outEncoder) override;
+
+ ComputeCommandEncoderImpl m_computeCommandEncoder;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ encodeComputeCommands(IComputeCommandEncoder** outEncoder) override;
+
+#if SLANG_GFX_HAS_DXR_SUPPORT
+ RayTracingCommandEncoderImpl m_rayTracingCommandEncoder;
+#endif
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ encodeRayTracingCommands(IRayTracingCommandEncoder** outEncoder) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL close() override;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-command-encoder.cpp b/tools/gfx/d3d12/d3d12-command-encoder.cpp
new file mode 100644
index 000000000..516053d39
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-command-encoder.cpp
@@ -0,0 +1,1406 @@
+// d3d12-command-encoder.cpp
+#include "d3d12-command-encoder.h"
+
+#include "d3d12-command-buffer.h"
+#include "d3d12-device.h"
+#include "d3d12-pipeline-state.h"
+#include "d3d12-query.h"
+#include "d3d12-shader-object.h"
+#include "d3d12-shader-program.h"
+#include "d3d12-shader-table.h"
+#include "d3d12-transient-heap.h"
+#include "d3d12-texture.h"
+#include "d3d12-vertex-layout.h"
+
+#include "d3d12-helper-functions.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+int PipelineCommandEncoder::getBindPointIndex(PipelineType type)
+{
+ switch (type)
+ {
+ case PipelineType::Graphics:
+ return 0;
+ case PipelineType::Compute:
+ return 1;
+ case PipelineType::RayTracing:
+ return 2;
+ default:
+ assert(!"unknown pipeline type.");
+ return -1;
+ }
+}
+
+void PipelineCommandEncoder::init(CommandBufferImpl* commandBuffer)
+{
+ m_commandBuffer = commandBuffer;
+ m_d3dCmdList = m_commandBuffer->m_cmdList;
+ m_renderer = commandBuffer->m_renderer;
+ m_transientHeap = commandBuffer->m_transientHeap;
+ m_device = commandBuffer->m_renderer->m_device;
+}
+
+Result PipelineCommandEncoder::bindPipelineImpl(
+ IPipelineState* pipelineState, IShaderObject** outRootObject)
+{
+ m_currentPipeline = static_cast<PipelineStateBase*>(pipelineState);
+ auto rootObject = &m_commandBuffer->m_rootShaderObject;
+ m_commandBuffer->m_mutableRootShaderObject = nullptr;
+ SLANG_RETURN_ON_FAIL(rootObject->reset(
+ m_renderer,
+ m_currentPipeline->getProgram<ShaderProgramImpl>()->m_rootObjectLayout,
+ m_commandBuffer->m_transientHeap));
+ *outRootObject = rootObject;
+ m_bindingDirty = true;
+ return SLANG_OK;
+}
+
+Result PipelineCommandEncoder::bindPipelineWithRootObjectImpl(
+ IPipelineState* pipelineState, IShaderObject* rootObject)
+{
+ m_currentPipeline = static_cast<PipelineStateBase*>(pipelineState);
+ m_commandBuffer->m_mutableRootShaderObject =
+ static_cast<MutableRootShaderObjectImpl*>(rootObject);
+ m_bindingDirty = true;
+ return SLANG_OK;
+}
+
+Result PipelineCommandEncoder::_bindRenderState(
+ Submitter* submitter, RefPtr<PipelineStateBase>& newPipeline)
+{
+ RootShaderObjectImpl* rootObjectImpl = m_commandBuffer->m_mutableRootShaderObject
+ ? m_commandBuffer->m_mutableRootShaderObject.Ptr()
+ : &m_commandBuffer->m_rootShaderObject;
+ SLANG_RETURN_ON_FAIL(
+ m_renderer->maybeSpecializePipeline(m_currentPipeline, rootObjectImpl, newPipeline));
+ PipelineStateBase* newPipelineImpl = static_cast<PipelineStateBase*>(newPipeline.Ptr());
+ auto commandList = m_d3dCmdList;
+ auto pipelineTypeIndex = (int)newPipelineImpl->desc.type;
+ auto programImpl = static_cast<ShaderProgramImpl*>(newPipelineImpl->m_program.Ptr());
+ newPipelineImpl->ensureAPIPipelineStateCreated();
+ submitter->setRootSignature(programImpl->m_rootObjectLayout->m_rootSignature);
+ submitter->setPipelineState(newPipelineImpl);
+ RootShaderObjectLayoutImpl* rootLayoutImpl = programImpl->m_rootObjectLayout;
+
+ // We need to set up a context for binding shader objects to the pipeline state.
+ // This type mostly exists to bundle together a bunch of parameters that would
+ // otherwise need to be tunneled down through all the shader object binding
+ // logic.
+ //
+ BindingContext context = {};
+ context.encoder = this;
+ context.submitter = submitter;
+ context.device = m_renderer;
+ context.transientHeap = m_transientHeap;
+ context.outOfMemoryHeap = (D3D12_DESCRIPTOR_HEAP_TYPE)(-1);
+ // We kick off binding of shader objects at the root object, and the objects
+ // themselves will be responsible for allocating, binding, and filling in
+ // any descriptor tables or other root parameters needed.
+ //
+ m_commandBuffer->bindDescriptorHeaps();
+ if (rootObjectImpl->bindAsRoot(&context, rootLayoutImpl) == SLANG_E_OUT_OF_MEMORY)
+ {
+ if (!m_transientHeap->canResize())
+ {
+ return SLANG_E_OUT_OF_MEMORY;
+ }
+
+ // If we run out of heap space while binding, allocate new descriptor heaps and try again.
+ ID3D12DescriptorHeap* d3dheap = nullptr;
+ m_commandBuffer->invalidateDescriptorHeapBinding();
+ switch (context.outOfMemoryHeap)
+ {
+ case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
+ SLANG_RETURN_ON_FAIL(m_transientHeap->allocateNewViewDescriptorHeap(m_renderer));
+ d3dheap = m_transientHeap->getCurrentViewHeap().getHeap();
+ m_commandBuffer->bindDescriptorHeaps();
+ break;
+ case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
+ SLANG_RETURN_ON_FAIL(m_transientHeap->allocateNewSamplerDescriptorHeap(m_renderer));
+ d3dheap = m_transientHeap->getCurrentSamplerHeap().getHeap();
+ m_commandBuffer->bindDescriptorHeaps();
+ break;
+ default:
+ assert(!"shouldn't be here");
+ return SLANG_FAIL;
+ }
+
+ // Try again.
+ SLANG_RETURN_ON_FAIL(rootObjectImpl->bindAsRoot(&context, rootLayoutImpl));
+ }
+
+ return SLANG_OK;
+}
+
+void ResourceCommandEncoderImpl::bufferBarrier(
+ GfxCount count, IBufferResource* const* buffers, ResourceState src, ResourceState dst)
+{
+ ShortList<D3D12_RESOURCE_BARRIER, 16> barriers;
+ for (GfxIndex i = 0; i < count; i++)
+ {
+ auto bufferImpl = static_cast<BufferResourceImpl*>(buffers[i]);
+
+ D3D12_RESOURCE_BARRIER barrier = {};
+ // If the src == dst, it must be a UAV barrier.
+ barrier.Type = (src == dst && dst == ResourceState::UnorderedAccess)
+ ? D3D12_RESOURCE_BARRIER_TYPE_UAV
+ : D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+
+ if (barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV)
+ {
+ barrier.UAV.pResource = bufferImpl->m_resource;
+ }
+ else
+ {
+ barrier.Transition.pResource = bufferImpl->m_resource;
+ barrier.Transition.StateBefore = D3DUtil::getResourceState(src);
+ barrier.Transition.StateAfter = D3DUtil::getResourceState(dst);
+ barrier.Transition.Subresource = 0;
+ if (barrier.Transition.StateAfter == barrier.Transition.StateBefore)
+ continue;
+ }
+ barriers.add(barrier);
+ }
+ if (barriers.getCount())
+ {
+ m_commandBuffer->m_cmdList4->ResourceBarrier(
+ (UINT)barriers.getCount(), barriers.getArrayView().getBuffer());
+ }
+}
+
+void ResourceCommandEncoderImpl::writeTimestamp(IQueryPool* pool, GfxIndex index)
+{
+ static_cast<QueryPoolImpl*>(pool)->writeTimestamp(m_commandBuffer->m_cmdList, index);
+}
+
+void ResourceCommandEncoderImpl::copyTexture(
+ ITextureResource* dst,
+ ResourceState dstState,
+ SubresourceRange dstSubresource,
+ ITextureResource::Offset3D dstOffset,
+ ITextureResource* src,
+ ResourceState srcState,
+ SubresourceRange srcSubresource,
+ ITextureResource::Offset3D srcOffset,
+ ITextureResource::Extents extent)
+{
+ auto dstTexture = static_cast<TextureResourceImpl*>(dst);
+ auto srcTexture = static_cast<TextureResourceImpl*>(src);
+
+ if (dstSubresource.layerCount == 0 && dstSubresource.mipLevelCount == 0 &&
+ srcSubresource.layerCount == 0 && srcSubresource.mipLevelCount == 0)
+ {
+ m_commandBuffer->m_cmdList->CopyResource(
+ dstTexture->m_resource.getResource(), srcTexture->m_resource.getResource());
+ return;
+ }
+
+ auto d3dFormat = D3DUtil::getMapFormat(dstTexture->getDesc()->format);
+ auto aspectMask = (int32_t)dstSubresource.aspectMask;
+ if (dstSubresource.aspectMask == TextureAspect::Default)
+ aspectMask = (int32_t)TextureAspect::Color;
+ while (aspectMask)
+ {
+ auto aspect = Math::getLowestBit((int32_t)aspectMask);
+ aspectMask &= ~aspect;
+ auto planeIndex = D3DUtil::getPlaneSlice(d3dFormat, (TextureAspect)aspect);
+ for (GfxIndex layer = 0; layer < dstSubresource.layerCount; layer++)
+ {
+ for (GfxIndex mipLevel = 0; mipLevel < dstSubresource.mipLevelCount; mipLevel++)
+ {
+ D3D12_TEXTURE_COPY_LOCATION dstRegion = {};
+
+ dstRegion.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dstRegion.pResource = dstTexture->m_resource.getResource();
+ dstRegion.SubresourceIndex = D3DUtil::getSubresourceIndex(
+ dstSubresource.mipLevel + mipLevel,
+ dstSubresource.baseArrayLayer + layer,
+ planeIndex,
+ dstTexture->getDesc()->numMipLevels,
+ dstTexture->getDesc()->arraySize);
+
+ D3D12_TEXTURE_COPY_LOCATION srcRegion = {};
+ srcRegion.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ srcRegion.pResource = srcTexture->m_resource.getResource();
+ srcRegion.SubresourceIndex = D3DUtil::getSubresourceIndex(
+ srcSubresource.mipLevel + mipLevel,
+ srcSubresource.baseArrayLayer + layer,
+ planeIndex,
+ srcTexture->getDesc()->numMipLevels,
+ srcTexture->getDesc()->arraySize);
+
+ D3D12_BOX srcBox = {};
+ srcBox.left = srcOffset.x;
+ srcBox.top = srcOffset.y;
+ srcBox.front = srcOffset.z;
+ srcBox.right = srcBox.left + extent.width;
+ srcBox.bottom = srcBox.top + extent.height;
+ srcBox.back = srcBox.front + extent.depth;
+
+ m_commandBuffer->m_cmdList->CopyTextureRegion(
+ &dstRegion, dstOffset.x, dstOffset.y, dstOffset.z, &srcRegion, &srcBox);
+ }
+ }
+ }
+}
+
+void ResourceCommandEncoderImpl::uploadTextureData(
+ ITextureResource* dst,
+ SubresourceRange subResourceRange,
+ ITextureResource::Offset3D offset,
+ ITextureResource::Extents extent,
+ ITextureResource::SubresourceData* subResourceData,
+ GfxCount subResourceDataCount)
+{
+ auto dstTexture = static_cast<TextureResourceImpl*>(dst);
+ auto baseSubresourceIndex = D3DUtil::getSubresourceIndex(
+ subResourceRange.mipLevel,
+ subResourceRange.baseArrayLayer,
+ 0,
+ dstTexture->getDesc()->numMipLevels,
+ dstTexture->getDesc()->arraySize);
+ auto textureSize = dstTexture->getDesc()->size;
+ FormatInfo formatInfo = {};
+ gfxGetFormatInfo(dstTexture->getDesc()->format, &formatInfo);
+ for (GfxCount i = 0; i < subResourceDataCount; i++)
+ {
+ auto subresourceIndex = baseSubresourceIndex + i;
+ // Get the footprint
+ D3D12_RESOURCE_DESC texDesc = dstTexture->m_resource.getResource()->GetDesc();
+
+ D3D12_TEXTURE_COPY_LOCATION dstRegion = {};
+
+ dstRegion.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dstRegion.SubresourceIndex = subresourceIndex;
+ dstRegion.pResource = dstTexture->m_resource.getResource();
+
+ D3D12_TEXTURE_COPY_LOCATION srcRegion = {};
+ srcRegion.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ D3D12_PLACED_SUBRESOURCE_FOOTPRINT& footprint = srcRegion.PlacedFootprint;
+ footprint.Offset = 0;
+ footprint.Footprint.Format = texDesc.Format;
+ uint32_t mipLevel =
+ D3DUtil::getSubresourceMipLevel(subresourceIndex, dstTexture->getDesc()->numMipLevels);
+ if (extent.width != ITextureResource::kRemainingTextureSize)
+ {
+ footprint.Footprint.Width = extent.width;
+ }
+ else
+ {
+ footprint.Footprint.Width = Math::Max(1, (textureSize.width >> mipLevel)) - offset.x;
+ }
+ if (extent.height != ITextureResource::kRemainingTextureSize)
+ {
+ footprint.Footprint.Height = extent.height;
+ }
+ else
+ {
+ footprint.Footprint.Height = Math::Max(1, (textureSize.height >> mipLevel)) - offset.y;
+ }
+ if (extent.depth != ITextureResource::kRemainingTextureSize)
+ {
+ footprint.Footprint.Depth = extent.depth;
+ }
+ else
+ {
+ footprint.Footprint.Depth = Math::Max(1, (textureSize.depth >> mipLevel)) - offset.z;
+ }
+ auto rowSize = (footprint.Footprint.Width + formatInfo.blockWidth - 1) /
+ formatInfo.blockWidth * formatInfo.blockSizeInBytes;
+ auto rowCount =
+ (footprint.Footprint.Height + formatInfo.blockHeight - 1) / formatInfo.blockHeight;
+ footprint.Footprint.RowPitch =
+ (UINT)D3DUtil::calcAligned(rowSize, (uint32_t)D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
+
+ auto bufferSize = footprint.Footprint.RowPitch * rowCount * footprint.Footprint.Depth;
+
+ IBufferResource* stagingBuffer;
+ Offset stagingBufferOffset = 0;
+ m_commandBuffer->m_transientHeap->allocateStagingBuffer(
+ bufferSize, stagingBuffer, stagingBufferOffset, MemoryType::Upload, true);
+ assert(stagingBufferOffset == 0);
+ BufferResourceImpl* bufferImpl = static_cast<BufferResourceImpl*>(stagingBuffer);
+ uint8_t* bufferData = nullptr;
+ D3D12_RANGE mapRange = { 0, 0 };
+ bufferImpl->m_resource.getResource()->Map(0, &mapRange, (void**)&bufferData);
+ for (uint32_t z = 0; z < footprint.Footprint.Depth; z++)
+ {
+ auto imageStart = bufferData + footprint.Footprint.RowPitch * rowCount * (Size)z;
+ auto srcData = (uint8_t*)subResourceData->data + subResourceData->strideZ * z;
+ for (uint32_t row = 0; row < rowCount; row++)
+ {
+ memcpy(
+ imageStart + row * (Size)footprint.Footprint.RowPitch,
+ srcData + subResourceData->strideY * row,
+ rowSize);
+ }
+ }
+ bufferImpl->m_resource.getResource()->Unmap(0, nullptr);
+ srcRegion.pResource = bufferImpl->m_resource.getResource();
+ m_commandBuffer->m_cmdList->CopyTextureRegion(
+ &dstRegion, offset.x, offset.y, offset.z, &srcRegion, nullptr);
+ }
+}
+
+void ResourceCommandEncoderImpl::clearResourceView(
+ IResourceView* view, ClearValue* clearValue, ClearResourceViewFlags::Enum flags)
+{
+ auto viewImpl = static_cast<ResourceViewImpl*>(view);
+ switch (view->getViewDesc()->type)
+ {
+ case IResourceView::Type::RenderTarget:
+ m_commandBuffer->m_cmdList->ClearRenderTargetView(
+ viewImpl->m_descriptor.cpuHandle, clearValue->color.floatValues, 0, nullptr);
+ break;
+ case IResourceView::Type::DepthStencil:
+ {
+ D3D12_CLEAR_FLAGS clearFlags = (D3D12_CLEAR_FLAGS)0;
+ if (flags & ClearResourceViewFlags::ClearDepth)
+ {
+ clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
+ }
+ if (flags & ClearResourceViewFlags::ClearStencil)
+ {
+ clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
+ }
+ m_commandBuffer->m_cmdList->ClearDepthStencilView(
+ viewImpl->m_descriptor.cpuHandle,
+ clearFlags,
+ clearValue->depthStencil.depth,
+ (UINT8)clearValue->depthStencil.stencil,
+ 0,
+ nullptr);
+ break;
+ }
+ case IResourceView::Type::UnorderedAccess:
+ {
+ ID3D12Resource* d3dResource = nullptr;
+ switch (viewImpl->m_resource->getType())
+ {
+ case IResource::Type::Buffer:
+ d3dResource = static_cast<BufferResourceImpl*>(viewImpl->m_resource.Ptr())
+ ->m_resource.getResource();
+ break;
+ default:
+ d3dResource = static_cast<TextureResourceImpl*>(viewImpl->m_resource.Ptr())
+ ->m_resource.getResource();
+ break;
+ }
+ auto gpuHandleIndex =
+ m_commandBuffer->m_transientHeap->getCurrentViewHeap().allocate(1);
+ if (gpuHandleIndex == -1)
+ {
+ m_commandBuffer->m_transientHeap->allocateNewViewDescriptorHeap(
+ m_commandBuffer->m_renderer);
+ gpuHandleIndex = m_commandBuffer->m_transientHeap->getCurrentViewHeap().allocate(1);
+ m_commandBuffer->bindDescriptorHeaps();
+ }
+ this->m_commandBuffer->m_renderer->m_device->CopyDescriptorsSimple(
+ 1,
+ m_commandBuffer->m_transientHeap->getCurrentViewHeap().getCpuHandle(gpuHandleIndex),
+ viewImpl->m_descriptor.cpuHandle,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
+ if (flags & ClearResourceViewFlags::FloatClearValues)
+ {
+ m_commandBuffer->m_cmdList->ClearUnorderedAccessViewFloat(
+ m_commandBuffer->m_transientHeap->getCurrentViewHeap().getGpuHandle(
+ gpuHandleIndex),
+ viewImpl->m_descriptor.cpuHandle,
+ d3dResource,
+ clearValue->color.floatValues,
+ 0,
+ nullptr);
+ }
+ else
+ {
+ m_commandBuffer->m_cmdList->ClearUnorderedAccessViewUint(
+ m_commandBuffer->m_transientHeap->getCurrentViewHeap().getGpuHandle(
+ gpuHandleIndex),
+ viewImpl->m_descriptor.cpuHandle,
+ d3dResource,
+ clearValue->color.uintValues,
+ 0,
+ nullptr);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void ResourceCommandEncoderImpl::resolveResource(
+ ITextureResource* source,
+ ResourceState sourceState,
+ SubresourceRange sourceRange,
+ ITextureResource* dest,
+ ResourceState destState,
+ SubresourceRange destRange)
+{
+ auto srcTexture = static_cast<TextureResourceImpl*>(source);
+ auto srcDesc = srcTexture->getDesc();
+ auto dstTexture = static_cast<TextureResourceImpl*>(dest);
+ auto dstDesc = dstTexture->getDesc();
+
+ for (GfxIndex layer = 0; layer < sourceRange.layerCount; ++layer)
+ {
+ for (GfxIndex mip = 0; mip < sourceRange.mipLevelCount; ++mip)
+ {
+ auto srcSubresourceIndex = D3DUtil::getSubresourceIndex(
+ mip + sourceRange.mipLevel,
+ layer + sourceRange.baseArrayLayer,
+ 0,
+ srcDesc->numMipLevels,
+ srcDesc->arraySize);
+ auto dstSubresourceIndex = D3DUtil::getSubresourceIndex(
+ mip + destRange.mipLevel,
+ layer + destRange.baseArrayLayer,
+ 0,
+ dstDesc->numMipLevels,
+ dstDesc->arraySize);
+
+ DXGI_FORMAT format = D3DUtil::getMapFormat(srcDesc->format);
+
+ m_commandBuffer->m_cmdList->ResolveSubresource(
+ dstTexture->m_resource.getResource(),
+ dstSubresourceIndex,
+ srcTexture->m_resource.getResource(),
+ srcSubresourceIndex,
+ format);
+ }
+ }
+}
+
+void ResourceCommandEncoderImpl::resolveQuery(
+ IQueryPool* queryPool, GfxIndex index, GfxCount count, IBufferResource* buffer, Offset offset)
+{
+ auto queryBase = static_cast<QueryPoolBase*>(queryPool);
+ switch (queryBase->m_desc.type)
+ {
+ case QueryType::AccelerationStructureCompactedSize:
+ case QueryType::AccelerationStructureCurrentSize:
+ case QueryType::AccelerationStructureSerializedSize:
+ {
+ auto queryPoolImpl = static_cast<PlainBufferProxyQueryPoolImpl*>(queryPool);
+ auto bufferImpl = static_cast<BufferResourceImpl*>(buffer);
+ auto srcQueryBuffer = queryPoolImpl->m_bufferResource->m_resource.getResource();
+
+ D3D12_RESOURCE_BARRIER barrier = {};
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
+ barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
+ barrier.Transition.pResource = srcQueryBuffer;
+ m_commandBuffer->m_cmdList->ResourceBarrier(1, &barrier);
+
+ m_commandBuffer->m_cmdList->CopyBufferRegion(
+ bufferImpl->m_resource.getResource(),
+ (uint64_t)offset,
+ srcQueryBuffer,
+ index * sizeof(uint64_t),
+ count * sizeof(uint64_t));
+
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
+ barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
+ barrier.Transition.pResource = srcQueryBuffer;
+ m_commandBuffer->m_cmdList->ResourceBarrier(1, &barrier);
+ }
+ break;
+ default:
+ {
+ auto queryPoolImpl = static_cast<QueryPoolImpl*>(queryPool);
+ auto bufferImpl = static_cast<BufferResourceImpl*>(buffer);
+ m_commandBuffer->m_cmdList->ResolveQueryData(
+ queryPoolImpl->m_queryHeap.get(),
+ queryPoolImpl->m_queryType,
+ index,
+ count,
+ bufferImpl->m_resource.getResource(),
+ offset);
+ }
+ break;
+ }
+}
+
+void ResourceCommandEncoderImpl::copyTextureToBuffer(
+ IBufferResource* dst,
+ Offset dstOffset,
+ Size dstSize,
+ Size dstRowStride,
+ ITextureResource* src,
+ ResourceState srcState,
+ SubresourceRange srcSubresource,
+ ITextureResource::Offset3D srcOffset,
+ ITextureResource::Extents extent)
+{
+ assert(srcSubresource.mipLevelCount <= 1);
+
+ auto srcTexture = static_cast<TextureResourceImpl*>(src);
+ auto dstBuffer = static_cast<BufferResourceImpl*>(dst);
+ auto baseSubresourceIndex = D3DUtil::getSubresourceIndex(
+ srcSubresource.mipLevel,
+ srcSubresource.baseArrayLayer,
+ 0,
+ srcTexture->getDesc()->numMipLevels,
+ srcTexture->getDesc()->arraySize);
+ auto textureSize = srcTexture->getDesc()->size;
+ FormatInfo formatInfo = {};
+ gfxGetFormatInfo(srcTexture->getDesc()->format, &formatInfo);
+ if (srcSubresource.mipLevelCount == 0)
+ srcSubresource.mipLevelCount = srcTexture->getDesc()->numMipLevels;
+ if (srcSubresource.layerCount == 0)
+ srcSubresource.layerCount = srcTexture->getDesc()->arraySize;
+
+ for (GfxCount layer = 0; layer < srcSubresource.layerCount; layer++)
+ {
+ // Get the footprint
+ D3D12_RESOURCE_DESC texDesc = srcTexture->m_resource.getResource()->GetDesc();
+
+ D3D12_TEXTURE_COPY_LOCATION dstRegion = {};
+ dstRegion.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ dstRegion.pResource = dstBuffer->m_resource.getResource();
+ D3D12_PLACED_SUBRESOURCE_FOOTPRINT& footprint = dstRegion.PlacedFootprint;
+
+ D3D12_TEXTURE_COPY_LOCATION srcRegion = {};
+ srcRegion.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ srcRegion.SubresourceIndex = D3DUtil::getSubresourceIndex(
+ srcSubresource.mipLevel,
+ layer + srcSubresource.baseArrayLayer,
+ 0,
+ srcTexture->getDesc()->numMipLevels,
+ srcTexture->getDesc()->arraySize);
+ srcRegion.pResource = srcTexture->m_resource.getResource();
+
+ footprint.Offset = dstOffset;
+ footprint.Footprint.Format = texDesc.Format;
+ uint32_t mipLevel = srcSubresource.mipLevel;
+ if (extent.width != 0xFFFFFFFF)
+ {
+ footprint.Footprint.Width = extent.width;
+ }
+ else
+ {
+ footprint.Footprint.Width = Math::Max(1, (textureSize.width >> mipLevel)) - srcOffset.x;
+ }
+ if (extent.height != 0xFFFFFFFF)
+ {
+ footprint.Footprint.Height = extent.height;
+ }
+ else
+ {
+ footprint.Footprint.Height =
+ Math::Max(1, (textureSize.height >> mipLevel)) - srcOffset.y;
+ }
+ if (extent.depth != 0xFFFFFFFF)
+ {
+ footprint.Footprint.Depth = extent.depth;
+ }
+ else
+ {
+ footprint.Footprint.Depth = Math::Max(1, (textureSize.depth >> mipLevel)) - srcOffset.z;
+ }
+
+ assert(dstRowStride % D3D12_TEXTURE_DATA_PITCH_ALIGNMENT == 0);
+ footprint.Footprint.RowPitch = (UINT)dstRowStride;
+
+ auto bufferSize =
+ footprint.Footprint.RowPitch * footprint.Footprint.Height * footprint.Footprint.Depth;
+
+ D3D12_BOX srcBox = {};
+ srcBox.left = srcOffset.x;
+ srcBox.top = srcOffset.y;
+ srcBox.front = srcOffset.z;
+ srcBox.right = srcOffset.x + extent.width;
+ srcBox.bottom = srcOffset.y + extent.height;
+ srcBox.back = srcOffset.z + extent.depth;
+ m_commandBuffer->m_cmdList->CopyTextureRegion(&dstRegion, 0, 0, 0, &srcRegion, &srcBox);
+ }
+}
+
+void ResourceCommandEncoderImpl::textureSubresourceBarrier(
+ ITextureResource* texture,
+ SubresourceRange subresourceRange,
+ ResourceState src,
+ ResourceState dst)
+{
+ auto textureImpl = static_cast<TextureResourceImpl*>(texture);
+
+ ShortList<D3D12_RESOURCE_BARRIER> barriers;
+ D3D12_RESOURCE_BARRIER barrier;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ if (src == dst && src == ResourceState::UnorderedAccess)
+ {
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
+ barrier.UAV.pResource = textureImpl->m_resource.getResource();
+ barriers.add(barrier);
+ }
+ else
+ {
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Transition.StateBefore = D3DUtil::getResourceState(src);
+ barrier.Transition.StateAfter = D3DUtil::getResourceState(dst);
+ if (barrier.Transition.StateBefore == barrier.Transition.StateAfter)
+ return;
+ barrier.Transition.pResource = textureImpl->m_resource.getResource();
+ auto d3dFormat = D3DUtil::getMapFormat(textureImpl->getDesc()->format);
+ auto aspectMask = (int32_t)subresourceRange.aspectMask;
+ if (subresourceRange.aspectMask == TextureAspect::Default)
+ aspectMask = (int32_t)TextureAspect::Color;
+ while (aspectMask)
+ {
+ auto aspect = Math::getLowestBit((int32_t)aspectMask);
+ aspectMask &= ~aspect;
+ auto planeIndex = D3DUtil::getPlaneSlice(d3dFormat, (TextureAspect)aspect);
+ for (GfxCount layer = 0; layer < subresourceRange.layerCount; layer++)
+ {
+ for (GfxCount mip = 0; mip < subresourceRange.mipLevelCount; mip++)
+ {
+ barrier.Transition.Subresource = D3DUtil::getSubresourceIndex(
+ mip + subresourceRange.mipLevel,
+ layer + subresourceRange.baseArrayLayer,
+ planeIndex,
+ textureImpl->getDesc()->numMipLevels,
+ textureImpl->getDesc()->arraySize);
+ barriers.add(barrier);
+ }
+ }
+ }
+ }
+ m_commandBuffer->m_cmdList->ResourceBarrier(
+ (UINT)barriers.getCount(), barriers.getArrayView().getBuffer());
+}
+
+void ResourceCommandEncoderImpl::beginDebugEvent(const char* name, float rgbColor[3])
+{
+ auto beginEvent = m_commandBuffer->m_renderer->m_BeginEventOnCommandList;
+ if (beginEvent)
+ {
+ beginEvent(
+ m_commandBuffer->m_cmdList,
+ 0xff000000 | (uint8_t(rgbColor[0] * 255.0f) << 16) |
+ (uint8_t(rgbColor[1] * 255.0f) << 8) | uint8_t(rgbColor[2] * 255.0f),
+ name);
+ }
+}
+
+void ResourceCommandEncoderImpl::endDebugEvent()
+{
+ auto endEvent = m_commandBuffer->m_renderer->m_EndEventOnCommandList;
+ if (endEvent)
+ {
+ endEvent(m_commandBuffer->m_cmdList);
+ }
+}
+
+void ResourceCommandEncoderImpl::copyBuffer(
+ IBufferResource* dst, Offset dstOffset, IBufferResource* src, Offset srcOffset, Size size)
+{
+ auto dstBuffer = static_cast<BufferResourceImpl*>(dst);
+ auto srcBuffer = static_cast<BufferResourceImpl*>(src);
+
+ m_commandBuffer->m_cmdList->CopyBufferRegion(
+ dstBuffer->m_resource.getResource(),
+ dstOffset,
+ srcBuffer->m_resource.getResource(),
+ srcOffset,
+ size);
+}
+
+void ResourceCommandEncoderImpl::uploadBufferData(
+ IBufferResource* dst, Offset offset, Size size, void* data)
+{
+ uploadBufferDataImpl(
+ m_commandBuffer->m_renderer->m_device,
+ m_commandBuffer->m_cmdList,
+ m_commandBuffer->m_transientHeap,
+ static_cast<BufferResourceImpl*>(dst),
+ offset,
+ size,
+ data);
+}
+
+void ResourceCommandEncoderImpl::textureBarrier(
+ GfxCount count, ITextureResource* const* textures, ResourceState src, ResourceState dst)
+{
+ ShortList<D3D12_RESOURCE_BARRIER> barriers;
+
+ for (GfxIndex i = 0; i < count; i++)
+ {
+ auto textureImpl = static_cast<TextureResourceImpl*>(textures[i]);
+ auto d3dFormat = D3DUtil::getMapFormat(textureImpl->getDesc()->format);
+ auto textureDesc = textureImpl->getDesc();
+ D3D12_RESOURCE_BARRIER barrier;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ if (src == dst && src == ResourceState::UnorderedAccess)
+ {
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
+ barrier.UAV.pResource = textureImpl->m_resource.getResource();
+ }
+ else
+ {
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Transition.StateBefore = D3DUtil::getResourceState(src);
+ barrier.Transition.StateAfter = D3DUtil::getResourceState(dst);
+ if (barrier.Transition.StateBefore == barrier.Transition.StateAfter)
+ continue;
+ barrier.Transition.pResource = textureImpl->m_resource.getResource();
+ auto planeCount =
+ D3DUtil::getPlaneSliceCount(D3DUtil::getMapFormat(textureImpl->getDesc()->format));
+ auto arraySize = textureDesc->arraySize;
+ if (arraySize == 0)
+ arraySize = 1;
+ barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ }
+ barriers.add(barrier);
+ }
+ if (barriers.getCount())
+ {
+ m_commandBuffer->m_cmdList->ResourceBarrier(
+ (UINT)barriers.getCount(), barriers.getArrayView().getBuffer());
+ }
+}
+
+void RenderCommandEncoderImpl::init(
+ DeviceImpl* renderer,
+ TransientResourceHeapImpl* transientHeap,
+ CommandBufferImpl* cmdBuffer,
+ RenderPassLayoutImpl* renderPass,
+ FramebufferImpl* framebuffer)
+{
+ PipelineCommandEncoder::init(cmdBuffer);
+ m_preCmdList = nullptr;
+ m_renderPass = renderPass;
+ m_framebuffer = framebuffer;
+ m_transientHeap = transientHeap;
+ m_boundVertexBuffers.clear();
+ m_boundIndexBuffer = nullptr;
+ m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+ m_primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ m_boundIndexFormat = DXGI_FORMAT_UNKNOWN;
+ m_boundIndexOffset = 0;
+ m_currentPipeline = nullptr;
+
+ // Set render target states.
+ if (!framebuffer)
+ {
+ return;
+ }
+ m_d3dCmdList->OMSetRenderTargets(
+ (UINT)framebuffer->renderTargetViews.getCount(),
+ framebuffer->renderTargetDescriptors.getArrayView().getBuffer(),
+ FALSE,
+ framebuffer->depthStencilView ? &framebuffer->depthStencilDescriptor : nullptr);
+
+ // Issue clear commands based on render pass set up.
+ for (Index i = 0; i < framebuffer->renderTargetViews.getCount(); i++)
+ {
+ if (i >= renderPass->m_renderTargetAccesses.getCount())
+ continue;
+
+ auto& access = renderPass->m_renderTargetAccesses[i];
+
+ // Transit resource states.
+ {
+ D3D12BarrierSubmitter submitter(m_d3dCmdList);
+ auto resourceViewImpl = framebuffer->renderTargetViews[i].Ptr();
+ if (resourceViewImpl)
+ {
+ auto textureResource =
+ static_cast<TextureResourceImpl*>(resourceViewImpl->m_resource.Ptr());
+ if (textureResource)
+ {
+ D3D12_RESOURCE_STATES initialState;
+ if (access.initialState == ResourceState::Undefined)
+ {
+ initialState = textureResource->m_defaultState;
+ }
+ else
+ {
+ initialState = D3DUtil::getResourceState(access.initialState);
+ }
+ textureResource->m_resource.transition(
+ initialState, D3D12_RESOURCE_STATE_RENDER_TARGET, submitter);
+ }
+ }
+ }
+ // Clear.
+ if (access.loadOp == IRenderPassLayout::TargetLoadOp::Clear)
+ {
+ m_d3dCmdList->ClearRenderTargetView(
+ framebuffer->renderTargetDescriptors[i],
+ framebuffer->renderTargetClearValues[i].values,
+ 0,
+ nullptr);
+ }
+ }
+
+ if (renderPass->m_hasDepthStencil)
+ {
+ // Transit resource states.
+ {
+ D3D12BarrierSubmitter submitter(m_d3dCmdList);
+ auto resourceViewImpl = framebuffer->depthStencilView.Ptr();
+ auto textureResource =
+ static_cast<TextureResourceImpl*>(resourceViewImpl->m_resource.Ptr());
+ D3D12_RESOURCE_STATES initialState;
+ if (renderPass->m_depthStencilAccess.initialState == ResourceState::Undefined)
+ {
+ initialState = textureResource->m_defaultState;
+ }
+ else
+ {
+ initialState =
+ D3DUtil::getResourceState(renderPass->m_depthStencilAccess.initialState);
+ }
+ textureResource->m_resource.transition(
+ initialState, D3D12_RESOURCE_STATE_DEPTH_WRITE, submitter);
+ }
+ // Clear.
+ uint32_t clearFlags = 0;
+ if (renderPass->m_depthStencilAccess.loadOp == IRenderPassLayout::TargetLoadOp::Clear)
+ {
+ clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
+ }
+ if (renderPass->m_depthStencilAccess.stencilLoadOp ==
+ IRenderPassLayout::TargetLoadOp::Clear)
+ {
+ clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
+ }
+ if (clearFlags)
+ {
+ m_d3dCmdList->ClearDepthStencilView(
+ framebuffer->depthStencilDescriptor,
+ (D3D12_CLEAR_FLAGS)clearFlags,
+ framebuffer->depthStencilClearValue.depth,
+ framebuffer->depthStencilClearValue.stencil,
+ 0,
+ nullptr);
+ }
+ }
+}
+
+Result RenderCommandEncoderImpl::bindPipeline(IPipelineState* state, IShaderObject** outRootObject)
+{
+ return bindPipelineImpl(state, outRootObject);
+}
+
+Result RenderCommandEncoderImpl::bindPipelineWithRootObject(
+ IPipelineState* state, IShaderObject* rootObject)
+{
+ return bindPipelineWithRootObjectImpl(state, rootObject);
+}
+
+void RenderCommandEncoderImpl::setViewports(GfxCount count, const Viewport* viewports)
+{
+ static const int kMaxViewports = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
+ assert(count <= kMaxViewports && count <= kMaxRTVCount);
+ for (GfxIndex ii = 0; ii < count; ++ii)
+ {
+ auto& inViewport = viewports[ii];
+ auto& dxViewport = m_viewports[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_d3dCmdList->RSSetViewports(UINT(count), m_viewports);
+}
+
+void RenderCommandEncoderImpl::setScissorRects(GfxCount count, const ScissorRect* rects)
+{
+ static const int kMaxScissorRects = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
+ assert(count <= kMaxScissorRects && count <= kMaxRTVCount);
+
+ for (GfxIndex ii = 0; ii < count; ++ii)
+ {
+ auto& inRect = rects[ii];
+ auto& dxRect = m_scissorRects[ii];
+
+ dxRect.left = LONG(inRect.minX);
+ dxRect.top = LONG(inRect.minY);
+ dxRect.right = LONG(inRect.maxX);
+ dxRect.bottom = LONG(inRect.maxY);
+ }
+
+ m_d3dCmdList->RSSetScissorRects(UINT(count), m_scissorRects);
+}
+
+void RenderCommandEncoderImpl::setPrimitiveTopology(PrimitiveTopology topology)
+{
+ m_primitiveTopologyType = D3DUtil::getPrimitiveType(topology);
+ m_primitiveTopology = D3DUtil::getPrimitiveTopology(topology);
+}
+
+void RenderCommandEncoderImpl::setVertexBuffers(
+ GfxIndex startSlot,
+ GfxCount slotCount,
+ IBufferResource* const* buffers,
+ const Offset* offsets)
+{
+ {
+ const Index num = startSlot + slotCount;
+ if (num > m_boundVertexBuffers.getCount())
+ {
+ m_boundVertexBuffers.setCount(num);
+ }
+ }
+
+ for (GfxIndex i = 0; i < slotCount; i++)
+ {
+ BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(buffers[i]);
+
+ BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i];
+ boundBuffer.m_buffer = buffer;
+ boundBuffer.m_offset = int(offsets[i]);
+ }
+}
+
+void RenderCommandEncoderImpl::setIndexBuffer(
+ IBufferResource* buffer, Format indexFormat, Offset offset)
+{
+ m_boundIndexBuffer = (BufferResourceImpl*)buffer;
+ m_boundIndexFormat = D3DUtil::getMapFormat(indexFormat);
+ m_boundIndexOffset = (UINT)offset;
+}
+
+void RenderCommandEncoderImpl::prepareDraw()
+{
+ auto pipelineState = m_currentPipeline.Ptr();
+ if (!pipelineState || (pipelineState->desc.type != PipelineType::Graphics))
+ {
+ assert(!"No graphics pipeline state set");
+ return;
+ }
+
+ // Submit - setting for graphics
+ {
+ GraphicsSubmitter submitter(m_d3dCmdList);
+ RefPtr<PipelineStateBase> newPipeline;
+ if (SLANG_FAILED(_bindRenderState(&submitter, newPipeline)))
+ {
+ assert(!"Failed to bind render state");
+ }
+ }
+
+ m_d3dCmdList->IASetPrimitiveTopology(m_primitiveTopology);
+
+ // Set up vertex buffer views
+ {
+ auto inputLayout = (InputLayoutImpl*)pipelineState->inputLayout.Ptr();
+ if (inputLayout)
+ {
+ 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 = inputLayout->m_vertexStreamStrides[i];
+ }
+ }
+ m_d3dCmdList->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;
+
+ m_d3dCmdList->IASetIndexBuffer(&indexBufferView);
+ }
+}
+
+void RenderCommandEncoderImpl::draw(GfxCount vertexCount, GfxIndex startVertex)
+{
+ prepareDraw();
+ m_d3dCmdList->DrawInstanced((uint32_t)vertexCount, 1, (uint32_t)startVertex, 0);
+}
+
+void RenderCommandEncoderImpl::drawIndexed(
+ GfxCount indexCount, GfxIndex startIndex, GfxIndex baseVertex)
+{
+ prepareDraw();
+ m_d3dCmdList->DrawIndexedInstanced((uint32_t)indexCount, 1, (uint32_t)startIndex, (uint32_t)baseVertex, 0);
+}
+
+void RenderCommandEncoderImpl::endEncoding()
+{
+ PipelineCommandEncoder::endEncodingImpl();
+ if (!m_framebuffer)
+ return;
+ // Issue clear commands based on render pass set up.
+ for (Index i = 0; i < m_renderPass->m_renderTargetAccesses.getCount(); i++)
+ {
+ auto& access = m_renderPass->m_renderTargetAccesses[i];
+
+ // Transit resource states.
+ {
+ D3D12BarrierSubmitter submitter(m_d3dCmdList);
+ auto resourceViewImpl = m_framebuffer->renderTargetViews[i].Ptr();
+ if (!resourceViewImpl)
+ continue;
+ auto textureResource =
+ static_cast<TextureResourceImpl*>(resourceViewImpl->m_resource.Ptr());
+ if (textureResource)
+ {
+ textureResource->m_resource.transition(
+ D3D12_RESOURCE_STATE_RENDER_TARGET,
+ D3DUtil::getResourceState(access.finalState),
+ submitter);
+ }
+ }
+ }
+
+ if (m_renderPass->m_hasDepthStencil)
+ {
+ // Transit resource states.
+ D3D12BarrierSubmitter submitter(m_d3dCmdList);
+ auto resourceViewImpl = m_framebuffer->depthStencilView.Ptr();
+ auto textureResource =
+ static_cast<TextureResourceImpl*>(resourceViewImpl->m_resource.Ptr());
+ textureResource->m_resource.transition(
+ D3D12_RESOURCE_STATE_DEPTH_WRITE,
+ D3DUtil::getResourceState(m_renderPass->m_depthStencilAccess.finalState),
+ submitter);
+ }
+ m_framebuffer = nullptr;
+}
+
+void RenderCommandEncoderImpl::setStencilReference(uint32_t referenceValue)
+{
+ m_d3dCmdList->OMSetStencilRef((UINT)referenceValue);
+}
+
+void RenderCommandEncoderImpl::drawIndirect(
+ GfxCount maxDrawCount,
+ IBufferResource* argBuffer,
+ Offset argOffset,
+ IBufferResource* countBuffer,
+ Offset countOffset)
+{
+ prepareDraw();
+
+ auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer);
+ auto countBufferImpl = static_cast<BufferResourceImpl*>(countBuffer);
+
+ m_d3dCmdList->ExecuteIndirect(
+ m_renderer->drawIndirectCmdSignature,
+ (uint32_t)maxDrawCount,
+ argBufferImpl->m_resource,
+ (uint64_t)argOffset,
+ countBufferImpl ? countBufferImpl->m_resource.getResource() : nullptr,
+ (uint64_t)countOffset);
+}
+
+void RenderCommandEncoderImpl::drawIndexedIndirect(
+ GfxCount maxDrawCount,
+ IBufferResource* argBuffer,
+ Offset argOffset,
+ IBufferResource* countBuffer,
+ Offset countOffset)
+{
+ prepareDraw();
+
+ auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer);
+ auto countBufferImpl = static_cast<BufferResourceImpl*>(countBuffer);
+
+ m_d3dCmdList->ExecuteIndirect(
+ m_renderer->drawIndexedIndirectCmdSignature,
+ (uint32_t)maxDrawCount,
+ argBufferImpl->m_resource,
+ (uint64_t)argOffset,
+ countBufferImpl ? countBufferImpl->m_resource.getResource() : nullptr,
+ (uint64_t)countOffset);
+}
+
+Result RenderCommandEncoderImpl::setSamplePositions(
+ GfxCount samplesPerPixel, GfxCount pixelCount, const SamplePosition* samplePositions)
+{
+ if (m_commandBuffer->m_cmdList1)
+ {
+ m_commandBuffer->m_cmdList1->SetSamplePositions(
+ (uint32_t)samplesPerPixel, (uint32_t)pixelCount, (D3D12_SAMPLE_POSITION*)samplePositions);
+ return SLANG_OK;
+ }
+ return SLANG_E_NOT_AVAILABLE;
+}
+
+void RenderCommandEncoderImpl::drawInstanced(
+ GfxCount vertexCount,
+ GfxCount instanceCount,
+ GfxIndex startVertex,
+ GfxIndex startInstanceLocation)
+{
+ prepareDraw();
+ m_d3dCmdList->DrawInstanced(
+ (uint32_t)vertexCount,
+ (uint32_t)instanceCount,
+ (uint32_t)startVertex,
+ (uint32_t)startInstanceLocation);
+}
+
+void RenderCommandEncoderImpl::drawIndexedInstanced(
+ GfxCount indexCount,
+ GfxCount instanceCount,
+ GfxIndex startIndexLocation,
+ GfxIndex baseVertexLocation,
+ GfxIndex startInstanceLocation)
+{
+ prepareDraw();
+ m_d3dCmdList->DrawIndexedInstanced(
+ (uint32_t)indexCount,
+ (uint32_t)instanceCount,
+ (uint32_t)startIndexLocation,
+ baseVertexLocation,
+ (uint32_t)startInstanceLocation);
+}
+
+void ComputeCommandEncoderImpl::endEncoding() { PipelineCommandEncoder::endEncodingImpl(); }
+
+void ComputeCommandEncoderImpl::init(
+ DeviceImpl* renderer, TransientResourceHeapImpl* transientHeap, CommandBufferImpl* cmdBuffer)
+{
+ PipelineCommandEncoder::init(cmdBuffer);
+ m_preCmdList = nullptr;
+ m_transientHeap = transientHeap;
+ m_currentPipeline = nullptr;
+}
+
+Result ComputeCommandEncoderImpl::bindPipeline(IPipelineState* state, IShaderObject** outRootObject)
+{
+ return bindPipelineImpl(state, outRootObject);
+}
+
+Result ComputeCommandEncoderImpl::bindPipelineWithRootObject(
+ IPipelineState* state, IShaderObject* rootObject)
+{
+ return bindPipelineWithRootObjectImpl(state, rootObject);
+}
+
+void ComputeCommandEncoderImpl::dispatchCompute(int x, int y, int z)
+{
+ // Submit binding for compute
+ {
+ ComputeSubmitter submitter(m_d3dCmdList);
+ RefPtr<PipelineStateBase> newPipeline;
+ if (SLANG_FAILED(_bindRenderState(&submitter, newPipeline)))
+ {
+ assert(!"Failed to bind render state");
+ }
+ }
+ m_d3dCmdList->Dispatch(x, y, z);
+}
+
+void ComputeCommandEncoderImpl::dispatchComputeIndirect(IBufferResource* argBuffer, Offset offset)
+{
+ // Submit binding for compute
+ {
+ ComputeSubmitter submitter(m_d3dCmdList);
+ RefPtr<PipelineStateBase> newPipeline;
+ if (SLANG_FAILED(_bindRenderState(&submitter, newPipeline)))
+ {
+ assert(!"Failed to bind render state");
+ }
+ }
+ auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer);
+
+ m_d3dCmdList->ExecuteIndirect(
+ m_renderer->dispatchIndirectCmdSignature, 1, argBufferImpl->m_resource, (uint64_t)offset, nullptr, 0);
+}
+
+#if SLANG_GFX_HAS_DXR_SUPPORT
+
+void RayTracingCommandEncoderImpl::buildAccelerationStructure(
+ const IAccelerationStructure::BuildDesc& desc,
+ GfxCount propertyQueryCount,
+ AccelerationStructureQueryDesc* queryDescs)
+{
+ if (!m_commandBuffer->m_cmdList4)
+ {
+ getDebugCallback()->handleMessage(
+ DebugMessageType::Error,
+ DebugMessageSource::Layer,
+ "Ray-tracing is not supported on current system.");
+ return;
+ }
+ AccelerationStructureImpl* destASImpl = nullptr;
+ if (desc.dest)
+ destASImpl = static_cast<AccelerationStructureImpl*>(desc.dest);
+ AccelerationStructureImpl* srcASImpl = nullptr;
+ if (desc.source)
+ srcASImpl = static_cast<AccelerationStructureImpl*>(desc.source);
+
+ D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC buildDesc = {};
+ buildDesc.DestAccelerationStructureData = destASImpl->getDeviceAddress();
+ buildDesc.SourceAccelerationStructureData = srcASImpl ? srcASImpl->getDeviceAddress() : 0;
+ buildDesc.ScratchAccelerationStructureData = desc.scratchData;
+ D3DAccelerationStructureInputsBuilder builder;
+ builder.build(desc.inputs, getDebugCallback());
+ buildDesc.Inputs = builder.desc;
+
+ List<D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC> postBuildInfoDescs;
+ translatePostBuildInfoDescs(propertyQueryCount, queryDescs, postBuildInfoDescs);
+ m_commandBuffer->m_cmdList4->BuildRaytracingAccelerationStructure(
+ &buildDesc, (UINT)propertyQueryCount, postBuildInfoDescs.getBuffer());
+}
+
+void RayTracingCommandEncoderImpl::copyAccelerationStructure(
+ IAccelerationStructure* dest, IAccelerationStructure* src, AccelerationStructureCopyMode mode)
+{
+ auto destASImpl = static_cast<AccelerationStructureImpl*>(dest);
+ auto srcASImpl = static_cast<AccelerationStructureImpl*>(src);
+ D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE copyMode;
+ switch (mode)
+ {
+ case AccelerationStructureCopyMode::Clone:
+ copyMode = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_CLONE;
+ break;
+ case AccelerationStructureCopyMode::Compact:
+ copyMode = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_COMPACT;
+ break;
+ default:
+ getDebugCallback()->handleMessage(
+ DebugMessageType::Error,
+ DebugMessageSource::Layer,
+ "Unsupported AccelerationStructureCopyMode.");
+ return;
+ }
+ m_commandBuffer->m_cmdList4->CopyRaytracingAccelerationStructure(
+ destASImpl->getDeviceAddress(), srcASImpl->getDeviceAddress(), copyMode);
+}
+
+void RayTracingCommandEncoderImpl::queryAccelerationStructureProperties(
+ GfxCount accelerationStructureCount,
+ IAccelerationStructure* const* accelerationStructures,
+ GfxCount queryCount,
+ AccelerationStructureQueryDesc* queryDescs)
+{
+ List<D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC> postBuildInfoDescs;
+ List<DeviceAddress> asAddresses;
+ asAddresses.setCount(accelerationStructureCount);
+ for (GfxIndex i = 0; i < accelerationStructureCount; i++)
+ asAddresses[i] = accelerationStructures[i]->getDeviceAddress();
+ translatePostBuildInfoDescs(queryCount, queryDescs, postBuildInfoDescs);
+ m_commandBuffer->m_cmdList4->EmitRaytracingAccelerationStructurePostbuildInfo(
+ postBuildInfoDescs.getBuffer(), (UINT)accelerationStructureCount, asAddresses.getBuffer());
+}
+
+void RayTracingCommandEncoderImpl::serializeAccelerationStructure(
+ DeviceAddress dest, IAccelerationStructure* src)
+{
+ auto srcASImpl = static_cast<AccelerationStructureImpl*>(src);
+ m_commandBuffer->m_cmdList4->CopyRaytracingAccelerationStructure(
+ dest,
+ srcASImpl->getDeviceAddress(),
+ D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_SERIALIZE);
+}
+
+void RayTracingCommandEncoderImpl::deserializeAccelerationStructure(
+ IAccelerationStructure* dest, DeviceAddress source)
+{
+ auto destASImpl = static_cast<AccelerationStructureImpl*>(dest);
+ m_commandBuffer->m_cmdList4->CopyRaytracingAccelerationStructure(
+ dest->getDeviceAddress(),
+ source,
+ D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_DESERIALIZE);
+}
+
+void RayTracingCommandEncoderImpl::bindPipeline(
+ IPipelineState* state, IShaderObject** outRootObject)
+{
+ bindPipelineImpl(state, outRootObject);
+}
+
+void RayTracingCommandEncoderImpl::dispatchRays(
+ GfxIndex rayGenShaderIndex,
+ IShaderTable* shaderTable,
+ GfxCount width,
+ GfxCount height,
+ GfxCount depth)
+{
+ RefPtr<PipelineStateBase> newPipeline;
+ PipelineStateBase* pipeline = m_currentPipeline.Ptr();
+ {
+ struct RayTracingSubmitter : public ComputeSubmitter
+ {
+ ID3D12GraphicsCommandList4* m_cmdList4;
+ RayTracingSubmitter(ID3D12GraphicsCommandList4* cmdList4)
+ : ComputeSubmitter(cmdList4)
+ , m_cmdList4(cmdList4)
+ {}
+ virtual void setPipelineState(PipelineStateBase* pipeline) override
+ {
+ auto pipelineImpl = static_cast<RayTracingPipelineStateImpl*>(pipeline);
+ m_cmdList4->SetPipelineState1(pipelineImpl->m_stateObject.get());
+ }
+ };
+ RayTracingSubmitter submitter(m_commandBuffer->m_cmdList4);
+ if (SLANG_FAILED(_bindRenderState(&submitter, newPipeline)))
+ {
+ assert(!"Failed to bind render state");
+ }
+ if (newPipeline)
+ pipeline = newPipeline.Ptr();
+ }
+ auto pipelineImpl = static_cast<RayTracingPipelineStateImpl*>(pipeline);
+
+ auto shaderTableImpl = static_cast<ShaderTableImpl*>(shaderTable);
+
+ auto shaderTableBuffer =
+ shaderTableImpl->getOrCreateBuffer(pipelineImpl, m_transientHeap, static_cast<ResourceCommandEncoderImpl*>(this));
+ auto shaderTableAddr = shaderTableBuffer->getDeviceAddress();
+
+ D3D12_DISPATCH_RAYS_DESC dispatchDesc = {};
+
+ dispatchDesc.RayGenerationShaderRecord.StartAddress =
+ shaderTableAddr + shaderTableImpl->m_rayGenTableOffset +
+ rayGenShaderIndex * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+ dispatchDesc.RayGenerationShaderRecord.SizeInBytes = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+
+ dispatchDesc.MissShaderTable.StartAddress =
+ shaderTableAddr + shaderTableImpl->m_missTableOffset;
+ dispatchDesc.MissShaderTable.SizeInBytes =
+ shaderTableImpl->m_missShaderCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+ dispatchDesc.MissShaderTable.StrideInBytes = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+
+ dispatchDesc.HitGroupTable.StartAddress =
+ shaderTableAddr + shaderTableImpl->m_hitGroupTableOffset;
+ dispatchDesc.HitGroupTable.SizeInBytes =
+ shaderTableImpl->m_hitGroupCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+ dispatchDesc.HitGroupTable.StrideInBytes = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+
+ dispatchDesc.Width = (UINT)width;
+ dispatchDesc.Height = (UINT)height;
+ dispatchDesc.Depth = (UINT)depth;
+ m_commandBuffer->m_cmdList4->DispatchRays(&dispatchDesc);
+}
+
+#endif
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-command-encoder.h b/tools/gfx/d3d12/d3d12-command-encoder.h
new file mode 100644
index 000000000..bf2f7710b
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-command-encoder.h
@@ -0,0 +1,305 @@
+// d3d12-command-encoder.h
+#pragma once
+
+#include "d3d12-base.h"
+#include "d3d12-buffer.h"
+#include "d3d12-framebuffer.h"
+#include "d3d12-render-pass.h"
+#include "d3d12-submitter.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+static const Int kMaxRTVCount = 8;
+
+class PipelineCommandEncoder
+{
+public:
+ bool m_isOpen = false;
+ bool m_bindingDirty = true;
+ CommandBufferImpl* m_commandBuffer;
+ TransientResourceHeapImpl* m_transientHeap;
+ DeviceImpl* m_renderer;
+ ID3D12Device* m_device;
+ ID3D12GraphicsCommandList* m_d3dCmdList;
+ ID3D12GraphicsCommandList* m_preCmdList = nullptr;
+
+ RefPtr<PipelineStateBase> m_currentPipeline;
+
+ static int getBindPointIndex(PipelineType type);
+
+ void init(CommandBufferImpl* commandBuffer);
+
+ void endEncodingImpl() { m_isOpen = false; }
+
+ Result bindPipelineImpl(IPipelineState* pipelineState, IShaderObject** outRootObject);
+
+ Result bindPipelineWithRootObjectImpl(IPipelineState* pipelineState, IShaderObject* rootObject);
+
+ /// Specializes the pipeline according to current root-object argument values,
+ /// applys the root object bindings and binds the pipeline state.
+ /// The newly specialized pipeline is held alive by the pipeline cache so users of
+ /// `newPipeline` do not need to maintain its lifespan.
+ Result _bindRenderState(Submitter* submitter, RefPtr<PipelineStateBase>& newPipeline);
+};
+
+class ResourceCommandEncoderImpl
+ : public IResourceCommandEncoder
+ , public PipelineCommandEncoder
+{
+public:
+ virtual SLANG_NO_THROW void SLANG_MCALL copyBuffer(
+ IBufferResource* dst,
+ Offset dstOffset,
+ IBufferResource* src,
+ Offset srcOffset,
+ Size size) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ uploadBufferData(IBufferResource* dst, Offset offset, Size size, void* data) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL textureBarrier(
+ GfxCount count,
+ ITextureResource* const* textures,
+ ResourceState src,
+ ResourceState dst) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL bufferBarrier(
+ GfxCount count,
+ IBufferResource* const* buffers,
+ ResourceState src,
+ ResourceState dst) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() {}
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ writeTimestamp(IQueryPool* pool, GfxIndex index) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL copyTexture(
+ ITextureResource* dst,
+ ResourceState dstState,
+ SubresourceRange dstSubresource,
+ ITextureResource::Offset3D dstOffset,
+ ITextureResource* src,
+ ResourceState srcState,
+ SubresourceRange srcSubresource,
+ ITextureResource::Offset3D srcOffset,
+ ITextureResource::Extents extent) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL uploadTextureData(
+ ITextureResource* dst,
+ SubresourceRange subResourceRange,
+ ITextureResource::Offset3D offset,
+ ITextureResource::Extents extent,
+ ITextureResource::SubresourceData* subResourceData,
+ GfxCount subResourceDataCount) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL clearResourceView(
+ IResourceView* view, ClearValue* clearValue, ClearResourceViewFlags::Enum flags) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL resolveResource(
+ ITextureResource* source,
+ ResourceState sourceState,
+ SubresourceRange sourceRange,
+ ITextureResource* dest,
+ ResourceState destState,
+ SubresourceRange destRange) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL resolveQuery(
+ IQueryPool* queryPool,
+ GfxIndex index,
+ GfxCount count,
+ IBufferResource* buffer,
+ Offset offset) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL copyTextureToBuffer(
+ IBufferResource* dst,
+ Offset dstOffset,
+ Size dstSize,
+ Size dstRowStride,
+ ITextureResource* src,
+ ResourceState srcState,
+ SubresourceRange srcSubresource,
+ ITextureResource::Offset3D srcOffset,
+ ITextureResource::Extents extent) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL textureSubresourceBarrier(
+ ITextureResource* texture,
+ SubresourceRange subresourceRange,
+ ResourceState src,
+ ResourceState dst) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ beginDebugEvent(const char* name, float rgbColor[3]) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL endDebugEvent() override;
+};
+
+class ComputeCommandEncoderImpl
+ : public IComputeCommandEncoder
+ , public ResourceCommandEncoderImpl
+{
+public:
+ SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoderImpl)
+public:
+ virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() override;
+ void init(
+ DeviceImpl* renderer,
+ TransientResourceHeapImpl* transientHeap,
+ CommandBufferImpl* cmdBuffer);
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ bindPipeline(IPipelineState* state, IShaderObject** outRootObject) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ bindPipelineWithRootObject(IPipelineState* state, IShaderObject* rootObject) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL dispatchCompute(int x, int y, int z) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ dispatchComputeIndirect(IBufferResource* argBuffer, Offset offset) override;
+};
+
+struct BoundVertexBuffer
+{
+ RefPtr<BufferResourceImpl> m_buffer;
+ int m_offset;
+};
+
+class RenderCommandEncoderImpl
+ : public IRenderCommandEncoder
+ , public ResourceCommandEncoderImpl
+{
+public:
+ SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoderImpl)
+public:
+ RefPtr<RenderPassLayoutImpl> m_renderPass;
+ RefPtr<FramebufferImpl> m_framebuffer;
+
+ List<BoundVertexBuffer> m_boundVertexBuffers;
+
+ RefPtr<BufferResourceImpl> m_boundIndexBuffer;
+
+ D3D12_VIEWPORT m_viewports[kMaxRTVCount];
+ D3D12_RECT m_scissorRects[kMaxRTVCount];
+
+ DXGI_FORMAT m_boundIndexFormat;
+ UINT m_boundIndexOffset;
+
+ D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType;
+ D3D12_PRIMITIVE_TOPOLOGY m_primitiveTopology;
+
+ void init(
+ DeviceImpl* renderer,
+ TransientResourceHeapImpl* transientHeap,
+ CommandBufferImpl* cmdBuffer,
+ RenderPassLayoutImpl* renderPass,
+ FramebufferImpl* framebuffer);
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ bindPipeline(IPipelineState* state, IShaderObject** outRootObject) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ bindPipelineWithRootObject(IPipelineState* state, IShaderObject* rootObject) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setViewports(GfxCount count, const Viewport* viewports) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setScissorRects(GfxCount count, const ScissorRect* rects) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setPrimitiveTopology(PrimitiveTopology topology) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL setVertexBuffers(
+ GfxIndex startSlot,
+ GfxCount slotCount,
+ IBufferResource* const* buffers,
+ const Offset* offsets) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ setIndexBuffer(IBufferResource* buffer, Format indexFormat, Offset offset = 0) override;
+
+ void prepareDraw();
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ draw(GfxCount vertexCount, GfxIndex startVertex = 0) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ drawIndexed(GfxCount indexCount, GfxIndex startIndex = 0, GfxIndex baseVertex = 0) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL setStencilReference(uint32_t referenceValue) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL drawIndirect(
+ GfxCount maxDrawCount,
+ IBufferResource* argBuffer,
+ Offset argOffset,
+ IBufferResource* countBuffer,
+ Offset countOffset) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL drawIndexedIndirect(
+ GfxCount maxDrawCount,
+ IBufferResource* argBuffer,
+ Offset argOffset,
+ IBufferResource* countBuffer,
+ Offset countOffset) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setSamplePositions(
+ GfxCount samplesPerPixel,
+ GfxCount pixelCount,
+ const SamplePosition* samplePositions) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL drawInstanced(
+ GfxCount vertexCount,
+ GfxCount instanceCount,
+ GfxIndex startVertex,
+ GfxIndex startInstanceLocation) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL drawIndexedInstanced(
+ GfxCount indexCount,
+ GfxCount instanceCount,
+ GfxIndex startIndexLocation,
+ GfxIndex baseVertexLocation,
+ GfxIndex startInstanceLocation) override;
+};
+
+#if SLANG_GFX_HAS_DXR_SUPPORT
+class RayTracingCommandEncoderImpl
+ : public IRayTracingCommandEncoder
+ , public ResourceCommandEncoderImpl
+{
+public:
+ SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoderImpl)
+public:
+ virtual SLANG_NO_THROW void SLANG_MCALL buildAccelerationStructure(
+ const IAccelerationStructure::BuildDesc& desc,
+ GfxCount propertyQueryCount,
+ AccelerationStructureQueryDesc* queryDescs) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL copyAccelerationStructure(
+ IAccelerationStructure* dest,
+ IAccelerationStructure* src,
+ AccelerationStructureCopyMode mode) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL queryAccelerationStructureProperties(
+ GfxCount accelerationStructureCount,
+ IAccelerationStructure* const* accelerationStructures,
+ GfxCount queryCount,
+ AccelerationStructureQueryDesc* queryDescs) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ serializeAccelerationStructure(DeviceAddress dest, IAccelerationStructure* source) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL deserializeAccelerationStructure(
+ IAccelerationStructure* dest, DeviceAddress source) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL
+ bindPipeline(IPipelineState* state, IShaderObject** outRootObject) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ bindPipelineWithRootObject(IPipelineState* state, IShaderObject* rootObject) override
+ {
+ return bindPipelineWithRootObjectImpl(state, rootObject);
+ }
+ virtual SLANG_NO_THROW void SLANG_MCALL dispatchRays(
+ GfxIndex rayGenShaderIndex,
+ IShaderTable* shaderTable,
+ GfxCount width,
+ GfxCount height,
+ GfxCount depth) override;
+ virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() {}
+};
+#endif
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-command-queue.cpp b/tools/gfx/d3d12/d3d12-command-queue.cpp
new file mode 100644
index 000000000..d0c567b13
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-command-queue.cpp
@@ -0,0 +1,110 @@
+// d3d12-command-queue.cpp
+#include "d3d12-command-queue.h"
+
+#include "d3d12-command-buffer.h"
+#include "d3d12-device.h"
+#include "d3d12-fence.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+Result CommandQueueImpl::init(DeviceImpl* device, uint32_t queueIndex)
+{
+ m_queueIndex = queueIndex;
+ m_renderer = device;
+ m_device = device->m_device;
+ D3D12_COMMAND_QUEUE_DESC queueDesc = {};
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+ SLANG_RETURN_ON_FAIL(
+ m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(m_d3dQueue.writeRef())));
+ SLANG_RETURN_ON_FAIL(
+ m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef())));
+ globalWaitHandle = CreateEventEx(
+ nullptr, nullptr, CREATE_EVENT_INITIAL_SET | CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
+ return SLANG_OK;
+}
+
+CommandQueueImpl::~CommandQueueImpl()
+{
+ waitOnHost();
+ CloseHandle(globalWaitHandle);
+ m_renderer->m_queueIndexAllocator.free((int)m_queueIndex, 1);
+}
+
+void CommandQueueImpl::executeCommandBuffers(
+ GfxCount count, ICommandBuffer* const* commandBuffers, IFence* fence, uint64_t valueToSignal)
+{
+ ShortList<ID3D12CommandList*> commandLists;
+ for (GfxCount i = 0; i < count; i++)
+ {
+ auto cmdImpl = static_cast<CommandBufferImpl*>(commandBuffers[i]);
+ commandLists.add(cmdImpl->m_cmdList);
+ }
+ if (count > 0)
+ {
+ m_d3dQueue->ExecuteCommandLists((UINT)count, commandLists.getArrayView().getBuffer());
+
+ m_fenceValue++;
+
+ for (GfxCount i = 0; i < count; i++)
+ {
+ if (i > 0 && commandBuffers[i] == commandBuffers[i - 1])
+ continue;
+ auto cmdImpl = static_cast<CommandBufferImpl*>(commandBuffers[i]);
+ auto transientHeap = cmdImpl->m_transientHeap;
+ auto& waitInfo = transientHeap->getQueueWaitInfo(m_queueIndex);
+ waitInfo.waitValue = m_fenceValue;
+ waitInfo.fence = m_fence;
+ waitInfo.queue = m_d3dQueue;
+ }
+ }
+
+ if (fence)
+ {
+ auto fenceImpl = static_cast<FenceImpl*>(fence);
+ m_d3dQueue->Signal(fenceImpl->m_fence.get(), valueToSignal);
+ }
+}
+
+void CommandQueueImpl::waitOnHost()
+{
+ m_fenceValue++;
+ m_d3dQueue->Signal(m_fence, m_fenceValue);
+ ResetEvent(globalWaitHandle);
+ m_fence->SetEventOnCompletion(m_fenceValue, globalWaitHandle);
+ WaitForSingleObject(globalWaitHandle, INFINITE);
+}
+
+Result CommandQueueImpl::waitForFenceValuesOnDevice(
+ GfxCount fenceCount, IFence** fences, uint64_t* waitValues)
+{
+ for (GfxCount i = 0; i < fenceCount; ++i)
+ {
+ auto fenceImpl = static_cast<FenceImpl*>(fences[i]);
+ m_d3dQueue->Wait(fenceImpl->m_fence.get(), waitValues[i]);
+ }
+ return SLANG_OK;
+}
+
+const CommandQueueImpl::Desc& CommandQueueImpl::getDesc() { return m_desc; }
+
+ICommandQueue* CommandQueueImpl::getInterface(const Guid& guid)
+{
+ if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ICommandQueue)
+ return static_cast<ICommandQueue*>(this);
+ return nullptr;
+}
+
+Result CommandQueueImpl::getNativeHandle(InteropHandle* handle)
+{
+ handle->api = InteropHandleAPI::D3D12;
+ handle->handleValue = (uint64_t)m_d3dQueue.get();
+ return SLANG_OK;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-command-queue.h b/tools/gfx/d3d12/d3d12-command-queue.h
new file mode 100644
index 000000000..f11df5894
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-command-queue.h
@@ -0,0 +1,51 @@
+// d3d12-command-queue.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class CommandQueueImpl
+ : public ICommandQueue
+ , public ComObject
+{
+public:
+ SLANG_COM_OBJECT_IUNKNOWN_ALL
+ ICommandQueue* getInterface(const Guid& guid);
+ void breakStrongReferenceToDevice() { m_renderer.breakStrongReference(); }
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* handle) override;
+
+public:
+ BreakableReference<DeviceImpl> m_renderer;
+ ComPtr<ID3D12Device> m_device;
+ ComPtr<ID3D12CommandQueue> m_d3dQueue;
+ ComPtr<ID3D12Fence> m_fence;
+ uint64_t m_fenceValue = 0;
+ HANDLE globalWaitHandle;
+ Desc m_desc;
+ uint32_t m_queueIndex = 0;
+
+ Result init(DeviceImpl* device, uint32_t queueIndex);
+ ~CommandQueueImpl();
+ virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL executeCommandBuffers(
+ GfxCount count,
+ ICommandBuffer* const* commandBuffers,
+ IFence* fence,
+ uint64_t valueToSignal) override;
+
+ virtual SLANG_NO_THROW void SLANG_MCALL waitOnHost() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL waitForFenceValuesOnDevice(
+ GfxCount fenceCount, IFence** fences, uint64_t* waitValues) override;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/descriptor-heap-d3d12.cpp b/tools/gfx/d3d12/d3d12-descriptor-heap.cpp
index f5e5fd09b..b9c0d28ba 100644
--- a/tools/gfx/d3d12/descriptor-heap-d3d12.cpp
+++ b/tools/gfx/d3d12/d3d12-descriptor-heap.cpp
@@ -1,5 +1,5 @@
-#include "descriptor-heap-d3d12.h"
+#include "d3d12-descriptor-heap.h"
namespace gfx {
using namespace Slang;
diff --git a/tools/gfx/d3d12/descriptor-heap-d3d12.h b/tools/gfx/d3d12/d3d12-descriptor-heap.h
index 6f82a3f42..6f82a3f42 100644
--- a/tools/gfx/d3d12/descriptor-heap-d3d12.h
+++ b/tools/gfx/d3d12/d3d12-descriptor-heap.h
diff --git a/tools/gfx/d3d12/d3d12-device.cpp b/tools/gfx/d3d12/d3d12-device.cpp
new file mode 100644
index 000000000..07ab818b4
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-device.cpp
@@ -0,0 +1,2036 @@
+// d3d12-device.cpp
+#include "d3d12-device.h"
+
+#include "../nvapi/nvapi-util.h"
+#include "d3d12-buffer.h"
+#include "d3d12-fence.h"
+#include "d3d12-framebuffer.h"
+#include "d3d12-pipeline-state.h"
+#include "d3d12-query.h"
+#include "d3d12-render-pass.h"
+#include "d3d12-resource-views.h"
+#include "d3d12-sampler.h"
+#include "d3d12-shader-object.h"
+#include "d3d12-shader-program.h"
+#include "d3d12-shader-table.h"
+#include "d3d12-swap-chain.h"
+#include "d3d12-vertex-layout.h"
+
+#include "d3d12-helper-functions.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+Result DeviceImpl::createBuffer(
+ const D3D12_RESOURCE_DESC& resourceDesc,
+ const void* srcData,
+ Size srcDataSize,
+ D3D12_RESOURCE_STATES finalState,
+ D3D12Resource& resourceOut,
+ bool isShared,
+ MemoryType memoryType)
+{
+ const Size bufferSize = Size(resourceDesc.Width);
+
+ D3D12_HEAP_PROPERTIES heapProps;
+ heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heapProps.CreationNodeMask = 1;
+ heapProps.VisibleNodeMask = 1;
+
+ D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE;
+ if (isShared)
+ flags |= D3D12_HEAP_FLAG_SHARED;
+
+ D3D12_RESOURCE_DESC desc = resourceDesc;
+
+ D3D12_RESOURCE_STATES initialState = finalState;
+
+ switch (memoryType)
+ {
+ case MemoryType::ReadBack:
+ assert(!srcData);
+
+ heapProps.Type = D3D12_HEAP_TYPE_READBACK;
+ desc.Flags = D3D12_RESOURCE_FLAG_NONE;
+ initialState |= D3D12_RESOURCE_STATE_COPY_DEST;
+
+ break;
+ case MemoryType::Upload:
+
+ heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
+ desc.Flags = D3D12_RESOURCE_FLAG_NONE;
+ initialState |= D3D12_RESOURCE_STATE_GENERIC_READ;
+
+ break;
+ case MemoryType::DeviceLocal:
+ heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
+ initialState = (srcData ? D3D12_RESOURCE_STATE_COPY_DEST : finalState);
+ break;
+ default:
+ return SLANG_FAIL;
+ }
+
+ // Create the resource.
+ SLANG_RETURN_ON_FAIL(
+ resourceOut.initCommitted(m_device, heapProps, flags, desc, initialState, nullptr));
+
+ if (srcData)
+ {
+ D3D12Resource uploadResource;
+
+ if (memoryType == MemoryType::DeviceLocal)
+ {
+ // If the buffer is on the default heap, create upload buffer.
+ D3D12_RESOURCE_DESC uploadDesc(resourceDesc);
+ uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
+ heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
+
+ SLANG_RETURN_ON_FAIL(uploadResource.initCommitted(
+ m_device,
+ heapProps,
+ D3D12_HEAP_FLAG_NONE,
+ uploadDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ,
+ nullptr));
+ }
+
+ // Be careful not to actually copy a resource here.
+ D3D12Resource& uploadResourceRef =
+ (memoryType == MemoryType::DeviceLocal) ? uploadResource : resourceOut;
+
+ // 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 = uploadResourceRef.getResource();
+
+ SLANG_RETURN_ON_FAIL(
+ dxUploadResource->Map(0, &readRange, reinterpret_cast<void**>(&dstData)));
+ ::memcpy(dstData, srcData, srcDataSize);
+ dxUploadResource->Unmap(0, nullptr);
+
+ if (memoryType == MemoryType::DeviceLocal)
+ {
+ auto encodeInfo = encodeResourceCommands();
+ encodeInfo.d3dCommandList->CopyBufferRegion(
+ resourceOut, 0, uploadResourceRef, 0, bufferSize);
+ submitResourceCommandsAndWait(encodeInfo);
+ }
+ }
+
+ return SLANG_OK;
+}
+
+Result DeviceImpl::captureTextureToSurface(
+ TextureResourceImpl* resourceImpl,
+ ResourceState state,
+ ISlangBlob** outBlob,
+ Size* outRowPitch,
+ Size* outPixelSize)
+{
+ auto& resource = resourceImpl->m_resource;
+
+ const D3D12_RESOURCE_STATES initialState = D3DUtil::getResourceState(state);
+
+ const ITextureResource::Desc& gfxDesc = *resourceImpl->getDesc();
+ 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;
+ }
+
+ FormatInfo formatInfo;
+ gfxGetFormatInfo(gfxDesc.format, &formatInfo);
+ Size bytesPerPixel = formatInfo.blockSizeInBytes / formatInfo.pixelsPerBlock;
+ Size rowPitch = int(desc.Width) * bytesPerPixel;
+ static const Size align = 256; // D3D requires minimum 256 byte alignment for texture data.
+ rowPitch = (rowPitch + align - 1) & ~(align - 1); // Bit trick for rounding up
+ Size bufferSize = rowPitch * int(desc.Height) * int(desc.DepthOrArraySize);
+ if (outRowPitch)
+ *outRowPitch = rowPitch;
+ if (outPixelSize)
+ *outPixelSize = bytesPerPixel;
+
+ 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));
+ }
+
+ auto encodeInfo = encodeResourceCommands();
+ auto currentState = D3DUtil::getResourceState(state);
+
+ {
+ D3D12BarrierSubmitter submitter(encodeInfo.d3dCommandList);
+ resource.transition(currentState, 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 = UINT(desc.DepthOrArraySize);
+ dstLoc.PlacedFootprint.Footprint.RowPitch = UINT(rowPitch);
+
+ encodeInfo.d3dCommandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr);
+ }
+
+ {
+ D3D12BarrierSubmitter submitter(encodeInfo.d3dCommandList);
+ resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, currentState, submitter);
+ }
+
+ // Submit the copy, and wait for copy to complete
+ submitResourceCommandsAndWait(encodeInfo);
+
+ {
+ ID3D12Resource* dxResource = stagingResource;
+
+ UINT8* data;
+ D3D12_RANGE readRange = { 0, bufferSize };
+
+ SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast<void**>(&data)));
+
+ RefPtr<Slang::ListBlob> resultBlob = new Slang::ListBlob();
+ resultBlob->m_data.setCount(bufferSize);
+ memcpy(resultBlob->m_data.getBuffer(), data, bufferSize);
+ dxResource->Unmap(0, nullptr);
+ returnComPtr(outBlob, resultBlob);
+ return SLANG_OK;
+ }
+}
+
+Result DeviceImpl::getNativeDeviceHandles(InteropHandles* outHandles)
+{
+ outHandles->handles[0].handleValue = (uint64_t)m_device;
+ outHandles->handles[0].api = InteropHandleAPI::D3D12;
+ return SLANG_OK;
+}
+
+Result DeviceImpl::_createDevice(
+ DeviceCheckFlags deviceCheckFlags,
+ const UnownedStringSlice& nameMatch,
+ D3D_FEATURE_LEVEL featureLevel,
+ D3D12DeviceInfo& outDeviceInfo)
+{
+ if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug))
+ {
+ m_dxDebug->EnableDebugLayer();
+ }
+
+ outDeviceInfo.clear();
+
+ ComPtr<IDXGIFactory> dxgiFactory;
+ SLANG_RETURN_ON_FAIL(D3DUtil::createFactory(deviceCheckFlags, dxgiFactory));
+
+ List<ComPtr<IDXGIAdapter>> dxgiAdapters;
+ SLANG_RETURN_ON_FAIL(
+ D3DUtil::findAdapters(deviceCheckFlags, nameMatch, dxgiFactory, dxgiAdapters));
+
+ ComPtr<ID3D12Device> device;
+ ComPtr<IDXGIAdapter> 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))
+ {
+ ComPtr<ID3D12InfoQueue> infoQueue;
+ if (SLANG_SUCCEEDED(device->QueryInterface(infoQueue.writeRef())))
+ {
+ // Make break
+ infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
+ if (m_extendedDesc.debugBreakOnD3D12Error)
+ {
+ infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
+ }
+ D3D12_MESSAGE_ID hideMessages[] = {
+ D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
+ D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
+ };
+ D3D12_INFO_QUEUE_FILTER f = {};
+ f.DenyList.NumIDs = (UINT)SLANG_COUNT_OF(hideMessages);
+ f.DenyList.pIDList = hideMessages;
+ infoQueue->AddStorageFilterEntries(&f);
+
+ // 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<IDXGIAdapter1> 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);
+ const UINT kMicrosoftVendorId = 5140;
+ outDeviceInfo.m_isSoftware =
+ outDeviceInfo.m_isWarp ||
+ ((outDeviceInfo.m_desc1.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0) ||
+ outDeviceInfo.m_desc.VendorId == kMicrosoftVendorId;
+
+ return SLANG_OK;
+}
+
+Result DeviceImpl::initialize(const Desc& desc)
+{
+ SLANG_RETURN_ON_FAIL(RendererBase::initialize(desc));
+
+ // Find extended desc.
+ for (GfxIndex i = 0; i < desc.extendedDescCount; i++)
+ {
+ StructType stype;
+ memcpy(&stype, desc.extendedDescs[i], sizeof(stype));
+ if (stype == StructType::D3D12ExtendedDesc)
+ {
+ memcpy(&m_extendedDesc, desc.extendedDescs[i], sizeof(m_extendedDesc));
+ }
+ }
+
+ // Initialize queue index allocator.
+ // Support max 32 queues.
+ m_queueIndexAllocator.initPool(32);
+
+ // Initialize DeviceInfo
+ {
+ m_info.deviceType = DeviceType::DirectX12;
+ m_info.bindingStyle = BindingStyle::DirectX;
+ m_info.projectionStyle = ProjectionStyle::DirectX;
+ m_info.apiName = "Direct3D 12";
+ static const float kIdentity[] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
+ ::memcpy(m_info.identityProjectionMatrix, kIdentity, sizeof(kIdentity));
+ }
+
+ // 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;
+ }
+
+ HMODULE pixModule = LoadLibraryW(L"WinPixEventRuntime.dll");
+ if (pixModule)
+ {
+ m_BeginEventOnCommandList =
+ (PFN_BeginEventOnCommandList)GetProcAddress(pixModule, "PIXBeginEventOnCommandList");
+ m_EndEventOnCommandList =
+ (PFN_EndEventOnCommandList)GetProcAddress(pixModule, "PIXEndEventOnCommandList");
+ }
+
+#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<ID3D12Debug1> debug1;
+ if (SLANG_SUCCEEDED(m_dxDebug->QueryInterface(debug1.writeRef())))
+ {
+ debug1->SetEnableGPUBasedValidation(true);
+ }
+# endif
+ }
+ }
+#endif
+
+ m_D3D12CreateDevice = (PFN_D3D12_CREATE_DEVICE)loadProc(d3dModule, "D3D12CreateDevice");
+ if (!m_D3D12CreateDevice)
+ {
+ return SLANG_FAIL;
+ }
+
+ if (desc.existingDeviceHandles.handles[0].handleValue == 0)
+ {
+ 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),
+ UnownedStringSlice(desc.adapter),
+ featureLevel,
+ m_deviceInfo)))
+ {
+ break;
+ }
+ }
+
+ if (!m_deviceInfo.m_adapter)
+ {
+ // Couldn't find an adapter
+ return SLANG_FAIL;
+ }
+ }
+ else
+ {
+ // Store the existing device handle in desc in m_deviceInfo
+ m_deviceInfo.m_device = (ID3D12Device*)desc.existingDeviceHandles.handles[0].handleValue;
+ }
+
+ // Set the device
+ m_device = m_deviceInfo.m_device;
+
+ if (m_deviceInfo.m_isSoftware)
+ {
+ m_features.add("software-device");
+ }
+ else
+ {
+ m_features.add("hardware-device");
+ }
+
+ // NVAPI
+ if (desc.nvapiExtnSlot >= 0)
+ {
+ if (SLANG_FAILED(NVAPIUtil::initialize()))
+ {
+ return SLANG_E_NOT_AVAILABLE;
+ }
+
+#ifdef GFX_NVAPI
+ // From DOCS: Applications are expected to bind null UAV to this slot.
+ // NOTE! We don't currently do this, but doesn't seem to be a problem.
+
+ const NvAPI_Status status =
+ NvAPI_D3D12_SetNvShaderExtnSlotSpace(m_device, NvU32(desc.nvapiExtnSlot), NvU32(0));
+
+ if (status != NVAPI_OK)
+ {
+ return SLANG_E_NOT_AVAILABLE;
+ }
+
+ if (isSupportedNVAPIOp(m_device, NV_EXTN_OP_UINT64_ATOMIC))
+ {
+ m_features.add("atomic-int64");
+ }
+ if (isSupportedNVAPIOp(m_device, NV_EXTN_OP_FP32_ATOMIC))
+ {
+ m_features.add("atomic-float");
+ }
+
+ m_nvapi = true;
+#endif
+ }
+
+ D3D12_FEATURE_DATA_SHADER_MODEL shaderModelData = {};
+ shaderModelData.HighestShaderModel = D3D_SHADER_MODEL_6_6;
+
+ // Find what features are supported
+ {
+ // Check this is how this is laid out...
+ SLANG_COMPILE_TIME_ASSERT(D3D_SHADER_MODEL_6_0 == 0x60);
+
+ {
+ // TODO: Currently warp causes a crash when using half, so disable for now
+ if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
+ D3D12_FEATURE_SHADER_MODEL, &shaderModelData, sizeof(shaderModelData))) &&
+ m_deviceInfo.m_isWarp == false && shaderModelData.HighestShaderModel >= 0x62)
+ {
+ // With sm_6_2 we have half
+ m_features.add("half");
+ }
+ }
+ {
+ D3D12_FEATURE_DATA_D3D12_OPTIONS options;
+ if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
+ D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
+ {
+ // Check double precision support
+ if (options.DoublePrecisionFloatShaderOps)
+ m_features.add("double");
+
+ // Check conservative-rasterization support
+ auto conservativeRasterTier = options.ConservativeRasterizationTier;
+ if (conservativeRasterTier == D3D12_CONSERVATIVE_RASTERIZATION_TIER_3)
+ {
+ m_features.add("conservative-rasterization-3");
+ m_features.add("conservative-rasterization-2");
+ m_features.add("conservative-rasterization-1");
+ }
+ else if (conservativeRasterTier == D3D12_CONSERVATIVE_RASTERIZATION_TIER_2)
+ {
+ m_features.add("conservative-rasterization-2");
+ m_features.add("conservative-rasterization-1");
+ }
+ else if (conservativeRasterTier == D3D12_CONSERVATIVE_RASTERIZATION_TIER_1)
+ {
+ m_features.add("conservative-rasterization-1");
+ }
+
+ // Check rasterizer ordered views support
+ if (options.ROVsSupported)
+ {
+ m_features.add("rasterizer-ordered-views");
+ }
+ }
+ }
+ {
+ D3D12_FEATURE_DATA_D3D12_OPTIONS2 options;
+ if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
+ D3D12_FEATURE_D3D12_OPTIONS2, &options, sizeof(options))))
+ {
+ // Check programmable sample positions support
+ switch (options.ProgrammableSamplePositionsTier)
+ {
+ case D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_2:
+ m_features.add("programmable-sample-positions-2");
+ m_features.add("programmable-sample-positions-1");
+ break;
+ case D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_1:
+ m_features.add("programmable-sample-positions-1");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ {
+ D3D12_FEATURE_DATA_D3D12_OPTIONS3 options;
+ if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
+ D3D12_FEATURE_D3D12_OPTIONS3, &options, sizeof(options))))
+ {
+ // Check barycentrics support
+ if (options.BarycentricsSupported)
+ {
+ m_features.add("barycentrics");
+ }
+ }
+ }
+ // Check ray tracing support
+ {
+ D3D12_FEATURE_DATA_D3D12_OPTIONS5 options;
+ if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
+ D3D12_FEATURE_D3D12_OPTIONS5, &options, sizeof(options))))
+ {
+ if (options.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED)
+ {
+ m_features.add("ray-tracing");
+ }
+ if (options.RaytracingTier >= D3D12_RAYTRACING_TIER_1_1)
+ {
+ m_features.add("ray-query");
+ }
+ }
+ }
+ }
+
+ m_desc = desc;
+
+ // Create a command queue for internal resource transfer operations.
+ SLANG_RETURN_ON_FAIL(createCommandQueueImpl(m_resourceCommandQueue.writeRef()));
+ // `CommandQueueImpl` holds a back reference to `D3D12Device`, make it a weak reference here
+ // since this object is already owned by `D3D12Device`.
+ m_resourceCommandQueue->breakStrongReferenceToDevice();
+ // Retrieve timestamp frequency.
+ m_resourceCommandQueue->m_d3dQueue->GetTimestampFrequency(&m_info.timestampFrequency);
+
+ SLANG_RETURN_ON_FAIL(createTransientResourceHeapImpl(
+ ITransientResourceHeap::Flags::AllowResizing,
+ 0,
+ 8,
+ 4,
+ m_resourceCommandTransientHeap.writeRef()));
+ // `TransientResourceHeap` holds a back reference to `D3D12Device`, make it a weak reference
+ // here since this object is already owned by `D3D12Device`.
+ m_resourceCommandTransientHeap->breakStrongReferenceToDevice();
+
+ m_cpuViewHeap = new D3D12GeneralExpandingDescriptorHeap();
+ SLANG_RETURN_ON_FAIL(m_cpuViewHeap->init(
+ m_device,
+ 1024 * 1024,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
+ D3D12_DESCRIPTOR_HEAP_FLAG_NONE));
+ m_cpuSamplerHeap = new D3D12GeneralExpandingDescriptorHeap();
+ SLANG_RETURN_ON_FAIL(m_cpuSamplerHeap->init(
+ m_device, 2048, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE));
+
+ m_rtvAllocator = new D3D12GeneralExpandingDescriptorHeap();
+ SLANG_RETURN_ON_FAIL(m_rtvAllocator->init(
+ m_device, 16 * 1024, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE));
+ m_dsvAllocator = new D3D12GeneralExpandingDescriptorHeap();
+ SLANG_RETURN_ON_FAIL(m_dsvAllocator->init(
+ m_device, 1024, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE));
+
+ ComPtr<IDXGIDevice> dxgiDevice;
+ if (m_deviceInfo.m_adapter)
+ {
+ DXGI_ADAPTER_DESC adapterDesc;
+ m_deviceInfo.m_adapter->GetDesc(&adapterDesc);
+ m_adapterName = String::fromWString(adapterDesc.Description);
+ m_info.adapterName = m_adapterName.begin();
+ }
+
+ // Initialize DXR interface.
+#if SLANG_GFX_HAS_DXR_SUPPORT
+ m_device->QueryInterface<ID3D12Device5>(m_deviceInfo.m_device5.writeRef());
+ m_device5 = m_deviceInfo.m_device5.get();
+#endif
+ // Check shader model version.
+ SlangCompileTarget compileTarget = SLANG_DXBC;
+ const char* profileName = "sm_5_1";
+ switch (shaderModelData.HighestShaderModel)
+ {
+ case D3D_SHADER_MODEL_5_1:
+ compileTarget = SLANG_DXBC;
+ profileName = "sm_5_1";
+ break;
+ case D3D_SHADER_MODEL_6_0:
+ compileTarget = SLANG_DXIL;
+ profileName = "sm_6_0";
+ break;
+ case D3D_SHADER_MODEL_6_1:
+ compileTarget = SLANG_DXIL;
+ profileName = "sm_6_1";
+ break;
+ case D3D_SHADER_MODEL_6_2:
+ compileTarget = SLANG_DXIL;
+ profileName = "sm_6_2";
+ break;
+ case D3D_SHADER_MODEL_6_3:
+ compileTarget = SLANG_DXIL;
+ profileName = "sm_6_3";
+ break;
+ case D3D_SHADER_MODEL_6_4:
+ compileTarget = SLANG_DXIL;
+ profileName = "sm_6_4";
+ break;
+ case D3D_SHADER_MODEL_6_5:
+ compileTarget = SLANG_DXIL;
+ profileName = "sm_6_5";
+ break;
+ default:
+ compileTarget = SLANG_DXIL;
+ profileName = "sm_6_6";
+ break;
+ }
+ m_features.add(profileName);
+ // If user specified a higher shader model than what the system supports, return failure.
+ int userSpecifiedShaderModel = D3DUtil::getShaderModelFromProfileName(desc.slang.targetProfile);
+ if (userSpecifiedShaderModel > shaderModelData.HighestShaderModel)
+ {
+ getDebugCallback()->handleMessage(
+ gfx::DebugMessageType::Error,
+ gfx::DebugMessageSource::Layer,
+ "The requested shader model is not supported by the system.");
+ return SLANG_E_NOT_AVAILABLE;
+ }
+ SLANG_RETURN_ON_FAIL(slangContext.initialize(
+ desc.slang,
+ compileTarget,
+ profileName,
+ makeArray(slang::PreprocessorMacroDesc{ "__D3D12__", "1" }).getView()));
+
+ // Allocate a D3D12 "command signature" object that matches the behavior
+ // of a D3D11-style `DrawInstancedIndirect` operation.
+ {
+ D3D12_INDIRECT_ARGUMENT_DESC args;
+ args.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
+
+ D3D12_COMMAND_SIGNATURE_DESC desc;
+ desc.ByteStride = sizeof(D3D12_DRAW_ARGUMENTS);
+ desc.NumArgumentDescs = 1;
+ desc.pArgumentDescs = &args;
+ desc.NodeMask = 0;
+
+ SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature(
+ &desc, nullptr, IID_PPV_ARGS(drawIndirectCmdSignature.writeRef())));
+ }
+
+ // Allocate a D3D12 "command signature" object that matches the behavior
+ // of a D3D11-style `DrawIndexedInstancedIndirect` operation.
+ {
+ D3D12_INDIRECT_ARGUMENT_DESC args;
+ args.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
+
+ D3D12_COMMAND_SIGNATURE_DESC desc;
+ desc.ByteStride = sizeof(D3D12_DRAW_INDEXED_ARGUMENTS);
+ desc.NumArgumentDescs = 1;
+ desc.pArgumentDescs = &args;
+ desc.NodeMask = 0;
+
+ SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature(
+ &desc, nullptr, IID_PPV_ARGS(drawIndexedIndirectCmdSignature.writeRef())));
+ }
+
+ // Allocate a D3D12 "command signature" object that matches the behavior
+ // of a D3D11-style `Dispatch` operation.
+ {
+ D3D12_INDIRECT_ARGUMENT_DESC args;
+ args.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
+
+ D3D12_COMMAND_SIGNATURE_DESC desc;
+ desc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS);
+ desc.NumArgumentDescs = 1;
+ desc.pArgumentDescs = &args;
+ desc.NodeMask = 0;
+
+ SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature(
+ &desc, nullptr, IID_PPV_ARGS(dispatchIndirectCmdSignature.writeRef())));
+ }
+ m_isInitialized = true;
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createTransientResourceHeap(
+ const ITransientResourceHeap::Desc& desc, ITransientResourceHeap** outHeap)
+{
+ RefPtr<TransientResourceHeapImpl> heap;
+ SLANG_RETURN_ON_FAIL(createTransientResourceHeapImpl(
+ desc.flags,
+ desc.constantBufferSize,
+ getViewDescriptorCount(desc),
+ Math::Max(1024, desc.samplerDescriptorCount),
+ heap.writeRef()));
+ returnComPtr(outHeap, heap);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue)
+{
+ RefPtr<CommandQueueImpl> queue;
+ SLANG_RETURN_ON_FAIL(createCommandQueueImpl(queue.writeRef()));
+ returnComPtr(outQueue, queue);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createSwapchain(
+ const ISwapchain::Desc& desc, WindowHandle window, ISwapchain** outSwapchain)
+{
+ RefPtr<SwapchainImpl> swapchain = new SwapchainImpl();
+ SLANG_RETURN_ON_FAIL(swapchain->init(this, desc, window));
+ returnComPtr(outSwapchain, swapchain);
+ return SLANG_OK;
+}
+
+SlangResult DeviceImpl::readTextureResource(
+ ITextureResource* resource,
+ ResourceState state,
+ ISlangBlob** outBlob,
+ Size* outRowPitch,
+ Size* outPixelSize)
+{
+ return captureTextureToSurface(
+ static_cast<TextureResourceImpl*>(resource), state, outBlob, outRowPitch, outPixelSize);
+}
+
+Result DeviceImpl::getTextureAllocationInfo(
+ const ITextureResource::Desc& desc, Size* outSize, Size* outAlignment)
+{
+ TextureResource::Desc srcDesc = fixupTextureDesc(desc);
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ initTextureResourceDesc(resourceDesc, srcDesc);
+ auto allocInfo = m_device->GetResourceAllocationInfo(0, 1, &resourceDesc);
+ *outSize = (Size)allocInfo.SizeInBytes;
+ *outAlignment = (Size)allocInfo.Alignment;
+ return SLANG_OK;
+}
+
+Result DeviceImpl::getTextureRowAlignment(Size* outAlignment)
+{
+ *outAlignment = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createTextureResource(
+ const ITextureResource::Desc& descIn,
+ const ITextureResource::SubresourceData* initData,
+ ITextureResource** outResource)
+{
+ // Description of uploading on Dx12
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/dn899215%28v=vs.85%29.aspx
+
+ TextureResource::Desc srcDesc = fixupTextureDesc(descIn);
+
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ initTextureResourceDesc(resourceDesc, srcDesc);
+ const int arraySize = calcEffectiveArraySize(srcDesc);
+ const int numMipMaps = srcDesc.numMipLevels;
+
+ RefPtr<TextureResourceImpl> 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;
+
+ D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE;
+ if (descIn.isShared)
+ flags |= D3D12_HEAP_FLAG_SHARED;
+
+ D3D12_CLEAR_VALUE clearValue;
+ D3D12_CLEAR_VALUE* clearValuePtr = &clearValue;
+ if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET |
+ D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 0)
+ {
+ clearValuePtr = nullptr;
+ }
+ if (isTypelessDepthFormat(resourceDesc.Format))
+ {
+ clearValuePtr = nullptr;
+ }
+ clearValue.Format = resourceDesc.Format;
+ memcpy(clearValue.Color, &descIn.optimalClearValue.color, sizeof(clearValue.Color));
+ clearValue.DepthStencil.Depth = descIn.optimalClearValue.depthStencil.depth;
+ clearValue.DepthStencil.Stencil = descIn.optimalClearValue.depthStencil.stencil;
+ SLANG_RETURN_ON_FAIL(texture->m_resource.initCommitted(
+ m_device,
+ heapProps,
+ flags,
+ resourceDesc,
+ D3D12_RESOURCE_STATE_COPY_DEST,
+ clearValuePtr));
+
+ texture->m_resource.setDebugName(L"Texture");
+ }
+
+ // Calculate the layout
+ List<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> layouts;
+ layouts.setCount(numMipMaps);
+ List<UInt64> mipRowSizeInBytes;
+ mipRowSizeInBytes.setCount(srcDesc.numMipLevels);
+ List<UInt32> mipNumRows;
+ mipNumRows.setCount(numMipMaps);
+
+ // NOTE! This is just the size for one array upload -> not for the whole texture
+ UInt64 requiredSize = 0;
+ m_device->GetCopyableFootprints(
+ &resourceDesc,
+ 0,
+ srcDesc.numMipLevels,
+ 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
+ if (initData)
+ {
+ // 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<void**>(&p));
+
+ for (int j = 0; j < numMipMaps; ++j)
+ {
+ auto srcSubresource = initData[subResourceIndex + j];
+
+ const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& layout = layouts[j];
+ const D3D12_SUBRESOURCE_FOOTPRINT& footprint = layout.Footprint;
+
+ TextureResource::Extents mipSize = calcMipSize(srcDesc.size, j);
+ if (gfxIsCompressedFormat(descIn.format))
+ {
+ mipSize.width = int(D3DUtil::calcAligned(mipSize.width, 4));
+ mipSize.height = int(D3DUtil::calcAligned(mipSize.height, 4));
+ }
+
+ assert(
+ footprint.Width == mipSize.width && footprint.Height == mipSize.height &&
+ footprint.Depth == mipSize.depth);
+
+ auto mipRowSize = mipRowSizeInBytes[j];
+
+ const ptrdiff_t dstMipRowPitch = ptrdiff_t(footprint.RowPitch);
+ const ptrdiff_t srcMipRowPitch = ptrdiff_t(srcSubresource.strideY);
+
+ const ptrdiff_t dstMipLayerPitch = ptrdiff_t(footprint.RowPitch * footprint.Height);
+ const ptrdiff_t srcMipLayerPitch = ptrdiff_t(srcSubresource.strideZ);
+
+ // Our outer loop will copy the depth layers one at a time.
+ //
+ const uint8_t* srcLayer = (const uint8_t*)srcSubresource.data;
+ uint8_t* dstLayer = p + layouts[j].Offset;
+ for (int l = 0; l < mipSize.depth; l++)
+ {
+ // Our inner loop will copy the rows one at a time.
+ //
+ const uint8_t* srcRow = srcLayer;
+ uint8_t* dstRow = dstLayer;
+ int j = gfxIsCompressedFormat(descIn.format)
+ ? 4
+ : 1; // BC compressed formats are organized into 4x4 blocks
+ for (int k = 0; k < mipSize.height; k += j)
+ {
+ ::memcpy(dstRow, srcRow, (Size)mipRowSize);
+
+ srcRow += srcMipRowPitch;
+ dstRow += dstMipRowPitch;
+ }
+
+ srcLayer += srcMipLayerPitch;
+ dstLayer += dstMipLayerPitch;
+ }
+
+ // assert(srcRow == (const uint8_t*)(srcMip.getBuffer() + srcMip.getCount()));
+ }
+ uploadResource->Unmap(0, nullptr);
+
+ auto encodeInfo = encodeResourceCommands();
+ 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;
+ encodeInfo.d3dCommandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
+
+ subResourceIndex++;
+ }
+
+ // Block - waiting for copy to complete (so can drop upload texture)
+ submitResourceCommandsAndWait(encodeInfo);
+ }
+ }
+ {
+ auto encodeInfo = encodeResourceCommands();
+ {
+ D3D12BarrierSubmitter submitter(encodeInfo.d3dCommandList);
+ texture->m_resource.transition(
+ D3D12_RESOURCE_STATE_COPY_DEST, texture->m_defaultState, submitter);
+ }
+ submitResourceCommandsAndWait(encodeInfo);
+ }
+
+ returnComPtr(outResource, texture);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createTextureFromNativeHandle(
+ InteropHandle handle, const ITextureResource::Desc& srcDesc, ITextureResource** outResource)
+{
+ RefPtr<TextureResourceImpl> texture(new TextureResourceImpl(srcDesc));
+
+ if (handle.api == InteropHandleAPI::D3D12)
+ {
+ texture->m_resource.setResource((ID3D12Resource*)handle.handleValue);
+ }
+ else
+ {
+ return SLANG_FAIL;
+ }
+
+ returnComPtr(outResource, texture);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createBufferResource(
+ const IBufferResource::Desc& descIn, const void* initData, IBufferResource** outResource)
+{
+ BufferResource::Desc srcDesc = fixupBufferDesc(descIn);
+
+ RefPtr<BufferResourceImpl> buffer(new BufferResourceImpl(srcDesc));
+
+ D3D12_RESOURCE_DESC bufferDesc;
+ initBufferResourceDesc(descIn.sizeInBytes, bufferDesc);
+
+ bufferDesc.Flags |= calcResourceFlags(srcDesc.allowedStates);
+
+ const D3D12_RESOURCE_STATES initialState = buffer->m_defaultState;
+ SLANG_RETURN_ON_FAIL(createBuffer(
+ bufferDesc,
+ initData,
+ srcDesc.sizeInBytes,
+ initialState,
+ buffer->m_resource,
+ descIn.isShared,
+ descIn.memoryType));
+
+ returnComPtr(outResource, buffer);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createBufferFromNativeHandle(
+ InteropHandle handle, const IBufferResource::Desc& srcDesc, IBufferResource** outResource)
+{
+ RefPtr<BufferResourceImpl> buffer(new BufferResourceImpl(srcDesc));
+
+ if (handle.api == InteropHandleAPI::D3D12)
+ {
+ buffer->m_resource.setResource((ID3D12Resource*)handle.handleValue);
+ }
+ else
+ {
+ return SLANG_FAIL;
+ }
+
+ returnComPtr(outResource, buffer);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createSamplerState(ISamplerState::Desc const& desc, ISamplerState** 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;
+
+ D3D12Descriptor cpuDescriptor;
+ samplerHeap->allocate(&cpuDescriptor);
+ m_device->CreateSampler(&dxDesc, cpuDescriptor.cpuHandle);
+
+ // 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<SamplerStateImpl> samplerImpl = new SamplerStateImpl();
+ samplerImpl->m_allocator = samplerHeap;
+ samplerImpl->m_descriptor = cpuDescriptor;
+ returnComPtr(outSampler, samplerImpl);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createTextureView(
+ ITextureResource* texture, IResourceView::Desc const& desc, IResourceView** outView)
+{
+ auto resourceImpl = (TextureResourceImpl*)texture;
+
+ RefPtr<ResourceViewImpl> viewImpl = new ResourceViewImpl();
+ viewImpl->m_resource = resourceImpl;
+ viewImpl->m_desc = desc;
+ bool isArray = resourceImpl ? resourceImpl->getDesc()->arraySize != 0 : false;
+ bool isMultiSample = resourceImpl ? resourceImpl->getDesc()->sampleDesc.numSamples > 1 : false;
+ switch (desc.type)
+ {
+ default:
+ return SLANG_FAIL;
+
+ case IResourceView::Type::RenderTarget:
+ {
+ SLANG_RETURN_ON_FAIL(m_rtvAllocator->allocate(&viewImpl->m_descriptor));
+ viewImpl->m_allocator = m_rtvAllocator;
+ D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
+ rtvDesc.Format = D3DUtil::getMapFormat(desc.format);
+ isArray = desc.subresourceRange.layerCount > 1;
+ switch (desc.renderTarget.shape)
+ {
+ case IResource::Type::Texture1D:
+ rtvDesc.ViewDimension =
+ isArray ? D3D12_RTV_DIMENSION_TEXTURE1DARRAY : D3D12_RTV_DIMENSION_TEXTURE1D;
+ rtvDesc.Texture1D.MipSlice = desc.subresourceRange.mipLevel;
+ break;
+ case IResource::Type::Texture2D:
+ if (isMultiSample)
+ {
+ rtvDesc.ViewDimension = isArray ? D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY
+ : D3D12_RTV_DIMENSION_TEXTURE2DMS;
+ rtvDesc.Texture2DMSArray.ArraySize = desc.subresourceRange.layerCount;
+ rtvDesc.Texture2DMSArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
+ }
+ else
+ {
+ rtvDesc.ViewDimension = isArray ? D3D12_RTV_DIMENSION_TEXTURE2DARRAY
+ : D3D12_RTV_DIMENSION_TEXTURE2D;
+ rtvDesc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel;
+ rtvDesc.Texture2DArray.PlaneSlice =
+ resourceImpl ? D3DUtil::getPlaneSlice(
+ D3DUtil::getMapFormat(resourceImpl->getDesc()->format),
+ desc.subresourceRange.aspectMask)
+ : 0;
+ rtvDesc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount;
+ rtvDesc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
+ }
+ break;
+ case IResource::Type::Texture3D:
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
+ rtvDesc.Texture3D.MipSlice = desc.subresourceRange.mipLevel;
+ rtvDesc.Texture3D.FirstWSlice = desc.subresourceRange.baseArrayLayer;
+ rtvDesc.Texture3D.WSize = (desc.subresourceRange.layerCount == 0) ? -1 : desc.subresourceRange.layerCount;
+ break;
+ case IResource::Type::Buffer:
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_BUFFER;
+ break;
+ default:
+ return SLANG_FAIL;
+ }
+ m_device->CreateRenderTargetView(
+ resourceImpl ? resourceImpl->m_resource.getResource() : nullptr,
+ &rtvDesc,
+ viewImpl->m_descriptor.cpuHandle);
+ }
+ break;
+
+ case IResourceView::Type::DepthStencil:
+ {
+ SLANG_RETURN_ON_FAIL(m_dsvAllocator->allocate(&viewImpl->m_descriptor));
+ viewImpl->m_allocator = m_dsvAllocator;
+ D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
+ dsvDesc.Format = D3DUtil::getMapFormat(desc.format);
+ isArray = desc.subresourceRange.layerCount > 1;
+ switch (desc.renderTarget.shape)
+ {
+ case IResource::Type::Texture1D:
+ dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1D;
+ dsvDesc.Texture1D.MipSlice = desc.subresourceRange.mipLevel;
+ break;
+ case IResource::Type::Texture2D:
+ if (isMultiSample)
+ {
+ dsvDesc.ViewDimension = isArray ? D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY
+ : D3D12_DSV_DIMENSION_TEXTURE2DMS;
+ dsvDesc.Texture2DMSArray.ArraySize = desc.subresourceRange.layerCount;
+ dsvDesc.Texture2DMSArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
+ }
+ else
+ {
+ dsvDesc.ViewDimension = isArray ? D3D12_DSV_DIMENSION_TEXTURE2DARRAY
+ : D3D12_DSV_DIMENSION_TEXTURE2D;
+ dsvDesc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel;
+ dsvDesc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount;
+ dsvDesc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
+ }
+ break;
+ default:
+ return SLANG_FAIL;
+ }
+ m_device->CreateDepthStencilView(
+ resourceImpl ? resourceImpl->m_resource.getResource() : nullptr,
+ &dsvDesc,
+ viewImpl->m_descriptor.cpuHandle);
+ }
+ break;
+
+ case IResourceView::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_cpuViewHeap->allocate(&viewImpl->m_descriptor));
+ viewImpl->m_allocator = m_cpuViewHeap;
+ D3D12_UNORDERED_ACCESS_VIEW_DESC d3d12desc = {};
+ auto& resourceDesc = *resourceImpl->getDesc();
+ d3d12desc.Format = gfxIsTypelessFormat(texture->getDesc()->format)
+ ? D3DUtil::getMapFormat(desc.format)
+ : D3DUtil::getMapFormat(texture->getDesc()->format);
+ switch (resourceImpl->getDesc()->type)
+ {
+ case IResource::Type::Texture1D:
+ d3d12desc.ViewDimension = resourceDesc.arraySize == 0
+ ? D3D12_UAV_DIMENSION_TEXTURE1D
+ : D3D12_UAV_DIMENSION_TEXTURE1DARRAY;
+ d3d12desc.Texture1D.MipSlice = desc.subresourceRange.mipLevel;
+ d3d12desc.Texture1DArray.ArraySize = desc.subresourceRange.layerCount == 0
+ ? resourceDesc.arraySize
+ : desc.subresourceRange.layerCount;
+ d3d12desc.Texture1DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
+
+ break;
+ case IResource::Type::Texture2D:
+ d3d12desc.ViewDimension = resourceDesc.arraySize == 0
+ ? D3D12_UAV_DIMENSION_TEXTURE2D
+ : D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
+ d3d12desc.Texture2D.MipSlice = desc.subresourceRange.mipLevel;
+ d3d12desc.Texture2D.PlaneSlice =
+ D3DUtil::getPlaneSlice(d3d12desc.Format, desc.subresourceRange.aspectMask);
+ d3d12desc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount == 0
+ ? resourceDesc.arraySize
+ : desc.subresourceRange.layerCount;
+ d3d12desc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
+ break;
+ case IResource::Type::Texture3D:
+ d3d12desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
+ d3d12desc.Texture3D.MipSlice = desc.subresourceRange.mipLevel;
+ d3d12desc.Texture3D.FirstWSlice = desc.subresourceRange.baseArrayLayer;
+ d3d12desc.Texture3D.WSize = resourceDesc.size.depth;
+ break;
+ default:
+ return SLANG_FAIL;
+ }
+ m_device->CreateUnorderedAccessView(
+ resourceImpl->m_resource, nullptr, &d3d12desc, viewImpl->m_descriptor.cpuHandle);
+ }
+ break;
+
+ case IResourceView::Type::ShaderResource:
+ {
+ SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&viewImpl->m_descriptor));
+ viewImpl->m_allocator = m_cpuViewHeap;
+
+ // 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 = desc.format == Format::Unknown
+ ? resourceDesc.Format
+ : D3DUtil::getMapFormat(desc.format);
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ initSrvDesc(
+ resourceImpl->getType(),
+ *resourceImpl->getDesc(),
+ resourceDesc,
+ pixelFormat,
+ desc.subresourceRange,
+ srvDesc);
+
+ m_device->CreateShaderResourceView(
+ resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle);
+ }
+ break;
+ }
+
+ returnComPtr(outView, viewImpl);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::getFormatSupportedResourceStates(Format format, ResourceStateSet* outStates)
+{
+ D3D12_FEATURE_DATA_FORMAT_SUPPORT support;
+ support.Format = D3DUtil::getMapFormat(format);
+ SLANG_RETURN_ON_FAIL(
+ m_device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support)));
+
+ ResourceStateSet allowedStates;
+
+ auto dxgi1 = support.Support1;
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_BUFFER)
+ allowedStates.add(ResourceState::ConstantBuffer);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER)
+ allowedStates.add(ResourceState::VertexBuffer);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_IA_INDEX_BUFFER)
+ allowedStates.add(ResourceState::IndexBuffer);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_SO_BUFFER)
+ allowedStates.add(ResourceState::StreamOutput);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURE1D)
+ allowedStates.add(ResourceState::ShaderResource);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)
+ allowedStates.add(ResourceState::ShaderResource);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURE3D)
+ allowedStates.add(ResourceState::ShaderResource);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURECUBE)
+ allowedStates.add(ResourceState::ShaderResource);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_LOAD)
+ allowedStates.add(ResourceState::ShaderResource);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE)
+ allowedStates.add(ResourceState::ShaderResource);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE_COMPARISON)
+ allowedStates.add(ResourceState::ShaderResource);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_GATHER)
+ allowedStates.add(ResourceState::ShaderResource);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_GATHER_COMPARISON)
+ allowedStates.add(ResourceState::ShaderResource);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET)
+ allowedStates.add(ResourceState::RenderTarget);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL)
+ allowedStates.add(ResourceState::DepthWrite);
+ if (dxgi1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW)
+ allowedStates.add(ResourceState::UnorderedAccess);
+
+ *outStates = allowedStates;
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createBufferView(
+ IBufferResource* buffer,
+ IBufferResource* counterBuffer,
+ IResourceView::Desc const& desc,
+ IResourceView** outView)
+{
+ auto resourceImpl = (BufferResourceImpl*)buffer;
+ auto resourceDesc = *resourceImpl->getDesc();
+
+ RefPtr<ResourceViewImpl> viewImpl = new ResourceViewImpl();
+ viewImpl->m_resource = resourceImpl;
+ viewImpl->m_desc = desc;
+
+ switch (desc.type)
+ {
+ default:
+ return SLANG_FAIL;
+
+ case IResourceView::Type::UnorderedAccess:
+ {
+ D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
+ uavDesc.Format = D3DUtil::getMapFormat(desc.format);
+ uavDesc.Buffer.FirstElement = desc.bufferRange.firstElement;
+ uint64_t viewSize = 0;
+ if (desc.bufferElementSize)
+ {
+ uavDesc.Buffer.StructureByteStride = (UINT)desc.bufferElementSize;
+ uavDesc.Buffer.NumElements =
+ desc.bufferRange.elementCount == 0
+ ? UINT(resourceDesc.sizeInBytes / desc.bufferElementSize)
+ : (UINT)desc.bufferRange.elementCount;
+ viewSize = (uint64_t)desc.bufferElementSize * uavDesc.Buffer.NumElements;
+ }
+ else if (desc.format == Format::Unknown)
+ {
+ uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
+ uavDesc.Buffer.NumElements = desc.bufferRange.elementCount == 0
+ ? UINT(resourceDesc.sizeInBytes / 4)
+ : UINT(desc.bufferRange.elementCount / 4);
+ uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW;
+ viewSize = 4ull * uavDesc.Buffer.NumElements;
+ }
+ else
+ {
+ FormatInfo sizeInfo;
+ gfxGetFormatInfo(desc.format, &sizeInfo);
+ assert(sizeInfo.pixelsPerBlock == 1);
+ uavDesc.Buffer.NumElements =
+ desc.bufferRange.elementCount == 0
+ ? UINT(resourceDesc.sizeInBytes / sizeInfo.blockSizeInBytes)
+ : (UINT)desc.bufferRange.elementCount;
+ viewSize = (uint64_t)uavDesc.Buffer.NumElements * sizeInfo.blockSizeInBytes;
+ }
+
+ if (viewSize >= (1ull << 32) - 8)
+ {
+ // D3D12 does not support view descriptors that has size near 4GB.
+ // We will not create actual SRV/UAVs for such large buffers.
+ // However, a buffer this large can still be bound as root parameter.
+ // So instead of failing, we quietly ignore descriptor creation.
+ viewImpl->m_descriptor.cpuHandle.ptr = 0;
+ }
+ else
+ {
+ auto counterResourceImpl = static_cast<BufferResourceImpl*>(counterBuffer);
+ SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&viewImpl->m_descriptor));
+ viewImpl->m_allocator = m_cpuViewHeap;
+ m_device->CreateUnorderedAccessView(
+ resourceImpl->m_resource,
+ counterResourceImpl ? counterResourceImpl->m_resource.getResource() : nullptr,
+ &uavDesc,
+ viewImpl->m_descriptor.cpuHandle);
+ }
+ }
+ break;
+
+ case IResourceView::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 = desc.bufferRange.firstElement;
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ uint64_t viewSize = 0;
+ if (desc.bufferElementSize)
+ {
+ srvDesc.Buffer.StructureByteStride = (UINT)desc.bufferElementSize;
+ srvDesc.Buffer.NumElements =
+ desc.bufferRange.elementCount == 0
+ ? UINT(resourceDesc.sizeInBytes / desc.bufferElementSize)
+ : (UINT)desc.bufferRange.elementCount;
+ viewSize = (uint64_t)desc.bufferElementSize * srvDesc.Buffer.NumElements;
+ }
+ else if (desc.format == Format::Unknown)
+ {
+ srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
+ srvDesc.Buffer.NumElements = desc.bufferRange.elementCount == 0
+ ? UINT(resourceDesc.sizeInBytes / 4)
+ : UINT(desc.bufferRange.elementCount / 4);
+ srvDesc.Buffer.Flags |= D3D12_BUFFER_SRV_FLAG_RAW;
+ viewSize = 4ull * srvDesc.Buffer.NumElements;
+ }
+ else
+ {
+ FormatInfo sizeInfo;
+ gfxGetFormatInfo(desc.format, &sizeInfo);
+ assert(sizeInfo.pixelsPerBlock == 1);
+ srvDesc.Buffer.NumElements =
+ desc.bufferRange.elementCount == 0
+ ? UINT(resourceDesc.sizeInBytes / sizeInfo.blockSizeInBytes)
+ : (UINT)desc.bufferRange.elementCount;
+ viewSize = (uint64_t)srvDesc.Buffer.NumElements * sizeInfo.blockSizeInBytes;
+ }
+ if (viewSize >= (1ull << 32) - 8)
+ {
+ // D3D12 does not support view descriptors that has size near 4GB.
+ // We will not create actual SRV/UAVs for such large buffers.
+ // However, a buffer this large can still be bound as root parameter.
+ // So instead of failing, we quietly ignore descriptor creation.
+ viewImpl->m_descriptor.cpuHandle.ptr = 0;
+ }
+ else
+ {
+ SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&viewImpl->m_descriptor));
+ viewImpl->m_allocator = m_cpuViewHeap;
+ m_device->CreateShaderResourceView(
+ resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle);
+ }
+ }
+ break;
+ }
+
+ returnComPtr(outView, viewImpl);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createFramebuffer(IFramebuffer::Desc const& desc, IFramebuffer** outFb)
+{
+ RefPtr<FramebufferImpl> framebuffer = new FramebufferImpl();
+ framebuffer->renderTargetViews.setCount(desc.renderTargetCount);
+ framebuffer->renderTargetDescriptors.setCount(desc.renderTargetCount);
+ framebuffer->renderTargetClearValues.setCount(desc.renderTargetCount);
+ for (GfxIndex i = 0; i < desc.renderTargetCount; i++)
+ {
+ framebuffer->renderTargetViews[i] =
+ static_cast<ResourceViewImpl*>(desc.renderTargetViews[i]);
+ framebuffer->renderTargetDescriptors[i] =
+ framebuffer->renderTargetViews[i]->m_descriptor.cpuHandle;
+ if (static_cast<ResourceViewImpl*>(desc.renderTargetViews[i])->m_resource.Ptr())
+ {
+ auto clearValue =
+ static_cast<TextureResourceImpl*>(
+ static_cast<ResourceViewImpl*>(desc.renderTargetViews[i])->m_resource.Ptr())
+ ->getDesc()
+ ->optimalClearValue.color;
+ memcpy(&framebuffer->renderTargetClearValues[i], &clearValue, sizeof(ColorClearValue));
+ }
+ else
+ {
+ memset(&framebuffer->renderTargetClearValues[i], 0, sizeof(ColorClearValue));
+ }
+ }
+ framebuffer->depthStencilView = static_cast<ResourceViewImpl*>(desc.depthStencilView);
+ if (desc.depthStencilView)
+ {
+ framebuffer->depthStencilClearValue =
+ static_cast<TextureResourceImpl*>(
+ static_cast<ResourceViewImpl*>(desc.depthStencilView)->m_resource.Ptr())
+ ->getDesc()
+ ->optimalClearValue.depthStencil;
+ framebuffer->depthStencilDescriptor =
+ static_cast<ResourceViewImpl*>(desc.depthStencilView)->m_descriptor.cpuHandle;
+ }
+ else
+ {
+ framebuffer->depthStencilDescriptor.ptr = 0;
+ }
+ returnComPtr(outFb, framebuffer);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createFramebufferLayout(
+ IFramebufferLayout::Desc const& desc, IFramebufferLayout** outLayout)
+{
+ RefPtr<FramebufferLayoutImpl> layout = new FramebufferLayoutImpl();
+ layout->m_renderTargets.setCount(desc.renderTargetCount);
+ for (GfxIndex i = 0; i < desc.renderTargetCount; i++)
+ {
+ layout->m_renderTargets[i] = desc.renderTargets[i];
+ }
+
+ if (desc.depthStencil)
+ {
+ layout->m_hasDepthStencil = true;
+ layout->m_depthStencil = *desc.depthStencil;
+ }
+ else
+ {
+ layout->m_hasDepthStencil = false;
+ }
+ returnComPtr(outLayout, layout);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createRenderPassLayout(
+ const IRenderPassLayout::Desc& desc, IRenderPassLayout** outRenderPassLayout)
+{
+ RefPtr<RenderPassLayoutImpl> result = new RenderPassLayoutImpl();
+ result->init(desc);
+ returnComPtr(outRenderPassLayout, result);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createInputLayout(IInputLayout::Desc const& desc, IInputLayout** outLayout)
+{
+ RefPtr<InputLayoutImpl> layout(new InputLayoutImpl);
+
+ // Work out a buffer size to hold all text
+ Size textSize = 0;
+ auto inputElementCount = desc.inputElementCount;
+ auto inputElements = desc.inputElements;
+ auto vertexStreamCount = desc.vertexStreamCount;
+ auto vertexStreams = desc.vertexStreams;
+ 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<D3D12_INPUT_ELEMENT_DESC>& elements = layout->m_elements;
+ elements.setCount(inputElementCount);
+
+ for (Int i = 0; i < inputElementCount; ++i)
+ {
+ const InputElementDesc& srcEle = inputElements[i];
+ const auto& srcStream = vertexStreams[srcEle.bufferSlotIndex];
+ 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 = (UINT)srcEle.bufferSlotIndex;
+ dstEle.AlignedByteOffset = (UINT)srcEle.offset;
+ dstEle.InputSlotClass = D3DUtil::getInputSlotClass(srcStream.slotClass);
+ dstEle.InstanceDataStepRate = (UINT)srcStream.instanceDataStepRate;
+ }
+
+ auto& vertexStreamStrides = layout->m_vertexStreamStrides;
+ vertexStreamStrides.setCount(vertexStreamCount);
+ for (GfxIndex i = 0; i < vertexStreamCount; ++i)
+ {
+ vertexStreamStrides[i] = (UINT)vertexStreams[i].stride;
+ }
+
+ returnComPtr(outLayout, layout);
+ return SLANG_OK;
+}
+
+const gfx::DeviceInfo& DeviceImpl::getDeviceInfo() const { return m_info; }
+
+Result DeviceImpl::readBufferResource(
+ IBufferResource* bufferIn, Offset offset, Size size, ISlangBlob** outBlob)
+{
+
+ BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(bufferIn);
+
+ const Size bufferSize = buffer->getDesc()->sizeInBytes;
+
+ // This will be slow!!! - it blocks CPU on GPU completion
+ D3D12Resource& resource = buffer->m_resource;
+
+ D3D12Resource stageBuf;
+ if (buffer->getDesc()->memoryType != MemoryType::ReadBack)
+ {
+ auto encodeInfo = encodeResourceCommands();
+
+ // 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(size, stagingDesc);
+
+ SLANG_RETURN_ON_FAIL(stageBuf.initCommitted(
+ m_device,
+ heapProps,
+ D3D12_HEAP_FLAG_NONE,
+ stagingDesc,
+ D3D12_RESOURCE_STATE_COPY_DEST,
+ nullptr));
+
+ // Do the copy
+ encodeInfo.d3dCommandList->CopyBufferRegion(stageBuf, 0, resource, offset, size);
+
+ // Wait until complete
+ submitResourceCommandsAndWait(encodeInfo);
+ }
+
+ D3D12Resource& stageBufRef =
+ buffer->getDesc()->memoryType != MemoryType::ReadBack ? stageBuf : resource;
+
+ // Map and copy
+ RefPtr<ListBlob> blob = new ListBlob();
+ {
+ UINT8* data;
+ D3D12_RANGE readRange = { 0, size };
+
+ SLANG_RETURN_ON_FAIL(
+ stageBufRef.getResource()->Map(0, &readRange, reinterpret_cast<void**>(&data)));
+
+ // Copy to memory buffer
+ blob->m_data.setCount(size);
+ ::memcpy(blob->m_data.getBuffer(), data, size);
+
+ stageBufRef.getResource()->Unmap(0, nullptr);
+ }
+ returnComPtr(outBlob, blob);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createProgram(
+ const IShaderProgram::Desc& desc, IShaderProgram** outProgram, ISlangBlob** outDiagnosticBlob)
+{
+ RefPtr<ShaderProgramImpl> shaderProgram = new ShaderProgramImpl();
+ shaderProgram->init(desc);
+ ComPtr<ID3DBlob> d3dDiagnosticBlob;
+ auto rootShaderLayoutResult = RootShaderObjectLayoutImpl::create(
+ this,
+ shaderProgram->linkedProgram,
+ shaderProgram->linkedProgram->getLayout(),
+ shaderProgram->m_rootObjectLayout.writeRef(),
+ d3dDiagnosticBlob.writeRef());
+ if (!SLANG_SUCCEEDED(rootShaderLayoutResult))
+ {
+ if (outDiagnosticBlob && d3dDiagnosticBlob)
+ {
+ RefPtr<StringBlob> diagnosticBlob =
+ new StringBlob(String((const char*)d3dDiagnosticBlob->GetBufferPointer()));
+ returnComPtr(outDiagnosticBlob, diagnosticBlob);
+ }
+ return rootShaderLayoutResult;
+ }
+ returnComPtr(outProgram, shaderProgram);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createShaderObjectLayout(
+ slang::TypeLayoutReflection* typeLayout, ShaderObjectLayoutBase** outLayout)
+{
+ RefPtr<ShaderObjectLayoutImpl> layout;
+ SLANG_RETURN_ON_FAIL(
+ ShaderObjectLayoutImpl::createForElementType(this, typeLayout, layout.writeRef()));
+ returnRefPtrMove(outLayout, layout);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject)
+{
+ RefPtr<ShaderObjectImpl> shaderObject;
+ SLANG_RETURN_ON_FAIL(ShaderObjectImpl::create(
+ this, reinterpret_cast<ShaderObjectLayoutImpl*>(layout), shaderObject.writeRef()));
+ returnComPtr(outObject, shaderObject);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createMutableShaderObject(
+ ShaderObjectLayoutBase* layout, IShaderObject** outObject)
+{
+ auto result = createShaderObject(layout, outObject);
+ SLANG_RETURN_ON_FAIL(result);
+ static_cast<ShaderObjectImpl*>(*outObject)->m_isMutable = true;
+ return result;
+}
+
+Result DeviceImpl::createMutableRootShaderObject(IShaderProgram* program, IShaderObject** outObject)
+{
+ RefPtr<MutableRootShaderObjectImpl> result = new MutableRootShaderObjectImpl();
+ result->init(this);
+ auto programImpl = static_cast<ShaderProgramImpl*>(program);
+ result->resetImpl(
+ this, programImpl->m_rootObjectLayout, m_cpuViewHeap.Ptr(), m_cpuSamplerHeap.Ptr(), true);
+ returnComPtr(outObject, result);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createShaderTable(const IShaderTable::Desc& desc, IShaderTable** outShaderTable)
+{
+ RefPtr<ShaderTableImpl> result = new ShaderTableImpl();
+ result->m_device = this;
+ result->init(desc);
+ returnComPtr(outShaderTable, result);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createGraphicsPipelineState(
+ const GraphicsPipelineStateDesc& desc, IPipelineState** outState)
+{
+ RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(this);
+ pipelineStateImpl->init(desc);
+ returnComPtr(outState, pipelineStateImpl);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createComputePipelineState(
+ const ComputePipelineStateDesc& desc, IPipelineState** outState)
+{
+ RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(this);
+ pipelineStateImpl->init(desc);
+ returnComPtr(outState, pipelineStateImpl);
+ return SLANG_OK;
+}
+
+DeviceImpl::ResourceCommandRecordInfo DeviceImpl::encodeResourceCommands()
+{
+ ResourceCommandRecordInfo info;
+ m_resourceCommandTransientHeap->createCommandBuffer(info.commandBuffer.writeRef());
+ info.d3dCommandList = static_cast<CommandBufferImpl*>(info.commandBuffer.get())->m_cmdList;
+ return info;
+}
+
+void DeviceImpl::submitResourceCommandsAndWait(const DeviceImpl::ResourceCommandRecordInfo& info)
+{
+ info.commandBuffer->close();
+ m_resourceCommandQueue->executeCommandBuffer(info.commandBuffer);
+ m_resourceCommandTransientHeap->finish();
+ m_resourceCommandTransientHeap->synchronizeAndReset();
+}
+
+Result DeviceImpl::createQueryPool(const IQueryPool::Desc& desc, IQueryPool** outState)
+{
+ switch (desc.type)
+ {
+ case QueryType::AccelerationStructureCompactedSize:
+ case QueryType::AccelerationStructureSerializedSize:
+ case QueryType::AccelerationStructureCurrentSize:
+ {
+ RefPtr<PlainBufferProxyQueryPoolImpl> queryPoolImpl =
+ new PlainBufferProxyQueryPoolImpl();
+ uint32_t stride = 8;
+ if (desc.type == QueryType::AccelerationStructureSerializedSize)
+ stride = 16;
+ SLANG_RETURN_ON_FAIL(queryPoolImpl->init(desc, this, stride));
+ returnComPtr(outState, queryPoolImpl);
+ return SLANG_OK;
+ }
+ default:
+ {
+ RefPtr<QueryPoolImpl> queryPoolImpl = new QueryPoolImpl();
+ SLANG_RETURN_ON_FAIL(queryPoolImpl->init(desc, this));
+ returnComPtr(outState, queryPoolImpl);
+ return SLANG_OK;
+ }
+ }
+}
+
+Result DeviceImpl::createFence(const IFence::Desc& desc, IFence** outFence)
+{
+ RefPtr<FenceImpl> fence = new FenceImpl();
+ SLANG_RETURN_ON_FAIL(fence->init(this, desc));
+ returnComPtr(outFence, fence);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::waitForFences(
+ GfxCount fenceCount, IFence** fences, uint64_t* fenceValues, bool waitForAll, uint64_t timeout)
+{
+ ShortList<HANDLE> waitHandles;
+ for (GfxCount i = 0; i < fenceCount; ++i)
+ {
+ auto fenceImpl = static_cast<FenceImpl*>(fences[i]);
+ waitHandles.add(fenceImpl->getWaitEvent());
+ SLANG_RETURN_ON_FAIL(
+ fenceImpl->m_fence->SetEventOnCompletion(fenceValues[i], fenceImpl->getWaitEvent()));
+ }
+ auto result = WaitForMultipleObjects(
+ fenceCount,
+ waitHandles.getArrayView().getBuffer(),
+ waitForAll ? TRUE : FALSE,
+ timeout == kTimeoutInfinite ? INFINITE : (DWORD)(timeout / 1000000));
+ if (result == WAIT_TIMEOUT)
+ return SLANG_E_TIME_OUT;
+ return result == WAIT_FAILED ? SLANG_FAIL : SLANG_OK;
+}
+
+Result DeviceImpl::getAccelerationStructurePrebuildInfo(
+ const IAccelerationStructure::BuildInputs& buildInputs,
+ IAccelerationStructure::PrebuildInfo* outPrebuildInfo)
+{
+ if (!m_device5)
+ return SLANG_E_NOT_AVAILABLE;
+
+ D3DAccelerationStructureInputsBuilder inputsBuilder;
+ SLANG_RETURN_ON_FAIL(inputsBuilder.build(buildInputs, getDebugCallback()));
+
+ D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO prebuildInfo;
+ m_device5->GetRaytracingAccelerationStructurePrebuildInfo(&inputsBuilder.desc, &prebuildInfo);
+
+ outPrebuildInfo->resultDataMaxSize = (Size)prebuildInfo.ResultDataMaxSizeInBytes;
+ outPrebuildInfo->scratchDataSize = (Size)prebuildInfo.ScratchDataSizeInBytes;
+ outPrebuildInfo->updateScratchDataSize = (Size)prebuildInfo.UpdateScratchDataSizeInBytes;
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createAccelerationStructure(
+ const IAccelerationStructure::CreateDesc& desc, IAccelerationStructure** outAS)
+{
+#if SLANG_GFX_HAS_DXR_SUPPORT
+ RefPtr<AccelerationStructureImpl> result = new AccelerationStructureImpl();
+ result->m_device5 = m_device5;
+ result->m_buffer = static_cast<BufferResourceImpl*>(desc.buffer);
+ result->m_size = desc.size;
+ result->m_offset = desc.offset;
+ result->m_allocator = m_cpuViewHeap;
+ result->m_desc.type = IResourceView::Type::AccelerationStructure;
+ SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&result->m_descriptor));
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ srvDesc.Format = DXGI_FORMAT_UNKNOWN;
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE;
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ srvDesc.RaytracingAccelerationStructure.Location =
+ result->m_buffer->getDeviceAddress() + desc.offset;
+ m_device->CreateShaderResourceView(nullptr, &srvDesc, result->m_descriptor.cpuHandle);
+ returnComPtr(outAS, result);
+ return SLANG_OK;
+#else
+ * outAS = nullptr;
+ return SLANG_FAIL;
+#endif
+}
+
+Result DeviceImpl::createRayTracingPipelineState(
+ const RayTracingPipelineStateDesc& inDesc, IPipelineState** outState)
+{
+ if (!m_device5)
+ {
+ return SLANG_E_NOT_AVAILABLE;
+ }
+
+ RefPtr<RayTracingPipelineStateImpl> pipelineStateImpl = new RayTracingPipelineStateImpl(this);
+ pipelineStateImpl->init(inDesc);
+ returnComPtr(outState, pipelineStateImpl);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createTransientResourceHeapImpl(
+ ITransientResourceHeap::Flags::Enum flags,
+ Size constantBufferSize,
+ uint32_t viewDescriptors,
+ uint32_t samplerDescriptors,
+ TransientResourceHeapImpl** outHeap)
+{
+ RefPtr<TransientResourceHeapImpl> result = new TransientResourceHeapImpl();
+ ITransientResourceHeap::Desc desc = {};
+ desc.flags = flags;
+ desc.samplerDescriptorCount = samplerDescriptors;
+ desc.constantBufferSize = constantBufferSize;
+ desc.constantBufferDescriptorCount = viewDescriptors;
+ desc.accelerationStructureDescriptorCount = viewDescriptors;
+ desc.srvDescriptorCount = viewDescriptors;
+ desc.uavDescriptorCount = viewDescriptors;
+ SLANG_RETURN_ON_FAIL(result->init(desc, this, viewDescriptors, samplerDescriptors));
+ returnRefPtrMove(outHeap, result);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createCommandQueueImpl(CommandQueueImpl** outQueue)
+{
+ int queueIndex = m_queueIndexAllocator.alloc(1);
+ // If we run out of queue index space, then the user is requesting too many queues.
+ if (queueIndex == -1)
+ return SLANG_FAIL;
+
+ RefPtr<CommandQueueImpl> queue = new CommandQueueImpl();
+ SLANG_RETURN_ON_FAIL(queue->init(this, (uint32_t)queueIndex));
+ returnRefPtrMove(outQueue, queue);
+ return SLANG_OK;
+}
+
+PROC DeviceImpl::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;
+}
+
+DeviceImpl::~DeviceImpl() { m_shaderObjectLayoutCache = decltype(m_shaderObjectLayoutCache)(); }
+
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-device.h b/tools/gfx/d3d12/d3d12-device.h
new file mode 100644
index 000000000..71daf9ffb
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-device.h
@@ -0,0 +1,253 @@
+// d3d12-device.h
+#pragma once
+#include "d3d12-command-buffer.h"
+#include "d3d12-command-queue.h"
+#include "d3d12-transient-heap.h"
+#include "d3d12-texture.h"
+
+#include <d3d12.h>
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+// Define function pointer types for PIX library.
+typedef HRESULT(WINAPI* PFN_BeginEventOnCommandList)(
+ ID3D12GraphicsCommandList* commandList, UINT64 color, _In_ PCSTR formatString);
+typedef HRESULT(WINAPI* PFN_EndEventOnCommandList)(ID3D12GraphicsCommandList* commandList);
+
+struct D3D12DeviceInfo
+{
+ void clear()
+ {
+ m_dxgiFactory.setNull();
+ m_device.setNull();
+ m_adapter.setNull();
+ m_desc = {};
+ m_desc1 = {};
+ m_isWarp = false;
+ m_isSoftware = false;
+ }
+
+ bool m_isWarp;
+ bool m_isSoftware;
+ ComPtr<IDXGIFactory> m_dxgiFactory;
+ ComPtr<ID3D12Device> m_device;
+ ComPtr<ID3D12Device5> m_device5;
+ ComPtr<IDXGIAdapter> m_adapter;
+ DXGI_ADAPTER_DESC m_desc;
+ DXGI_ADAPTER_DESC1 m_desc1;
+};
+
+class DeviceImpl : public RendererBase
+{
+public:
+ Desc m_desc;
+ D3D12DeviceExtendedDesc m_extendedDesc;
+
+ DeviceInfo m_info;
+ String m_adapterName;
+
+ bool m_isInitialized = false;
+
+ ComPtr<ID3D12Debug> m_dxDebug;
+
+ D3D12DeviceInfo m_deviceInfo;
+ ID3D12Device* m_device = nullptr;
+ ID3D12Device5* m_device5 = nullptr;
+
+ VirtualObjectPool m_queueIndexAllocator;
+
+ RefPtr<CommandQueueImpl> m_resourceCommandQueue;
+ RefPtr<TransientResourceHeapImpl> m_resourceCommandTransientHeap;
+
+ RefPtr<D3D12GeneralExpandingDescriptorHeap> m_rtvAllocator;
+ RefPtr<D3D12GeneralExpandingDescriptorHeap> m_dsvAllocator;
+
+ // Space in the GPU-visible heaps is precious, so we will also keep
+ // around CPU-visible heaps for storing shader-objects' descriptors in a format
+ // that is ready for copying into the GPU-visible heaps as needed.
+ //
+ RefPtr<D3D12GeneralExpandingDescriptorHeap> m_cpuViewHeap; ///< Cbv, Srv, Uav
+ RefPtr<D3D12GeneralExpandingDescriptorHeap> m_cpuSamplerHeap; ///< Heap for samplers
+
+ // 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;
+
+ PFN_BeginEventOnCommandList m_BeginEventOnCommandList = nullptr;
+ PFN_EndEventOnCommandList m_EndEventOnCommandList = nullptr;
+
+ bool m_nvapi = false;
+
+ // Command signatures required for indirect draws. These indicate the format of the indirect
+ // as well as the command type to be used (DrawInstanced and DrawIndexedInstanced, in this
+ // case).
+ ComPtr<ID3D12CommandSignature> drawIndirectCmdSignature;
+ ComPtr<ID3D12CommandSignature> drawIndexedIndirectCmdSignature;
+ ComPtr<ID3D12CommandSignature> dispatchIndirectCmdSignature;
+
+public:
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL initialize(const Desc& desc) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getFormatSupportedResourceStates(Format format, ResourceStateSet* outStates) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTransientResourceHeap(
+ const ITransientResourceHeap::Desc& desc, ITransientResourceHeap** outHeap) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createSwapchain(
+ const ISwapchain::Desc& desc, WindowHandle window, ISwapchain** outSwapchain) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getTextureAllocationInfo(
+ const ITextureResource::Desc& desc, Size* outSize, Size* outAlignment) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL getTextureRowAlignment(Size* outAlignment) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTextureResource(
+ const ITextureResource::Desc& desc,
+ const ITextureResource::SubresourceData* initData,
+ ITextureResource** outResource) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTextureFromNativeHandle(
+ InteropHandle handle,
+ const ITextureResource::Desc& srcDesc,
+ ITextureResource** outResource) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createBufferResource(
+ const IBufferResource::Desc& desc,
+ const void* initData,
+ IBufferResource** outResource) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createBufferFromNativeHandle(
+ InteropHandle handle,
+ const IBufferResource::Desc& srcDesc,
+ IBufferResource** outResource) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createSamplerState(ISamplerState::Desc const& desc, ISamplerState** outSampler) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTextureView(
+ ITextureResource* texture,
+ IResourceView::Desc const& desc,
+ IResourceView** outView) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createBufferView(
+ IBufferResource* buffer,
+ IBufferResource* counterBuffer,
+ IResourceView::Desc const& desc,
+ IResourceView** outView) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createFramebuffer(IFramebuffer::Desc const& desc, IFramebuffer** outFrameBuffer) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL createFramebufferLayout(
+ IFramebufferLayout::Desc const& desc, IFramebufferLayout** outLayout) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL createRenderPassLayout(
+ const IRenderPassLayout::Desc& desc, IRenderPassLayout** outRenderPassLayout) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createInputLayout(IInputLayout::Desc const& desc, IInputLayout** outLayout) override;
+
+ virtual Result createShaderObjectLayout(
+ slang::TypeLayoutReflection* typeLayout, ShaderObjectLayoutBase** outLayout) override;
+ virtual Result createShaderObject(
+ ShaderObjectLayoutBase* layout, IShaderObject** outObject) override;
+ virtual Result createMutableShaderObject(
+ ShaderObjectLayoutBase* layout, IShaderObject** outObject) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createMutableRootShaderObject(IShaderProgram* program, IShaderObject** outObject) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createShaderTable(const IShaderTable::Desc& desc, IShaderTable** outShaderTable) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createProgram(
+ const IShaderProgram::Desc& desc,
+ IShaderProgram** outProgram,
+ ISlangBlob** outDiagnostics) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createGraphicsPipelineState(
+ const GraphicsPipelineStateDesc& desc, IPipelineState** outState) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createComputePipelineState(
+ const ComputePipelineStateDesc& desc, IPipelineState** outState) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createQueryPool(const IQueryPool::Desc& desc, IQueryPool** outState) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createFence(const IFence::Desc& desc, IFence** outFence) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL waitForFences(
+ GfxCount fenceCount,
+ IFence** fences,
+ uint64_t* fenceValues,
+ bool waitForAll,
+ uint64_t timeout) override;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL readTextureResource(
+ ITextureResource* resource,
+ ResourceState state,
+ ISlangBlob** outBlob,
+ Size* outRowPitch,
+ Size* outPixelSize) override;
+
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL readBufferResource(
+ IBufferResource* resource, Offset offset, Size size, ISlangBlob** outBlob) override;
+
+ virtual SLANG_NO_THROW const gfx::DeviceInfo& SLANG_MCALL getDeviceInfo() const override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getNativeDeviceHandles(InteropHandles* outHandles) override;
+
+ ~DeviceImpl();
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getAccelerationStructurePrebuildInfo(
+ const IAccelerationStructure::BuildInputs& buildInputs,
+ IAccelerationStructure::PrebuildInfo* outPrebuildInfo) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createAccelerationStructure(
+ const IAccelerationStructure::CreateDesc& desc, IAccelerationStructure** outView) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createRayTracingPipelineState(
+ const RayTracingPipelineStateDesc& desc, IPipelineState** outState) override;
+
+public:
+ static PROC loadProc(HMODULE module, char const* name);
+
+ Result createCommandQueueImpl(CommandQueueImpl** outQueue);
+
+ Result createTransientResourceHeapImpl(
+ ITransientResourceHeap::Flags::Enum flags,
+ Size constantBufferSize,
+ uint32_t viewDescriptors,
+ uint32_t samplerDescriptors,
+ TransientResourceHeapImpl** outHeap);
+
+ Result createBuffer(
+ const D3D12_RESOURCE_DESC& resourceDesc,
+ const void* srcData,
+ Size srcDataSize,
+ D3D12_RESOURCE_STATES finalState,
+ D3D12Resource& resourceOut,
+ bool isShared,
+ MemoryType access = MemoryType::DeviceLocal);
+
+ Result captureTextureToSurface(
+ TextureResourceImpl* resource,
+ ResourceState state,
+ ISlangBlob** blob,
+ Size* outRowPitch,
+ Size* outPixelSize);
+
+ Result _createDevice(
+ DeviceCheckFlags deviceCheckFlags,
+ const UnownedStringSlice& nameMatch,
+ D3D_FEATURE_LEVEL featureLevel,
+ D3D12DeviceInfo& outDeviceInfo);
+
+ struct ResourceCommandRecordInfo
+ {
+ ComPtr<ICommandBuffer> commandBuffer;
+ ID3D12GraphicsCommandList* d3dCommandList;
+ };
+ ResourceCommandRecordInfo encodeResourceCommands();
+ void submitResourceCommandsAndWait(const ResourceCommandRecordInfo& info);
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-fence.cpp b/tools/gfx/d3d12/d3d12-fence.cpp
new file mode 100644
index 000000000..be3d94fcd
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-fence.cpp
@@ -0,0 +1,74 @@
+// d3d12-fence.cpp
+#include "d3d12-fence.h"
+
+#include "d3d12-device.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+FenceImpl::~FenceImpl()
+{
+ if (m_waitEvent)
+ CloseHandle(m_waitEvent);
+}
+
+HANDLE FenceImpl::getWaitEvent()
+{
+ if (m_waitEvent)
+ return m_waitEvent;
+ m_waitEvent = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
+ return m_waitEvent;
+}
+
+Result FenceImpl::init(DeviceImpl* device, const IFence::Desc& desc)
+{
+ SLANG_RETURN_ON_FAIL(device->m_device->CreateFence(
+ desc.initialValue,
+ desc.isShared ? D3D12_FENCE_FLAG_SHARED : D3D12_FENCE_FLAG_NONE,
+ IID_PPV_ARGS(m_fence.writeRef())));
+ return SLANG_OK;
+}
+
+Result FenceImpl::getCurrentValue(uint64_t* outValue)
+{
+ *outValue = m_fence->GetCompletedValue();
+ return SLANG_OK;
+}
+
+Result FenceImpl::setCurrentValue(uint64_t value)
+{
+ SLANG_RETURN_ON_FAIL(m_fence->Signal(value));
+ return SLANG_OK;
+}
+
+Result FenceImpl::getSharedHandle(InteropHandle* outHandle)
+{
+ // Check if a shared handle already exists.
+ if (sharedHandle.handleValue != 0)
+ {
+ *outHandle = sharedHandle;
+ return SLANG_OK;
+ }
+
+ ComPtr<ID3D12Device> devicePtr;
+ m_fence->GetDevice(IID_PPV_ARGS(devicePtr.writeRef()));
+ SLANG_RETURN_ON_FAIL(devicePtr->CreateSharedHandle(
+ m_fence, NULL, GENERIC_ALL, nullptr, (HANDLE*)&outHandle->handleValue));
+ outHandle->api = InteropHandleAPI::D3D12;
+ sharedHandle = *outHandle;
+ return SLANG_OK;
+}
+
+Result FenceImpl::getNativeHandle(InteropHandle* outNativeHandle)
+{
+ outNativeHandle->api = gfx::InteropHandleAPI::D3D12;
+ outNativeHandle->handleValue = (uint64_t)m_fence.get();
+ return SLANG_OK;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-fence.h b/tools/gfx/d3d12/d3d12-fence.h
new file mode 100644
index 000000000..9a109ae65
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-fence.h
@@ -0,0 +1,37 @@
+// d3d12-fence.h
+#pragma once
+
+#include "d3d12-base.h"
+
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class FenceImpl : public FenceBase
+{
+public:
+ ComPtr<ID3D12Fence> m_fence;
+ HANDLE m_waitEvent = 0;
+
+ ~FenceImpl();
+
+ HANDLE getWaitEvent();
+
+ Result init(DeviceImpl* device, const IFence::Desc& desc);
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getCurrentValue(uint64_t* outValue) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setCurrentValue(uint64_t value) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getNativeHandle(InteropHandle* outNativeHandle) override;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-framebuffer.h b/tools/gfx/d3d12/d3d12-framebuffer.h
new file mode 100644
index 000000000..34ebc1e2e
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-framebuffer.h
@@ -0,0 +1,38 @@
+// d3d12-framebuffer.h
+#pragma once
+
+#include "d3d12-base.h"
+#include "d3d12-resource-views.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class FramebufferLayoutImpl : public FramebufferLayoutBase
+{
+public:
+ ShortList<IFramebufferLayout::TargetLayout> m_renderTargets;
+ bool m_hasDepthStencil = false;
+ IFramebufferLayout::TargetLayout m_depthStencil;
+};
+
+class FramebufferImpl : public FramebufferBase
+{
+public:
+ ShortList<RefPtr<ResourceViewImpl>> renderTargetViews;
+ RefPtr<ResourceViewImpl> depthStencilView;
+ ShortList<D3D12_CPU_DESCRIPTOR_HANDLE> renderTargetDescriptors;
+ struct Color4f
+ {
+ float values[4];
+ };
+ ShortList<Color4f> renderTargetClearValues;
+ D3D12_CPU_DESCRIPTOR_HANDLE depthStencilDescriptor;
+ DepthStencilClearValue depthStencilClearValue;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-helper-functions.cpp b/tools/gfx/d3d12/d3d12-helper-functions.cpp
new file mode 100644
index 000000000..76e80b017
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-helper-functions.cpp
@@ -0,0 +1,590 @@
+// d3d12-helper-functions.cpp
+#include "d3d12-helper-functions.h"
+
+#ifdef GFX_NVAPI
+# include "../nvapi/nvapi-include.h"
+#endif
+
+#include "../nvapi/nvapi-util.h"
+#include "d3d12-buffer.h"
+#include "d3d12-transient-heap.h"
+#include "d3d12-query.h"
+
+#ifdef _DEBUG
+# define ENABLE_DEBUG_LAYER 1
+#else
+# define ENABLE_DEBUG_LAYER 0
+#endif
+
+namespace gfx
+{
+
+using namespace Slang;
+
+namespace d3d12
+{
+
+bool isSupportedNVAPIOp(ID3D12Device* dev, uint32_t op)
+{
+#ifdef GFX_NVAPI
+ {
+ bool isSupported;
+ NvAPI_Status status =
+ NvAPI_D3D12_IsNvShaderExtnOpCodeSupported(dev, NvU32(op), &isSupported);
+ return status == NVAPI_OK && isSupported;
+ }
+#else
+ return false;
+#endif
+}
+
+D3D12_RESOURCE_FLAGS calcResourceFlag(ResourceState state)
+{
+ switch (state)
+ {
+ case ResourceState::RenderTarget:
+ return D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+ case ResourceState::DepthRead:
+ case ResourceState::DepthWrite:
+ return D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
+ case ResourceState::UnorderedAccess:
+ case ResourceState::AccelerationStructure:
+ return D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+ default:
+ return D3D12_RESOURCE_FLAG_NONE;
+ }
+}
+
+D3D12_RESOURCE_FLAGS calcResourceFlags(ResourceStateSet states)
+{
+ int dstFlags = 0;
+ for (uint32_t i = 0; i < (uint32_t)ResourceState::_Count; i++)
+ {
+ auto state = (ResourceState)i;
+ if (states.contains(state))
+ dstFlags |= calcResourceFlag(state);
+ }
+ return (D3D12_RESOURCE_FLAGS)dstFlags;
+}
+
+D3D12_RESOURCE_DIMENSION calcResourceDimension(IResource::Type type)
+{
+ switch (type)
+ {
+ case IResource::Type::Buffer:
+ return D3D12_RESOURCE_DIMENSION_BUFFER;
+ case IResource::Type::Texture1D:
+ return D3D12_RESOURCE_DIMENSION_TEXTURE1D;
+ case IResource::Type::TextureCube:
+ case IResource::Type::Texture2D:
+ {
+ return D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ }
+ case IResource::Type::Texture3D:
+ return D3D12_RESOURCE_DIMENSION_TEXTURE3D;
+ default:
+ return D3D12_RESOURCE_DIMENSION_UNKNOWN;
+ }
+}
+
+DXGI_FORMAT getTypelessFormatFromDepthFormat(Format format)
+{
+ switch (format)
+ {
+ case Format::D16_UNORM:
+ return DXGI_FORMAT_R16_TYPELESS;
+ case Format::D32_FLOAT:
+ return DXGI_FORMAT_R32_TYPELESS;
+ default:
+ return D3DUtil::getMapFormat(format);
+ }
+}
+
+bool isTypelessDepthFormat(DXGI_FORMAT format)
+{
+ switch (format)
+ {
+ case DXGI_FORMAT_R16_TYPELESS:
+ case DXGI_FORMAT_R32_TYPELESS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+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
+ }
+}
+
+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
+ }
+}
+
+uint32_t getViewDescriptorCount(const ITransientResourceHeap::Desc& desc)
+{
+ return Math::Max(
+ Math::Max(
+ desc.srvDescriptorCount,
+ desc.uavDescriptorCount,
+ desc.accelerationStructureDescriptorCount),
+ desc.constantBufferDescriptorCount,
+ 2048);
+}
+
+void initSrvDesc(
+ IResource::Type resourceType,
+ const ITextureResource::Desc& textureDesc,
+ const D3D12_RESOURCE_DESC& desc,
+ DXGI_FORMAT pixelFormat,
+ SubresourceRange subresourceRange,
+ 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;
+ descOut.Texture1D.MipLevels = subresourceRange.mipLevelCount == 0
+ ? desc.MipLevels - subresourceRange.mipLevel
+ : subresourceRange.mipLevelCount;
+ descOut.Texture1D.MostDetailedMip = subresourceRange.mipLevel;
+ break;
+ case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
+ descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ descOut.Texture2D.PlaneSlice =
+ D3DUtil::getPlaneSlice(descOut.Format, subresourceRange.aspectMask);
+ descOut.Texture2D.ResourceMinLODClamp = 0.0f;
+ descOut.Texture2D.MipLevels = subresourceRange.mipLevelCount == 0
+ ? desc.MipLevels - subresourceRange.mipLevel
+ : subresourceRange.mipLevelCount;
+ descOut.Texture2D.MostDetailedMip = subresourceRange.mipLevel;
+ break;
+ case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
+ descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
+ descOut.Texture3D.MipLevels = subresourceRange.mipLevelCount == 0
+ ? desc.MipLevels - subresourceRange.mipLevel
+ : subresourceRange.mipLevelCount;
+ descOut.Texture3D.MostDetailedMip = subresourceRange.mipLevel;
+ break;
+ default:
+ assert(!"Unknown dimension");
+ }
+ }
+ else if (resourceType == IResource::Type::TextureCube)
+ {
+ if (textureDesc.arraySize > 1)
+ {
+ descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
+
+ descOut.TextureCubeArray.NumCubes = subresourceRange.layerCount == 0
+ ? textureDesc.arraySize
+ : subresourceRange.layerCount / 6;
+ descOut.TextureCubeArray.First2DArrayFace = subresourceRange.baseArrayLayer;
+ descOut.TextureCubeArray.MipLevels = subresourceRange.mipLevelCount == 0
+ ? desc.MipLevels - subresourceRange.mipLevel
+ : subresourceRange.mipLevelCount;
+ descOut.TextureCubeArray.MostDetailedMip = subresourceRange.mipLevel;
+ descOut.TextureCubeArray.ResourceMinLODClamp = 0;
+ }
+ else
+ {
+ descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
+
+ descOut.TextureCube.MipLevels = subresourceRange.mipLevelCount == 0
+ ? desc.MipLevels - subresourceRange.mipLevel
+ : subresourceRange.mipLevelCount;
+ descOut.TextureCube.MostDetailedMip = subresourceRange.mipLevel;
+ descOut.TextureCube.ResourceMinLODClamp = 0;
+ }
+ }
+ else
+ {
+ assert(desc.DepthOrArraySize > 1);
+
+ switch (desc.Dimension)
+ {
+ case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
+ descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
+ descOut.Texture1D.MostDetailedMip = subresourceRange.mipLevel;
+ descOut.Texture1D.MipLevels = subresourceRange.mipLevelCount == 0
+ ? desc.MipLevels
+ : subresourceRange.mipLevelCount;
+ descOut.Texture1DArray.ArraySize = subresourceRange.layerCount == 0
+ ? desc.DepthOrArraySize
+ : subresourceRange.layerCount;
+ descOut.Texture1DArray.FirstArraySlice = subresourceRange.baseArrayLayer;
+ descOut.Texture1DArray.ResourceMinLODClamp = 0;
+ descOut.Texture1DArray.MostDetailedMip = subresourceRange.mipLevel;
+ descOut.Texture1DArray.MipLevels = subresourceRange.mipLevelCount == 0
+ ? desc.MipLevels - subresourceRange.mipLevel
+ : subresourceRange.mipLevelCount;
+ break;
+ case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
+ descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
+ descOut.Texture2DArray.ArraySize = subresourceRange.layerCount == 0
+ ? desc.DepthOrArraySize
+ : subresourceRange.layerCount;
+ descOut.Texture2DArray.FirstArraySlice = subresourceRange.baseArrayLayer;
+ descOut.Texture2DArray.PlaneSlice =
+ D3DUtil::getPlaneSlice(descOut.Format, subresourceRange.aspectMask);
+ descOut.Texture2DArray.ResourceMinLODClamp = 0;
+ descOut.Texture2DArray.MostDetailedMip = subresourceRange.mipLevel;
+ descOut.Texture2DArray.MipLevels = subresourceRange.mipLevelCount == 0
+ ? desc.MipLevels - subresourceRange.mipLevel
+ : subresourceRange.mipLevelCount;
+ break;
+ case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
+ descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
+ descOut.Texture3D.MostDetailedMip = subresourceRange.mipLevel;
+ descOut.Texture3D.MipLevels = subresourceRange.mipLevelCount == 0
+ ? desc.MipLevels
+ : subresourceRange.mipLevelCount;
+ break;
+
+ default:
+ assert(!"Unknown dimension");
+ }
+ }
+}
+
+Result initTextureResourceDesc(
+ D3D12_RESOURCE_DESC& resourceDesc, const ITextureResource::Desc& srcDesc)
+{
+ const DXGI_FORMAT pixelFormat = D3DUtil::getMapFormat(srcDesc.format);
+ if (pixelFormat == DXGI_FORMAT_UNKNOWN)
+ {
+ return SLANG_FAIL;
+ }
+
+ const int arraySize = calcEffectiveArraySize(srcDesc);
+
+ const D3D12_RESOURCE_DIMENSION dimension = calcResourceDimension(srcDesc.type);
+ if (dimension == D3D12_RESOURCE_DIMENSION_UNKNOWN)
+ {
+ return SLANG_FAIL;
+ }
+
+ const int numMipMaps = srcDesc.numMipLevels;
+ 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.Flags |= calcResourceFlags(srcDesc.allowedStates);
+
+ resourceDesc.Alignment = 0;
+
+ if (isDepthFormat(srcDesc.format) &&
+ (srcDesc.allowedStates.contains(ResourceState::ShaderResource) ||
+ srcDesc.allowedStates.contains(ResourceState::UnorderedAccess)))
+ {
+ resourceDesc.Format = getTypelessFormatFromDepthFormat(srcDesc.format);
+ }
+
+ return SLANG_OK;
+}
+
+void initBufferResourceDesc(Size 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 uploadBufferDataImpl(
+ ID3D12Device* device,
+ ID3D12GraphicsCommandList* cmdList,
+ TransientResourceHeapImpl* transientHeap,
+ BufferResourceImpl* buffer,
+ Offset offset,
+ Size size,
+ void* data)
+{
+ IBufferResource* uploadResource;
+ Offset uploadResourceOffset = 0;
+ if (buffer->getDesc()->memoryType != MemoryType::Upload)
+ {
+ SLANG_RETURN_ON_FAIL(transientHeap->allocateStagingBuffer(
+ size, uploadResource, uploadResourceOffset, MemoryType::Upload));
+ }
+
+ D3D12Resource& uploadResourceRef =
+ (buffer->getDesc()->memoryType == MemoryType::Upload)
+ ? buffer->m_resource
+ : static_cast<BufferResourceImpl*>(uploadResource)->m_resource;
+
+ D3D12_RANGE readRange = {};
+ readRange.Begin = 0;
+ readRange.End = 0;
+ void* uploadData;
+ SLANG_RETURN_ON_FAIL(
+ uploadResourceRef.getResource()->Map(0, &readRange, reinterpret_cast<void**>(&uploadData)));
+ memcpy((uint8_t*)uploadData + uploadResourceOffset + offset, data, size);
+ D3D12_RANGE writtenRange = {};
+ writtenRange.Begin = uploadResourceOffset + offset;
+ writtenRange.End = uploadResourceOffset + offset + size;
+ uploadResourceRef.getResource()->Unmap(0, &writtenRange);
+
+ if (buffer->getDesc()->memoryType != MemoryType::Upload)
+ {
+ cmdList->CopyBufferRegion(
+ buffer->m_resource.getResource(),
+ offset,
+ uploadResourceRef.getResource(),
+ uploadResourceOffset + offset,
+ size);
+ }
+
+ return SLANG_OK;
+}
+
+Result createNullDescriptor(
+ ID3D12Device* d3dDevice,
+ D3D12_CPU_DESCRIPTOR_HANDLE destDescriptor,
+ const ShaderObjectLayoutImpl::BindingRangeInfo& bindingRange)
+{
+ switch (bindingRange.bindingType)
+ {
+ case slang::BindingType::ConstantBuffer:
+ {
+ D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
+ cbvDesc.BufferLocation = 0;
+ cbvDesc.SizeInBytes = 0;
+ d3dDevice->CreateConstantBufferView(&cbvDesc, destDescriptor);
+ }
+ break;
+ case slang::BindingType::MutableRawBuffer:
+ {
+ D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
+ uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
+ uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
+ d3dDevice->CreateUnorderedAccessView(nullptr, nullptr, &uavDesc, destDescriptor);
+ }
+ break;
+ case slang::BindingType::MutableTypedBuffer:
+ {
+ D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
+ uavDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ d3dDevice->CreateUnorderedAccessView(nullptr, nullptr, &uavDesc, destDescriptor);
+ }
+ break;
+ case slang::BindingType::RawBuffer:
+ {
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
+ srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
+ srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ d3dDevice->CreateShaderResourceView(nullptr, &srvDesc, destDescriptor);
+ }
+ break;
+ case slang::BindingType::TypedBuffer:
+ {
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
+ srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ d3dDevice->CreateShaderResourceView(nullptr, &srvDesc, destDescriptor);
+ }
+ break;
+ case slang::BindingType::Texture:
+ {
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ switch (bindingRange.resourceShape)
+ {
+ case SLANG_TEXTURE_1D:
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
+ break;
+ case SLANG_TEXTURE_1D_ARRAY:
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
+ break;
+ case SLANG_TEXTURE_2D:
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ break;
+ case SLANG_TEXTURE_2D_ARRAY:
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
+ break;
+ case SLANG_TEXTURE_3D:
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
+ break;
+ case SLANG_TEXTURE_CUBE:
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
+ break;
+ case SLANG_TEXTURE_CUBE_ARRAY:
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
+ break;
+ case SLANG_TEXTURE_2D_MULTISAMPLE:
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
+ break;
+ case SLANG_TEXTURE_2D_MULTISAMPLE_ARRAY:
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
+ break;
+ default:
+ return SLANG_OK;
+ }
+ d3dDevice->CreateShaderResourceView(nullptr, &srvDesc, destDescriptor);
+ }
+ break;
+ default:
+ break;
+ }
+ return SLANG_OK;
+}
+
+void translatePostBuildInfoDescs(
+ int propertyQueryCount,
+ AccelerationStructureQueryDesc* queryDescs,
+ List<D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC>& postBuildInfoDescs)
+{
+ postBuildInfoDescs.setCount(propertyQueryCount);
+ for (int i = 0; i < propertyQueryCount; i++)
+ {
+ switch (queryDescs[i].queryType)
+ {
+ case QueryType::AccelerationStructureCompactedSize:
+ postBuildInfoDescs[i].InfoType =
+ D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_COMPACTED_SIZE;
+ postBuildInfoDescs[i].DestBuffer =
+ static_cast<PlainBufferProxyQueryPoolImpl*>(queryDescs[i].queryPool)
+ ->m_bufferResource->getDeviceAddress() +
+ sizeof(D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_COMPACTED_SIZE_DESC) *
+ queryDescs[i].firstQueryIndex;
+ break;
+ case QueryType::AccelerationStructureCurrentSize:
+ postBuildInfoDescs[i].InfoType =
+ D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_CURRENT_SIZE;
+ postBuildInfoDescs[i].DestBuffer =
+ static_cast<PlainBufferProxyQueryPoolImpl*>(queryDescs[i].queryPool)
+ ->m_bufferResource->getDeviceAddress() +
+ sizeof(D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_COMPACTED_SIZE_DESC) *
+ queryDescs[i].firstQueryIndex;
+ break;
+ case QueryType::AccelerationStructureSerializedSize:
+ postBuildInfoDescs[i].InfoType =
+ D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_SERIALIZATION;
+ postBuildInfoDescs[i].DestBuffer =
+ static_cast<PlainBufferProxyQueryPoolImpl*>(queryDescs[i].queryPool)
+ ->m_bufferResource->getDeviceAddress() +
+ sizeof(D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_SERIALIZATION_DESC) *
+ queryDescs[i].firstQueryIndex;
+ break;
+ }
+ }
+}
+
+} // namespace d3d12
+
+Result SLANG_MCALL createD3D12Device(const IDevice::Desc* desc, IDevice** outDevice)
+{
+ RefPtr<d3d12::DeviceImpl> result = new d3d12::DeviceImpl();
+ SLANG_RETURN_ON_FAIL(result->initialize(*desc));
+ returnComPtr(outDevice, result);
+ return SLANG_OK;
+}
+
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-helper-functions.h b/tools/gfx/d3d12/d3d12-helper-functions.h
new file mode 100644
index 000000000..4882e0034
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-helper-functions.h
@@ -0,0 +1,81 @@
+// d3d12-helper-functions.h
+#pragma once
+
+#include "slang-gfx.h"
+#include "d3d12-base.h"
+#include "d3d12-shader-object-layout.h"
+#include "d3d12-submitter.h"
+
+#ifndef __ID3D12GraphicsCommandList1_FWD_DEFINED__
+// If can't find a definition of CommandList1, just use an empty definition
+struct ID3D12GraphicsCommandList1
+{};
+#endif
+
+namespace gfx
+{
+
+using namespace Slang;
+
+namespace d3d12
+{
+/// Contextual data and operations required when binding shader objects to the pipeline state
+struct BindingContext
+{
+ PipelineCommandEncoder* encoder;
+ Submitter* submitter;
+ TransientResourceHeapImpl* transientHeap;
+ DeviceImpl* device;
+ D3D12_DESCRIPTOR_HEAP_TYPE
+ outOfMemoryHeap; // The type of descriptor heap that is OOM during binding.
+};
+
+bool isSupportedNVAPIOp(ID3D12Device* dev, uint32_t op);
+
+D3D12_RESOURCE_FLAGS calcResourceFlag(ResourceState state);
+D3D12_RESOURCE_FLAGS calcResourceFlags(ResourceStateSet states);
+D3D12_RESOURCE_DIMENSION calcResourceDimension(IResource::Type type);
+
+DXGI_FORMAT getTypelessFormatFromDepthFormat(Format format);
+bool isTypelessDepthFormat(DXGI_FORMAT format);
+
+D3D12_FILTER_TYPE translateFilterMode(TextureFilteringMode mode);
+D3D12_FILTER_REDUCTION_TYPE translateFilterReduction(TextureReductionOp op);
+D3D12_TEXTURE_ADDRESS_MODE translateAddressingMode(TextureAddressingMode mode);
+D3D12_COMPARISON_FUNC translateComparisonFunc(ComparisonFunc func);
+
+uint32_t getViewDescriptorCount(const ITransientResourceHeap::Desc& desc);
+void initSrvDesc(
+ IResource::Type resourceType,
+ const ITextureResource::Desc& textureDesc,
+ const D3D12_RESOURCE_DESC& desc,
+ DXGI_FORMAT pixelFormat,
+ SubresourceRange subresourceRange,
+ D3D12_SHADER_RESOURCE_VIEW_DESC& descOut);
+Result initTextureResourceDesc(
+ D3D12_RESOURCE_DESC& resourceDesc, const ITextureResource::Desc& srcDesc);
+void initBufferResourceDesc(Size bufferSize, D3D12_RESOURCE_DESC& out);
+Result uploadBufferDataImpl(
+ ID3D12Device* device,
+ ID3D12GraphicsCommandList* cmdList,
+ TransientResourceHeapImpl* transientHeap,
+ BufferResourceImpl* buffer,
+ Offset offset,
+ Size size,
+ void* data);
+
+Result createNullDescriptor(
+ ID3D12Device* d3dDevice,
+ D3D12_CPU_DESCRIPTOR_HANDLE destDescriptor,
+ const ShaderObjectLayoutImpl::BindingRangeInfo& bindingRange);
+
+void translatePostBuildInfoDescs(
+ int propertyQueryCount,
+ AccelerationStructureQueryDesc* queryDescs,
+ List<D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC>& postBuildInfoDescs);
+
+} // namespace d3d12
+
+Result SLANG_MCALL createD3D12Device(const IDevice::Desc* desc, IDevice** outDevice);
+
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-pipeline-state.cpp b/tools/gfx/d3d12/d3d12-pipeline-state.cpp
new file mode 100644
index 000000000..983348409
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-pipeline-state.cpp
@@ -0,0 +1,469 @@
+// d3d12-ray-tracing.cpp
+#include "d3d12-pipeline-state.h"
+
+#ifdef GFX_NVAPI
+# include "../nvapi/nvapi-include.h"
+#endif
+
+#include "../nvapi/nvapi-util.h"
+#include "d3d12-device.h"
+#include "d3d12-framebuffer.h"
+#include "d3d12-shader-program.h"
+#include "d3d12-vertex-layout.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+void PipelineStateImpl::init(const GraphicsPipelineStateDesc& inDesc)
+{
+ PipelineStateDesc pipelineDesc;
+ pipelineDesc.type = PipelineType::Graphics;
+ pipelineDesc.graphics = inDesc;
+ initializeBase(pipelineDesc);
+}
+
+void PipelineStateImpl::init(const ComputePipelineStateDesc& inDesc)
+{
+ PipelineStateDesc pipelineDesc;
+ pipelineDesc.type = PipelineType::Compute;
+ pipelineDesc.compute = inDesc;
+ initializeBase(pipelineDesc);
+}
+
+Result PipelineStateImpl::getNativeHandle(InteropHandle* outHandle)
+{
+ SLANG_RETURN_ON_FAIL(ensureAPIPipelineStateCreated());
+ outHandle->api = InteropHandleAPI::D3D12;
+ outHandle->handleValue = reinterpret_cast<uint64_t>(m_pipelineState.get());
+ return SLANG_OK;
+}
+
+Result PipelineStateImpl::ensureAPIPipelineStateCreated()
+{
+ if (m_pipelineState)
+ return SLANG_OK;
+
+ auto programImpl = static_cast<ShaderProgramImpl*>(m_program.Ptr());
+ if (programImpl->m_shaders.getCount() == 0)
+ {
+ SLANG_RETURN_ON_FAIL(programImpl->compileShaders());
+ }
+ if (desc.type == PipelineType::Graphics)
+ {
+ // Only actually create a D3D12 pipeline state if the pipeline is fully specialized.
+ auto inputLayoutImpl = (InputLayoutImpl*)desc.graphics.inputLayout;
+
+ // Describe and create the graphics pipeline state object (PSO)
+ D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
+
+ psoDesc.pRootSignature = programImpl->m_rootObjectLayout->m_rootSignature;
+
+ for (auto& shaderBin : programImpl->m_shaders)
+ {
+ switch (shaderBin.stage)
+ {
+ case SLANG_STAGE_VERTEX:
+ psoDesc.VS = { shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount()) };
+ break;
+ case SLANG_STAGE_FRAGMENT:
+ psoDesc.PS = { shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount()) };
+ break;
+ case SLANG_STAGE_DOMAIN:
+ psoDesc.DS = { shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount()) };
+ break;
+ case SLANG_STAGE_HULL:
+ psoDesc.HS = { shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount()) };
+ break;
+ case SLANG_STAGE_GEOMETRY:
+ psoDesc.GS = { shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount()) };
+ break;
+ default:
+ getDebugCallback()->handleMessage(
+ DebugMessageType::Error,
+ DebugMessageSource::Layer,
+ "Unsupported shader stage.");
+ return SLANG_E_NOT_AVAILABLE;
+ }
+ }
+
+ if (inputLayoutImpl)
+ {
+ psoDesc.InputLayout = {
+ inputLayoutImpl->m_elements.getBuffer(),
+ UINT(inputLayoutImpl->m_elements.getCount()) };
+ }
+
+ psoDesc.PrimitiveTopologyType = D3DUtil::getPrimitiveType(desc.graphics.primitiveType);
+
+ {
+ auto framebufferLayout =
+ static_cast<FramebufferLayoutImpl*>(desc.graphics.framebufferLayout);
+ const int numRenderTargets = int(framebufferLayout->m_renderTargets.getCount());
+
+ if (framebufferLayout->m_hasDepthStencil)
+ {
+ psoDesc.DSVFormat = D3DUtil::getMapFormat(framebufferLayout->m_depthStencil.format);
+ psoDesc.SampleDesc.Count = framebufferLayout->m_depthStencil.sampleCount;
+ }
+ else
+ {
+ psoDesc.DSVFormat = DXGI_FORMAT_UNKNOWN;
+ if (framebufferLayout->m_renderTargets.getCount())
+ {
+ psoDesc.SampleDesc.Count = framebufferLayout->m_renderTargets[0].sampleCount;
+ }
+ }
+ psoDesc.NumRenderTargets = numRenderTargets;
+ for (Int i = 0; i < numRenderTargets; i++)
+ {
+ psoDesc.RTVFormats[i] =
+ D3DUtil::getMapFormat(framebufferLayout->m_renderTargets[i].format);
+ }
+
+ psoDesc.SampleDesc.Quality = 0;
+ psoDesc.SampleMask = UINT_MAX;
+ }
+
+ {
+ auto& rs = psoDesc.RasterizerState;
+ rs.FillMode = D3DUtil::getFillMode(desc.graphics.rasterizer.fillMode);
+ rs.CullMode = D3DUtil::getCullMode(desc.graphics.rasterizer.cullMode);
+ rs.FrontCounterClockwise =
+ desc.graphics.rasterizer.frontFace == gfx::FrontFaceMode::CounterClockwise ? TRUE
+ : FALSE;
+ rs.DepthBias = desc.graphics.rasterizer.depthBias;
+ rs.DepthBiasClamp = desc.graphics.rasterizer.depthBiasClamp;
+ rs.SlopeScaledDepthBias = desc.graphics.rasterizer.slopeScaledDepthBias;
+ rs.DepthClipEnable = desc.graphics.rasterizer.depthClipEnable ? TRUE : FALSE;
+ rs.MultisampleEnable = desc.graphics.rasterizer.multisampleEnable ? TRUE : FALSE;
+ rs.AntialiasedLineEnable =
+ desc.graphics.rasterizer.antialiasedLineEnable ? TRUE : FALSE;
+ rs.ForcedSampleCount = desc.graphics.rasterizer.forcedSampleCount;
+ rs.ConservativeRaster = desc.graphics.rasterizer.enableConservativeRasterization
+ ? D3D12_CONSERVATIVE_RASTERIZATION_MODE_ON
+ : D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
+ }
+
+ {
+ D3D12_BLEND_DESC& blend = psoDesc.BlendState;
+ blend.IndependentBlendEnable = FALSE;
+ blend.AlphaToCoverageEnable = desc.graphics.blend.alphaToCoverageEnable ? TRUE : FALSE;
+ blend.RenderTarget[0].RenderTargetWriteMask = (uint8_t)RenderTargetWriteMask::EnableAll;
+ for (GfxIndex i = 0; i < desc.graphics.blend.targetCount; i++)
+ {
+ auto& d3dDesc = blend.RenderTarget[i];
+ d3dDesc.BlendEnable = desc.graphics.blend.targets[i].enableBlend ? TRUE : FALSE;
+ d3dDesc.BlendOp = D3DUtil::getBlendOp(desc.graphics.blend.targets[i].color.op);
+ d3dDesc.BlendOpAlpha = D3DUtil::getBlendOp(desc.graphics.blend.targets[i].alpha.op);
+ d3dDesc.DestBlend =
+ D3DUtil::getBlendFactor(desc.graphics.blend.targets[i].color.dstFactor);
+ d3dDesc.DestBlendAlpha =
+ D3DUtil::getBlendFactor(desc.graphics.blend.targets[i].alpha.dstFactor);
+ d3dDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
+ d3dDesc.LogicOpEnable = FALSE;
+ d3dDesc.RenderTargetWriteMask = desc.graphics.blend.targets[i].writeMask;
+ d3dDesc.SrcBlend =
+ D3DUtil::getBlendFactor(desc.graphics.blend.targets[i].color.srcFactor);
+ d3dDesc.SrcBlendAlpha =
+ D3DUtil::getBlendFactor(desc.graphics.blend.targets[i].alpha.srcFactor);
+ }
+ for (GfxIndex i = 1; i < desc.graphics.blend.targetCount; i++)
+ {
+ if (memcmp(
+ &desc.graphics.blend.targets[i],
+ &desc.graphics.blend.targets[0],
+ sizeof(desc.graphics.blend.targets[0])) != 0)
+ {
+ blend.IndependentBlendEnable = TRUE;
+ break;
+ }
+ }
+ for (uint32_t i = (uint32_t)desc.graphics.blend.targetCount;
+ i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT;
+ ++i)
+ {
+ blend.RenderTarget[i] = blend.RenderTarget[0];
+ }
+ }
+
+ {
+ auto& ds = psoDesc.DepthStencilState;
+
+ ds.DepthEnable = desc.graphics.depthStencil.depthTestEnable;
+ ds.DepthWriteMask = desc.graphics.depthStencil.depthWriteEnable
+ ? D3D12_DEPTH_WRITE_MASK_ALL
+ : D3D12_DEPTH_WRITE_MASK_ZERO;
+ ds.DepthFunc = D3DUtil::getComparisonFunc(desc.graphics.depthStencil.depthFunc);
+ ds.StencilEnable = desc.graphics.depthStencil.stencilEnable;
+ ds.StencilReadMask = (UINT8)desc.graphics.depthStencil.stencilReadMask;
+ ds.StencilWriteMask = (UINT8)desc.graphics.depthStencil.stencilWriteMask;
+ ds.FrontFace = D3DUtil::translateStencilOpDesc(desc.graphics.depthStencil.frontFace);
+ ds.BackFace = D3DUtil::translateStencilOpDesc(desc.graphics.depthStencil.backFace);
+ }
+
+ psoDesc.PrimitiveTopologyType = D3DUtil::getPrimitiveType(desc.graphics.primitiveType);
+
+ if (m_device->m_pipelineCreationAPIDispatcher)
+ {
+ SLANG_RETURN_ON_FAIL(
+ m_device->m_pipelineCreationAPIDispatcher->createGraphicsPipelineState(
+ m_device,
+ programImpl->linkedProgram.get(),
+ &psoDesc,
+ (void**)m_pipelineState.writeRef()));
+ }
+ else
+ {
+ SLANG_RETURN_ON_FAIL(m_device->m_device->CreateGraphicsPipelineState(
+ &psoDesc, IID_PPV_ARGS(m_pipelineState.writeRef())));
+ }
+ }
+ else
+ {
+
+ // Only actually create a D3D12 pipeline state if the pipeline is fully specialized.
+ ComPtr<ID3D12PipelineState> pipelineState;
+ if (!programImpl->isSpecializable())
+ {
+ // Describe and create the compute pipeline state object
+ D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {};
+ computeDesc.pRootSignature =
+ desc.compute.d3d12RootSignatureOverride
+ ? static_cast<ID3D12RootSignature*>(desc.compute.d3d12RootSignatureOverride)
+ : programImpl->m_rootObjectLayout->m_rootSignature;
+ computeDesc.CS = {
+ programImpl->m_shaders[0].code.getBuffer(),
+ SIZE_T(programImpl->m_shaders[0].code.getCount()) };
+
+#ifdef GFX_NVAPI
+ if (m_nvapi)
+ {
+ // Also fill the extension structure.
+ // Use the same UAV slot index and register space that are declared in the shader.
+
+ // For simplicities sake we just use u0
+ NVAPI_D3D12_PSO_SET_SHADER_EXTENSION_SLOT_DESC extensionDesc;
+ extensionDesc.baseVersion = NV_PSO_EXTENSION_DESC_VER;
+ extensionDesc.version = NV_SET_SHADER_EXTENSION_SLOT_DESC_VER;
+ extensionDesc.uavSlot = 0;
+ extensionDesc.registerSpace = 0;
+
+ // Put the pointer to the extension into an array - there can be multiple extensions
+ // enabled at once.
+ const NVAPI_D3D12_PSO_EXTENSION_DESC* extensions[] = { &extensionDesc };
+
+ // Now create the PSO.
+ const NvAPI_Status nvapiStatus = NvAPI_D3D12_CreateComputePipelineState(
+ m_device->m_device,
+ &computeDesc,
+ SLANG_COUNT_OF(extensions),
+ extensions,
+ m_pipelineState.writeRef());
+
+ if (nvapiStatus != NVAPI_OK)
+ {
+ return SLANG_FAIL;
+ }
+ }
+ else
+#endif
+ {
+ if (m_device->m_pipelineCreationAPIDispatcher)
+ {
+ SLANG_RETURN_ON_FAIL(
+ m_device->m_pipelineCreationAPIDispatcher->createComputePipelineState(
+ m_device,
+ programImpl->linkedProgram.get(),
+ &computeDesc,
+ (void**)m_pipelineState.writeRef()));
+ }
+ else
+ {
+ SLANG_RETURN_ON_FAIL(m_device->m_device->CreateComputePipelineState(
+ &computeDesc, IID_PPV_ARGS(m_pipelineState.writeRef())));
+ }
+ }
+ }
+ }
+
+ return SLANG_OK;
+}
+
+#if SLANG_GFX_HAS_DXR_SUPPORT
+
+RayTracingPipelineStateImpl::RayTracingPipelineStateImpl(DeviceImpl* device)
+ : m_device(device)
+{}
+
+void RayTracingPipelineStateImpl::init(const RayTracingPipelineStateDesc& inDesc)
+{
+ PipelineStateDesc pipelineDesc;
+ pipelineDesc.type = PipelineType::RayTracing;
+ pipelineDesc.rayTracing.set(inDesc);
+ initializeBase(pipelineDesc);
+}
+
+Result RayTracingPipelineStateImpl::getNativeHandle(InteropHandle* outHandle)
+{
+ SLANG_RETURN_ON_FAIL(ensureAPIPipelineStateCreated());
+ outHandle->api = InteropHandleAPI::D3D12;
+ outHandle->handleValue = reinterpret_cast<uint64_t>(m_stateObject.get());
+ return SLANG_OK;
+}
+
+Result RayTracingPipelineStateImpl::ensureAPIPipelineStateCreated()
+{
+ if (m_stateObject)
+ return SLANG_OK;
+
+ auto program = static_cast<ShaderProgramImpl*>(m_program.Ptr());
+ auto slangGlobalScope = program->linkedProgram;
+ auto programLayout = slangGlobalScope->getLayout();
+
+ List<D3D12_STATE_SUBOBJECT> subObjects;
+ ChunkedList<D3D12_DXIL_LIBRARY_DESC> dxilLibraries;
+ ChunkedList<D3D12_HIT_GROUP_DESC> hitGroups;
+ ChunkedList<ComPtr<ISlangBlob>> codeBlobs;
+ ChunkedList<D3D12_EXPORT_DESC> exports;
+ ChunkedList<const wchar_t*> strPtrs;
+
+ ComPtr<ISlangBlob> diagnostics;
+ ChunkedList<OSString> stringPool;
+ auto getWStr = [&](const char* name)
+ {
+ String str = String(name);
+ auto wstr = str.toWString();
+ return stringPool.add(wstr)->begin();
+ };
+ auto compileShader = [&](slang::EntryPointLayout* entryPointInfo,
+ slang::IComponentType* component,
+ SlangInt entryPointIndex)
+ {
+ ComPtr<ISlangBlob> codeBlob;
+ auto compileResult = component->getEntryPointCode(
+ entryPointIndex, 0, codeBlob.writeRef(), diagnostics.writeRef());
+ if (diagnostics.get())
+ {
+ getDebugCallback()->handleMessage(
+ compileResult == SLANG_OK ? DebugMessageType::Warning : DebugMessageType::Error,
+ DebugMessageSource::Slang,
+ (char*)diagnostics->getBufferPointer());
+ }
+ SLANG_RETURN_ON_FAIL(compileResult);
+ codeBlobs.add(codeBlob);
+ D3D12_DXIL_LIBRARY_DESC library = {};
+ library.DXILLibrary.BytecodeLength = codeBlob->getBufferSize();
+ library.DXILLibrary.pShaderBytecode = codeBlob->getBufferPointer();
+ library.NumExports = 1;
+ D3D12_EXPORT_DESC exportDesc = {};
+ exportDesc.Name = getWStr(entryPointInfo->getNameOverride());
+ exportDesc.ExportToRename = getWStr(entryPointInfo->getNameOverride());
+ exportDesc.Flags = D3D12_EXPORT_FLAG_NONE;
+ library.pExports = exports.add(exportDesc);
+
+ D3D12_STATE_SUBOBJECT dxilSubObject = {};
+ dxilSubObject.Type = D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY;
+ dxilSubObject.pDesc = dxilLibraries.add(library);
+ subObjects.add(dxilSubObject);
+ return SLANG_OK;
+ };
+ if (program->linkedEntryPoints.getCount() == 0)
+ {
+ for (SlangUInt i = 0; i < programLayout->getEntryPointCount(); i++)
+ {
+ SLANG_RETURN_ON_FAIL(compileShader(
+ programLayout->getEntryPointByIndex(i), program->linkedProgram, (SlangInt)i));
+ }
+ }
+ else
+ {
+ for (auto& entryPoint : program->linkedEntryPoints)
+ {
+ SLANG_RETURN_ON_FAIL(
+ compileShader(entryPoint->getLayout()->getEntryPointByIndex(0), entryPoint, 0));
+ }
+ }
+
+ for (Index i = 0; i < desc.rayTracing.hitGroupDescs.getCount(); i++)
+ {
+ auto& hitGroup = desc.rayTracing.hitGroups[i];
+ D3D12_HIT_GROUP_DESC hitGroupDesc = {};
+ hitGroupDesc.Type = hitGroup.intersectionEntryPoint.getLength() == 0
+ ? D3D12_HIT_GROUP_TYPE_TRIANGLES
+ : D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE;
+
+ if (hitGroup.anyHitEntryPoint.getLength())
+ {
+ hitGroupDesc.AnyHitShaderImport = getWStr(hitGroup.anyHitEntryPoint.getBuffer());
+ }
+ if (hitGroup.closestHitEntryPoint.getLength())
+ {
+ hitGroupDesc.ClosestHitShaderImport =
+ getWStr(hitGroup.closestHitEntryPoint.getBuffer());
+ }
+ if (hitGroup.intersectionEntryPoint.getLength())
+ {
+ hitGroupDesc.IntersectionShaderImport =
+ getWStr(hitGroup.intersectionEntryPoint.getBuffer());
+ }
+ hitGroupDesc.HitGroupExport = getWStr(hitGroup.hitGroupName.getBuffer());
+
+ D3D12_STATE_SUBOBJECT hitGroupSubObject = {};
+ hitGroupSubObject.Type = D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP;
+ hitGroupSubObject.pDesc = hitGroups.add(hitGroupDesc);
+ subObjects.add(hitGroupSubObject);
+ }
+
+ D3D12_RAYTRACING_SHADER_CONFIG shaderConfig = {};
+ // According to DXR spec, fixed function triangle intersections must use float2 as ray
+ // attributes that defines the barycentric coordinates at intersection.
+ shaderConfig.MaxAttributeSizeInBytes = (UINT)desc.rayTracing.maxAttributeSizeInBytes;
+ shaderConfig.MaxPayloadSizeInBytes = (UINT)desc.rayTracing.maxRayPayloadSize;
+ D3D12_STATE_SUBOBJECT shaderConfigSubObject = {};
+ shaderConfigSubObject.Type = D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG;
+ shaderConfigSubObject.pDesc = &shaderConfig;
+ subObjects.add(shaderConfigSubObject);
+
+ D3D12_GLOBAL_ROOT_SIGNATURE globalSignatureDesc = {};
+ globalSignatureDesc.pGlobalRootSignature = program->m_rootObjectLayout->m_rootSignature.get();
+ D3D12_STATE_SUBOBJECT globalSignatureSubobject = {};
+ globalSignatureSubobject.Type = D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE;
+ globalSignatureSubobject.pDesc = &globalSignatureDesc;
+ subObjects.add(globalSignatureSubobject);
+
+ D3D12_RAYTRACING_PIPELINE_CONFIG pipelineConfig = {};
+ pipelineConfig.MaxTraceRecursionDepth = desc.rayTracing.maxRecursion;
+ D3D12_STATE_SUBOBJECT pipelineConfigSubobject = {};
+ pipelineConfigSubobject.Type = D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG;
+ pipelineConfigSubobject.pDesc = &pipelineConfig;
+ subObjects.add(pipelineConfigSubobject);
+
+ if (m_device->m_pipelineCreationAPIDispatcher)
+ {
+ m_device->m_pipelineCreationAPIDispatcher->beforeCreateRayTracingState(
+ m_device, slangGlobalScope);
+ }
+
+ D3D12_STATE_OBJECT_DESC rtpsoDesc = {};
+ rtpsoDesc.Type = D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE;
+ rtpsoDesc.NumSubobjects = (UINT)subObjects.getCount();
+ rtpsoDesc.pSubobjects = subObjects.getBuffer();
+ SLANG_RETURN_ON_FAIL(
+ m_device->m_device5->CreateStateObject(&rtpsoDesc, IID_PPV_ARGS(m_stateObject.writeRef())));
+
+ if (m_device->m_pipelineCreationAPIDispatcher)
+ {
+ m_device->m_pipelineCreationAPIDispatcher->afterCreateRayTracingState(
+ m_device, slangGlobalScope);
+ }
+ return SLANG_OK;
+}
+
+#endif
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-pipeline-state.h b/tools/gfx/d3d12/d3d12-pipeline-state.h
new file mode 100644
index 000000000..a22e04a05
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-pipeline-state.h
@@ -0,0 +1,41 @@
+// d3d12-ray-tracing.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class PipelineStateImpl : public PipelineStateBase
+{
+public:
+ PipelineStateImpl(DeviceImpl* device)
+ : m_device(device)
+ {}
+ DeviceImpl* m_device;
+ ComPtr<ID3D12PipelineState> m_pipelineState;
+ void init(const GraphicsPipelineStateDesc& inDesc);
+ void init(const ComputePipelineStateDesc& inDesc);
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+ virtual Result ensureAPIPipelineStateCreated() override;
+};
+
+#if SLANG_GFX_HAS_DXR_SUPPORT
+class RayTracingPipelineStateImpl : public PipelineStateBase
+{
+public:
+ ComPtr<ID3D12StateObject> m_stateObject;
+ DeviceImpl* m_device;
+ RayTracingPipelineStateImpl(DeviceImpl* device);
+ void init(const RayTracingPipelineStateDesc& inDesc);
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+ virtual Result ensureAPIPipelineStateCreated() override;
+};
+#endif
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-query.cpp b/tools/gfx/d3d12/d3d12-query.cpp
new file mode 100644
index 000000000..319976bbc
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-query.cpp
@@ -0,0 +1,207 @@
+// d3d12-query.cpp
+#include "d3d12-query.h"
+
+#include "d3d12-command-queue.h"
+
+#include "d3d12-helper-functions.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+Result QueryPoolImpl::init(const IQueryPool::Desc& desc, DeviceImpl* device)
+{
+ m_desc = desc;
+
+ // Translate query type.
+ D3D12_QUERY_HEAP_DESC heapDesc = {};
+ heapDesc.Count = (UINT)desc.count;
+ heapDesc.NodeMask = 1;
+ switch (desc.type)
+ {
+ case QueryType::Timestamp:
+ heapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP;
+ m_queryType = D3D12_QUERY_TYPE_TIMESTAMP;
+ break;
+ default:
+ return SLANG_E_INVALID_ARG;
+ }
+
+ // Create query heap.
+ auto d3dDevice = device->m_device;
+ SLANG_RETURN_ON_FAIL(
+ d3dDevice->CreateQueryHeap(&heapDesc, IID_PPV_ARGS(m_queryHeap.writeRef())));
+
+ // Create readback buffer.
+ 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;
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ initBufferResourceDesc(sizeof(uint64_t) * desc.count, resourceDesc);
+ SLANG_RETURN_ON_FAIL(m_readBackBuffer.initCommitted(
+ d3dDevice,
+ heapProps,
+ D3D12_HEAP_FLAG_NONE,
+ resourceDesc,
+ D3D12_RESOURCE_STATE_COPY_DEST,
+ nullptr));
+
+ // Create command allocator.
+ SLANG_RETURN_ON_FAIL(d3dDevice->CreateCommandAllocator(
+ D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_commandAllocator.writeRef())));
+
+ // Create command list.
+ SLANG_RETURN_ON_FAIL(d3dDevice->CreateCommandList(
+ 0,
+ D3D12_COMMAND_LIST_TYPE_DIRECT,
+ m_commandAllocator,
+ nullptr,
+ IID_PPV_ARGS(m_commandList.writeRef())));
+ m_commandList->Close();
+
+ // Create fence.
+ SLANG_RETURN_ON_FAIL(
+ d3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef())));
+
+ // Get command queue from device.
+ m_commandQueue = device->m_resourceCommandQueue->m_d3dQueue;
+
+ // Create wait event.
+ m_waitEvent = CreateEventEx(nullptr, false, 0, EVENT_ALL_ACCESS);
+
+ return SLANG_OK;
+}
+
+Result QueryPoolImpl::getResult(GfxIndex queryIndex, GfxCount count, uint64_t* data)
+{
+ m_commandList->Reset(m_commandAllocator, nullptr);
+ m_commandList->ResolveQueryData(
+ m_queryHeap,
+ m_queryType,
+ (UINT)queryIndex,
+ (UINT)count,
+ m_readBackBuffer,
+ sizeof(uint64_t) * queryIndex);
+ m_commandList->Close();
+ ID3D12CommandList* cmdList = m_commandList;
+ m_commandQueue->ExecuteCommandLists(1, &cmdList);
+ m_eventValue++;
+ m_fence->SetEventOnCompletion(m_eventValue, m_waitEvent);
+ m_commandQueue->Signal(m_fence, m_eventValue);
+ WaitForSingleObject(m_waitEvent, INFINITE);
+ m_commandAllocator->Reset();
+
+ int8_t* mappedData = nullptr;
+ D3D12_RANGE readRange = {
+ sizeof(uint64_t) * queryIndex, sizeof(uint64_t) * (queryIndex + count) };
+ m_readBackBuffer.getResource()->Map(0, &readRange, (void**)&mappedData);
+ memcpy(data, mappedData + sizeof(uint64_t) * queryIndex, sizeof(uint64_t) * count);
+ m_readBackBuffer.getResource()->Unmap(0, nullptr);
+ return SLANG_OK;
+}
+
+void QueryPoolImpl::writeTimestamp(ID3D12GraphicsCommandList* cmdList, GfxIndex index)
+{
+ cmdList->EndQuery(m_queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, (UINT)index);
+}
+
+IQueryPool* PlainBufferProxyQueryPoolImpl::getInterface(const Guid& guid)
+{
+ if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IQueryPool)
+ return static_cast<IQueryPool*>(this);
+ return nullptr;
+}
+
+Result PlainBufferProxyQueryPoolImpl::init(
+ const IQueryPool::Desc& desc, DeviceImpl* device, uint32_t stride)
+{
+ ComPtr<IBufferResource> bufferResource;
+ IBufferResource::Desc bufferDesc = {};
+ bufferDesc.defaultState = ResourceState::CopySource;
+ bufferDesc.elementSize = 0;
+ bufferDesc.type = IResource::Type::Buffer;
+ bufferDesc.sizeInBytes = desc.count * stride;
+ bufferDesc.format = Format::Unknown;
+ bufferDesc.allowedStates.add(ResourceState::UnorderedAccess);
+ SLANG_RETURN_ON_FAIL(
+ device->createBufferResource(bufferDesc, nullptr, bufferResource.writeRef()));
+ m_bufferResource = static_cast<BufferResourceImpl*>(bufferResource.get());
+ m_queryType = desc.type;
+ m_device = device;
+ m_stride = stride;
+ m_count = (uint32_t)desc.count;
+ m_desc = desc;
+ return SLANG_OK;
+}
+
+Result PlainBufferProxyQueryPoolImpl::reset()
+{
+ m_resultDirty = true;
+ auto encodeInfo = m_device->encodeResourceCommands();
+ D3D12_RESOURCE_BARRIER barrier = {};
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
+ barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
+ barrier.Transition.pResource = m_bufferResource->m_resource.getResource();
+ encodeInfo.d3dCommandList->ResourceBarrier(1, &barrier);
+ m_device->submitResourceCommandsAndWait(encodeInfo);
+ return SLANG_OK;
+}
+
+Result PlainBufferProxyQueryPoolImpl::getResult(GfxIndex queryIndex, GfxCount count, uint64_t* data)
+{
+ if (m_resultDirty)
+ {
+ auto encodeInfo = m_device->encodeResourceCommands();
+ D3D12_RESOURCE_BARRIER barrier = {};
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
+ barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
+ barrier.Transition.pResource = m_bufferResource->m_resource.getResource();
+ encodeInfo.d3dCommandList->ResourceBarrier(1, &barrier);
+
+ D3D12Resource stageBuf;
+
+ auto size = (Size)m_count * m_stride;
+ 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;
+
+ D3D12_RESOURCE_DESC stagingDesc;
+ initBufferResourceDesc(size, stagingDesc);
+
+ SLANG_RETURN_ON_FAIL(stageBuf.initCommitted(
+ m_device->m_device,
+ heapProps,
+ D3D12_HEAP_FLAG_NONE,
+ stagingDesc,
+ D3D12_RESOURCE_STATE_COPY_DEST,
+ nullptr));
+
+ encodeInfo.d3dCommandList->CopyBufferRegion(
+ stageBuf, 0, m_bufferResource->m_resource.getResource(), 0, size);
+ m_device->submitResourceCommandsAndWait(encodeInfo);
+ void* ptr = nullptr;
+ stageBuf.getResource()->Map(0, nullptr, &ptr);
+ m_result.setCount(m_count * m_stride);
+ memcpy(m_result.getBuffer(), ptr, m_result.getCount());
+
+ m_resultDirty = false;
+ }
+
+ memcpy(data, m_result.getBuffer() + queryIndex * m_stride, count * m_stride);
+
+ return SLANG_OK;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-query.h b/tools/gfx/d3d12/d3d12-query.h
new file mode 100644
index 000000000..770990e81
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-query.h
@@ -0,0 +1,64 @@
+// d3d12-query.h
+#pragma once
+
+#include "d3d12-base.h"
+#include "d3d12-device.h"
+#include "d3d12-buffer.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class QueryPoolImpl : public QueryPoolBase
+{
+public:
+ Result init(const IQueryPool::Desc& desc, DeviceImpl* device);
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getResult(GfxIndex queryIndex, GfxCount count, uint64_t* data) override;
+
+ void writeTimestamp(ID3D12GraphicsCommandList* cmdList, GfxIndex index);
+
+public:
+ D3D12_QUERY_TYPE m_queryType;
+ ComPtr<ID3D12QueryHeap> m_queryHeap;
+ D3D12Resource m_readBackBuffer;
+ ComPtr<ID3D12CommandAllocator> m_commandAllocator;
+ ComPtr<ID3D12GraphicsCommandList> m_commandList;
+ ComPtr<ID3D12Fence> m_fence;
+ ComPtr<ID3D12CommandQueue> m_commandQueue;
+ HANDLE m_waitEvent;
+ UINT64 m_eventValue = 0;
+};
+
+/// Implements the IQueryPool interface with a plain buffer.
+/// Used for query types that does not correspond to a D3D query,
+/// such as ray-tracing acceleration structure post-build info.
+class PlainBufferProxyQueryPoolImpl : public QueryPoolBase
+{
+public:
+ SLANG_COM_OBJECT_IUNKNOWN_ALL
+ IQueryPool* getInterface(const Guid& guid);
+
+public:
+ Result init(const IQueryPool::Desc& desc, DeviceImpl* device, uint32_t stride);
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL reset() override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getResult(GfxIndex queryIndex, GfxCount count, uint64_t* data) override;
+
+public:
+ QueryType m_queryType;
+ RefPtr<BufferResourceImpl> m_bufferResource;
+ RefPtr<DeviceImpl> m_device;
+ List<uint8_t> m_result;
+ bool m_resultDirty = true;
+ uint32_t m_stride = 0;
+ uint32_t m_count = 0;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-render-pass.cpp b/tools/gfx/d3d12/d3d12-render-pass.cpp
new file mode 100644
index 000000000..bdd159fd5
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-render-pass.cpp
@@ -0,0 +1,19 @@
+// d3d12-render-pass.cpp
+#include "d3d12-render-pass.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+void RenderPassLayoutImpl::init(const IRenderPassLayout::Desc& desc)
+{
+ SimpleRenderPassLayout::init(desc);
+ m_framebufferLayout = static_cast<FramebufferLayoutImpl*>(desc.framebufferLayout);
+ m_hasDepthStencil = m_framebufferLayout->m_hasDepthStencil;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-render-pass.h b/tools/gfx/d3d12/d3d12-render-pass.h
new file mode 100644
index 000000000..82e53a6c6
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-render-pass.h
@@ -0,0 +1,22 @@
+// d3d12-render-pass.h
+#pragma once
+
+#include "d3d12-base.h"
+#include "d3d12-framebuffer.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class RenderPassLayoutImpl : public SimpleRenderPassLayout
+{
+public:
+ RefPtr<FramebufferLayoutImpl> m_framebufferLayout;
+ void init(const IRenderPassLayout::Desc& desc);
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-resource-views.cpp b/tools/gfx/d3d12/d3d12-resource-views.cpp
new file mode 100644
index 000000000..5a044dd3b
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-resource-views.cpp
@@ -0,0 +1,41 @@
+// d3d12-resource-views.cpp
+#include "d3d12-resource-views.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+ResourceViewInternalImpl::~ResourceViewInternalImpl()
+{
+ if (m_descriptor.cpuHandle.ptr)
+ m_allocator->free(m_descriptor);
+}
+
+Result ResourceViewImpl::getNativeHandle(InteropHandle* outHandle)
+{
+ outHandle->api = InteropHandleAPI::D3D12CpuDescriptorHandle;
+ outHandle->handleValue = m_descriptor.cpuHandle.ptr;
+ return SLANG_OK;
+}
+
+#if SLANG_GFX_HAS_DXR_SUPPORT
+
+DeviceAddress AccelerationStructureImpl::getDeviceAddress()
+{
+ return m_buffer->getDeviceAddress() + m_offset;
+}
+
+Result AccelerationStructureImpl::getNativeHandle(InteropHandle* outHandle)
+{
+ outHandle->api = InteropHandleAPI::DeviceAddress;
+ outHandle->handleValue = getDeviceAddress();
+ return SLANG_OK;
+}
+
+#endif // SLANG_GFX_HAS_DXR_SUPPORT
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-resource-views.h b/tools/gfx/d3d12/d3d12-resource-views.h
new file mode 100644
index 000000000..12e3d0714
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-resource-views.h
@@ -0,0 +1,52 @@
+// d3d12-resource-views.h
+#pragma once
+
+#include "d3d12-base.h"
+#include "../d3d/d3d-util.h"
+#include "d3d12-buffer.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class ResourceViewInternalImpl
+{
+public:
+ D3D12Descriptor m_descriptor;
+ RefPtr<D3D12GeneralExpandingDescriptorHeap> m_allocator;
+ ~ResourceViewInternalImpl();
+};
+
+class ResourceViewImpl
+ : public ResourceViewBase
+ , public ResourceViewInternalImpl
+{
+public:
+ Slang::RefPtr<Resource> m_resource;
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+};
+
+#if SLANG_GFX_HAS_DXR_SUPPORT
+
+class AccelerationStructureImpl
+ : public AccelerationStructureBase
+ , public ResourceViewInternalImpl
+{
+public:
+ RefPtr<BufferResourceImpl> m_buffer;
+ uint64_t m_offset;
+ uint64_t m_size;
+ ComPtr<ID3D12Device5> m_device5;
+
+public:
+ virtual SLANG_NO_THROW DeviceAddress SLANG_MCALL getDeviceAddress() override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+};
+
+#endif
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/resource-d3d12.cpp b/tools/gfx/d3d12/d3d12-resource.cpp
index 7329b28fd..8975f5825 100644
--- a/tools/gfx/d3d12/resource-d3d12.cpp
+++ b/tools/gfx/d3d12/d3d12-resource.cpp
@@ -1,5 +1,5 @@
-// resource-d3d12.cpp
-#include "resource-d3d12.h"
+// d3d12-resource.cpp
+#include "d3d12-resource.h"
namespace gfx {
using namespace Slang;
diff --git a/tools/gfx/d3d12/resource-d3d12.h b/tools/gfx/d3d12/d3d12-resource.h
index 39b6c13db..ab9244470 100644
--- a/tools/gfx/d3d12/resource-d3d12.h
+++ b/tools/gfx/d3d12/d3d12-resource.h
@@ -1,4 +1,4 @@
-// resource-d3d12.h
+// d3d12-resource.h
#pragma once
#define WIN32_LEAN_AND_MEAN
diff --git a/tools/gfx/d3d12/d3d12-sampler.cpp b/tools/gfx/d3d12/d3d12-sampler.cpp
new file mode 100644
index 000000000..b96fb4c90
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-sampler.cpp
@@ -0,0 +1,21 @@
+// d3d12-sampler.cpp
+#include "d3d12-sampler.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+SamplerStateImpl::~SamplerStateImpl() { m_allocator->free(m_descriptor); }
+
+Result SamplerStateImpl::getNativeHandle(InteropHandle* outHandle)
+{
+ outHandle->api = InteropHandleAPI::D3D12CpuDescriptorHandle;
+ outHandle->handleValue = m_descriptor.cpuHandle.ptr;
+ return SLANG_OK;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-sampler.h b/tools/gfx/d3d12/d3d12-sampler.h
new file mode 100644
index 000000000..fd5f0800c
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-sampler.h
@@ -0,0 +1,23 @@
+// d3d12-sampler.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class SamplerStateImpl : public SamplerStateBase
+{
+public:
+ D3D12Descriptor m_descriptor;
+ RefPtr<D3D12GeneralExpandingDescriptorHeap> m_allocator;
+ ~SamplerStateImpl();
+ virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-shader-object-layout.cpp b/tools/gfx/d3d12/d3d12-shader-object-layout.cpp
new file mode 100644
index 000000000..3e8598d51
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-shader-object-layout.cpp
@@ -0,0 +1,1004 @@
+// d3d12-shader-object-layout.cpp
+#include "d3d12-shader-object-layout.h"
+
+#include "d3d12-device.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+ShaderObjectLayoutImpl::SubObjectRangeOffset::SubObjectRangeOffset(
+ slang::VariableLayoutReflection* varLayout)
+{
+ if (auto pendingLayout = varLayout->getPendingDataLayout())
+ {
+ pendingOrdinaryData = (uint32_t)pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
+}
+
+ShaderObjectLayoutImpl::SubObjectRangeStride::SubObjectRangeStride(
+ slang::TypeLayoutReflection* typeLayout)
+{
+ if (auto pendingLayout = typeLayout->getPendingDataTypeLayout())
+ {
+ pendingOrdinaryData = (uint32_t)pendingLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
+}
+
+bool ShaderObjectLayoutImpl::isBindingRangeRootParameter(
+ SlangSession* globalSession,
+ const char* rootParameterAttributeName,
+ slang::TypeLayoutReflection* typeLayout,
+ Index bindingRangeIndex)
+{
+ bool isRootParameter = false;
+ if (rootParameterAttributeName)
+ {
+ if (auto leafVariable = typeLayout->getBindingRangeLeafVariable(bindingRangeIndex))
+ {
+ if (leafVariable->findUserAttributeByName(globalSession, rootParameterAttributeName))
+ {
+ isRootParameter = true;
+ }
+ }
+ }
+ return isRootParameter;
+}
+
+Result ShaderObjectLayoutImpl::createForElementType(
+ RendererBase* renderer,
+ slang::TypeLayoutReflection* elementType,
+ ShaderObjectLayoutImpl** outLayout)
+{
+ Builder builder(renderer);
+ builder.setElementTypeLayout(elementType);
+ return builder.build(outLayout);
+}
+
+Result ShaderObjectLayoutImpl::init(Builder* builder)
+{
+ auto renderer = builder->m_renderer;
+
+ initBase(renderer, builder->m_elementTypeLayout);
+
+ m_containerType = builder->m_containerType;
+
+ m_bindingRanges = _Move(builder->m_bindingRanges);
+ m_subObjectRanges = _Move(builder->m_subObjectRanges);
+ m_rootParamsInfo = _Move(builder->m_rootParamsInfo);
+
+ m_ownCounts = builder->m_ownCounts;
+ m_totalCounts = builder->m_totalCounts;
+ m_subObjectCount = builder->m_subObjectCount;
+ m_childRootParameterCount = builder->m_childRootParameterCount;
+ m_totalOrdinaryDataSize = builder->m_totalOrdinaryDataSize;
+
+ return SLANG_OK;
+}
+
+Result ShaderObjectLayoutImpl::Builder::setElementTypeLayout(
+ slang::TypeLayoutReflection* typeLayout)
+{
+ typeLayout = _unwrapParameterGroups(typeLayout, m_containerType);
+ m_elementTypeLayout = typeLayout;
+
+ // If the type contains any ordinary data, then we must reserve a buffer
+ // descriptor to hold it when binding as a parameter block.
+ //
+ m_totalOrdinaryDataSize = (uint32_t)typeLayout->getSize();
+ if (m_totalOrdinaryDataSize != 0)
+ {
+ m_ownCounts.resource++;
+ }
+
+ // We will scan over the reflected Slang binding ranges and add them
+ // to our array. There are two main things we compute along the way:
+ //
+ // * For each binding range we compute a `flatIndex` that can be
+ // used to identify where the values for the given range begin
+ // in the flattened arrays (e.g., `m_objects`) and descriptor
+ // tables that hold the state of a shader object.
+ //
+ // * We also update the various counters taht keep track of the number
+ // of sub-objects, resources, samplers, etc. that are being
+ // consumed. These counters will contribute to figuring out
+ // the descriptor table(s) that might be needed to represent
+ // the object.
+ //
+ SlangInt bindingRangeCount = typeLayout->getBindingRangeCount();
+ for (SlangInt r = 0; r < bindingRangeCount; ++r)
+ {
+ slang::BindingType slangBindingType = typeLayout->getBindingRangeType(r);
+ uint32_t count = (uint32_t)typeLayout->getBindingRangeBindingCount(r);
+ slang::TypeLayoutReflection* slangLeafTypeLayout =
+ typeLayout->getBindingRangeLeafTypeLayout(r);
+ BindingRangeInfo bindingRangeInfo = {};
+ bindingRangeInfo.bindingType = slangBindingType;
+ bindingRangeInfo.resourceShape = slangLeafTypeLayout->getResourceShape();
+ bindingRangeInfo.count = count;
+ bindingRangeInfo.isRootParameter = isBindingRangeRootParameter(
+ m_renderer->slangContext.globalSession,
+ static_cast<DeviceImpl*>(m_renderer)->m_extendedDesc.rootParameterShaderAttributeName,
+ typeLayout,
+ r);
+ if (bindingRangeInfo.isRootParameter)
+ {
+ RootParameterInfo rootInfo = {};
+ switch (slangBindingType)
+ {
+ case slang::BindingType::RayTracingAccelerationStructure:
+ rootInfo.type = IResourceView::Type::AccelerationStructure;
+ break;
+ case slang::BindingType::RawBuffer:
+ case slang::BindingType::TypedBuffer:
+ rootInfo.type = IResourceView::Type::ShaderResource;
+ break;
+ case slang::BindingType::MutableRawBuffer:
+ case slang::BindingType::MutableTypedBuffer:
+ rootInfo.type = IResourceView::Type::UnorderedAccess;
+ break;
+ }
+ bindingRangeInfo.baseIndex = (uint32_t)m_rootParamsInfo.getCount();
+ for (uint32_t i = 0; i < count; i++)
+ {
+ m_rootParamsInfo.add(rootInfo);
+ }
+ }
+ else
+ {
+ switch (slangBindingType)
+ {
+ case slang::BindingType::ConstantBuffer:
+ case slang::BindingType::ParameterBlock:
+ case slang::BindingType::ExistentialValue:
+ bindingRangeInfo.baseIndex = m_subObjectCount;
+ bindingRangeInfo.subObjectIndex = m_subObjectCount;
+ m_subObjectCount += count;
+ break;
+ case slang::BindingType::RawBuffer:
+ case slang::BindingType::MutableRawBuffer:
+ if (slangLeafTypeLayout->getType()->getElementType() != nullptr)
+ {
+ // A structured buffer occupies both a resource slot and
+ // a sub-object slot.
+ bindingRangeInfo.subObjectIndex = m_subObjectCount;
+ m_subObjectCount += count;
+ }
+ bindingRangeInfo.baseIndex = m_ownCounts.resource;
+ m_ownCounts.resource += count;
+ break;
+ case slang::BindingType::Sampler:
+ bindingRangeInfo.baseIndex = m_ownCounts.sampler;
+ m_ownCounts.sampler += count;
+ break;
+
+ case slang::BindingType::CombinedTextureSampler:
+ // TODO: support this case...
+ break;
+
+ case slang::BindingType::VaryingInput:
+ case slang::BindingType::VaryingOutput:
+ break;
+
+ default:
+ bindingRangeInfo.baseIndex = m_ownCounts.resource;
+ m_ownCounts.resource += count;
+ break;
+ }
+ }
+ m_bindingRanges.add(bindingRangeInfo);
+ }
+
+ // At this point we've computed the number of resources/samplers that
+ // the type needs to represent its *own* state, and stored those counts
+ // in `m_ownCounts`. Next we need to consider any resources/samplers
+ // and root parameters needed to represent the state of the transitive
+ // sub-objects of this objet, so that we can compute the total size
+ // of the object when bound to the pipeline.
+
+ m_totalCounts = m_ownCounts;
+
+ SlangInt subObjectRangeCount = typeLayout->getSubObjectRangeCount();
+ for (SlangInt r = 0; r < subObjectRangeCount; ++r)
+ {
+ SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r);
+ auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
+ auto count = (uint32_t)typeLayout->getBindingRangeBindingCount(bindingRangeIndex);
+ slang::TypeLayoutReflection* slangLeafTypeLayout =
+ typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+
+ // A sub-object range can either represent a sub-object of a known
+ // type, like a `ConstantBuffer<Foo>` or `ParameterBlock<Foo>`
+ // (in which case we can pre-compute a layout to use, based on
+ // the type `Foo`) *or* it can represent a sub-object of some
+ // existential type (e.g., `IBar`) in which case we cannot
+ // know the appropraite type/layout of sub-object to allocate.
+ //
+ RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
+ if (slangBindingType == slang::BindingType::ExistentialValue)
+ {
+ if (auto pendingTypeLayout = slangLeafTypeLayout->getPendingDataTypeLayout())
+ {
+ createForElementType(m_renderer, pendingTypeLayout, subObjectLayout.writeRef());
+ }
+ }
+ else
+ {
+ createForElementType(
+ m_renderer,
+ slangLeafTypeLayout->getElementTypeLayout(),
+ subObjectLayout.writeRef());
+ }
+
+ SubObjectRangeInfo subObjectRange;
+ subObjectRange.bindingRangeIndex = bindingRangeIndex;
+ subObjectRange.layout = subObjectLayout;
+
+ // The Slang reflection API stors offset information for sub-object ranges,
+ // and we care about *some* of that information: in particular, we need
+ // the offset of sub-objects in terms of uniform/ordinary data for the
+ // cases where we need to fill in "pending" data in our ordinary buffer.
+ //
+ subObjectRange.offset = SubObjectRangeOffset(typeLayout->getSubObjectRangeOffset(r));
+ subObjectRange.stride = SubObjectRangeStride(slangLeafTypeLayout);
+
+ // The remaining offset information is computed based on the counters
+ // we are generating here, which depend only on the in-memory layout
+ // decisions being made in our implementation. Remember that the
+ // `register` and `space` values coming from DXBC/DXIL do *not*
+ // dictate the in-memory layout we use.
+ //
+ // Note: One subtle point here is that the `.rootParam` offset we are computing
+ // here does *not* include any root parameters that would be allocated
+ // for the parent object type itself (e.g., for descriptor tables
+ // used if it were bound as a parameter block). The later logic when
+ // we actually go to bind things will need to apply those offsets.
+ //
+ // Note: An even *more* subtle point is that the `.resource` offset
+ // being computed here *does* include the resource descriptor allocated
+ // for holding the ordinary data buffer, if any. The implications of
+ // this for later offset math is subtle.
+ //
+ subObjectRange.offset.rootParam = m_childRootParameterCount;
+ subObjectRange.offset.resource = m_totalCounts.resource;
+ subObjectRange.offset.sampler = m_totalCounts.sampler;
+
+ // Along with the offset information, we also need to compute the
+ // "stride" between consecutive sub-objects in the range. The actual
+ // size/stride of a single object depends on the type of range we
+ // are dealing with.
+ //
+ BindingOffset objectCounts;
+ switch (slangBindingType)
+ {
+ default:
+ {
+ // We only treat buffers of interface types as actual sub-object binding
+ // range.
+ auto bindingRangeTypeLayout =
+ typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+ if (!bindingRangeTypeLayout)
+ continue;
+ auto elementType = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex)
+ ->getElementTypeLayout();
+ if (!elementType)
+ continue;
+ if (elementType->getKind() != slang::TypeReflection::Kind::Interface)
+ {
+ continue;
+ }
+ }
+ break;
+
+ case slang::BindingType::ConstantBuffer:
+ {
+ SLANG_ASSERT(subObjectLayout);
+
+ // The resource and sampler descriptors of a nested
+ // constant buffer will "leak" into those of the
+ // parent type, and we need to account for them
+ // whenever we allocate storage.
+ //
+ objectCounts.resource = subObjectLayout->getTotalResourceDescriptorCount();
+ objectCounts.sampler = subObjectLayout->getTotalSamplerDescriptorCount();
+ objectCounts.rootParam = subObjectRange.layout->getChildRootParameterCount();
+ }
+ break;
+
+ case slang::BindingType::ParameterBlock:
+ {
+ SLANG_ASSERT(subObjectLayout);
+
+ // In contrast to a constant buffer, a parameter block can hide
+ // the resource and sampler descriptor allocation it uses (since they
+ // are allocated into the tables that make up the parameter block.
+ //
+ // The only resource usage that leaks into the surrounding context
+ // is the number of root parameters consumed.
+ //
+ objectCounts.rootParam = subObjectRange.layout->getTotalRootTableParameterCount();
+ }
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ // An unspecialized existential/interface value cannot consume any resources
+ // as part of the parent object (it needs to fit inside the fixed-size
+ // represnetation of existential types).
+ //
+ // However, if we are statically specializing to a type that doesn't "fit"
+ // we may need to account for additional information that needs to be
+ // allocaated.
+ //
+ if (subObjectLayout)
+ {
+ // The ordinary data for an existential-type value is allocated into
+ // the same buffer as the parent object, so we only want to consider
+ // the resource descriptors *other than* the ordinary data buffer.
+ //
+ // Otherwise the logic here is identical to the constant buffer case.
+ //
+ objectCounts.resource =
+ subObjectLayout->getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer();
+ objectCounts.sampler = subObjectLayout->getTotalSamplerDescriptorCount();
+ objectCounts.rootParam = subObjectRange.layout->getChildRootParameterCount();
+
+ // Note: In the implementation for some other graphics API (e.g.,
+ // Vulkan) there needs to be more work done to handle the fact that
+ // "pending" data from interface-type sub-objects get allocated to a
+ // distinct offset after all the "primary" data. We are consciously
+ // ignoring that issue here, and the physical layout of a shader object
+ // into the D3D12 binding state may end up interleaving
+ // resources/samplers for "primary" and "pending" data.
+ //
+ // If this choice ever causes issues, we can revisit the approach here.
+
+ // An interface-type range that includes ordinary data can
+ // increase the size of the ordinary data buffer we need to
+ // allocate for the parent object.
+ //
+ uint32_t ordinaryDataEnd =
+ subObjectRange.offset.pendingOrdinaryData +
+ (uint32_t)count * subObjectRange.stride.pendingOrdinaryData;
+
+ if (ordinaryDataEnd > m_totalOrdinaryDataSize)
+ {
+ m_totalOrdinaryDataSize = ordinaryDataEnd;
+ }
+ }
+ break;
+ }
+
+ // Once we've computed the usage for each object in the range, we can
+ // easily compute the usage for the entire range.
+ //
+ auto rangeResourceCount = count * objectCounts.resource;
+ auto rangeSamplerCount = count * objectCounts.sampler;
+ auto rangeRootParamCount = count * objectCounts.rootParam;
+
+ m_totalCounts.resource += rangeResourceCount;
+ m_totalCounts.sampler += rangeSamplerCount;
+ m_childRootParameterCount += rangeRootParamCount;
+
+ m_subObjectRanges.add(subObjectRange);
+ }
+
+ // Once we have added up the resource usage from all the sub-objects
+ // we can look at the total number of resources and samplers that
+ // need to be bound as part of this objects descriptor tables and
+ // that will allow us to decide whether we need to allocate a root
+ // parameter for a resource table or not, ans similarly for a
+ // sampler table.
+ //
+ if (m_totalCounts.resource)
+ m_ownCounts.rootParam++;
+ if (m_totalCounts.sampler)
+ m_ownCounts.rootParam++;
+
+ m_totalCounts.rootParam = m_ownCounts.rootParam + m_childRootParameterCount;
+
+ return SLANG_OK;
+}
+
+Result ShaderObjectLayoutImpl::Builder::build(ShaderObjectLayoutImpl** outLayout)
+{
+ auto layout = RefPtr<ShaderObjectLayoutImpl>(new ShaderObjectLayoutImpl());
+ SLANG_RETURN_ON_FAIL(layout->init(this));
+
+ returnRefPtrMove(outLayout, layout);
+ return SLANG_OK;
+}
+
+Result RootShaderObjectLayoutImpl::Builder::build(RootShaderObjectLayoutImpl** outLayout)
+{
+ RefPtr<RootShaderObjectLayoutImpl> layout = new RootShaderObjectLayoutImpl();
+ SLANG_RETURN_ON_FAIL(layout->init(this));
+
+ returnRefPtrMove(outLayout, layout);
+ return SLANG_OK;
+}
+
+void RootShaderObjectLayoutImpl::Builder::addGlobalParams(
+ slang::VariableLayoutReflection* globalsLayout)
+{
+ setElementTypeLayout(globalsLayout->getTypeLayout());
+}
+
+void RootShaderObjectLayoutImpl::Builder::addEntryPoint(
+ SlangStage stage, ShaderObjectLayoutImpl* entryPointLayout)
+{
+ EntryPointInfo info;
+ info.layout = entryPointLayout;
+
+ info.offset.resource = m_totalCounts.resource;
+ info.offset.sampler = m_totalCounts.sampler;
+ info.offset.rootParam = m_childRootParameterCount;
+
+ m_totalCounts.resource += entryPointLayout->getTotalResourceDescriptorCount();
+ m_totalCounts.sampler += entryPointLayout->getTotalSamplerDescriptorCount();
+
+ // TODO(tfoley): Check this to make sure it is reasonable...
+ m_childRootParameterCount += entryPointLayout->getChildRootParameterCount();
+
+ m_entryPoints.add(info);
+}
+
+Result RootShaderObjectLayoutImpl::RootSignatureDescBuilder::translateDescriptorRangeType(
+ slang::BindingType c, D3D12_DESCRIPTOR_RANGE_TYPE* outType)
+{
+ switch (c)
+ {
+ case slang::BindingType::ConstantBuffer:
+ *outType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
+ return SLANG_OK;
+ case slang::BindingType::RawBuffer:
+ case slang::BindingType::Texture:
+ case slang::BindingType::TypedBuffer:
+ case slang::BindingType::RayTracingAccelerationStructure:
+ *outType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+ return SLANG_OK;
+ case slang::BindingType::MutableRawBuffer:
+ case slang::BindingType::MutableTexture:
+ case slang::BindingType::MutableTypedBuffer:
+ *outType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
+ return SLANG_OK;
+ case slang::BindingType::Sampler:
+ *outType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
+ return SLANG_OK;
+ default:
+ return SLANG_FAIL;
+ }
+}
+
+/// Add a new descriptor set to the layout being computed.
+///
+/// Note that a "descriptor set" in the layout may amount to
+/// zero, one, or two different descriptor *tables* in the
+/// final D3D12 root signature. Each descriptor set may
+/// contain zero or more view ranges (CBV/SRV/UAV) and zero
+/// or more sampler ranges. It maps to a view descriptor table
+/// if the number of view ranges is non-zero and to a sampler
+/// descriptor table if the number of sampler ranges is non-zero.
+///
+
+uint32_t RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addDescriptorSet()
+{
+ auto result = (uint32_t)m_descriptorSets.getCount();
+ m_descriptorSets.add(DescriptorSetLayout{});
+ return result;
+}
+
+Result RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addDescriptorRange(
+ Index physicalDescriptorSetIndex,
+ D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
+ UINT registerIndex,
+ UINT spaceIndex,
+ UINT count,
+ bool isRootParameter)
+{
+ if (isRootParameter)
+ {
+ D3D12_ROOT_PARAMETER rootParam = {};
+ switch (rangeType)
+ {
+ case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
+ rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
+ break;
+ case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
+ rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
+ break;
+ default:
+ getDebugCallback()->handleMessage(
+ DebugMessageType::Error,
+ DebugMessageSource::Layer,
+ "A shader parameter marked as root parameter is neither SRV nor UAV.");
+ return SLANG_FAIL;
+ }
+ rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParam.Descriptor.RegisterSpace = spaceIndex;
+ rootParam.Descriptor.ShaderRegister = registerIndex;
+ m_rootParameters.add(rootParam);
+ return SLANG_OK;
+ }
+
+ auto& descriptorSet = m_descriptorSets[physicalDescriptorSetIndex];
+
+ D3D12_DESCRIPTOR_RANGE range = {};
+ range.RangeType = rangeType;
+ range.NumDescriptors = count;
+ range.BaseShaderRegister = registerIndex;
+ range.RegisterSpace = spaceIndex;
+ range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+ if (range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER)
+ {
+ descriptorSet.m_samplerRanges.add(range);
+ descriptorSet.m_samplerCount += range.NumDescriptors;
+ }
+ else
+ {
+ descriptorSet.m_resourceRanges.add(range);
+ descriptorSet.m_resourceCount += range.NumDescriptors;
+ }
+
+ return SLANG_OK;
+}
+
+/// Add one descriptor range as specified in Slang reflection information to the layout.
+///
+/// The layout information is taken from `typeLayout` for the descriptor
+/// range with the given `descriptorRangeIndex` within the logical
+/// descriptor set (reflected by Slang) with the given `logicalDescriptorSetIndex`.
+///
+/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
+/// the descriptor set that the range should be added to.
+///
+/// The `offset` encodes information about space and/or register offsets that
+/// should be applied to descrptor ranges.
+///
+/// This operation can fail if the given descriptor range encodes a range that
+/// doesn't map to anything directly supported by D3D12. Higher-level routines
+/// will often want to ignore such failures.
+///
+
+Result RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addDescriptorRange(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffset const& containerOffset,
+ BindingRegisterOffset const& elementOffset,
+ Index logicalDescriptorSetIndex,
+ Index descriptorRangeIndex,
+ bool isRootParameter)
+{
+ auto bindingType = typeLayout->getDescriptorSetDescriptorRangeType(
+ logicalDescriptorSetIndex, descriptorRangeIndex);
+ auto count = typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(
+ logicalDescriptorSetIndex, descriptorRangeIndex);
+ auto index = typeLayout->getDescriptorSetDescriptorRangeIndexOffset(
+ logicalDescriptorSetIndex, descriptorRangeIndex);
+ auto space = typeLayout->getDescriptorSetSpaceOffset(logicalDescriptorSetIndex);
+
+ D3D12_DESCRIPTOR_RANGE_TYPE rangeType;
+ SLANG_RETURN_ON_FAIL(translateDescriptorRangeType(bindingType, &rangeType));
+
+ return addDescriptorRange(
+ physicalDescriptorSetIndex,
+ rangeType,
+ (UINT)index + elementOffset[rangeType],
+ (UINT)space + containerOffset.spaceOffset,
+ (UINT)count,
+ isRootParameter);
+}
+
+/// Add one binding range to the computed layout.
+///
+/// The layout information is taken from `typeLayout` for the binding
+/// range with the given `bindingRangeIndex`.
+///
+/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
+/// the descriptor set that the range should be added to.
+///
+/// The `offset` encodes information about space and/or register offsets that
+/// should be applied to descrptor ranges.
+///
+/// Note that a single binding range may encompass zero or more descriptor ranges.
+///
+
+void RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addBindingRange(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffset const& containerOffset,
+ BindingRegisterOffset const& elementOffset,
+ Index bindingRangeIndex)
+{
+ auto logicalDescriptorSetIndex =
+ typeLayout->getBindingRangeDescriptorSetIndex(bindingRangeIndex);
+ auto firstDescriptorRangeIndex =
+ typeLayout->getBindingRangeFirstDescriptorRangeIndex(bindingRangeIndex);
+ Index descriptorRangeCount = typeLayout->getBindingRangeDescriptorRangeCount(bindingRangeIndex);
+ bool isRootParameter = isBindingRangeRootParameter(
+ m_device->slangContext.globalSession,
+ m_device->m_extendedDesc.rootParameterShaderAttributeName,
+ typeLayout,
+ bindingRangeIndex);
+ for (Index i = 0; i < descriptorRangeCount; ++i)
+ {
+ auto descriptorRangeIndex = firstDescriptorRangeIndex + i;
+
+ // Note: we ignore the `Result` returned by `addDescriptorRange()` because we
+ // want to silently skip any ranges that represent kinds of bindings that
+ // don't actually exist in D3D12.
+ //
+ addDescriptorRange(
+ typeLayout,
+ physicalDescriptorSetIndex,
+ containerOffset,
+ elementOffset,
+ logicalDescriptorSetIndex,
+ descriptorRangeIndex,
+ isRootParameter);
+ }
+}
+
+void RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addAsValue(
+ slang::VariableLayoutReflection* varLayout, Index physicalDescriptorSetIndex)
+{
+ BindingRegisterOffsetPair offset(varLayout);
+ addAsValue(varLayout->getTypeLayout(), physicalDescriptorSetIndex, offset, offset);
+}
+
+/// Add binding ranges and parameter blocks to the root signature.
+///
+/// The layout information is taken from `typeLayout` which should
+/// be a layout for either a program or an entry point.
+///
+/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
+/// the descriptor set that binding ranges not belonging to nested
+/// parameter blocks should be added to.
+///
+/// The `offset` encodes information about space and/or register offsets that
+/// should be applied to descrptor ranges.
+///
+
+void RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addAsConstantBuffer(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffsetPair const& containerOffset,
+ BindingRegisterOffsetPair const& elementOffset)
+{
+ if (typeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0)
+ {
+ auto descriptorRangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
+ auto& offsetForRangeType = containerOffset.primary.offsetForRangeType[descriptorRangeType];
+ addDescriptorRange(
+ physicalDescriptorSetIndex,
+ descriptorRangeType,
+ offsetForRangeType,
+ containerOffset.primary.spaceOffset,
+ 1,
+ false);
+ }
+
+ addAsValue(typeLayout, physicalDescriptorSetIndex, containerOffset, elementOffset);
+}
+
+void RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addAsValue(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffsetPair const& containerOffset,
+ BindingRegisterOffsetPair const& elementOffset)
+{
+ // Our first task is to add the binding ranges for stuff that is
+ // directly contained in `typeLayout` rather than via sub-objects.
+ //
+ // Our goal is to have the descriptors for directly-contained views/samplers
+ // always be contiguous in CPU and GPU memory, so that we can write
+ // to them easily with a single operaiton.
+ //
+ Index bindingRangeCount = typeLayout->getBindingRangeCount();
+ for (Index bindingRangeIndex = 0; bindingRangeIndex < bindingRangeCount; bindingRangeIndex++)
+ {
+ // We will look at the type of each binding range and intentionally
+ // skip those that represent sub-objects.
+ //
+ auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
+ switch (bindingType)
+ {
+ case slang::BindingType::ConstantBuffer:
+ case slang::BindingType::ParameterBlock:
+ case slang::BindingType::ExistentialValue:
+ continue;
+
+ default:
+ break;
+ }
+
+ // For binding ranges that don't represent sub-objects, we will add
+ // all of the descriptor ranges they encompass to the root signature.
+ //
+ addBindingRange(
+ typeLayout,
+ physicalDescriptorSetIndex,
+ containerOffset.primary,
+ elementOffset.primary,
+ bindingRangeIndex);
+ }
+
+ // Next we need to recursively include everything bound via sub-objects
+ Index subObjectRangeCount = typeLayout->getSubObjectRangeCount();
+ for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount;
+ subObjectRangeIndex++)
+ {
+ auto bindingRangeIndex =
+ typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex);
+ auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
+
+ auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+
+ BindingRegisterOffsetPair subObjectRangeContainerOffset = containerOffset;
+ subObjectRangeContainerOffset +=
+ BindingRegisterOffsetPair(typeLayout->getSubObjectRangeOffset(subObjectRangeIndex));
+ BindingRegisterOffsetPair subObjectRangeElementOffset = elementOffset;
+ subObjectRangeElementOffset +=
+ BindingRegisterOffsetPair(typeLayout->getSubObjectRangeOffset(subObjectRangeIndex));
+
+ switch (bindingType)
+ {
+ case slang::BindingType::ConstantBuffer:
+ {
+ auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
+ SLANG_ASSERT(containerVarLayout);
+
+ auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
+ SLANG_ASSERT(elementVarLayout);
+
+ auto elementTypeLayout = elementVarLayout->getTypeLayout();
+ SLANG_ASSERT(elementTypeLayout);
+
+ BindingRegisterOffsetPair containerOffset = subObjectRangeContainerOffset;
+ containerOffset += BindingRegisterOffsetPair(containerVarLayout);
+
+ BindingRegisterOffsetPair elementOffset = subObjectRangeElementOffset;
+ elementOffset += BindingRegisterOffsetPair(elementVarLayout);
+
+ addAsConstantBuffer(
+ elementTypeLayout, physicalDescriptorSetIndex, containerOffset, elementOffset);
+ }
+ break;
+
+ case slang::BindingType::ParameterBlock:
+ {
+ auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
+ SLANG_ASSERT(containerVarLayout);
+
+ auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
+ SLANG_ASSERT(elementVarLayout);
+
+ auto elementTypeLayout = elementVarLayout->getTypeLayout();
+ SLANG_ASSERT(elementTypeLayout);
+
+ BindingRegisterOffsetPair subDescriptorSetOffset;
+ subDescriptorSetOffset.primary.spaceOffset =
+ subObjectRangeElementOffset.primary.spaceOffset;
+ subDescriptorSetOffset.pending.spaceOffset =
+ subObjectRangeElementOffset.pending.spaceOffset;
+
+ auto subPhysicalDescriptorSetIndex = addDescriptorSet();
+
+ BindingRegisterOffsetPair containerOffset = subDescriptorSetOffset;
+ containerOffset += BindingRegisterOffsetPair(containerVarLayout);
+
+ BindingRegisterOffsetPair elementOffset = subDescriptorSetOffset;
+ elementOffset += BindingRegisterOffsetPair(elementVarLayout);
+
+ addAsConstantBuffer(
+ elementTypeLayout,
+ subPhysicalDescriptorSetIndex,
+ containerOffset,
+ elementOffset);
+ }
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ {
+ // Any nested binding ranges in the sub-object will "leak" into the
+ // binding ranges for the surrounding context.
+ //
+ auto specializedTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout();
+ if (specializedTypeLayout)
+ {
+ BindingRegisterOffsetPair pendingOffset;
+ pendingOffset.primary = subObjectRangeElementOffset.pending;
+
+ addAsValue(
+ specializedTypeLayout,
+ physicalDescriptorSetIndex,
+ pendingOffset,
+ pendingOffset);
+ }
+ }
+ break;
+ }
+ }
+}
+
+D3D12_ROOT_SIGNATURE_DESC& RootShaderObjectLayoutImpl::RootSignatureDescBuilder::build()
+{
+ for (Index i = 0; i < m_descriptorSets.getCount(); i++)
+ {
+ auto& descriptorSet = m_descriptorSets[i];
+ if (descriptorSet.m_resourceRanges.getCount())
+ {
+ D3D12_ROOT_PARAMETER rootParam = {};
+ rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParam.DescriptorTable.NumDescriptorRanges =
+ (UINT)descriptorSet.m_resourceRanges.getCount();
+ rootParam.DescriptorTable.pDescriptorRanges =
+ descriptorSet.m_resourceRanges.getBuffer();
+ m_rootParameters.add(rootParam);
+ }
+ if (descriptorSet.m_samplerRanges.getCount())
+ {
+ D3D12_ROOT_PARAMETER rootParam = {};
+ rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParam.DescriptorTable.NumDescriptorRanges =
+ (UINT)descriptorSet.m_samplerRanges.getCount();
+ rootParam.DescriptorTable.pDescriptorRanges = descriptorSet.m_samplerRanges.getBuffer();
+ m_rootParameters.add(rootParam);
+ }
+ }
+
+ m_rootSignatureDesc.NumParameters = UINT(m_rootParameters.getCount());
+ m_rootSignatureDesc.pParameters = m_rootParameters.getBuffer();
+
+ // TODO: static samplers should be reasonably easy to support...
+ m_rootSignatureDesc.NumStaticSamplers = 0;
+ m_rootSignatureDesc.pStaticSamplers = nullptr;
+
+ // TODO: only set this flag if needed (requires creating root
+ // signature at same time as pipeline state...).
+ //
+ m_rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
+
+ return m_rootSignatureDesc;
+}
+
+Result RootShaderObjectLayoutImpl::createRootSignatureFromSlang(
+ DeviceImpl* device,
+ RootShaderObjectLayoutImpl* rootLayout,
+ slang::IComponentType* program,
+ ID3D12RootSignature** outRootSignature,
+ ID3DBlob** outError)
+{
+ // We are going to build up the root signature by adding
+ // binding/descritpor ranges and nested parameter blocks
+ // based on the computed layout information for `program`.
+ //
+ RootSignatureDescBuilder builder(device);
+ auto layout = program->getLayout();
+
+ // The layout information computed by Slang breaks up shader
+ // parameters into what we can think of as "logical" descriptor
+ // sets based on whether or not parameters have the same `space`.
+ //
+ // We want to basically ignore that decomposition and generate a
+ // single descriptor set to hold all top-level parameters, and only
+ // generate distinct descriptor sets when the shader has opted in
+ // via explicit parameter blocks.
+ //
+ // To achieve this goal, we will manually allocate a default descriptor
+ // set for root parameters in our signature, and then recursively
+ // add all the binding/descriptor ranges implied by the global-scope
+ // parameters.
+ //
+ auto rootDescriptorSetIndex = builder.addDescriptorSet();
+ builder.addAsValue(layout->getGlobalParamsVarLayout(), rootDescriptorSetIndex);
+
+ for (SlangUInt i = 0; i < layout->getEntryPointCount(); i++)
+ {
+ // Entry-point parameters should also be added to the default root
+ // descriptor set.
+ //
+ // We add the parameters using the "variable layout" for the entry point
+ // and not just its type layout, to ensure that any offset information is
+ // applied correctly to the `register` and `space` information for entry-point
+ // parameters.
+ //
+ // Note: When we start to support DXR we will need to handle entry-point parameters
+ // differently because they will need to map to local root signatures rather than
+ // being included in the global root signature as is being done here.
+ //
+ auto entryPoint = layout->getEntryPointByIndex(i);
+ builder.addAsValue(entryPoint->getVarLayout(), rootDescriptorSetIndex);
+ }
+
+ auto& rootSignatureDesc = builder.build();
+
+ ComPtr<ID3DBlob> signature;
+ ComPtr<ID3DBlob> error;
+ if (SLANG_FAILED(device->m_D3D12SerializeRootSignature(
+ &rootSignatureDesc,
+ D3D_ROOT_SIGNATURE_VERSION_1,
+ signature.writeRef(),
+ error.writeRef())))
+ {
+ getDebugCallback()->handleMessage(
+ DebugMessageType::Error,
+ DebugMessageSource::Layer,
+ "error: D3D12SerializeRootSignature failed");
+ if (error)
+ {
+ getDebugCallback()->handleMessage(
+ DebugMessageType::Error,
+ DebugMessageSource::Driver,
+ (const char*)error->GetBufferPointer());
+ if (outError)
+ returnComPtr(outError, error);
+ }
+ return SLANG_FAIL;
+ }
+
+ SLANG_RETURN_ON_FAIL(device->m_device->CreateRootSignature(
+ 0,
+ signature->GetBufferPointer(),
+ signature->GetBufferSize(),
+ IID_PPV_ARGS(outRootSignature)));
+ return SLANG_OK;
+}
+
+Result RootShaderObjectLayoutImpl::create(
+ DeviceImpl* device,
+ slang::IComponentType* program,
+ slang::ProgramLayout* programLayout,
+ RootShaderObjectLayoutImpl** outLayout,
+ ID3DBlob** outError)
+{
+ RootShaderObjectLayoutImpl::Builder builder(device, program, programLayout);
+ builder.addGlobalParams(programLayout->getGlobalParamsVarLayout());
+
+ SlangInt entryPointCount = programLayout->getEntryPointCount();
+ for (SlangInt e = 0; e < entryPointCount; ++e)
+ {
+ auto slangEntryPoint = programLayout->getEntryPointByIndex(e);
+ RefPtr<ShaderObjectLayoutImpl> entryPointLayout;
+ SLANG_RETURN_ON_FAIL(ShaderObjectLayoutImpl::createForElementType(
+ device, slangEntryPoint->getTypeLayout(), entryPointLayout.writeRef()));
+ builder.addEntryPoint(slangEntryPoint->getStage(), entryPointLayout);
+ }
+
+ RefPtr<RootShaderObjectLayoutImpl> layout;
+ SLANG_RETURN_ON_FAIL(builder.build(layout.writeRef()));
+
+ if (program->getSpecializationParamCount() == 0)
+ {
+ // For root object, we would like know the union of all binding slots
+ // including all sub-objects in the shader-object hierarchy, so at
+ // parameter binding time we can easily know how many GPU descriptor tables
+ // to create without walking through the shader-object hierarchy again.
+ // We build out this array along with root signature construction and store
+ // it in `m_gpuDescriptorSetInfos`.
+ SLANG_RETURN_ON_FAIL(createRootSignatureFromSlang(
+ device, layout, program, layout->m_rootSignature.writeRef(), outError));
+ }
+
+ *outLayout = layout.detach();
+
+ return SLANG_OK;
+}
+
+Result RootShaderObjectLayoutImpl::init(Builder* builder)
+{
+ auto renderer = builder->m_renderer;
+
+ SLANG_RETURN_ON_FAIL(Super::init(builder));
+
+ m_program = builder->m_program;
+ m_programLayout = builder->m_programLayout;
+ m_entryPoints = builder->m_entryPoints;
+ return SLANG_OK;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-shader-object-layout.h b/tools/gfx/d3d12/d3d12-shader-object-layout.h
new file mode 100644
index 000000000..d70b194c8
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-shader-object-layout.h
@@ -0,0 +1,485 @@
+// d3d12-shader-object-layout.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+/// A representation of the offset at which to bind a shader parameter or sub-object
+struct BindingOffset
+{
+ // Note: When we actually bind a shader object to the pipeline we do not care about
+ // HLSL-specific notions like `t` registers and `space`s. Those concepts are all
+ // mediated by the root signature.
+ //
+ // Instead, we need to consider the offsets at which the object will be bound
+ // into the actual D3D12 API state, which consists of the index of the current
+ // root parameter to bind from, as well as indices into the current descriptor
+ // tables (for resource views and samplers).
+
+ uint32_t rootParam = 0;
+ uint32_t resource = 0;
+ uint32_t sampler = 0;
+
+ void operator+=(BindingOffset const& offset)
+ {
+ rootParam += offset.rootParam;
+ resource += offset.resource;
+ sampler += offset.sampler;
+ }
+};
+
+// Provides information on how binding ranges are stored in descriptor tables for
+// a shader object.
+// We allocate one CPU descriptor table for each descriptor heap type for the shader
+// object. In `ShaderObjectLayoutImpl`, we store the offset into the descriptor tables
+// for each binding, so we know where to write the descriptor when the user sets
+// a resource or sampler binding.
+class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
+{
+public:
+ /// Information about a single logical binding range
+ struct BindingRangeInfo
+ {
+ // Some of the information we store on binding ranges is redundant with
+ // the information that Slang's reflection information stores, but having
+ // it here can make the code more compact and obvious.
+
+ /// The type of binding in this range.
+ slang::BindingType bindingType;
+
+ /// The shape of the resource
+ SlangResourceShape resourceShape;
+
+ /// The number of distinct bindings in this range.
+ uint32_t count;
+
+ /// A "flat" index for this range in whatever array provides backing storage for it
+ uint32_t baseIndex;
+
+ /// An index into the sub-object array if this binding range is treated
+ /// as a sub-object.
+ uint32_t subObjectIndex;
+
+ bool isRootParameter;
+ };
+
+ /// Offset information for a sub-object range
+ struct SubObjectRangeOffset : BindingOffset
+ {
+ SubObjectRangeOffset() {}
+
+ SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout);
+
+ /// The offset for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
+ };
+
+ /// Stride information for a sub-object range
+ struct SubObjectRangeStride : BindingOffset
+ {
+ SubObjectRangeStride() {}
+
+ SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout);
+
+ /// The strid for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
+ };
+
+ /// Information about a sub-objecrt range
+ struct SubObjectRangeInfo
+ {
+ /// The index of the binding range corresponding to this sub-object range
+ Index bindingRangeIndex = 0;
+
+ /// Layout information for the type of sub-object expected to be bound, if known
+ RefPtr<ShaderObjectLayoutImpl> layout;
+
+ /// The offset to use when binding the first object in this range
+ SubObjectRangeOffset offset;
+
+ /// Stride between consecutive objects in this range
+ SubObjectRangeStride stride;
+ };
+
+ struct RootParameterInfo
+ {
+ IResourceView::Type type;
+ };
+
+ static bool isBindingRangeRootParameter(
+ SlangSession* globalSession,
+ const char* rootParameterAttributeName,
+ slang::TypeLayoutReflection* typeLayout,
+ Index bindingRangeIndex);
+
+ struct Builder
+ {
+ public:
+ Builder(RendererBase* renderer)
+ : m_renderer(renderer)
+ {}
+
+ RendererBase* m_renderer;
+ slang::TypeLayoutReflection* m_elementTypeLayout;
+ List<BindingRangeInfo> m_bindingRanges;
+ List<SubObjectRangeInfo> m_subObjectRanges;
+ List<RootParameterInfo> m_rootParamsInfo;
+
+ /// The number of sub-objects (not just sub-object *ranges*) stored in instances of this
+ /// layout
+ uint32_t m_subObjectCount = 0;
+
+ /// Counters for the number of root parameters, resources, and samplers in this object
+ /// itself
+ BindingOffset m_ownCounts;
+
+ /// Counters for the number of root parameters, resources, and sampler in this object
+ /// and transitive sub-objects
+ BindingOffset m_totalCounts;
+
+ /// The number of root parameter consumed by (transitive) sub-objects
+ uint32_t m_childRootParameterCount = 0;
+
+ /// The total size in bytes of the ordinary data for this object and transitive
+ /// sub-object.
+ uint32_t m_totalOrdinaryDataSize = 0;
+
+ /// The container type of this shader object. When `m_containerType` is
+ /// `StructuredBuffer` or `UnsizedArray`, this shader object represents a collection
+ /// instead of a single object.
+ ShaderObjectContainerType m_containerType = ShaderObjectContainerType::None;
+
+ Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout);
+
+ Result build(ShaderObjectLayoutImpl** outLayout);
+ };
+
+ static Result createForElementType(
+ RendererBase* renderer,
+ slang::TypeLayoutReflection* elementType,
+ ShaderObjectLayoutImpl** outLayout);
+
+ List<BindingRangeInfo> const& getBindingRanges() { return m_bindingRanges; }
+
+ Index getBindingRangeCount() { return m_bindingRanges.getCount(); }
+
+ BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; }
+
+ uint32_t getResourceSlotCount() { return m_ownCounts.resource; }
+ uint32_t getSamplerSlotCount() { return m_ownCounts.sampler; }
+ Index getSubObjectSlotCount() { return m_subObjectCount; }
+ Index getSubObjectCount() { return m_subObjectCount; }
+
+ uint32_t getTotalResourceDescriptorCount() { return m_totalCounts.resource; }
+ uint32_t getTotalSamplerDescriptorCount() { return m_totalCounts.sampler; }
+
+ uint32_t getOrdinaryDataBufferCount() { return m_totalOrdinaryDataSize ? 1 : 0; }
+ bool hasOrdinaryDataBuffer() { return m_totalOrdinaryDataSize != 0; }
+
+ uint32_t getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer()
+ {
+ return m_totalCounts.resource - getOrdinaryDataBufferCount();
+ }
+
+ uint32_t getOwnUserRootParameterCount() { return (uint32_t)m_rootParamsInfo.getCount(); }
+ uint32_t getTotalRootTableParameterCount() { return m_totalCounts.rootParam; }
+ uint32_t getChildRootParameterCount() { return m_childRootParameterCount; }
+
+ uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
+
+ SubObjectRangeInfo const& getSubObjectRange(Index index) { return m_subObjectRanges[index]; }
+ List<SubObjectRangeInfo> const& getSubObjectRanges() { return m_subObjectRanges; }
+
+ RendererBase* getRenderer() { return m_renderer; }
+
+ slang::TypeReflection* getType() { return m_elementTypeLayout->getType(); }
+
+ const RootParameterInfo& getRootParameterInfo(Index index) { return m_rootParamsInfo[index]; }
+
+protected:
+ Result init(Builder* builder);
+
+ List<BindingRangeInfo> m_bindingRanges;
+ List<SubObjectRangeInfo> m_subObjectRanges;
+ List<RootParameterInfo> m_rootParamsInfo;
+
+ BindingOffset m_ownCounts;
+ BindingOffset m_totalCounts;
+
+ uint32_t m_subObjectCount = 0;
+ uint32_t m_childRootParameterCount = 0;
+
+ uint32_t m_totalOrdinaryDataSize = 0;
+};
+
+class RootShaderObjectLayoutImpl : public ShaderObjectLayoutImpl
+{
+ typedef ShaderObjectLayoutImpl Super;
+
+public:
+ struct EntryPointInfo
+ {
+ RefPtr<ShaderObjectLayoutImpl> layout;
+ BindingOffset offset;
+ };
+
+ struct Builder : Super::Builder
+ {
+ Builder(
+ RendererBase* renderer,
+ slang::IComponentType* program,
+ slang::ProgramLayout* programLayout)
+ : Super::Builder(renderer)
+ , m_program(program)
+ , m_programLayout(programLayout)
+ {}
+
+ Result build(RootShaderObjectLayoutImpl** outLayout);
+
+ void addGlobalParams(slang::VariableLayoutReflection* globalsLayout);
+
+ void addEntryPoint(SlangStage stage, ShaderObjectLayoutImpl* entryPointLayout);
+
+ slang::IComponentType* m_program;
+ slang::ProgramLayout* m_programLayout;
+ List<EntryPointInfo> m_entryPoints;
+ };
+
+ EntryPointInfo& getEntryPoint(Index index) { return m_entryPoints[index]; }
+
+ List<EntryPointInfo>& getEntryPoints() { return m_entryPoints; }
+
+ struct DescriptorSetLayout
+ {
+ List<D3D12_DESCRIPTOR_RANGE> m_resourceRanges;
+ List<D3D12_DESCRIPTOR_RANGE> m_samplerRanges;
+ uint32_t m_resourceCount = 0;
+ uint32_t m_samplerCount = 0;
+ };
+
+ struct RootSignatureDescBuilder
+ {
+ DeviceImpl* m_device;
+
+ RootSignatureDescBuilder(DeviceImpl* device)
+ : m_device(device)
+ {}
+
+ // We will use one descriptor set for the global scope and one additional
+ // descriptor set for each `ParameterBlock` binding range in the shader object
+ // hierarchy, regardless of the shader's `space` indices.
+ List<DescriptorSetLayout> m_descriptorSets;
+ List<D3D12_ROOT_PARAMETER> m_rootParameters;
+ D3D12_ROOT_SIGNATURE_DESC m_rootSignatureDesc = {};
+
+ static Result translateDescriptorRangeType(
+ slang::BindingType c, D3D12_DESCRIPTOR_RANGE_TYPE* outType);
+
+ /// Stores offset information to apply to the reflected register/space for a descriptor
+ /// range.
+ ///
+ struct BindingRegisterOffset
+ {
+ uint32_t spaceOffset = 0; // The `space` index as specified in shader.
+
+ enum
+ {
+ kRangeTypeCount = 4
+ };
+
+ /// An offset to apply for each D3D12 register class, as given
+ /// by a `D3D12_DESCRIPTOR_RANGE_TYPE`.
+ ///
+ /// Note that the `D3D12_DESCRIPTOR_RANGE_TYPE` enumeration has
+ /// values between 0 and 3, inclusive.
+ ///
+ uint32_t offsetForRangeType[kRangeTypeCount] = { 0, 0, 0, 0 };
+
+ uint32_t& operator[](D3D12_DESCRIPTOR_RANGE_TYPE type)
+ {
+ return offsetForRangeType[int(type)];
+ }
+
+ uint32_t operator[](D3D12_DESCRIPTOR_RANGE_TYPE type) const
+ {
+ return offsetForRangeType[int(type)];
+ }
+
+ BindingRegisterOffset() {}
+
+ BindingRegisterOffset(slang::VariableLayoutReflection* varLayout)
+ {
+ if (varLayout)
+ {
+ spaceOffset =
+ (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE);
+ offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] =
+ (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER);
+ offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SRV] =
+ (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE);
+ offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_UAV] =
+ (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS);
+ offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER] =
+ (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE);
+ }
+ }
+
+ void operator+=(BindingRegisterOffset const& other)
+ {
+ spaceOffset += other.spaceOffset;
+ for (int i = 0; i < kRangeTypeCount; ++i)
+ {
+ offsetForRangeType[i] += other.offsetForRangeType[i];
+ }
+ }
+ };
+
+ struct BindingRegisterOffsetPair
+ {
+ BindingRegisterOffset primary;
+ BindingRegisterOffset pending;
+
+ BindingRegisterOffsetPair() {}
+
+ BindingRegisterOffsetPair(slang::VariableLayoutReflection* varLayout)
+ : primary(varLayout)
+ , pending(varLayout->getPendingDataLayout())
+ {}
+
+ void operator+=(BindingRegisterOffsetPair const& other)
+ {
+ primary += other.primary;
+ pending += other.pending;
+ }
+ };
+ /// Add a new descriptor set to the layout being computed.
+ ///
+ /// Note that a "descriptor set" in the layout may amount to
+ /// zero, one, or two different descriptor *tables* in the
+ /// final D3D12 root signature. Each descriptor set may
+ /// contain zero or more view ranges (CBV/SRV/UAV) and zero
+ /// or more sampler ranges. It maps to a view descriptor table
+ /// if the number of view ranges is non-zero and to a sampler
+ /// descriptor table if the number of sampler ranges is non-zero.
+ ///
+ uint32_t addDescriptorSet();
+
+ Result addDescriptorRange(
+ Index physicalDescriptorSetIndex,
+ D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
+ UINT registerIndex,
+ UINT spaceIndex,
+ UINT count,
+ bool isRootParameter);
+ /// Add one descriptor range as specified in Slang reflection information to the layout.
+ ///
+ /// The layout information is taken from `typeLayout` for the descriptor
+ /// range with the given `descriptorRangeIndex` within the logical
+ /// descriptor set (reflected by Slang) with the given `logicalDescriptorSetIndex`.
+ ///
+ /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
+ /// the descriptor set that the range should be added to.
+ ///
+ /// The `offset` encodes information about space and/or register offsets that
+ /// should be applied to descrptor ranges.
+ ///
+ /// This operation can fail if the given descriptor range encodes a range that
+ /// doesn't map to anything directly supported by D3D12. Higher-level routines
+ /// will often want to ignore such failures.
+ ///
+ Result addDescriptorRange(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffset const& containerOffset,
+ BindingRegisterOffset const& elementOffset,
+ Index logicalDescriptorSetIndex,
+ Index descriptorRangeIndex,
+ bool isRootParameter);
+
+ /// Add one binding range to the computed layout.
+ ///
+ /// The layout information is taken from `typeLayout` for the binding
+ /// range with the given `bindingRangeIndex`.
+ ///
+ /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
+ /// the descriptor set that the range should be added to.
+ ///
+ /// The `offset` encodes information about space and/or register offsets that
+ /// should be applied to descrptor ranges.
+ ///
+ /// Note that a single binding range may encompass zero or more descriptor ranges.
+ ///
+ void addBindingRange(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffset const& containerOffset,
+ BindingRegisterOffset const& elementOffset,
+ Index bindingRangeIndex);
+
+ void addAsValue(
+ slang::VariableLayoutReflection* varLayout, Index physicalDescriptorSetIndex);
+
+ /// Add binding ranges and parameter blocks to the root signature.
+ ///
+ /// The layout information is taken from `typeLayout` which should
+ /// be a layout for either a program or an entry point.
+ ///
+ /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
+ /// the descriptor set that binding ranges not belonging to nested
+ /// parameter blocks should be added to.
+ ///
+ /// The `offset` encodes information about space and/or register offsets that
+ /// should be applied to descrptor ranges.
+ ///
+ void addAsConstantBuffer(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffsetPair const& containerOffset,
+ BindingRegisterOffsetPair const& elementOffset);
+
+ void addAsValue(
+ slang::TypeLayoutReflection* typeLayout,
+ Index physicalDescriptorSetIndex,
+ BindingRegisterOffsetPair const& containerOffset,
+ BindingRegisterOffsetPair const& elementOffset);
+
+ D3D12_ROOT_SIGNATURE_DESC& build();
+ };
+
+ static Result createRootSignatureFromSlang(
+ DeviceImpl* device,
+ RootShaderObjectLayoutImpl* rootLayout,
+ slang::IComponentType* program,
+ ID3D12RootSignature** outRootSignature,
+ ID3DBlob** outError);
+
+ static Result create(
+ DeviceImpl* device,
+ slang::IComponentType* program,
+ slang::ProgramLayout* programLayout,
+ RootShaderObjectLayoutImpl** outLayout,
+ ID3DBlob** outError);
+
+ slang::IComponentType* getSlangProgram() const { return m_program; }
+ slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
+
+protected:
+ Result init(Builder* builder);
+
+ ComPtr<slang::IComponentType> m_program;
+ slang::ProgramLayout* m_programLayout = nullptr;
+
+ List<EntryPointInfo> m_entryPoints;
+
+public:
+ ComPtr<ID3D12RootSignature> m_rootSignature;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-shader-object.cpp b/tools/gfx/d3d12/d3d12-shader-object.cpp
new file mode 100644
index 000000000..e5875f55c
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-shader-object.cpp
@@ -0,0 +1,1179 @@
+// d3d12-shader-object.cpp
+#include "d3d12-shader-object.h"
+
+#include "d3d12-buffer.h"
+#include "d3d12-command-encoder.h"
+#include "d3d12-device.h"
+#include "d3d12-resource-views.h"
+#include "d3d12-sampler.h"
+#include "d3d12-shader-object-layout.h"
+#include "d3d12-transient-heap.h"
+
+#include "d3d12-helper-functions.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+GfxCount ShaderObjectImpl::getEntryPointCount() { return 0; }
+
+Result ShaderObjectImpl::getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint)
+{
+ *outEntryPoint = nullptr;
+ return SLANG_OK;
+}
+
+const void* ShaderObjectImpl::getRawData() { return m_data.getBuffer(); }
+
+Size ShaderObjectImpl::getSize() { return (Size)m_data.getCount(); }
+
+// TODO: Change Index to Offset/Size?
+Result ShaderObjectImpl::setData(ShaderOffset const& inOffset, void const* data, size_t inSize)
+{
+ Index offset = inOffset.uniformOffset;
+ Index size = inSize;
+
+ char* dest = m_data.getBuffer();
+ Index availableSize = m_data.getCount();
+
+ // TODO: We really should bounds-check access rather than silently ignoring sets
+ // that are too large, but we have several test cases that set more data than
+ // an object actually stores on several targets...
+ //
+ if (offset < 0)
+ {
+ size += offset;
+ offset = 0;
+ }
+ if ((offset + size) >= availableSize)
+ {
+ size = availableSize - offset;
+ }
+
+ memcpy(dest + offset, data, size);
+
+ m_isConstantBufferDirty = true;
+
+ m_version++;
+
+ return SLANG_OK;
+}
+
+Result ShaderObjectImpl::setObject(ShaderOffset const& offset, IShaderObject* object)
+{
+ SLANG_RETURN_ON_FAIL(Super::setObject(offset, object));
+ if (m_isMutable)
+ {
+ auto subObjectIndex = getSubObjectIndex(offset);
+ if (subObjectIndex >= m_subObjectVersions.getCount())
+ m_subObjectVersions.setCount(subObjectIndex + 1);
+ m_subObjectVersions[subObjectIndex] = static_cast<ShaderObjectImpl*>(object)->m_version;
+ m_version++;
+ }
+ return SLANG_OK;
+}
+
+Result ShaderObjectImpl::setSampler(ShaderOffset const& offset, ISamplerState* sampler)
+{
+ if (offset.bindingRangeIndex < 0)
+ return SLANG_E_INVALID_ARG;
+ auto layout = getLayout();
+ if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
+ return SLANG_E_INVALID_ARG;
+ auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
+ auto samplerImpl = static_cast<SamplerStateImpl*>(sampler);
+ ID3D12Device* d3dDevice = static_cast<DeviceImpl*>(getDevice())->m_device;
+ d3dDevice->CopyDescriptorsSimple(
+ 1,
+ m_descriptorSet.samplerTable.getCpuHandle(
+ bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex),
+ samplerImpl->m_descriptor.cpuHandle,
+ D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+ m_version++;
+ return SLANG_OK;
+}
+
+Result ShaderObjectImpl::setCombinedTextureSampler(
+ ShaderOffset const& offset, IResourceView* textureView, ISamplerState* sampler)
+{
+#if 0
+ if (offset.bindingRangeIndex < 0)
+ return SLANG_E_INVALID_ARG;
+ auto layout = getLayout();
+ if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
+ return SLANG_E_INVALID_ARG;
+ auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
+ auto resourceViewImpl = static_cast<ResourceViewImpl*>(textureView);
+ ID3D12Device* d3dDevice = static_cast<DeviceImpl*>(getDevice())->m_device;
+ d3dDevice->CopyDescriptorsSimple(
+ 1,
+ m_resourceHeap.getCpuHandle(
+ m_descriptorSet.m_resourceTable +
+ bindingRange.binding.offsetInDescriptorTable.resource +
+ (int32_t)offset.bindingArrayIndex),
+ resourceViewImpl->m_descriptor.cpuHandle,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ auto samplerImpl = static_cast<SamplerStateImpl*>(sampler);
+ d3dDevice->CopyDescriptorsSimple(
+ 1,
+ m_samplerHeap.getCpuHandle(
+ m_descriptorSet.m_samplerTable +
+ bindingRange.binding.offsetInDescriptorTable.sampler +
+ (int32_t)offset.bindingArrayIndex),
+ samplerImpl->m_descriptor.cpuHandle,
+ D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+#endif
+ m_version++;
+ return SLANG_OK;
+}
+
+Result ShaderObjectImpl::init(
+ DeviceImpl* device,
+ ShaderObjectLayoutImpl* layout,
+ DescriptorHeapReference viewHeap,
+ DescriptorHeapReference samplerHeap)
+{
+ m_device = device;
+
+ m_layout = layout;
+
+ m_cachedTransientHeap = nullptr;
+ m_cachedTransientHeapVersion = 0;
+ m_isConstantBufferDirty = true;
+
+ // If the layout tells us that there is any uniform data,
+ // then we will allocate a CPU memory buffer to hold that data
+ // while it is being set from the host.
+ //
+ // Once the user is done setting the parameters/fields of this
+ // shader object, we will produce a GPU-memory version of the
+ // uniform data (which includes values from this object and
+ // any existential-type sub-objects).
+ //
+ size_t uniformSize = layout->getElementTypeLayout()->getSize();
+ if (uniformSize)
+ {
+ m_data.setCount(uniformSize);
+ memset(m_data.getBuffer(), 0, uniformSize);
+ }
+ m_rootArguments.setCount(layout->getOwnUserRootParameterCount());
+ memset(
+ m_rootArguments.getBuffer(),
+ 0,
+ sizeof(D3D12_GPU_VIRTUAL_ADDRESS) * m_rootArguments.getCount());
+ // Each shader object will own CPU descriptor heap memory
+ // for any resource or sampler descriptors it might store
+ // as part of its value.
+ //
+ // This allocate includes a reservation for any constant
+ // buffer descriptor pertaining to the ordinary data,
+ // but does *not* include any descriptors that are managed
+ // as part of sub-objects.
+ //
+ if (auto resourceCount = layout->getResourceSlotCount())
+ {
+ m_descriptorSet.resourceTable.allocate(viewHeap, resourceCount);
+
+ // We must also ensure that the memory for any resources
+ // referenced by descriptors in this object does not get
+ // freed while the object is still live.
+ //
+ m_boundResources.setCount(resourceCount);
+ }
+ if (auto samplerCount = layout->getSamplerSlotCount())
+ {
+ m_descriptorSet.samplerTable.allocate(samplerHeap, samplerCount);
+ }
+
+ // If the layout specifies that we have any sub-objects, then
+ // we need to size the array to account for them.
+ //
+ Index subObjectCount = layout->getSubObjectSlotCount();
+ m_objects.setCount(subObjectCount);
+
+ for (auto subObjectRangeInfo : layout->getSubObjectRanges())
+ {
+ auto subObjectLayout = subObjectRangeInfo.layout;
+
+ // In the case where the sub-object range represents an
+ // existential-type leaf field (e.g., an `IBar`), we
+ // cannot pre-allocate the object(s) to go into that
+ // range, since we can't possibly know what to allocate
+ // at this point.
+ //
+ if (!subObjectLayout)
+ continue;
+ //
+ // Otherwise, we will allocate a sub-object to fill
+ // in each entry in this range, based on the layout
+ // information we already have.
+
+ auto& bindingRangeInfo = layout->getBindingRange(subObjectRangeInfo.bindingRangeIndex);
+ for (uint32_t i = 0; i < bindingRangeInfo.count; ++i)
+ {
+ RefPtr<ShaderObjectImpl> subObject;
+ SLANG_RETURN_ON_FAIL(
+ ShaderObjectImpl::create(device, subObjectLayout, subObject.writeRef()));
+ m_objects[bindingRangeInfo.subObjectIndex + i] = subObject;
+ }
+ }
+
+ return SLANG_OK;
+}
+
+/// Write the uniform/ordinary data of this object into the given `dest` buffer at the given
+/// `offset`
+
+Result ShaderObjectImpl::_writeOrdinaryData(
+ PipelineCommandEncoder* encoder,
+ BufferResourceImpl* buffer,
+ Offset offset,
+ Size destSize,
+ ShaderObjectLayoutImpl* specializedLayout)
+{
+ auto src = m_data.getBuffer();
+ auto srcSize = Size(m_data.getCount());
+
+ SLANG_ASSERT(srcSize <= destSize);
+
+ uploadBufferDataImpl(
+ encoder->m_device,
+ encoder->m_d3dCmdList,
+ encoder->m_transientHeap,
+ buffer,
+ offset,
+ srcSize,
+ src);
+
+ // In the case where this object has any sub-objects of
+ // existential/interface type, we need to recurse on those objects
+ // that need to write their state into an appropriate "pending" allocation.
+ //
+ // Note: Any values that could fit into the "payload" included
+ // in the existential-type field itself will have already been
+ // written as part of `setObject()`. This loop only needs to handle
+ // those sub-objects that do not "fit."
+ //
+ // An implementers looking at this code might wonder if things could be changed
+ // so that *all* writes related to sub-objects for interface-type fields could
+ // be handled in this one location, rather than having some in `setObject()` and
+ // others handled here.
+ //
+ Index subObjectRangeCounter = 0;
+ for (auto const& subObjectRangeInfo : specializedLayout->getSubObjectRanges())
+ {
+ Index subObjectRangeIndex = subObjectRangeCounter++;
+ auto const& bindingRangeInfo =
+ specializedLayout->getBindingRange(subObjectRangeInfo.bindingRangeIndex);
+
+ // We only need to handle sub-object ranges for interface/existential-type fields,
+ // because fields of constant-buffer or parameter-block type are responsible for
+ // the ordinary/uniform data of their own existential/interface-type sub-objects.
+ //
+ if (bindingRangeInfo.bindingType != slang::BindingType::ExistentialValue)
+ continue;
+
+ // Each sub-object range represents a single "leaf" field, but might be nested
+ // under zero or more outer arrays, such that the number of existential values
+ // in the same range can be one or more.
+ //
+ auto count = bindingRangeInfo.count;
+
+ // We are not concerned with the case where the existential value(s) in the range
+ // git into the payload part of the leaf field.
+ //
+ // In the case where the value didn't fit, the Slang layout strategy would have
+ // considered the requirements of the value as a "pending" allocation, and would
+ // allocate storage for the ordinary/uniform part of that pending allocation inside
+ // of the parent object's type layout.
+ //
+ // Here we assume that the Slang reflection API can provide us with a single byte
+ // offset and stride for the location of the pending data allocation in the
+ // specialized type layout, which will store the values for this sub-object range.
+ //
+ // TODO: The reflection API functions we are assuming here haven't been implemented
+ // yet, so the functions being called here are stubs.
+ //
+ // TODO: It might not be that a single sub-object range can reliably map to a single
+ // contiguous array with a single stride; we need to carefully consider what the
+ // layout logic does for complex cases with multiple layers of nested arrays and
+ // structures.
+ //
+ Offset subObjectRangePendingDataOffset = subObjectRangeInfo.offset.pendingOrdinaryData;
+ Size subObjectRangePendingDataStride = subObjectRangeInfo.stride.pendingOrdinaryData;
+
+ // If the range doesn't actually need/use the "pending" allocation at all, then
+ // we need to detect that case and skip such ranges.
+ //
+ // TODO: This should probably be handled on a per-object basis by caching a "does it
+ // fit?" bit as part of the information for bound sub-objects, given that we already
+ // compute the "does it fit?" status as part of `setObject()`.
+ //
+ if (subObjectRangePendingDataOffset == 0)
+ continue;
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ auto subObject = m_objects[bindingRangeInfo.subObjectIndex + i];
+
+ RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
+ SLANG_RETURN_ON_FAIL(subObject->getSpecializedLayout(subObjectLayout.writeRef()));
+
+ auto subObjectOffset =
+ subObjectRangePendingDataOffset + i * subObjectRangePendingDataStride;
+
+ subObject->_writeOrdinaryData(
+ encoder,
+ buffer,
+ offset + subObjectOffset,
+ destSize - subObjectOffset,
+ subObjectLayout);
+ }
+ }
+
+ return SLANG_OK;
+}
+
+bool ShaderObjectImpl::shouldAllocateConstantBuffer(TransientResourceHeapImpl* transientHeap)
+{
+ if (m_isConstantBufferDirty || m_cachedTransientHeap != transientHeap ||
+ m_cachedTransientHeapVersion != transientHeap->getVersion())
+ {
+ return true;
+ }
+ return false;
+}
+
+/// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
+
+Result ShaderObjectImpl::_ensureOrdinaryDataBufferCreatedIfNeeded(
+ PipelineCommandEncoder* encoder, ShaderObjectLayoutImpl* specializedLayout)
+{
+ // If data has been changed since last allocation/filling of constant buffer,
+ // we will need to allocate a new one.
+ //
+ if (!shouldAllocateConstantBuffer(encoder->m_transientHeap))
+ {
+ return SLANG_OK;
+ }
+ m_isConstantBufferDirty = false;
+ m_cachedTransientHeap = encoder->m_transientHeap;
+ m_cachedTransientHeapVersion = encoder->m_transientHeap->getVersion();
+
+ // Computing the size of the ordinary data buffer is *not* just as simple
+ // as using the size of the `m_ordinayData` array that we store. The reason
+ // for the added complexity is that interface-type fields may lead to the
+ // storage being specialized such that it needs extra appended data to
+ // store the concrete values that logically belong in those interface-type
+ // fields but wouldn't fit in the fixed-size allocation we gave them.
+ //
+ m_constantBufferSize = specializedLayout->getTotalOrdinaryDataSize();
+ if (m_constantBufferSize == 0)
+ {
+ return SLANG_OK;
+ }
+
+ // Once we have computed how large the buffer should be, we can allocate
+ // it from the transient resource heap.
+ //
+ auto alignedConstantBufferSize = D3DUtil::calcAligned(m_constantBufferSize, 256);
+ SLANG_RETURN_ON_FAIL(encoder->m_commandBuffer->m_transientHeap->allocateConstantBuffer(
+ alignedConstantBufferSize, m_constantBufferWeakPtr, m_constantBufferOffset));
+
+ // Once the buffer is allocated, we can use `_writeOrdinaryData` to fill it in.
+ //
+ // Note that `_writeOrdinaryData` is potentially recursive in the case
+ // where this object contains interface/existential-type fields, so we
+ // don't need or want to inline it into this call site.
+ //
+ SLANG_RETURN_ON_FAIL(_writeOrdinaryData(
+ encoder,
+ static_cast<BufferResourceImpl*>(m_constantBufferWeakPtr),
+ m_constantBufferOffset,
+ m_constantBufferSize,
+ specializedLayout));
+
+ {
+ // We also create and store a descriptor for our root constant buffer
+ // into the descriptor table allocation that was reserved for them.
+ //
+ // We always know that the ordinary data buffer will be the first descriptor
+ // in the table of resource views.
+ //
+ auto descriptorTable = m_descriptorSet.resourceTable;
+ D3D12_CONSTANT_BUFFER_VIEW_DESC viewDesc = {};
+ viewDesc.BufferLocation = static_cast<BufferResourceImpl*>(m_constantBufferWeakPtr)
+ ->m_resource.getResource()
+ ->GetGPUVirtualAddress() +
+ m_constantBufferOffset;
+ viewDesc.SizeInBytes = (UINT)alignedConstantBufferSize;
+ encoder->m_device->CreateConstantBufferView(&viewDesc, descriptorTable.getCpuHandle());
+ }
+
+ return SLANG_OK;
+}
+
+void ShaderObjectImpl::updateSubObjectsRecursive()
+{
+ if (!m_isMutable)
+ return;
+ auto& subObjectRanges = getLayout()->getSubObjectRanges();
+ for (Slang::Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRanges.getCount();
+ subObjectRangeIndex++)
+ {
+ auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
+ auto const& bindingRange = getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
+ Slang::Index count = bindingRange.count;
+
+ for (Slang::Index subObjectIndexInRange = 0; subObjectIndexInRange < count;
+ subObjectIndexInRange++)
+ {
+ Slang::Index objectIndex = bindingRange.subObjectIndex + subObjectIndexInRange;
+ auto subObject = m_objects[objectIndex].Ptr();
+ if (!subObject)
+ continue;
+ subObject->updateSubObjectsRecursive();
+ if (m_subObjectVersions[objectIndex] != m_objects[objectIndex]->m_version)
+ {
+ ShaderOffset offset;
+ offset.bindingRangeIndex = (GfxIndex)subObjectRange.bindingRangeIndex;
+ offset.bindingArrayIndex = (GfxIndex)subObjectIndexInRange;
+ setObject(offset, subObject);
+ }
+ }
+ }
+}
+
+/// Prepare to bind this object as a parameter block.
+///
+/// This involves allocating and binding any descriptor tables necessary
+/// to to store the state of the object. The function returns a descriptor
+/// set formed from any table(s) allocated. In addition, the `ioOffset`
+/// parameter will be adjusted to be correct for binding values into
+/// the resulting descriptor set.
+///
+/// Returns:
+/// SLANG_OK when successful,
+/// SLANG_E_OUT_OF_MEMORY when descriptor heap is full.
+///
+
+Result ShaderObjectImpl::prepareToBindAsParameterBlock(
+ BindingContext* context,
+ BindingOffset& ioOffset,
+ ShaderObjectLayoutImpl* specializedLayout,
+ DescriptorSet& outDescriptorSet)
+{
+ auto transientHeap = context->transientHeap;
+ auto submitter = context->submitter;
+
+ // When writing into the new descriptor set, resource and sampler
+ // descriptors will need to start at index zero in the respective
+ // tables.
+ //
+ ioOffset.resource = 0;
+ ioOffset.sampler = 0;
+
+ // The index of the next root parameter to bind will be maintained,
+ // but needs to be incremented by the number of descriptor tables
+ // we allocate (zero or one resource table and zero or one sampler
+ // table).
+ //
+ auto& rootParamIndex = ioOffset.rootParam;
+
+ if (auto descriptorCount = specializedLayout->getTotalResourceDescriptorCount())
+ {
+ // There is a non-zero number of resource descriptors needed,
+ // so we will allocate a table out of the appropriate heap,
+ // and store it into the appropriate part of `descriptorSet`.
+ //
+ auto descriptorHeap = &transientHeap->getCurrentViewHeap();
+ auto& table = outDescriptorSet.resourceTable;
+
+ // Allocate the table.
+ //
+ if (!table.allocate(descriptorHeap, descriptorCount))
+ {
+ context->outOfMemoryHeap = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+ return SLANG_E_OUT_OF_MEMORY;
+ }
+
+ // Bind the table to the pipeline, consuming the next available
+ // root parameter.
+ //
+ auto tableRootParamIndex = rootParamIndex++;
+ submitter->setRootDescriptorTable(tableRootParamIndex, table.getGpuHandle());
+ }
+ if (auto descriptorCount = specializedLayout->getTotalSamplerDescriptorCount())
+ {
+ // There is a non-zero number of sampler descriptors needed,
+ // so we will allocate a table out of the appropriate heap,
+ // and store it into the appropriate part of `descriptorSet`.
+ //
+ auto descriptorHeap = &transientHeap->getCurrentSamplerHeap();
+ auto& table = outDescriptorSet.samplerTable;
+
+ // Allocate the table.
+ //
+ if (!table.allocate(descriptorHeap, descriptorCount))
+ {
+ context->outOfMemoryHeap = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
+ return SLANG_E_OUT_OF_MEMORY;
+ }
+
+ // Bind the table to the pipeline, consuming the next available
+ // root parameter.
+ //
+ auto tableRootParamIndex = rootParamIndex++;
+ submitter->setRootDescriptorTable(tableRootParamIndex, table.getGpuHandle());
+ }
+
+ return SLANG_OK;
+}
+
+bool ShaderObjectImpl::checkIfCachedDescriptorSetIsValidRecursive(BindingContext* context)
+{
+ if (shouldAllocateConstantBuffer(context->transientHeap))
+ return false;
+ if (m_isMutable && m_version != m_cachedGPUDescriptorSetVersion)
+ return false;
+ if (m_cachedGPUDescriptorSet.resourceTable.getDescriptorCount() != 0 &&
+ m_cachedGPUDescriptorSet.resourceTable.m_heap.ptr.linearHeap->getHeap() !=
+ m_cachedTransientHeap->getCurrentViewHeap().getHeap())
+ return false;
+ if (m_cachedGPUDescriptorSet.samplerTable.getDescriptorCount() != 0 &&
+ m_cachedGPUDescriptorSet.samplerTable.m_heap.ptr.linearHeap->getHeap() !=
+ m_cachedTransientHeap->getCurrentSamplerHeap().getHeap())
+ return false;
+
+ auto& subObjectRanges = getLayout()->getSubObjectRanges();
+ for (Slang::Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRanges.getCount();
+ subObjectRangeIndex++)
+ {
+ auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
+ auto const& bindingRange = getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
+ if (bindingRange.bindingType != slang::BindingType::ParameterBlock)
+ continue;
+ Slang::Index count = bindingRange.count;
+
+ for (Slang::Index subObjectIndexInRange = 0; subObjectIndexInRange < count;
+ subObjectIndexInRange++)
+ {
+ Slang::Index objectIndex = bindingRange.subObjectIndex + subObjectIndexInRange;
+ auto subObject = m_objects[objectIndex].Ptr();
+ if (!subObject)
+ continue;
+ if (subObject->checkIfCachedDescriptorSetIsValidRecursive(context))
+ return false;
+ }
+ }
+ return true;
+}
+
+/// Bind this object as a `ParameterBlock<X>`
+
+Result ShaderObjectImpl::bindAsParameterBlock(
+ BindingContext* context, BindingOffset const& offset, ShaderObjectLayoutImpl* specializedLayout)
+{
+ if (checkIfCachedDescriptorSetIsValidRecursive(context))
+ {
+ // If we already have a valid gpu descriptor table in the current
+ // heap, bind it.
+ auto rootParamIndex = offset.rootParam;
+ if (m_cachedGPUDescriptorSet.resourceTable.getDescriptorCount())
+ {
+ auto tableRootParamIndex = rootParamIndex++;
+ context->submitter->setRootDescriptorTable(
+ tableRootParamIndex, m_cachedGPUDescriptorSet.resourceTable.getGpuHandle());
+ }
+ if (m_cachedGPUDescriptorSet.samplerTable.getDescriptorCount())
+ {
+ auto tableRootParamIndex = rootParamIndex++;
+ context->submitter->setRootDescriptorTable(
+ tableRootParamIndex, m_cachedGPUDescriptorSet.samplerTable.getGpuHandle());
+ }
+ return SLANG_OK;
+ }
+
+ // The first step to binding an object as a parameter block is to allocate a descriptor
+ // set (consisting of zero or one resource descriptor table and zero or one sampler
+ // descriptor table) to represent its values.
+ //
+ BindingOffset subOffset = offset;
+ SLANG_RETURN_ON_FAIL(prepareToBindAsParameterBlock(
+ context, /* inout */ subOffset, specializedLayout, m_cachedGPUDescriptorSet));
+
+ // Next we bind the object into that descriptor set as if it were being used
+ // as a `ConstantBuffer<X>`.
+ //
+ SLANG_RETURN_ON_FAIL(
+ bindAsConstantBuffer(context, m_cachedGPUDescriptorSet, subOffset, specializedLayout));
+
+ m_cachedGPUDescriptorSetVersion = m_version;
+ return SLANG_OK;
+}
+
+/// Bind this object as a `ConstantBuffer<X>`
+
+Result ShaderObjectImpl::bindAsConstantBuffer(
+ BindingContext* context,
+ DescriptorSet const& descriptorSet,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout)
+{
+ // If we are to bind as a constant buffer we first need to ensure that
+ // the ordinary data buffer is created, if this object needs one.
+ //
+ SLANG_RETURN_ON_FAIL(
+ _ensureOrdinaryDataBufferCreatedIfNeeded(context->encoder, specializedLayout));
+
+ // Next, we need to bind all of the resource descriptors for this object
+ // (including any ordinary data buffer) into the provided `descriptorSet`.
+ //
+ auto resourceCount = specializedLayout->getResourceSlotCount();
+ if (resourceCount)
+ {
+ auto& dstTable = descriptorSet.resourceTable;
+ auto& srcTable = m_descriptorSet.resourceTable;
+
+ context->device->m_device->CopyDescriptorsSimple(
+ UINT(resourceCount),
+ dstTable.getCpuHandle(offset.resource),
+ srcTable.getCpuHandle(),
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ }
+
+ // Finally, we delegate to `_bindImpl` to bind samplers and sub-objects,
+ // since the logic is shared with the `bindAsValue()` case below.
+ //
+ SLANG_RETURN_ON_FAIL(_bindImpl(context, descriptorSet, offset, specializedLayout));
+ return SLANG_OK;
+}
+
+/// Bind this object as a value (for an interface-type parameter)
+
+Result ShaderObjectImpl::bindAsValue(
+ BindingContext* context,
+ DescriptorSet const& descriptorSet,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout)
+{
+ // When binding a value for an interface-type field we do *not* want
+ // to bind a buffer for the ordinary data (if there is any) because
+ // ordinary data for interface-type fields gets allocated into the
+ // parent object's ordinary data buffer.
+ //
+ // This CPU-memory descriptor table that holds resource descriptors
+ // will have already been allocated to have space for an ordinary data
+ // buffer (if needed), so we need to take care to skip over that
+ // descriptor when copying descriptors from the CPU-memory set
+ // to the GPU-memory `descriptorSet`.
+ //
+ auto skipResourceCount = specializedLayout->getOrdinaryDataBufferCount();
+ auto resourceCount = specializedLayout->getResourceSlotCount() - skipResourceCount;
+ if (resourceCount)
+ {
+ auto& dstTable = descriptorSet.resourceTable;
+ auto& srcTable = m_descriptorSet.resourceTable;
+
+ context->device->m_device->CopyDescriptorsSimple(
+ UINT(resourceCount),
+ dstTable.getCpuHandle(offset.resource),
+ srcTable.getCpuHandle(skipResourceCount),
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ }
+
+ // Finally, we delegate to `_bindImpl` to bind samplers and sub-objects,
+ // since the logic is shared with the `bindAsConstantBuffer()` case above.
+ //
+ // Note: Just like we had to do some subtle handling of the ordinary data buffer
+ // above, here we need to contend with the fact that the `offset.resource` fields
+ // computed for sub-object ranges were baked to take the ordinary data buffer
+ // into account, so that if `skipResourceCount` is non-zero then they are all
+ // too high by `skipResourceCount`.
+ //
+ // We will address the problem here by computing a modified offset that adjusts
+ // for the ordinary data buffer that we have not bound after all.
+ //
+ BindingOffset subOffset = offset;
+ subOffset.resource -= skipResourceCount;
+ SLANG_RETURN_ON_FAIL(_bindImpl(context, descriptorSet, subOffset, specializedLayout));
+ return SLANG_OK;
+}
+
+/// Shared logic for `bindAsConstantBuffer()` and `bindAsValue()`
+
+Result ShaderObjectImpl::_bindImpl(
+ BindingContext* context,
+ DescriptorSet const& descriptorSet,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout)
+{
+ // We start by binding all the sampler decriptors, if needed.
+ //
+ // Note: resource descriptors were handled in either `bindAsConstantBuffer()`
+ // or `bindAsValue()` before calling into `_bindImpl()`.
+ //
+ if (auto samplerCount = specializedLayout->getSamplerSlotCount())
+ {
+ auto& dstTable = descriptorSet.samplerTable;
+ auto& srcTable = m_descriptorSet.samplerTable;
+
+ context->device->m_device->CopyDescriptorsSimple(
+ UINT(samplerCount),
+ dstTable.getCpuHandle(offset.sampler),
+ srcTable.getCpuHandle(),
+ D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+ }
+
+ // Next we iterate over the sub-object ranges and bind anything they require.
+ //
+ auto& subObjectRanges = specializedLayout->getSubObjectRanges();
+ auto subObjectRangeCount = subObjectRanges.getCount();
+ for (Index i = 0; i < subObjectRangeCount; i++)
+ {
+ auto& subObjectRange = specializedLayout->getSubObjectRange(i);
+ auto& bindingRange = specializedLayout->getBindingRange(subObjectRange.bindingRangeIndex);
+ auto subObjectIndex = bindingRange.subObjectIndex;
+ auto subObjectLayout = subObjectRange.layout.Ptr();
+
+ BindingOffset rangeOffset = offset;
+ rangeOffset += subObjectRange.offset;
+
+ BindingOffset rangeStride = subObjectRange.stride;
+
+ switch (bindingRange.bindingType)
+ {
+ case slang::BindingType::ConstantBuffer:
+ {
+ auto objOffset = rangeOffset;
+ for (uint32_t j = 0; j < bindingRange.count; j++)
+ {
+ auto& object = m_objects[subObjectIndex + j];
+ SLANG_RETURN_ON_FAIL(object->bindAsConstantBuffer(
+ context, descriptorSet, objOffset, subObjectLayout));
+ objOffset += rangeStride;
+ }
+ }
+ break;
+
+ case slang::BindingType::ParameterBlock:
+ {
+ auto objOffset = rangeOffset;
+ for (uint32_t j = 0; j < bindingRange.count; j++)
+ {
+ auto& object = m_objects[subObjectIndex + j];
+ SLANG_RETURN_ON_FAIL(
+ object->bindAsParameterBlock(context, objOffset, subObjectLayout));
+ objOffset += rangeStride;
+ }
+ }
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ if (subObjectLayout)
+ {
+ auto objOffset = rangeOffset;
+ for (uint32_t j = 0; j < bindingRange.count; j++)
+ {
+ auto& object = m_objects[subObjectIndex + j];
+ SLANG_RETURN_ON_FAIL(
+ object->bindAsValue(context, descriptorSet, objOffset, subObjectLayout));
+ objOffset += rangeStride;
+ }
+ }
+ break;
+ }
+ }
+
+ return SLANG_OK;
+}
+
+Result ShaderObjectImpl::bindRootArguments(BindingContext* context, uint32_t& index)
+{
+ auto layoutImpl = getLayout();
+ for (Index i = 0; i < m_rootArguments.getCount(); i++)
+ {
+ switch (layoutImpl->getRootParameterInfo(i).type)
+ {
+ case IResourceView::Type::ShaderResource:
+ case IResourceView::Type::AccelerationStructure:
+ context->submitter->setRootSRV(index, m_rootArguments[i]);
+ break;
+ case IResourceView::Type::UnorderedAccess:
+ context->submitter->setRootUAV(index, m_rootArguments[i]);
+ break;
+ default:
+ continue;
+ }
+ index++;
+ }
+ for (auto& subObject : m_objects)
+ {
+ if (subObject)
+ {
+ SLANG_RETURN_ON_FAIL(subObject->bindRootArguments(context, index));
+ }
+ }
+ return SLANG_OK;
+}
+
+/// Get the layout of this shader object with specialization arguments considered
+///
+/// This operation should only be called after the shader object has been
+/// fully filled in and finalized.
+///
+
+Result ShaderObjectImpl::getSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
+{
+ if (!m_specializedLayout)
+ {
+ SLANG_RETURN_ON_FAIL(_createSpecializedLayout(m_specializedLayout.writeRef()));
+ }
+ returnRefPtr(outLayout, m_specializedLayout);
+ return SLANG_OK;
+}
+
+/// Create the layout for this shader object with specialization arguments considered
+///
+/// This operation is virtual so that it can be customized by `RootShaderObject`.
+///
+
+Result ShaderObjectImpl::_createSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
+{
+ ExtendedShaderObjectType extendedType;
+ SLANG_RETURN_ON_FAIL(getSpecializedShaderObjectType(&extendedType));
+
+ auto renderer = getRenderer();
+ RefPtr<ShaderObjectLayoutImpl> layout;
+ SLANG_RETURN_ON_FAIL(renderer->getShaderObjectLayout(
+ extendedType.slangType,
+ m_layout->getContainerType(),
+ (ShaderObjectLayoutBase**)layout.writeRef()));
+
+ returnRefPtrMove(outLayout, layout);
+ return SLANG_OK;
+}
+
+Result ShaderObjectImpl::setResource(ShaderOffset const& offset, IResourceView* resourceView)
+{
+ if (offset.bindingRangeIndex < 0)
+ return SLANG_E_INVALID_ARG;
+ auto layout = getLayout();
+ if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
+ return SLANG_E_INVALID_ARG;
+
+ m_version++;
+
+ ID3D12Device* d3dDevice = static_cast<DeviceImpl*>(getDevice())->m_device;
+
+ auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
+
+ if (bindingRange.isRootParameter && resourceView)
+ {
+ auto& rootArg = m_rootArguments[bindingRange.baseIndex];
+ switch (resourceView->getViewDesc()->type)
+ {
+ case IResourceView::Type::AccelerationStructure:
+ {
+ auto resourceViewImpl = static_cast<AccelerationStructureImpl*>(resourceView);
+ rootArg = resourceViewImpl->getDeviceAddress();
+ }
+ break;
+ case IResourceView::Type::ShaderResource:
+ case IResourceView::Type::UnorderedAccess:
+ {
+ auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView);
+ if (resourceViewImpl->m_resource->isBuffer())
+ {
+ rootArg = static_cast<BufferResourceImpl*>(resourceViewImpl->m_resource.Ptr())
+ ->getDeviceAddress();
+ }
+ else
+ {
+ getDebugCallback()->handleMessage(
+ DebugMessageType::Error,
+ DebugMessageSource::Layer,
+ "The shader parameter at the specified offset is a root parameter, and "
+ "therefore can only be a buffer view.");
+ return SLANG_FAIL;
+ }
+ }
+ break;
+ }
+ return SLANG_OK;
+ }
+
+ if (resourceView == nullptr)
+ {
+ // Create null descriptor for the binding.
+ auto destDescriptor = m_descriptorSet.resourceTable.getCpuHandle(
+ bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex);
+ return createNullDescriptor(d3dDevice, destDescriptor, bindingRange);
+ }
+
+ ResourceViewInternalImpl* internalResourceView = nullptr;
+ switch (resourceView->getViewDesc()->type)
+ {
+#if SLANG_GFX_HAS_DXR_SUPPORT
+ case IResourceView::Type::AccelerationStructure:
+ {
+ auto asImpl = static_cast<AccelerationStructureImpl*>(resourceView);
+ // Hold a reference to the resource to prevent its destruction.
+ m_boundResources[bindingRange.baseIndex + offset.bindingArrayIndex] = asImpl->m_buffer;
+ internalResourceView = asImpl;
+ }
+ break;
+#endif
+ default:
+ {
+ auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView);
+ // Hold a reference to the resource to prevent its destruction.
+ m_boundResources[bindingRange.baseIndex + offset.bindingArrayIndex] =
+ resourceViewImpl->m_resource;
+ internalResourceView = resourceViewImpl;
+ }
+ break;
+ }
+
+ auto descriptorSlotIndex = bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex;
+ if (internalResourceView->m_descriptor.cpuHandle.ptr)
+ {
+ d3dDevice->CopyDescriptorsSimple(
+ 1,
+ m_descriptorSet.resourceTable.getCpuHandle(
+ bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex),
+ internalResourceView->m_descriptor.cpuHandle,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ }
+ else
+ {
+ getDebugCallback()->handleMessage(
+ DebugMessageType::Error,
+ DebugMessageSource::Layer,
+ "IShaderObject::setResource: the resource view cannot be set to this shader parameter. "
+ "A possible reason is that the view is too large to be supported by D3D12.");
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+}
+
+Result ShaderObjectImpl::create(
+ DeviceImpl* device, ShaderObjectLayoutImpl* layout, ShaderObjectImpl** outShaderObject)
+{
+ auto object = RefPtr<ShaderObjectImpl>(new ShaderObjectImpl());
+ SLANG_RETURN_ON_FAIL(
+ object->init(device, layout, device->m_cpuViewHeap.Ptr(), device->m_cpuSamplerHeap.Ptr()));
+ returnRefPtrMove(outShaderObject, object);
+ return SLANG_OK;
+}
+
+ShaderObjectImpl::~ShaderObjectImpl() { m_descriptorSet.freeIfSupported(); }
+
+RootShaderObjectLayoutImpl* RootShaderObjectImpl::getLayout()
+{
+ return static_cast<RootShaderObjectLayoutImpl*>(m_layout.Ptr());
+}
+
+GfxCount RootShaderObjectImpl::getEntryPointCount() { return (GfxCount)m_entryPoints.getCount(); }
+
+SlangResult RootShaderObjectImpl::getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint)
+{
+ returnComPtr(outEntryPoint, m_entryPoints[index]);
+ return SLANG_OK;
+}
+
+Result RootShaderObjectImpl::collectSpecializationArgs(ExtendedShaderObjectTypeList& args)
+{
+ SLANG_RETURN_ON_FAIL(ShaderObjectImpl::collectSpecializationArgs(args));
+ for (auto& entryPoint : m_entryPoints)
+ {
+ SLANG_RETURN_ON_FAIL(entryPoint->collectSpecializationArgs(args));
+ }
+ return SLANG_OK;
+}
+
+Result RootShaderObjectImpl::_createSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
+{
+ ExtendedShaderObjectTypeList specializationArgs;
+ SLANG_RETURN_ON_FAIL(collectSpecializationArgs(specializationArgs));
+
+ // Note: There is an important policy decision being made here that we need
+ // to approach carefully.
+ //
+ // We are doing two different things that affect the layout of a program:
+ //
+ // 1. We are *composing* one or more pieces of code (notably the shared global/module
+ // stuff and the per-entry-point stuff).
+ //
+ // 2. We are *specializing* code that includes generic/existential parameters
+ // to concrete types/values.
+ //
+ // We need to decide the relative *order* of these two steps, because of how it impacts
+ // layout. The layout for `specialize(compose(A,B), X, Y)` is potentially different
+ // form that of `compose(specialize(A,X), speciealize(B,Y))`, even when both are
+ // semantically equivalent programs.
+ //
+ // Right now we are using the first option: we are first generating a full composition
+ // of all the code we plan to use (global scope plus all entry points), and then
+ // specializing it to the concatenated specialization argumenst for all of that.
+ //
+ // In some cases, though, this model isn't appropriate. For example, when dealing with
+ // ray-tracing shaders and local root signatures, we really want the parameters of each
+ // entry point (actually, each entry-point *group*) to be allocated distinct storage,
+ // which really means we want to compute something like:
+ //
+ // SpecializedGlobals = specialize(compose(ModuleA, ModuleB, ...), X, Y, ...)
+ //
+ // SpecializedEP1 = compose(SpecializedGlobals, specialize(EntryPoint1, T, U, ...))
+ // SpecializedEP2 = compose(SpecializedGlobals, specialize(EntryPoint2, A, B, ...))
+ //
+ // Note how in this case all entry points agree on the layout for the shared/common
+ // parmaeters, but their layouts are also independent of one another.
+ //
+ // Furthermore, in this example, loading another entry point into the system would not
+ // rquire re-computing the layouts (or generated kernel code) for any of the entry
+ // points that had already been loaded (in contrast to a compose-then-specialize
+ // approach).
+ //
+ ComPtr<slang::IComponentType> specializedComponentType;
+ ComPtr<slang::IBlob> diagnosticBlob;
+ auto result = getLayout()->getSlangProgram()->specialize(
+ specializationArgs.components.getArrayView().getBuffer(),
+ specializationArgs.getCount(),
+ specializedComponentType.writeRef(),
+ diagnosticBlob.writeRef());
+
+ if (diagnosticBlob && diagnosticBlob->getBufferSize())
+ {
+ getDebugCallback()->handleMessage(
+ SLANG_FAILED(result) ? DebugMessageType::Error : DebugMessageType::Info,
+ DebugMessageSource::Layer,
+ (const char*)diagnosticBlob->getBufferPointer());
+ }
+
+ if (SLANG_FAILED(result))
+ return result;
+
+ ComPtr<ID3DBlob> d3dDiagnosticBlob;
+ auto slangSpecializedLayout = specializedComponentType->getLayout();
+ RefPtr<RootShaderObjectLayoutImpl> specializedLayout;
+ auto rootLayoutResult = RootShaderObjectLayoutImpl::create(
+ static_cast<DeviceImpl*>(getRenderer()),
+ specializedComponentType,
+ slangSpecializedLayout,
+ specializedLayout.writeRef(),
+ d3dDiagnosticBlob.writeRef());
+
+ if (SLANG_FAILED(rootLayoutResult))
+ {
+ return rootLayoutResult;
+ }
+
+ // Note: Computing the layout for the specialized program will have also computed
+ // the layouts for the entry points, and we really need to attach that information
+ // to them so that they don't go and try to compute their own specializations.
+ //
+ // TODO: Well, if we move to the specialization model described above then maybe
+ // we *will* want entry points to do their own specialization work...
+ //
+ auto entryPointCount = m_entryPoints.getCount();
+ for (Index i = 0; i < entryPointCount; ++i)
+ {
+ auto entryPointInfo = specializedLayout->getEntryPoint(i);
+ auto entryPointVars = m_entryPoints[i];
+
+ entryPointVars->m_specializedLayout = entryPointInfo.layout;
+ }
+
+ returnRefPtrMove(outLayout, specializedLayout);
+ return SLANG_OK;
+}
+
+Result RootShaderObjectImpl::copyFrom(IShaderObject* object, ITransientResourceHeap* transientHeap)
+{
+ if (auto srcObj = dynamic_cast<MutableRootShaderObjectImpl*>(object))
+ {
+ *this = *srcObj;
+ return SLANG_OK;
+ }
+ return SLANG_FAIL;
+}
+
+Result RootShaderObjectImpl::bindAsRoot(
+ BindingContext* context, RootShaderObjectLayoutImpl* specializedLayout)
+{
+ // Pull updates from sub-objects when this is a mutable root shader object.
+ updateSubObjectsRecursive();
+
+ // A root shader object always binds as if it were a parameter block,
+ // insofar as it needs to allocate a descriptor set to hold the bindings
+ // for its own state and any sub-objects.
+ //
+ // Note: We do not direclty use `bindAsParameterBlock` here because we also
+ // need to bind the entry points into the same descriptor set that is
+ // being used for the root object.
+
+ BindingOffset rootOffset;
+
+ // Bind all root parameters first.
+ Super::bindRootArguments(context, rootOffset.rootParam);
+
+ DescriptorSet descriptorSet;
+ SLANG_RETURN_ON_FAIL(prepareToBindAsParameterBlock(
+ context, /* inout */ rootOffset, specializedLayout, descriptorSet));
+
+ SLANG_RETURN_ON_FAIL(
+ Super::bindAsConstantBuffer(context, descriptorSet, rootOffset, specializedLayout));
+
+ auto entryPointCount = m_entryPoints.getCount();
+ for (Index i = 0; i < entryPointCount; ++i)
+ {
+ auto entryPoint = m_entryPoints[i];
+ auto& entryPointInfo = specializedLayout->getEntryPoint(i);
+
+ auto entryPointOffset = rootOffset;
+ entryPointOffset += entryPointInfo.offset;
+
+ entryPoint->updateSubObjectsRecursive();
+
+ SLANG_RETURN_ON_FAIL(entryPoint->bindAsConstantBuffer(
+ context, descriptorSet, entryPointOffset, entryPointInfo.layout));
+ }
+
+ return SLANG_OK;
+}
+
+Result RootShaderObjectImpl::resetImpl(
+ DeviceImpl* device,
+ RootShaderObjectLayoutImpl* layout,
+ DescriptorHeapReference viewHeap,
+ DescriptorHeapReference samplerHeap,
+ bool isMutable)
+{
+ SLANG_RETURN_ON_FAIL(Super::init(device, layout, viewHeap, samplerHeap));
+ m_isMutable = isMutable;
+ m_specializedLayout = nullptr;
+ m_entryPoints.clear();
+ for (auto entryPointInfo : layout->getEntryPoints())
+ {
+ RefPtr<ShaderObjectImpl> entryPoint;
+ SLANG_RETURN_ON_FAIL(
+ ShaderObjectImpl::create(device, entryPointInfo.layout, entryPoint.writeRef()));
+ entryPoint->m_isMutable = isMutable;
+ m_entryPoints.add(entryPoint);
+ }
+ return SLANG_OK;
+}
+
+Result RootShaderObjectImpl::reset(
+ DeviceImpl* device, RootShaderObjectLayoutImpl* layout, TransientResourceHeapImpl* heap)
+{
+ return resetImpl(
+ device, layout, &heap->m_stagingCpuViewHeap, &heap->m_stagingCpuSamplerHeap, false);
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-shader-object.h b/tools/gfx/d3d12/d3d12-shader-object.h
new file mode 100644
index 000000000..d6560628c
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-shader-object.h
@@ -0,0 +1,294 @@
+// d3d12-shader-object.h
+#pragma once
+
+#include "d3d12-base.h"
+#include "d3d12-helper-functions.h"
+#include "d3d12-submitter.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+struct DescriptorTable
+{
+ DescriptorHeapReference m_heap;
+ uint32_t m_offset = 0;
+ uint32_t m_count = 0;
+
+ SLANG_FORCE_INLINE uint32_t getDescriptorCount() const { return m_count; }
+
+ /// Get the GPU handle at the specified index
+ SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(uint32_t index = 0) const
+ {
+ SLANG_ASSERT(index < getDescriptorCount());
+ return m_heap.getGpuHandle(m_offset + index);
+ }
+
+ /// Get the CPU handle at the specified index
+ SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(uint32_t index = 0) const
+ {
+ SLANG_ASSERT(index < getDescriptorCount());
+ return m_heap.getCpuHandle(m_offset + index);
+ }
+
+ void freeIfSupported()
+ {
+ if (m_count)
+ {
+ m_heap.freeIfSupported(m_offset, m_count);
+ m_offset = 0;
+ m_count = 0;
+ }
+ }
+
+ bool allocate(uint32_t count)
+ {
+ auto allocatedOffset = m_heap.allocate(count);
+ if (allocatedOffset == -1)
+ return false;
+ m_offset = allocatedOffset;
+ m_count = count;
+ return true;
+ }
+
+ bool allocate(DescriptorHeapReference heap, uint32_t count)
+ {
+ auto allocatedOffset = heap.allocate(count);
+ if (allocatedOffset == -1)
+ return false;
+ m_heap = heap;
+ m_offset = allocatedOffset;
+ m_count = count;
+ return true;
+ }
+};
+
+/// A reprsentation of an allocated descriptor set, consisting of an option resource table and
+/// an optional sampler table
+struct DescriptorSet
+{
+ DescriptorTable resourceTable;
+ DescriptorTable samplerTable;
+
+ void freeIfSupported()
+ {
+ resourceTable.freeIfSupported();
+ samplerTable.freeIfSupported();
+ }
+};
+
+class ShaderObjectImpl
+ : public ShaderObjectBaseImpl<ShaderObjectImpl, ShaderObjectLayoutImpl, SimpleShaderObjectData>
+{
+ typedef ShaderObjectBaseImpl<ShaderObjectImpl, ShaderObjectLayoutImpl, SimpleShaderObjectData>
+ Super;
+
+public:
+ static Result create(
+ DeviceImpl* device, ShaderObjectLayoutImpl* layout, ShaderObjectImpl** outShaderObject);
+
+ ~ShaderObjectImpl();
+
+ RendererBase* getDevice() { return m_device.get(); }
+
+ virtual SLANG_NO_THROW GfxCount SLANG_MCALL getEntryPointCount() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint) override;
+
+ virtual SLANG_NO_THROW const void* SLANG_MCALL getRawData() override;
+
+ virtual SLANG_NO_THROW Size SLANG_MCALL getSize() override;
+
+ // TODO: What to do with size_t?
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ setData(ShaderOffset const& inOffset, void const* data, size_t inSize) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ setObject(ShaderOffset const& offset, IShaderObject* object) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ setResource(ShaderOffset const& offset, IResourceView* resourceView) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ setSampler(ShaderOffset const& offset, ISamplerState* sampler) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setCombinedTextureSampler(
+ ShaderOffset const& offset, IResourceView* textureView, ISamplerState* sampler) override;
+
+protected:
+ Result init(
+ DeviceImpl* device,
+ ShaderObjectLayoutImpl* layout,
+ DescriptorHeapReference viewHeap,
+ DescriptorHeapReference samplerHeap);
+
+ /// Write the uniform/ordinary data of this object into the given `dest` buffer at the given
+ /// `offset`
+ Result _writeOrdinaryData(
+ PipelineCommandEncoder* encoder,
+ BufferResourceImpl* buffer,
+ Offset offset,
+ Size destSize,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ bool shouldAllocateConstantBuffer(TransientResourceHeapImpl* transientHeap);
+
+ /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
+ Result _ensureOrdinaryDataBufferCreatedIfNeeded(
+ PipelineCommandEncoder* encoder, ShaderObjectLayoutImpl* specializedLayout);
+
+public:
+ void updateSubObjectsRecursive();
+ /// Prepare to bind this object as a parameter block.
+ ///
+ /// This involves allocating and binding any descriptor tables necessary
+ /// to to store the state of the object. The function returns a descriptor
+ /// set formed from any table(s) allocated. In addition, the `ioOffset`
+ /// parameter will be adjusted to be correct for binding values into
+ /// the resulting descriptor set.
+ ///
+ /// Returns:
+ /// SLANG_OK when successful,
+ /// SLANG_E_OUT_OF_MEMORY when descriptor heap is full.
+ ///
+ Result prepareToBindAsParameterBlock(
+ BindingContext* context,
+ BindingOffset& ioOffset,
+ ShaderObjectLayoutImpl* specializedLayout,
+ DescriptorSet& outDescriptorSet);
+
+ bool checkIfCachedDescriptorSetIsValidRecursive(BindingContext* context);
+
+ /// Bind this object as a `ParameterBlock<X>`
+ Result bindAsParameterBlock(
+ BindingContext* context,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ /// Bind this object as a `ConstantBuffer<X>`
+ Result bindAsConstantBuffer(
+ BindingContext* context,
+ DescriptorSet const& descriptorSet,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ /// Bind this object as a value (for an interface-type parameter)
+ Result bindAsValue(
+ BindingContext* context,
+ DescriptorSet const& descriptorSet,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ /// Shared logic for `bindAsConstantBuffer()` and `bindAsValue()`
+ Result _bindImpl(
+ BindingContext* context,
+ DescriptorSet const& descriptorSet,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout);
+
+ Result bindRootArguments(BindingContext* context, uint32_t& index);
+ /// A CPU-memory descriptor set holding any descriptors used to represent the
+ /// resources/samplers in this object's state
+ DescriptorSet m_descriptorSet;
+ /// A cached descriptor set on GPU heap.
+ DescriptorSet m_cachedGPUDescriptorSet;
+
+ ShortList<RefPtr<Resource>, 8> m_boundResources;
+ List<D3D12_GPU_VIRTUAL_ADDRESS> m_rootArguments;
+ /// A constant buffer used to stored ordinary data for this object
+ /// and existential-type sub-objects.
+ ///
+ /// Allocated from transient heap on demand with `_createOrdinaryDataBufferIfNeeded()`
+ IBufferResource* m_constantBufferWeakPtr = nullptr;
+ Offset m_constantBufferOffset = 0;
+ Size m_constantBufferSize = 0;
+
+ /// Dirty bit tracking whether the constant buffer needs to be updated.
+ bool m_isConstantBufferDirty = true;
+ /// The transient heap from which the constant buffer and descriptor set is allocated.
+ TransientResourceHeapImpl* m_cachedTransientHeap;
+ /// The version of the transient heap when the constant buffer and descriptor set is
+ /// allocated.
+ uint64_t m_cachedTransientHeapVersion;
+
+ /// Whether this shader object is allowed to be mutable.
+ bool m_isMutable = false;
+ /// The version of a mutable shader object.
+ uint32_t m_version = 0;
+ /// The version of this mutable shader object when the gpu descriptor table is cached.
+ uint32_t m_cachedGPUDescriptorSetVersion = -1;
+ /// The versions of bound subobjects.
+ List<uint32_t> m_subObjectVersions;
+
+ /// Get the layout of this shader object with specialization arguments considered
+ ///
+ /// This operation should only be called after the shader object has been
+ /// fully filled in and finalized.
+ ///
+ Result getSpecializedLayout(ShaderObjectLayoutImpl** outLayout);
+
+ /// Create the layout for this shader object with specialization arguments considered
+ ///
+ /// This operation is virtual so that it can be customized by `RootShaderObject`.
+ ///
+ virtual Result _createSpecializedLayout(ShaderObjectLayoutImpl** outLayout);
+
+ RefPtr<ShaderObjectLayoutImpl> m_specializedLayout;
+};
+
+class RootShaderObjectImpl : public ShaderObjectImpl
+{
+ typedef ShaderObjectImpl Super;
+
+public:
+ // Override default reference counting behavior to disable lifetime management via ComPtr.
+ // Root objects are managed by command buffer and does not need to be freed by the user.
+ SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; }
+ SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; }
+
+public:
+ RootShaderObjectLayoutImpl* getLayout();
+
+ virtual SLANG_NO_THROW GfxCount SLANG_MCALL getEntryPointCount() override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint) override;
+ virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ copyFrom(IShaderObject* object, ITransientResourceHeap* transientHeap) override;
+
+public:
+ Result bindAsRoot(BindingContext* context, RootShaderObjectLayoutImpl* specializedLayout);
+
+public:
+ Result init(DeviceImpl* device) { return SLANG_OK; }
+
+ Result resetImpl(
+ DeviceImpl* device,
+ RootShaderObjectLayoutImpl* layout,
+ DescriptorHeapReference viewHeap,
+ DescriptorHeapReference samplerHeap,
+ bool isMutable);
+
+ Result reset(
+ DeviceImpl* device, RootShaderObjectLayoutImpl* layout, TransientResourceHeapImpl* heap);
+
+protected:
+ virtual Result _createSpecializedLayout(ShaderObjectLayoutImpl** outLayout) override;
+
+ List<RefPtr<ShaderObjectImpl>> m_entryPoints;
+};
+
+class MutableRootShaderObjectImpl : public RootShaderObjectImpl
+{
+public:
+ // Override default reference counting behavior to disable lifetime management via ComPtr.
+ // Root objects are managed by command buffer and does not need to be freed by the user.
+ SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return ShaderObjectBase::addRef(); }
+ SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return ShaderObjectBase::release(); }
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-shader-program.cpp b/tools/gfx/d3d12/d3d12-shader-program.cpp
new file mode 100644
index 000000000..f2476232c
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-shader-program.cpp
@@ -0,0 +1,25 @@
+// d3d12-shader-program.cpp
+#include "d3d12-shader-program.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+Result ShaderProgramImpl::createShaderModule(
+ slang::EntryPointReflection* entryPointInfo, ComPtr<ISlangBlob> kernelCode)
+{
+ ShaderBinary shaderBin;
+ shaderBin.stage = entryPointInfo->getStage();
+ shaderBin.entryPointInfo = entryPointInfo;
+ shaderBin.code.addRange(
+ reinterpret_cast<const uint8_t*>(kernelCode->getBufferPointer()),
+ (Index)kernelCode->getBufferSize());
+ m_shaders.add(_Move(shaderBin));
+ return SLANG_OK;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-shader-program.h b/tools/gfx/d3d12/d3d12-shader-program.h
new file mode 100644
index 000000000..eafa898fe
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-shader-program.h
@@ -0,0 +1,33 @@
+// d3d12-shader-program.h
+#pragma once
+
+#include "d3d12-base.h"
+#include "d3d12-shader-object-layout.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+struct ShaderBinary
+{
+ SlangStage stage;
+ slang::EntryPointReflection* entryPointInfo;
+ String actualEntryPointNameInAPI;
+ List<uint8_t> code;
+};
+
+class ShaderProgramImpl : public ShaderProgramBase
+{
+public:
+ RefPtr<RootShaderObjectLayoutImpl> m_rootObjectLayout;
+ List<ShaderBinary> m_shaders;
+
+ virtual Result createShaderModule(
+ slang::EntryPointReflection* entryPointInfo, ComPtr<ISlangBlob> kernelCode) override;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-shader-table.cpp b/tools/gfx/d3d12/d3d12-shader-table.cpp
new file mode 100644
index 000000000..ef4feecc7
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-shader-table.cpp
@@ -0,0 +1,106 @@
+// d3d12-shader-table.cpp
+#include "d3d12-shader-table.h"
+
+#include "d3d12-device.h"
+#include "d3d12-pipeline-state.h"
+#include "d3d12-transient-heap.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+RefPtr<BufferResource> ShaderTableImpl::createDeviceBuffer(
+ PipelineStateBase* pipeline,
+ TransientResourceHeapBase* transientHeap,
+ IResourceCommandEncoder* encoder)
+{
+ uint32_t raygenTableSize = m_rayGenShaderCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+ uint32_t missTableSize = m_missShaderCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+ uint32_t hitgroupTableSize = m_hitGroupCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
+ m_rayGenTableOffset = 0;
+ m_missTableOffset = (uint32_t)D3DUtil::calcAligned(
+ raygenTableSize, D3D12_RAYTRACING_SHADER_TABLE_BYTE_ALIGNMENT);
+ m_hitGroupTableOffset = (uint32_t)D3DUtil::calcAligned(
+ m_missTableOffset + missTableSize, D3D12_RAYTRACING_SHADER_TABLE_BYTE_ALIGNMENT);
+ uint32_t tableSize = m_hitGroupTableOffset + hitgroupTableSize;
+
+ auto pipelineImpl = static_cast<RayTracingPipelineStateImpl*>(pipeline);
+ ComPtr<IBufferResource> bufferResource;
+ IBufferResource::Desc bufferDesc = {};
+ bufferDesc.memoryType = gfx::MemoryType::DeviceLocal;
+ bufferDesc.defaultState = ResourceState::General;
+ bufferDesc.type = IResource::Type::Buffer;
+ bufferDesc.sizeInBytes = tableSize;
+ m_device->createBufferResource(bufferDesc, nullptr, bufferResource.writeRef());
+
+ ComPtr<ID3D12StateObjectProperties> stateObjectProperties;
+ pipelineImpl->m_stateObject->QueryInterface(stateObjectProperties.writeRef());
+
+ TransientResourceHeapImpl* transientHeapImpl =
+ static_cast<TransientResourceHeapImpl*>(transientHeap);
+
+ IBufferResource* stagingBuffer = nullptr;
+ Offset stagingBufferOffset = 0;
+ transientHeapImpl->allocateStagingBuffer(
+ tableSize, stagingBuffer, stagingBufferOffset, MemoryType::Upload);
+
+ assert(stagingBuffer);
+ void* stagingPtr = nullptr;
+ stagingBuffer->map(nullptr, &stagingPtr);
+
+ auto copyShaderIdInto = [&](void* dest, String& name, const ShaderRecordOverwrite& overwrite)
+ {
+ if (name.getLength())
+ {
+ void* shaderId = stateObjectProperties->GetShaderIdentifier(name.toWString().begin());
+ memcpy(dest, shaderId, D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES);
+ }
+ else
+ {
+ memset(dest, 0, D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES);
+ }
+ if (overwrite.size)
+ {
+ memcpy((uint8_t*)dest + overwrite.offset, overwrite.data, overwrite.size);
+ }
+ };
+
+ uint8_t* stagingBufferPtr = (uint8_t*)stagingPtr + stagingBufferOffset;
+ for (uint32_t i = 0; i < m_rayGenShaderCount; i++)
+ {
+ copyShaderIdInto(
+ stagingBufferPtr + m_rayGenTableOffset + D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES * i,
+ m_shaderGroupNames[i],
+ m_recordOverwrites[i]);
+ }
+ for (uint32_t i = 0; i < m_missShaderCount; i++)
+ {
+ copyShaderIdInto(
+ stagingBufferPtr + m_missTableOffset + D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES * i,
+ m_shaderGroupNames[m_rayGenShaderCount + i],
+ m_recordOverwrites[m_rayGenShaderCount + i]);
+ }
+ for (uint32_t i = 0; i < m_hitGroupCount; i++)
+ {
+ copyShaderIdInto(
+ stagingBufferPtr + m_hitGroupTableOffset + D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES * i,
+ m_shaderGroupNames[m_rayGenShaderCount + m_missShaderCount + i],
+ m_recordOverwrites[m_rayGenShaderCount + m_missShaderCount + i]);
+ }
+
+ stagingBuffer->unmap(nullptr);
+ encoder->copyBuffer(bufferResource, 0, stagingBuffer, stagingBufferOffset, tableSize);
+ encoder->bufferBarrier(
+ 1,
+ bufferResource.readRef(),
+ gfx::ResourceState::CopyDestination,
+ gfx::ResourceState::ShaderResource);
+ RefPtr<BufferResource> resultPtr = static_cast<BufferResource*>(bufferResource.get());
+ return _Move(resultPtr);
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-shader-table.h b/tools/gfx/d3d12/d3d12-shader-table.h
new file mode 100644
index 000000000..8cfd74874
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-shader-table.h
@@ -0,0 +1,29 @@
+// d3d12-shader-table.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class ShaderTableImpl : public ShaderTableBase
+{
+public:
+ uint32_t m_rayGenTableOffset;
+ uint32_t m_missTableOffset;
+ uint32_t m_hitGroupTableOffset;
+
+ DeviceImpl* m_device;
+
+ virtual RefPtr<BufferResource> createDeviceBuffer(
+ PipelineStateBase* pipeline,
+ TransientResourceHeapBase* transientHeap,
+ IResourceCommandEncoder* encoder) override;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-submitter.cpp b/tools/gfx/d3d12/d3d12-submitter.cpp
new file mode 100644
index 000000000..0abd21d70
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-submitter.cpp
@@ -0,0 +1,100 @@
+// d3d12-submitter.cpp
+#include "d3d12-submitter.h"
+
+#include "d3d12-pipeline-state.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+void GraphicsSubmitter::setRootConstantBufferView(
+ int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation)
+{
+ m_commandList->SetGraphicsRootConstantBufferView(index, gpuBufferLocation);
+}
+
+void GraphicsSubmitter::setRootUAV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation)
+{
+ m_commandList->SetGraphicsRootUnorderedAccessView(index, gpuBufferLocation);
+}
+
+void GraphicsSubmitter::setRootSRV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation)
+{
+ m_commandList->SetGraphicsRootShaderResourceView(index, gpuBufferLocation);
+}
+
+void GraphicsSubmitter::setRootDescriptorTable(
+ int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor)
+{
+ m_commandList->SetGraphicsRootDescriptorTable(index, baseDescriptor);
+}
+
+void GraphicsSubmitter::setRootSignature(ID3D12RootSignature* rootSignature)
+{
+ m_commandList->SetGraphicsRootSignature(rootSignature);
+}
+
+void GraphicsSubmitter::setRootConstants(
+ Index rootParamIndex,
+ Index dstOffsetIn32BitValues,
+ Index countOf32BitValues,
+ void const* srcData)
+{
+ m_commandList->SetGraphicsRoot32BitConstants(
+ UINT(rootParamIndex), UINT(countOf32BitValues), srcData, UINT(dstOffsetIn32BitValues));
+}
+
+void GraphicsSubmitter::setPipelineState(PipelineStateBase* pipeline)
+{
+ auto pipelineImpl = static_cast<PipelineStateImpl*>(pipeline);
+ m_commandList->SetPipelineState(pipelineImpl->m_pipelineState.get());
+}
+
+void ComputeSubmitter::setRootConstantBufferView(
+ int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation)
+{
+ m_commandList->SetComputeRootConstantBufferView(index, gpuBufferLocation);
+}
+
+void ComputeSubmitter::setRootUAV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation)
+{
+ m_commandList->SetComputeRootUnorderedAccessView(index, gpuBufferLocation);
+}
+
+void ComputeSubmitter::setRootSRV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation)
+{
+ m_commandList->SetComputeRootShaderResourceView(index, gpuBufferLocation);
+}
+
+void ComputeSubmitter::setRootDescriptorTable(
+ int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor)
+{
+ m_commandList->SetComputeRootDescriptorTable(index, baseDescriptor);
+}
+
+void ComputeSubmitter::setRootSignature(ID3D12RootSignature* rootSignature)
+{
+ m_commandList->SetComputeRootSignature(rootSignature);
+}
+
+void ComputeSubmitter::setRootConstants(
+ Index rootParamIndex,
+ Index dstOffsetIn32BitValues,
+ Index countOf32BitValues,
+ void const* srcData)
+{
+ m_commandList->SetComputeRoot32BitConstants(
+ UINT(rootParamIndex), UINT(countOf32BitValues), srcData, UINT(dstOffsetIn32BitValues));
+}
+
+void ComputeSubmitter::setPipelineState(PipelineStateBase* pipeline)
+{
+ auto pipelineImpl = static_cast<PipelineStateImpl*>(pipeline);
+ m_commandList->SetPipelineState(pipelineImpl->m_pipelineState.get());
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-submitter.h b/tools/gfx/d3d12/d3d12-submitter.h
new file mode 100644
index 000000000..0b9f885a2
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-submitter.h
@@ -0,0 +1,75 @@
+// d3d12-submitter.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+struct Submitter
+{
+ virtual void setRootConstantBufferView(
+ int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0;
+ virtual void setRootUAV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0;
+ virtual void setRootSRV(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;
+ virtual void setRootConstants(
+ Index rootParamIndex,
+ Index dstOffsetIn32BitValues,
+ Index countOf32BitValues,
+ void const* srcData) = 0;
+ virtual void setPipelineState(PipelineStateBase* pipelineState) = 0;
+};
+
+struct GraphicsSubmitter : public Submitter
+{
+ virtual void setRootConstantBufferView(
+ int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override;
+ virtual void setRootUAV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override;
+ virtual void setRootSRV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override;
+ virtual void setRootDescriptorTable(
+ int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override;
+ void setRootSignature(ID3D12RootSignature* rootSignature);
+ void setRootConstants(
+ Index rootParamIndex,
+ Index dstOffsetIn32BitValues,
+ Index countOf32BitValues,
+ void const* srcData) override;
+ virtual void setPipelineState(PipelineStateBase* pipeline) override;
+
+ GraphicsSubmitter(ID3D12GraphicsCommandList* commandList)
+ : m_commandList(commandList)
+ {}
+
+ ID3D12GraphicsCommandList* m_commandList;
+};
+
+struct ComputeSubmitter : public Submitter
+{
+ virtual void setRootConstantBufferView(
+ int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override;
+ virtual void setRootUAV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override;
+ virtual void setRootSRV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override;
+ virtual void setRootDescriptorTable(
+ int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override;
+ void setRootSignature(ID3D12RootSignature* rootSignature);
+ void setRootConstants(
+ Index rootParamIndex,
+ Index dstOffsetIn32BitValues,
+ Index countOf32BitValues,
+ void const* srcData) override;
+ virtual void setPipelineState(PipelineStateBase* pipeline) override;
+ ComputeSubmitter(ID3D12GraphicsCommandList* commandList)
+ : m_commandList(commandList)
+ {}
+
+ ID3D12GraphicsCommandList* m_commandList;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-swap-chain.cpp b/tools/gfx/d3d12/d3d12-swap-chain.cpp
new file mode 100644
index 000000000..39a4565e0
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-swap-chain.cpp
@@ -0,0 +1,101 @@
+// d3d12-swap-chain.cpp
+#include "d3d12-swap-chain.h"
+
+#include "d3d12-command-queue.h"
+#include "d3d12-device.h"
+#include "d3d12-texture.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+Result SwapchainImpl::init(
+ DeviceImpl* renderer, const ISwapchain::Desc& swapchainDesc, WindowHandle window)
+{
+ m_queue = static_cast<CommandQueueImpl*>(swapchainDesc.queue)->m_d3dQueue;
+ m_dxgiFactory = renderer->m_deviceInfo.m_dxgiFactory;
+ SLANG_RETURN_ON_FAIL(
+ D3DSwapchainBase::init(swapchainDesc, window, DXGI_SWAP_EFFECT_FLIP_DISCARD));
+ renderer->m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef()));
+
+ SLANG_RETURN_ON_FAIL(m_swapChain->QueryInterface(m_swapChain3.writeRef()));
+ for (GfxIndex i = 0; i < swapchainDesc.imageCount; i++)
+ {
+ m_frameEvents.add(CreateEventEx(
+ nullptr,
+ false,
+ CREATE_EVENT_INITIAL_SET | CREATE_EVENT_MANUAL_RESET,
+ EVENT_ALL_ACCESS));
+ }
+ return SLANG_OK;
+}
+
+Result SwapchainImpl::resize(GfxCount width, GfxCount height)
+{
+ for (auto evt : m_frameEvents)
+ SetEvent(evt);
+ SLANG_RETURN_ON_FAIL(D3DSwapchainBase::resize(width, height));
+ return SLANG_OK;
+}
+
+void SwapchainImpl::createSwapchainBufferImages()
+{
+ m_images.clear();
+
+ for (GfxIndex i = 0; i < m_desc.imageCount; i++)
+ {
+ ComPtr<ID3D12Resource> d3dResource;
+ m_swapChain->GetBuffer(i, IID_PPV_ARGS(d3dResource.writeRef()));
+ ITextureResource::Desc imageDesc = {};
+ imageDesc.allowedStates = ResourceStateSet(
+ ResourceState::Present, ResourceState::RenderTarget, ResourceState::CopyDestination);
+ imageDesc.type = IResource::Type::Texture2D;
+ imageDesc.arraySize = 0;
+ imageDesc.format = m_desc.format;
+ imageDesc.size.width = m_desc.width;
+ imageDesc.size.height = m_desc.height;
+ imageDesc.size.depth = 1;
+ imageDesc.numMipLevels = 1;
+ imageDesc.defaultState = ResourceState::Present;
+ RefPtr<TextureResourceImpl> image = new TextureResourceImpl(imageDesc);
+ image->m_resource.setResource(d3dResource.get());
+ image->m_defaultState = D3D12_RESOURCE_STATE_PRESENT;
+ m_images.add(image);
+ }
+ for (auto evt : m_frameEvents)
+ SetEvent(evt);
+}
+
+int SwapchainImpl::acquireNextImage()
+{
+ auto result = (int)m_swapChain3->GetCurrentBackBufferIndex();
+ WaitForSingleObject(m_frameEvents[result], INFINITE);
+ ResetEvent(m_frameEvents[result]);
+ return result;
+}
+
+Result SwapchainImpl::present()
+{
+ m_fence->SetEventOnCompletion(
+ fenceValue, m_frameEvents[m_swapChain3->GetCurrentBackBufferIndex()]);
+ SLANG_RETURN_ON_FAIL(D3DSwapchainBase::present());
+ fenceValue++;
+ m_queue->Signal(m_fence, fenceValue);
+ return SLANG_OK;
+}
+
+bool SwapchainImpl::isOccluded()
+{
+ return (m_swapChain3->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED);
+}
+
+Result SwapchainImpl::setFullScreenMode(bool mode)
+{
+ return m_swapChain3->SetFullscreenState(mode, nullptr);
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-swap-chain.h b/tools/gfx/d3d12/d3d12-swap-chain.h
new file mode 100644
index 000000000..45a57efed
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-swap-chain.h
@@ -0,0 +1,38 @@
+// d3d12-swap-chain.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+static const Int kMaxNumRenderFrames = 4;
+
+class SwapchainImpl : public D3DSwapchainBase
+{
+public:
+ ComPtr<ID3D12CommandQueue> m_queue;
+ ComPtr<IDXGIFactory> m_dxgiFactory;
+ ComPtr<IDXGISwapChain3> m_swapChain3;
+ ComPtr<ID3D12Fence> m_fence;
+ ShortList<HANDLE, kMaxNumRenderFrames> m_frameEvents;
+ uint64_t fenceValue = 0;
+ Result init(DeviceImpl* renderer, const ISwapchain::Desc& swapchainDesc, WindowHandle window);
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL resize(GfxCount width, GfxCount height) override;
+
+ virtual void createSwapchainBufferImages() override;
+ virtual IDXGIFactory* getDXGIFactory() override { return m_dxgiFactory; }
+ virtual IUnknown* getOwningDevice() override { return m_queue; }
+ virtual SLANG_NO_THROW int SLANG_MCALL acquireNextImage() override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL present() override;
+ virtual SLANG_NO_THROW bool SLANG_MCALL isOccluded() override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL setFullScreenMode(bool mode) override;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-texture.cpp b/tools/gfx/d3d12/d3d12-texture.cpp
new file mode 100644
index 000000000..4f1898ef7
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-texture.cpp
@@ -0,0 +1,58 @@
+// d3d12-texture.cpp
+#include "d3d12-texture.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+TextureResourceImpl::TextureResourceImpl(const Desc& desc)
+ : Parent(desc)
+ , m_defaultState(D3DUtil::getResourceState(desc.defaultState))
+{}
+
+TextureResourceImpl::~TextureResourceImpl()
+{
+ if (sharedHandle.handleValue != 0)
+ {
+ CloseHandle((HANDLE)sharedHandle.handleValue);
+ }
+}
+
+Result TextureResourceImpl::getNativeResourceHandle(InteropHandle* outHandle)
+{
+ outHandle->handleValue = (uint64_t)m_resource.getResource();
+ outHandle->api = InteropHandleAPI::D3D12;
+ return SLANG_OK;
+}
+
+Result TextureResourceImpl::getSharedHandle(InteropHandle* outHandle)
+{
+ // Check if a shared handle already exists for this resource.
+ if (sharedHandle.handleValue != 0)
+ {
+ *outHandle = sharedHandle;
+ return SLANG_OK;
+ }
+
+ // If a shared handle doesn't exist, create one and store it.
+ ComPtr<ID3D12Device> pDevice;
+ auto pResource = m_resource.getResource();
+ pResource->GetDevice(IID_PPV_ARGS(pDevice.writeRef()));
+ SLANG_RETURN_ON_FAIL(pDevice->CreateSharedHandle(
+ pResource, NULL, GENERIC_ALL, nullptr, (HANDLE*)&outHandle->handleValue));
+ outHandle->api = InteropHandleAPI::D3D12;
+ return SLANG_OK;
+}
+
+Result TextureResourceImpl::setDebugName(const char* name)
+{
+ Parent::setDebugName(name);
+ m_resource.setDebugName(name);
+ return SLANG_OK;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-texture.h b/tools/gfx/d3d12/d3d12-texture.h
new file mode 100644
index 000000000..3f6ed398b
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-texture.h
@@ -0,0 +1,34 @@
+// d3d12-texture.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class TextureResourceImpl : public TextureResource
+{
+public:
+ typedef TextureResource Parent;
+
+ TextureResourceImpl(const Desc& desc);
+
+ ~TextureResourceImpl();
+
+ D3D12Resource m_resource;
+ D3D12_RESOURCE_STATES m_defaultState;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getNativeResourceHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL setDebugName(const char* name) override;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-transient-heap.cpp b/tools/gfx/d3d12/d3d12-transient-heap.cpp
new file mode 100644
index 000000000..e33aa473c
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-transient-heap.cpp
@@ -0,0 +1,222 @@
+// d3d12-transient-heap.cpp
+#include "d3d12-transient-heap.h"
+
+#include "d3d12-device.h"
+#include "d3d12-buffer.h"
+#include "d3d12-command-buffer.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+Result TransientResourceHeapImpl::synchronize()
+{
+ WaitForMultipleObjects(
+ (DWORD)m_waitHandles.getCount(), m_waitHandles.getArrayView().getBuffer(), TRUE, INFINITE);
+ m_waitHandles.clear();
+ return SLANG_OK;
+}
+
+Result TransientResourceHeapImpl::synchronizeAndReset()
+{
+ synchronize();
+
+ m_currentViewHeapIndex = -1;
+ m_currentSamplerHeapIndex = -1;
+ allocateNewViewDescriptorHeap(m_device);
+ allocateNewSamplerDescriptorHeap(m_device);
+ m_stagingCpuSamplerHeap.freeAll();
+ m_stagingCpuViewHeap.freeAll();
+ m_commandListAllocId = 0;
+ SLANG_RETURN_ON_FAIL(m_commandAllocator->Reset());
+ Super::reset();
+ return SLANG_OK;
+}
+
+Result TransientResourceHeapImpl::finish()
+{
+ for (auto& waitInfo : m_waitInfos)
+ {
+ if (waitInfo.waitValue == 0)
+ continue;
+ if (waitInfo.fence)
+ {
+ waitInfo.queue->Signal(waitInfo.fence, waitInfo.waitValue);
+ waitInfo.fence->SetEventOnCompletion(waitInfo.waitValue, waitInfo.fenceEvent);
+ m_waitHandles.add(waitInfo.fenceEvent);
+ }
+ }
+ return SLANG_OK;
+}
+
+TransientResourceHeapImpl::QueueWaitInfo& TransientResourceHeapImpl::getQueueWaitInfo(
+ uint32_t queueIndex)
+{
+ if (queueIndex < (uint32_t)m_waitInfos.getCount())
+ {
+ return m_waitInfos[queueIndex];
+ }
+ auto oldCount = m_waitInfos.getCount();
+ m_waitInfos.setCount(queueIndex + 1);
+ for (auto i = oldCount; i < m_waitInfos.getCount(); i++)
+ {
+ m_waitInfos[i].waitValue = 0;
+ m_waitInfos[i].fenceEvent = CreateEventEx(nullptr, false, 0, EVENT_ALL_ACCESS);
+ }
+ return m_waitInfos[queueIndex];
+}
+
+D3D12DescriptorHeap& TransientResourceHeapImpl::getCurrentViewHeap()
+{
+ return m_viewHeaps[m_currentViewHeapIndex];
+}
+
+D3D12DescriptorHeap& TransientResourceHeapImpl::getCurrentSamplerHeap()
+{
+ return m_samplerHeaps[m_currentSamplerHeapIndex];
+}
+
+Result TransientResourceHeapImpl::queryInterface(SlangUUID const& uuid, void** outObject)
+{
+ if (uuid == GfxGUID::IID_ID3D12TransientResourceHeap)
+ {
+ *outObject = static_cast<ID3D12TransientResourceHeap*>(this);
+ addRef();
+ return SLANG_OK;
+ }
+ return Super::queryInterface(uuid, outObject);
+}
+
+Result TransientResourceHeapImpl::allocateTransientDescriptorTable(
+ DescriptorType type,
+ GfxCount count,
+ Offset& outDescriptorOffset,
+ void** outD3DDescriptorHeapHandle)
+{
+ auto& heap =
+ (type == DescriptorType::ResourceView) ? getCurrentViewHeap() : getCurrentSamplerHeap();
+ int allocResult = heap.allocate((int)count);
+ if (allocResult == -1)
+ {
+ return SLANG_E_OUT_OF_MEMORY;
+ }
+ outDescriptorOffset = (Offset)allocResult;
+ *outD3DDescriptorHeapHandle = heap.getHeap();
+ return SLANG_OK;
+}
+
+TransientResourceHeapImpl::~TransientResourceHeapImpl()
+{
+ synchronize();
+ for (auto& waitInfo : m_waitInfos)
+ CloseHandle(waitInfo.fenceEvent);
+}
+
+Result TransientResourceHeapImpl::init(
+ const ITransientResourceHeap::Desc& desc,
+ DeviceImpl* device,
+ uint32_t viewHeapSize,
+ uint32_t samplerHeapSize)
+{
+ Super::init(desc, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, device);
+ m_canResize = (desc.flags & ITransientResourceHeap::Flags::AllowResizing) != 0;
+ m_viewHeapSize = viewHeapSize;
+ m_samplerHeapSize = samplerHeapSize;
+
+ m_stagingCpuViewHeap.init(
+ device->m_device,
+ 1000000,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
+ D3D12_DESCRIPTOR_HEAP_FLAG_NONE);
+ m_stagingCpuSamplerHeap.init(
+ device->m_device,
+ 1000000,
+ D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
+ D3D12_DESCRIPTOR_HEAP_FLAG_NONE);
+
+ auto d3dDevice = device->m_device;
+ SLANG_RETURN_ON_FAIL(d3dDevice->CreateCommandAllocator(
+ D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_commandAllocator.writeRef())));
+
+ allocateNewViewDescriptorHeap(device);
+ allocateNewSamplerDescriptorHeap(device);
+
+ return SLANG_OK;
+}
+
+Result TransientResourceHeapImpl::allocateNewViewDescriptorHeap(DeviceImpl* device)
+{
+ auto nextHeapIndex = m_currentViewHeapIndex + 1;
+ if (nextHeapIndex < m_viewHeaps.getCount())
+ {
+ m_viewHeaps[nextHeapIndex].deallocateAll();
+ m_currentViewHeapIndex = nextHeapIndex;
+ return SLANG_OK;
+ }
+ auto d3dDevice = device->m_device;
+ D3D12DescriptorHeap viewHeap;
+ SLANG_RETURN_ON_FAIL(viewHeap.init(
+ d3dDevice,
+ m_viewHeapSize,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
+ D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE));
+ m_currentViewHeapIndex = (int32_t)m_viewHeaps.getCount();
+ m_viewHeaps.add(_Move(viewHeap));
+ return SLANG_OK;
+}
+
+Result TransientResourceHeapImpl::allocateNewSamplerDescriptorHeap(DeviceImpl* device)
+{
+ auto nextHeapIndex = m_currentSamplerHeapIndex + 1;
+ if (nextHeapIndex < m_samplerHeaps.getCount())
+ {
+ m_samplerHeaps[nextHeapIndex].deallocateAll();
+ m_currentSamplerHeapIndex = nextHeapIndex;
+ return SLANG_OK;
+ }
+ auto d3dDevice = device->m_device;
+ D3D12DescriptorHeap samplerHeap;
+ SLANG_RETURN_ON_FAIL(samplerHeap.init(
+ d3dDevice,
+ m_samplerHeapSize,
+ D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
+ D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE));
+ m_currentSamplerHeapIndex = (int32_t)m_samplerHeaps.getCount();
+ m_samplerHeaps.add(_Move(samplerHeap));
+ return SLANG_OK;
+}
+
+Result TransientResourceHeapImpl::createCommandBuffer(ICommandBuffer** outCmdBuffer)
+{
+ if ((Index)m_commandListAllocId < m_commandBufferPool.getCount())
+ {
+ auto result =
+ static_cast<CommandBufferImpl*>(m_commandBufferPool[m_commandListAllocId].Ptr());
+ m_d3dCommandListPool[m_commandListAllocId]->Reset(m_commandAllocator, nullptr);
+ result->reinit();
+ ++m_commandListAllocId;
+ returnComPtr(outCmdBuffer, result);
+ return SLANG_OK;
+ }
+ ComPtr<ID3D12GraphicsCommandList> cmdList;
+ m_device->m_device->CreateCommandList(
+ 0,
+ D3D12_COMMAND_LIST_TYPE_DIRECT,
+ m_commandAllocator,
+ nullptr,
+ IID_PPV_ARGS(cmdList.writeRef()));
+
+ m_d3dCommandListPool.add(cmdList);
+ RefPtr<CommandBufferImpl> cmdBuffer = new CommandBufferImpl();
+ cmdBuffer->init(m_device, cmdList, this);
+ m_commandBufferPool.add(cmdBuffer);
+ ++m_commandListAllocId;
+ returnComPtr(outCmdBuffer, cmdBuffer);
+ return SLANG_OK;
+}
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-transient-heap.h b/tools/gfx/d3d12/d3d12-transient-heap.h
new file mode 100644
index 000000000..8d9ac261a
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-transient-heap.h
@@ -0,0 +1,94 @@
+// d3d12-transient-heap.h
+#pragma once
+
+#include "d3d12-base.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class TransientResourceHeapImpl
+ : public TransientResourceHeapBaseImpl<DeviceImpl, BufferResourceImpl>
+ , public ID3D12TransientResourceHeap
+{
+private:
+ typedef TransientResourceHeapBaseImpl<DeviceImpl, BufferResourceImpl> Super;
+
+public:
+ ComPtr<ID3D12CommandAllocator> m_commandAllocator;
+ List<ComPtr<ID3D12GraphicsCommandList>> m_d3dCommandListPool;
+ List<RefPtr<RefObject>> m_commandBufferPool;
+ uint32_t m_commandListAllocId = 0;
+ // Wait values for each command queue.
+ struct QueueWaitInfo
+ {
+ uint64_t waitValue;
+ HANDLE fenceEvent;
+ ComPtr<ID3D12CommandQueue> queue;
+ ComPtr<ID3D12Fence> fence = nullptr;
+ };
+ ShortList<QueueWaitInfo, 4> m_waitInfos;
+ ShortList<HANDLE, 4> m_waitHandles;
+
+ QueueWaitInfo& getQueueWaitInfo(uint32_t queueIndex);
+ // 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.
+ ShortList<D3D12DescriptorHeap, 4> m_viewHeaps; // Cbv, Srv, Uav
+ ShortList<D3D12DescriptorHeap, 4> m_samplerHeaps; // Heap for samplers
+ int32_t m_currentViewHeapIndex = -1;
+ int32_t m_currentSamplerHeapIndex = -1;
+ bool m_canResize = false;
+
+ uint32_t m_viewHeapSize;
+ uint32_t m_samplerHeapSize;
+
+ D3D12DescriptorHeap& getCurrentViewHeap();
+ D3D12DescriptorHeap& getCurrentSamplerHeap();
+
+ D3D12LinearExpandingDescriptorHeap m_stagingCpuViewHeap;
+ D3D12LinearExpandingDescriptorHeap m_stagingCpuSamplerHeap;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ queryInterface(SlangUUID const& uuid, void** outObject) override;
+
+ virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return Super::addRef(); }
+ virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return Super::release(); }
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL allocateTransientDescriptorTable(
+ DescriptorType type,
+ GfxCount count,
+ Offset& outDescriptorOffset,
+ void** outD3DDescriptorHeapHandle) override;
+
+ ~TransientResourceHeapImpl();
+
+ bool canResize() { return m_canResize; }
+
+ Result init(
+ const ITransientResourceHeap::Desc& desc,
+ DeviceImpl* device,
+ uint32_t viewHeapSize,
+ uint32_t samplerHeapSize);
+
+ Result allocateNewViewDescriptorHeap(DeviceImpl* device);
+
+ Result allocateNewSamplerDescriptorHeap(DeviceImpl* device);
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ createCommandBuffer(ICommandBuffer** outCommandBuffer) override;
+
+ Result synchronize();
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() override;
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL finish() override;
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/d3d12-vertex-layout.h b/tools/gfx/d3d12/d3d12-vertex-layout.h
new file mode 100644
index 000000000..1e84f9f6d
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-vertex-layout.h
@@ -0,0 +1,23 @@
+// d3d12-vertex-layout.h
+#pragma once
+
+#include "d3d12-base.h"
+#include "d3d12-buffer.h"
+
+namespace gfx
+{
+namespace d3d12
+{
+
+using namespace Slang;
+
+class InputLayoutImpl : public InputLayoutBase
+{
+public:
+ List<D3D12_INPUT_ELEMENT_DESC> m_elements;
+ List<UINT> m_vertexStreamStrides;
+ List<char> m_text; ///< Holds all strings to keep in scope
+};
+
+} // namespace d3d12
+} // namespace gfx
diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp
deleted file mode 100644
index 2506c3b63..000000000
--- a/tools/gfx/d3d12/render-d3d12.cpp
+++ /dev/null
@@ -1,7667 +0,0 @@
-// render-d3d12.cpp
-#include "render-d3d12.h"
-
-#ifdef GFX_NVAPI
-# include "../nvapi/nvapi-include.h"
-#endif
-
-#include "../d3d/d3d-util.h"
-#include "../flag-combiner.h"
-#include "../nvapi/nvapi-util.h"
-#include "slang-com-ptr.h"
-#include <stdio.h>
-
-#ifdef _DEBUG
-# define ENABLE_DEBUG_LAYER 1
-#else
-# define ENABLE_DEBUG_LAYER 0
-#endif
-
-namespace gfx
-{
-using namespace Slang;
-
-namespace d3d12
-{
-
-namespace
-{
-bool isSupportedNVAPIOp(ID3D12Device* dev, uint32_t op)
-{
-#ifdef GFX_NVAPI
- {
- bool isSupported;
- NvAPI_Status status =
- NvAPI_D3D12_IsNvShaderExtnOpCodeSupported(dev, NvU32(op), &isSupported);
- return status == NVAPI_OK && isSupported;
- }
-#else
- return false;
-#endif
-}
-
-D3D12_RESOURCE_FLAGS calcResourceFlag(ResourceState state)
-{
- switch (state)
- {
- case ResourceState::RenderTarget:
- return D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
- case ResourceState::DepthRead:
- case ResourceState::DepthWrite:
- return D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
- case ResourceState::UnorderedAccess:
- case ResourceState::AccelerationStructure:
- return D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
- default:
- return D3D12_RESOURCE_FLAG_NONE;
- }
-}
-
-D3D12_RESOURCE_FLAGS calcResourceFlags(ResourceStateSet states)
-{
- int dstFlags = 0;
- for (uint32_t i = 0; i < (uint32_t)ResourceState::_Count; i++)
- {
- auto state = (ResourceState)i;
- if (states.contains(state))
- dstFlags |= calcResourceFlag(state);
- }
- return (D3D12_RESOURCE_FLAGS)dstFlags;
-}
-
-D3D12_RESOURCE_DIMENSION calcResourceDimension(IResource::Type type)
-{
- switch (type)
- {
- case IResource::Type::Buffer:
- return D3D12_RESOURCE_DIMENSION_BUFFER;
- case IResource::Type::Texture1D:
- return D3D12_RESOURCE_DIMENSION_TEXTURE1D;
- case IResource::Type::TextureCube:
- case IResource::Type::Texture2D:
- {
- return D3D12_RESOURCE_DIMENSION_TEXTURE2D;
- }
- case IResource::Type::Texture3D:
- return D3D12_RESOURCE_DIMENSION_TEXTURE3D;
- default:
- return D3D12_RESOURCE_DIMENSION_UNKNOWN;
- }
-}
-
-DXGI_FORMAT getTypelessFormatFromDepthFormat(Format format)
-{
- switch (format)
- {
- case Format::D16_UNORM:
- return DXGI_FORMAT_R16_TYPELESS;
- case Format::D32_FLOAT:
- return DXGI_FORMAT_R32_TYPELESS;
- default:
- return D3DUtil::getMapFormat(format);
- }
-}
-
-bool isTypelessDepthFormat(DXGI_FORMAT format)
-{
- switch (format)
- {
- case DXGI_FORMAT_R16_TYPELESS:
- case DXGI_FORMAT_R32_TYPELESS:
- return true;
- default:
- return false;
- }
-}
-
-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
- }
-}
-
-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
- }
-}
-
-uint32_t getViewDescriptorCount(const ITransientResourceHeap::Desc& desc)
-{
- return Math::Max(
- Math::Max(
- desc.srvDescriptorCount,
- desc.uavDescriptorCount,
- desc.accelerationStructureDescriptorCount),
- desc.constantBufferDescriptorCount,
- 2048);
-}
-
-void initSrvDesc(
- IResource::Type resourceType,
- const ITextureResource::Desc& textureDesc,
- const D3D12_RESOURCE_DESC& desc,
- DXGI_FORMAT pixelFormat,
- SubresourceRange subresourceRange,
- 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;
- descOut.Texture1D.MipLevels = subresourceRange.mipLevelCount == 0
- ? desc.MipLevels - subresourceRange.mipLevel
- : subresourceRange.mipLevelCount;
- descOut.Texture1D.MostDetailedMip = subresourceRange.mipLevel;
- break;
- case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
- descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
- descOut.Texture2D.PlaneSlice =
- D3DUtil::getPlaneSlice(descOut.Format, subresourceRange.aspectMask);
- descOut.Texture2D.ResourceMinLODClamp = 0.0f;
- descOut.Texture2D.MipLevels = subresourceRange.mipLevelCount == 0
- ? desc.MipLevels - subresourceRange.mipLevel
- : subresourceRange.mipLevelCount;
- descOut.Texture2D.MostDetailedMip = subresourceRange.mipLevel;
- break;
- case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
- descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
- descOut.Texture3D.MipLevels = subresourceRange.mipLevelCount == 0
- ? desc.MipLevels - subresourceRange.mipLevel
- : subresourceRange.mipLevelCount;
- descOut.Texture3D.MostDetailedMip = subresourceRange.mipLevel;
- break;
- default:
- assert(!"Unknown dimension");
- }
- }
- else if (resourceType == IResource::Type::TextureCube)
- {
- if (textureDesc.arraySize > 1)
- {
- descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
-
- descOut.TextureCubeArray.NumCubes = subresourceRange.layerCount == 0
- ? textureDesc.arraySize
- : subresourceRange.layerCount / 6;
- descOut.TextureCubeArray.First2DArrayFace = subresourceRange.baseArrayLayer;
- descOut.TextureCubeArray.MipLevels = subresourceRange.mipLevelCount == 0
- ? desc.MipLevels - subresourceRange.mipLevel
- : subresourceRange.mipLevelCount;
- descOut.TextureCubeArray.MostDetailedMip = subresourceRange.mipLevel;
- descOut.TextureCubeArray.ResourceMinLODClamp = 0;
- }
- else
- {
- descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
-
- descOut.TextureCube.MipLevels = subresourceRange.mipLevelCount == 0
- ? desc.MipLevels - subresourceRange.mipLevel
- : subresourceRange.mipLevelCount;
- descOut.TextureCube.MostDetailedMip = subresourceRange.mipLevel;
- descOut.TextureCube.ResourceMinLODClamp = 0;
- }
- }
- else
- {
- assert(desc.DepthOrArraySize > 1);
-
- switch (desc.Dimension)
- {
- case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
- descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
- descOut.Texture1D.MostDetailedMip = subresourceRange.mipLevel;
- descOut.Texture1D.MipLevels = subresourceRange.mipLevelCount == 0
- ? desc.MipLevels
- : subresourceRange.mipLevelCount;
- descOut.Texture1DArray.ArraySize = subresourceRange.layerCount == 0
- ? desc.DepthOrArraySize
- : subresourceRange.layerCount;
- descOut.Texture1DArray.FirstArraySlice = subresourceRange.baseArrayLayer;
- descOut.Texture1DArray.ResourceMinLODClamp = 0;
- descOut.Texture1DArray.MostDetailedMip = subresourceRange.mipLevel;
- descOut.Texture1DArray.MipLevels = subresourceRange.mipLevelCount == 0
- ? desc.MipLevels - subresourceRange.mipLevel
- : subresourceRange.mipLevelCount;
- break;
- case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
- descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
- descOut.Texture2DArray.ArraySize = subresourceRange.layerCount == 0
- ? desc.DepthOrArraySize
- : subresourceRange.layerCount;
- descOut.Texture2DArray.FirstArraySlice = subresourceRange.baseArrayLayer;
- descOut.Texture2DArray.PlaneSlice =
- D3DUtil::getPlaneSlice(descOut.Format, subresourceRange.aspectMask);
- descOut.Texture2DArray.ResourceMinLODClamp = 0;
- descOut.Texture2DArray.MostDetailedMip = subresourceRange.mipLevel;
- descOut.Texture2DArray.MipLevels = subresourceRange.mipLevelCount == 0
- ? desc.MipLevels - subresourceRange.mipLevel
- : subresourceRange.mipLevelCount;
- break;
- case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
- descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
- descOut.Texture3D.MostDetailedMip = subresourceRange.mipLevel;
- descOut.Texture3D.MipLevels = subresourceRange.mipLevelCount == 0
- ? desc.MipLevels
- : subresourceRange.mipLevelCount;
- break;
-
- default:
- assert(!"Unknown dimension");
- }
- }
-}
-
-Result initTextureResourceDesc(
- D3D12_RESOURCE_DESC& resourceDesc, const ITextureResource::Desc& srcDesc)
-{
- const DXGI_FORMAT pixelFormat = D3DUtil::getMapFormat(srcDesc.format);
- if (pixelFormat == DXGI_FORMAT_UNKNOWN)
- {
- return SLANG_FAIL;
- }
-
- const int arraySize = calcEffectiveArraySize(srcDesc);
-
- const D3D12_RESOURCE_DIMENSION dimension = calcResourceDimension(srcDesc.type);
- if (dimension == D3D12_RESOURCE_DIMENSION_UNKNOWN)
- {
- return SLANG_FAIL;
- }
-
- const int numMipMaps = srcDesc.numMipLevels;
- 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.Flags |= calcResourceFlags(srcDesc.allowedStates);
-
- resourceDesc.Alignment = 0;
-
- if (isDepthFormat(srcDesc.format) &&
- (srcDesc.allowedStates.contains(ResourceState::ShaderResource) ||
- srcDesc.allowedStates.contains(ResourceState::UnorderedAccess)))
- {
- resourceDesc.Format = getTypelessFormatFromDepthFormat(srcDesc.format);
- }
-
- return SLANG_OK;
-}
-
-void initBufferResourceDesc(Size 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 uploadBufferDataImpl(
- ID3D12Device* device,
- ID3D12GraphicsCommandList* cmdList,
- TransientResourceHeapImpl* transientHeap,
- BufferResourceImpl* buffer,
- Offset offset,
- Size size,
- void* data)
-{
- IBufferResource* uploadResource;
- Offset uploadResourceOffset = 0;
- if (buffer->getDesc()->memoryType != MemoryType::Upload)
- {
- SLANG_RETURN_ON_FAIL(transientHeap->allocateStagingBuffer(
- size, uploadResource, uploadResourceOffset, MemoryType::Upload));
- }
-
- D3D12Resource& uploadResourceRef =
- (buffer->getDesc()->memoryType == MemoryType::Upload)
- ? buffer->m_resource
- : static_cast<BufferResourceImpl*>(uploadResource)->m_resource;
-
- D3D12_RANGE readRange = {};
- readRange.Begin = 0;
- readRange.End = 0;
- void* uploadData;
- SLANG_RETURN_ON_FAIL(
- uploadResourceRef.getResource()->Map(0, &readRange, reinterpret_cast<void**>(&uploadData)));
- memcpy((uint8_t*)uploadData + uploadResourceOffset + offset, data, size);
- D3D12_RANGE writtenRange = {};
- writtenRange.Begin = uploadResourceOffset + offset;
- writtenRange.End = uploadResourceOffset + offset + size;
- uploadResourceRef.getResource()->Unmap(0, &writtenRange);
-
- if (buffer->getDesc()->memoryType != MemoryType::Upload)
- {
- cmdList->CopyBufferRegion(
- buffer->m_resource.getResource(),
- offset,
- uploadResourceRef.getResource(),
- uploadResourceOffset + offset,
- size);
- }
-
- return SLANG_OK;
-}
-
-Result createNullDescriptor(
- ID3D12Device* d3dDevice,
- D3D12_CPU_DESCRIPTOR_HANDLE destDescriptor,
- const ShaderObjectLayoutImpl::BindingRangeInfo& bindingRange)
-{
- switch (bindingRange.bindingType)
- {
- case slang::BindingType::ConstantBuffer:
- {
- D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
- cbvDesc.BufferLocation = 0;
- cbvDesc.SizeInBytes = 0;
- d3dDevice->CreateConstantBufferView(&cbvDesc, destDescriptor);
- }
- break;
- case slang::BindingType::MutableRawBuffer:
- {
- D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
- uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
- uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
- uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
- d3dDevice->CreateUnorderedAccessView(nullptr, nullptr, &uavDesc, destDescriptor);
- }
- break;
- case slang::BindingType::MutableTypedBuffer:
- {
- D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
- uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
- uavDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
- d3dDevice->CreateUnorderedAccessView(nullptr, nullptr, &uavDesc, destDescriptor);
- }
- break;
- case slang::BindingType::RawBuffer:
- {
- D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
- srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
- srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
- srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
- d3dDevice->CreateShaderResourceView(nullptr, &srvDesc, destDescriptor);
- }
- break;
- case slang::BindingType::TypedBuffer:
- {
- D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
- srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
- srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
- d3dDevice->CreateShaderResourceView(nullptr, &srvDesc, destDescriptor);
- }
- break;
- case slang::BindingType::Texture:
- {
- D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
- srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
- srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
- switch (bindingRange.resourceShape)
- {
- case SLANG_TEXTURE_1D:
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
- break;
- case SLANG_TEXTURE_1D_ARRAY:
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
- break;
- case SLANG_TEXTURE_2D:
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
- break;
- case SLANG_TEXTURE_2D_ARRAY:
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
- break;
- case SLANG_TEXTURE_3D:
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
- break;
- case SLANG_TEXTURE_CUBE:
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
- break;
- case SLANG_TEXTURE_CUBE_ARRAY:
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
- break;
- case SLANG_TEXTURE_2D_MULTISAMPLE:
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
- break;
- case SLANG_TEXTURE_2D_MULTISAMPLE_ARRAY:
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
- break;
- default:
- return SLANG_OK;
- }
- d3dDevice->CreateShaderResourceView(nullptr, &srvDesc, destDescriptor);
- }
- break;
- default:
- break;
- }
- return SLANG_OK;
-}
-} // namespace
-
-Result DeviceImpl::createBuffer(
- const D3D12_RESOURCE_DESC& resourceDesc,
- const void* srcData,
- Size srcDataSize,
- D3D12_RESOURCE_STATES finalState,
- D3D12Resource& resourceOut,
- bool isShared,
- MemoryType memoryType)
-{
- const Size bufferSize = Size(resourceDesc.Width);
-
- D3D12_HEAP_PROPERTIES heapProps;
- heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
- heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
- heapProps.CreationNodeMask = 1;
- heapProps.VisibleNodeMask = 1;
-
- D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE;
- if (isShared)
- flags |= D3D12_HEAP_FLAG_SHARED;
-
- D3D12_RESOURCE_DESC desc = resourceDesc;
-
- D3D12_RESOURCE_STATES initialState = finalState;
-
- switch (memoryType)
- {
- case MemoryType::ReadBack:
- assert(!srcData);
-
- heapProps.Type = D3D12_HEAP_TYPE_READBACK;
- desc.Flags = D3D12_RESOURCE_FLAG_NONE;
- initialState |= D3D12_RESOURCE_STATE_COPY_DEST;
-
- break;
- case MemoryType::Upload:
-
- heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
- desc.Flags = D3D12_RESOURCE_FLAG_NONE;
- initialState |= D3D12_RESOURCE_STATE_GENERIC_READ;
-
- break;
- case MemoryType::DeviceLocal:
- heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
- initialState = (srcData ? D3D12_RESOURCE_STATE_COPY_DEST : finalState);
- break;
- default:
- return SLANG_FAIL;
- }
-
- // Create the resource.
- SLANG_RETURN_ON_FAIL(
- resourceOut.initCommitted(m_device, heapProps, flags, desc, initialState, nullptr));
-
- if (srcData)
- {
- D3D12Resource uploadResource;
-
- if (memoryType == MemoryType::DeviceLocal)
- {
- // If the buffer is on the default heap, create upload buffer.
- D3D12_RESOURCE_DESC uploadDesc(resourceDesc);
- uploadDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
- heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
-
- SLANG_RETURN_ON_FAIL(uploadResource.initCommitted(
- m_device,
- heapProps,
- D3D12_HEAP_FLAG_NONE,
- uploadDesc,
- D3D12_RESOURCE_STATE_GENERIC_READ,
- nullptr));
- }
-
- // Be careful not to actually copy a resource here.
- D3D12Resource& uploadResourceRef =
- (memoryType == MemoryType::DeviceLocal) ? uploadResource : resourceOut;
-
- // 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 = uploadResourceRef.getResource();
-
- SLANG_RETURN_ON_FAIL(
- dxUploadResource->Map(0, &readRange, reinterpret_cast<void**>(&dstData)));
- ::memcpy(dstData, srcData, srcDataSize);
- dxUploadResource->Unmap(0, nullptr);
-
- if (memoryType == MemoryType::DeviceLocal)
- {
- auto encodeInfo = encodeResourceCommands();
- encodeInfo.d3dCommandList->CopyBufferRegion(
- resourceOut, 0, uploadResourceRef, 0, bufferSize);
- submitResourceCommandsAndWait(encodeInfo);
- }
- }
-
- return SLANG_OK;
-}
-
-Result DeviceImpl::captureTextureToSurface(
- TextureResourceImpl* resourceImpl,
- ResourceState state,
- ISlangBlob** outBlob,
- Size* outRowPitch,
- Size* outPixelSize)
-{
- auto& resource = resourceImpl->m_resource;
-
- const D3D12_RESOURCE_STATES initialState = D3DUtil::getResourceState(state);
-
- const ITextureResource::Desc& gfxDesc = *resourceImpl->getDesc();
- 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;
- }
-
- FormatInfo formatInfo;
- gfxGetFormatInfo(gfxDesc.format, &formatInfo);
- Size bytesPerPixel = formatInfo.blockSizeInBytes / formatInfo.pixelsPerBlock;
- Size rowPitch = int(desc.Width) * bytesPerPixel;
- static const Size align = 256; // D3D requires minimum 256 byte alignment for texture data.
- rowPitch = (rowPitch + align - 1) & ~(align - 1); // Bit trick for rounding up
- Size bufferSize = rowPitch * int(desc.Height) * int(desc.DepthOrArraySize);
- if (outRowPitch)
- *outRowPitch = rowPitch;
- if (outPixelSize)
- *outPixelSize = bytesPerPixel;
-
- 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));
- }
-
- auto encodeInfo = encodeResourceCommands();
- auto currentState = D3DUtil::getResourceState(state);
-
- {
- D3D12BarrierSubmitter submitter(encodeInfo.d3dCommandList);
- resource.transition(currentState, 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 = UINT(desc.DepthOrArraySize);
- dstLoc.PlacedFootprint.Footprint.RowPitch = UINT(rowPitch);
-
- encodeInfo.d3dCommandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr);
- }
-
- {
- D3D12BarrierSubmitter submitter(encodeInfo.d3dCommandList);
- resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, currentState, submitter);
- }
-
- // Submit the copy, and wait for copy to complete
- submitResourceCommandsAndWait(encodeInfo);
-
- {
- ID3D12Resource* dxResource = stagingResource;
-
- UINT8* data;
- D3D12_RANGE readRange = {0, bufferSize};
-
- SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast<void**>(&data)));
-
- RefPtr<Slang::ListBlob> resultBlob = new Slang::ListBlob();
- resultBlob->m_data.setCount(bufferSize);
- memcpy(resultBlob->m_data.getBuffer(), data, bufferSize);
- dxResource->Unmap(0, nullptr);
- returnComPtr(outBlob, resultBlob);
- return SLANG_OK;
- }
-}
-
-Result DeviceImpl::getNativeDeviceHandles(InteropHandles* outHandles)
-{
- outHandles->handles[0].handleValue = (uint64_t)m_device;
- outHandles->handles[0].api = InteropHandleAPI::D3D12;
- return SLANG_OK;
-}
-
-Result DeviceImpl::_createDevice(
- DeviceCheckFlags deviceCheckFlags,
- const UnownedStringSlice& nameMatch,
- D3D_FEATURE_LEVEL featureLevel,
- D3D12DeviceInfo& outDeviceInfo)
-{
- if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug))
- {
- m_dxDebug->EnableDebugLayer();
- }
-
- outDeviceInfo.clear();
-
- ComPtr<IDXGIFactory> dxgiFactory;
- SLANG_RETURN_ON_FAIL(D3DUtil::createFactory(deviceCheckFlags, dxgiFactory));
-
- List<ComPtr<IDXGIAdapter>> dxgiAdapters;
- SLANG_RETURN_ON_FAIL(
- D3DUtil::findAdapters(deviceCheckFlags, nameMatch, dxgiFactory, dxgiAdapters));
-
- ComPtr<ID3D12Device> device;
- ComPtr<IDXGIAdapter> 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))
- {
- ComPtr<ID3D12InfoQueue> infoQueue;
- if (SLANG_SUCCEEDED(device->QueryInterface(infoQueue.writeRef())))
- {
- // Make break
- infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
- if (m_extendedDesc.debugBreakOnD3D12Error)
- {
- infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
- }
- D3D12_MESSAGE_ID hideMessages[] = {
- D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
- D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
- };
- D3D12_INFO_QUEUE_FILTER f = {};
- f.DenyList.NumIDs = (UINT)SLANG_COUNT_OF(hideMessages);
- f.DenyList.pIDList = hideMessages;
- infoQueue->AddStorageFilterEntries(&f);
-
- // 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<IDXGIAdapter1> 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);
- const UINT kMicrosoftVendorId = 5140;
- outDeviceInfo.m_isSoftware =
- outDeviceInfo.m_isWarp ||
- ((outDeviceInfo.m_desc1.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0) ||
- outDeviceInfo.m_desc.VendorId == kMicrosoftVendorId;
-
- return SLANG_OK;
-}
-
-Result DeviceImpl::initialize(const Desc& desc)
-{
- SLANG_RETURN_ON_FAIL(RendererBase::initialize(desc));
-
- // Find extended desc.
- for (GfxIndex i = 0; i < desc.extendedDescCount; i++)
- {
- StructType stype;
- memcpy(&stype, desc.extendedDescs[i], sizeof(stype));
- if (stype == StructType::D3D12ExtendedDesc)
- {
- memcpy(&m_extendedDesc, desc.extendedDescs[i], sizeof(m_extendedDesc));
- }
- }
-
- // Initialize queue index allocator.
- // Support max 32 queues.
- m_queueIndexAllocator.initPool(32);
-
- // Initialize DeviceInfo
- {
- m_info.deviceType = DeviceType::DirectX12;
- m_info.bindingStyle = BindingStyle::DirectX;
- m_info.projectionStyle = ProjectionStyle::DirectX;
- m_info.apiName = "Direct3D 12";
- static const float kIdentity[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
- ::memcpy(m_info.identityProjectionMatrix, kIdentity, sizeof(kIdentity));
- }
-
- // 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;
- }
-
- HMODULE pixModule = LoadLibraryW(L"WinPixEventRuntime.dll");
- if (pixModule)
- {
- m_BeginEventOnCommandList =
- (PFN_BeginEventOnCommandList)GetProcAddress(pixModule, "PIXBeginEventOnCommandList");
- m_EndEventOnCommandList =
- (PFN_EndEventOnCommandList)GetProcAddress(pixModule, "PIXEndEventOnCommandList");
- }
-
-#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<ID3D12Debug1> debug1;
- if (SLANG_SUCCEEDED(m_dxDebug->QueryInterface(debug1.writeRef())))
- {
- debug1->SetEnableGPUBasedValidation(true);
- }
-# endif
- }
- }
-#endif
-
- m_D3D12CreateDevice = (PFN_D3D12_CREATE_DEVICE)loadProc(d3dModule, "D3D12CreateDevice");
- if (!m_D3D12CreateDevice)
- {
- return SLANG_FAIL;
- }
-
- if (desc.existingDeviceHandles.handles[0].handleValue == 0)
- {
- 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),
- UnownedStringSlice(desc.adapter),
- featureLevel,
- m_deviceInfo)))
- {
- break;
- }
- }
-
- if (!m_deviceInfo.m_adapter)
- {
- // Couldn't find an adapter
- return SLANG_FAIL;
- }
- }
- else
- {
- // Store the existing device handle in desc in m_deviceInfo
- m_deviceInfo.m_device = (ID3D12Device*)desc.existingDeviceHandles.handles[0].handleValue;
- }
-
- // Set the device
- m_device = m_deviceInfo.m_device;
-
- if (m_deviceInfo.m_isSoftware)
- {
- m_features.add("software-device");
- }
- else
- {
- m_features.add("hardware-device");
- }
-
- // NVAPI
- if (desc.nvapiExtnSlot >= 0)
- {
- if (SLANG_FAILED(NVAPIUtil::initialize()))
- {
- return SLANG_E_NOT_AVAILABLE;
- }
-
-#ifdef GFX_NVAPI
- // From DOCS: Applications are expected to bind null UAV to this slot.
- // NOTE! We don't currently do this, but doesn't seem to be a problem.
-
- const NvAPI_Status status =
- NvAPI_D3D12_SetNvShaderExtnSlotSpace(m_device, NvU32(desc.nvapiExtnSlot), NvU32(0));
-
- if (status != NVAPI_OK)
- {
- return SLANG_E_NOT_AVAILABLE;
- }
-
- if (isSupportedNVAPIOp(m_device, NV_EXTN_OP_UINT64_ATOMIC))
- {
- m_features.add("atomic-int64");
- }
- if (isSupportedNVAPIOp(m_device, NV_EXTN_OP_FP32_ATOMIC))
- {
- m_features.add("atomic-float");
- }
-
- m_nvapi = true;
-#endif
- }
-
- D3D12_FEATURE_DATA_SHADER_MODEL shaderModelData = {};
- shaderModelData.HighestShaderModel = D3D_SHADER_MODEL_6_6;
-
- // Find what features are supported
- {
- // Check this is how this is laid out...
- SLANG_COMPILE_TIME_ASSERT(D3D_SHADER_MODEL_6_0 == 0x60);
-
- {
- // TODO: Currently warp causes a crash when using half, so disable for now
- if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
- D3D12_FEATURE_SHADER_MODEL, &shaderModelData, sizeof(shaderModelData))) &&
- m_deviceInfo.m_isWarp == false && shaderModelData.HighestShaderModel >= 0x62)
- {
- // With sm_6_2 we have half
- m_features.add("half");
- }
- }
- {
- D3D12_FEATURE_DATA_D3D12_OPTIONS options;
- if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
- D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
- {
- // Check double precision support
- if (options.DoublePrecisionFloatShaderOps)
- m_features.add("double");
-
- // Check conservative-rasterization support
- auto conservativeRasterTier = options.ConservativeRasterizationTier;
- if (conservativeRasterTier == D3D12_CONSERVATIVE_RASTERIZATION_TIER_3)
- {
- m_features.add("conservative-rasterization-3");
- m_features.add("conservative-rasterization-2");
- m_features.add("conservative-rasterization-1");
- }
- else if (conservativeRasterTier == D3D12_CONSERVATIVE_RASTERIZATION_TIER_2)
- {
- m_features.add("conservative-rasterization-2");
- m_features.add("conservative-rasterization-1");
- }
- else if (conservativeRasterTier == D3D12_CONSERVATIVE_RASTERIZATION_TIER_1)
- {
- m_features.add("conservative-rasterization-1");
- }
-
- // Check rasterizer ordered views support
- if (options.ROVsSupported)
- {
- m_features.add("rasterizer-ordered-views");
- }
- }
- }
- {
- D3D12_FEATURE_DATA_D3D12_OPTIONS2 options;
- if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
- D3D12_FEATURE_D3D12_OPTIONS2, &options, sizeof(options))))
- {
- // Check programmable sample positions support
- switch (options.ProgrammableSamplePositionsTier)
- {
- case D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_2:
- m_features.add("programmable-sample-positions-2");
- m_features.add("programmable-sample-positions-1");
- break;
- case D3D12_PROGRAMMABLE_SAMPLE_POSITIONS_TIER_1:
- m_features.add("programmable-sample-positions-1");
- break;
- default:
- break;
- }
- }
- }
- {
- D3D12_FEATURE_DATA_D3D12_OPTIONS3 options;
- if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
- D3D12_FEATURE_D3D12_OPTIONS3, &options, sizeof(options))))
- {
- // Check barycentrics support
- if (options.BarycentricsSupported)
- {
- m_features.add("barycentrics");
- }
- }
- }
- // Check ray tracing support
- {
- D3D12_FEATURE_DATA_D3D12_OPTIONS5 options;
- if (SLANG_SUCCEEDED(m_device->CheckFeatureSupport(
- D3D12_FEATURE_D3D12_OPTIONS5, &options, sizeof(options))))
- {
- if (options.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED)
- {
- m_features.add("ray-tracing");
- }
- if (options.RaytracingTier >= D3D12_RAYTRACING_TIER_1_1)
- {
- m_features.add("ray-query");
- }
- }
- }
- }
-
- m_desc = desc;
-
- // Create a command queue for internal resource transfer operations.
- SLANG_RETURN_ON_FAIL(createCommandQueueImpl(m_resourceCommandQueue.writeRef()));
- // `CommandQueueImpl` holds a back reference to `D3D12Device`, make it a weak reference here
- // since this object is already owned by `D3D12Device`.
- m_resourceCommandQueue->breakStrongReferenceToDevice();
- // Retrieve timestamp frequency.
- m_resourceCommandQueue->m_d3dQueue->GetTimestampFrequency(&m_info.timestampFrequency);
-
- SLANG_RETURN_ON_FAIL(createTransientResourceHeapImpl(
- ITransientResourceHeap::Flags::AllowResizing,
- 0,
- 8,
- 4,
- m_resourceCommandTransientHeap.writeRef()));
- // `TransientResourceHeap` holds a back reference to `D3D12Device`, make it a weak reference
- // here since this object is already owned by `D3D12Device`.
- m_resourceCommandTransientHeap->breakStrongReferenceToDevice();
-
- m_cpuViewHeap = new D3D12GeneralExpandingDescriptorHeap();
- SLANG_RETURN_ON_FAIL(m_cpuViewHeap->init(
- m_device,
- 1024 * 1024,
- D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
- D3D12_DESCRIPTOR_HEAP_FLAG_NONE));
- m_cpuSamplerHeap = new D3D12GeneralExpandingDescriptorHeap();
- SLANG_RETURN_ON_FAIL(m_cpuSamplerHeap->init(
- m_device, 2048, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE));
-
- m_rtvAllocator = new D3D12GeneralExpandingDescriptorHeap();
- SLANG_RETURN_ON_FAIL(m_rtvAllocator->init(
- m_device, 16 * 1024, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE));
- m_dsvAllocator = new D3D12GeneralExpandingDescriptorHeap();
- SLANG_RETURN_ON_FAIL(m_dsvAllocator->init(
- m_device, 1024, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE));
-
- ComPtr<IDXGIDevice> dxgiDevice;
- if (m_deviceInfo.m_adapter)
- {
- DXGI_ADAPTER_DESC adapterDesc;
- m_deviceInfo.m_adapter->GetDesc(&adapterDesc);
- m_adapterName = String::fromWString(adapterDesc.Description);
- m_info.adapterName = m_adapterName.begin();
- }
-
- // Initialize DXR interface.
-#if SLANG_GFX_HAS_DXR_SUPPORT
- m_device->QueryInterface<ID3D12Device5>(m_deviceInfo.m_device5.writeRef());
- m_device5 = m_deviceInfo.m_device5.get();
-#endif
- // Check shader model version.
- SlangCompileTarget compileTarget = SLANG_DXBC;
- const char* profileName = "sm_5_1";
- switch (shaderModelData.HighestShaderModel)
- {
- case D3D_SHADER_MODEL_5_1:
- compileTarget = SLANG_DXBC;
- profileName = "sm_5_1";
- break;
- case D3D_SHADER_MODEL_6_0:
- compileTarget = SLANG_DXIL;
- profileName = "sm_6_0";
- break;
- case D3D_SHADER_MODEL_6_1:
- compileTarget = SLANG_DXIL;
- profileName = "sm_6_1";
- break;
- case D3D_SHADER_MODEL_6_2:
- compileTarget = SLANG_DXIL;
- profileName = "sm_6_2";
- break;
- case D3D_SHADER_MODEL_6_3:
- compileTarget = SLANG_DXIL;
- profileName = "sm_6_3";
- break;
- case D3D_SHADER_MODEL_6_4:
- compileTarget = SLANG_DXIL;
- profileName = "sm_6_4";
- break;
- case D3D_SHADER_MODEL_6_5:
- compileTarget = SLANG_DXIL;
- profileName = "sm_6_5";
- break;
- default:
- compileTarget = SLANG_DXIL;
- profileName = "sm_6_6";
- break;
- }
- m_features.add(profileName);
- // If user specified a higher shader model than what the system supports, return failure.
- int userSpecifiedShaderModel = D3DUtil::getShaderModelFromProfileName(desc.slang.targetProfile);
- if (userSpecifiedShaderModel > shaderModelData.HighestShaderModel)
- {
- getDebugCallback()->handleMessage(
- gfx::DebugMessageType::Error,
- gfx::DebugMessageSource::Layer,
- "The requested shader model is not supported by the system.");
- return SLANG_E_NOT_AVAILABLE;
- }
- SLANG_RETURN_ON_FAIL(slangContext.initialize(
- desc.slang,
- compileTarget,
- profileName,
- makeArray(slang::PreprocessorMacroDesc{"__D3D12__", "1"}).getView()));
-
- // Allocate a D3D12 "command signature" object that matches the behavior
- // of a D3D11-style `DrawInstancedIndirect` operation.
- {
- D3D12_INDIRECT_ARGUMENT_DESC args;
- args.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
-
- D3D12_COMMAND_SIGNATURE_DESC desc;
- desc.ByteStride = sizeof(D3D12_DRAW_ARGUMENTS);
- desc.NumArgumentDescs = 1;
- desc.pArgumentDescs = &args;
- desc.NodeMask = 0;
-
- SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature(
- &desc, nullptr, IID_PPV_ARGS(drawIndirectCmdSignature.writeRef())));
- }
-
- // Allocate a D3D12 "command signature" object that matches the behavior
- // of a D3D11-style `DrawIndexedInstancedIndirect` operation.
- {
- D3D12_INDIRECT_ARGUMENT_DESC args;
- args.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
-
- D3D12_COMMAND_SIGNATURE_DESC desc;
- desc.ByteStride = sizeof(D3D12_DRAW_INDEXED_ARGUMENTS);
- desc.NumArgumentDescs = 1;
- desc.pArgumentDescs = &args;
- desc.NodeMask = 0;
-
- SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature(
- &desc, nullptr, IID_PPV_ARGS(drawIndexedIndirectCmdSignature.writeRef())));
- }
-
- // Allocate a D3D12 "command signature" object that matches the behavior
- // of a D3D11-style `Dispatch` operation.
- {
- D3D12_INDIRECT_ARGUMENT_DESC args;
- args.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
-
- D3D12_COMMAND_SIGNATURE_DESC desc;
- desc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS);
- desc.NumArgumentDescs = 1;
- desc.pArgumentDescs = &args;
- desc.NodeMask = 0;
-
- SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature(
- &desc, nullptr, IID_PPV_ARGS(dispatchIndirectCmdSignature.writeRef())));
- }
- m_isInitialized = true;
- return SLANG_OK;
-}
-
-Result DeviceImpl::createTransientResourceHeap(
- const ITransientResourceHeap::Desc& desc, ITransientResourceHeap** outHeap)
-{
- RefPtr<TransientResourceHeapImpl> heap;
- SLANG_RETURN_ON_FAIL(createTransientResourceHeapImpl(
- desc.flags,
- desc.constantBufferSize,
- getViewDescriptorCount(desc),
- Math::Max(1024, desc.samplerDescriptorCount),
- heap.writeRef()));
- returnComPtr(outHeap, heap);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue)
-{
- RefPtr<CommandQueueImpl> queue;
- SLANG_RETURN_ON_FAIL(createCommandQueueImpl(queue.writeRef()));
- returnComPtr(outQueue, queue);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createSwapchain(
- const ISwapchain::Desc& desc, WindowHandle window, ISwapchain** outSwapchain)
-{
- RefPtr<SwapchainImpl> swapchain = new SwapchainImpl();
- SLANG_RETURN_ON_FAIL(swapchain->init(this, desc, window));
- returnComPtr(outSwapchain, swapchain);
- return SLANG_OK;
-}
-
-SlangResult DeviceImpl::readTextureResource(
- ITextureResource* resource,
- ResourceState state,
- ISlangBlob** outBlob,
- Size* outRowPitch,
- Size* outPixelSize)
-{
- return captureTextureToSurface(
- static_cast<TextureResourceImpl*>(resource), state, outBlob, outRowPitch, outPixelSize);
-}
-
-Result DeviceImpl::getTextureAllocationInfo(
- const ITextureResource::Desc& desc, Size* outSize, Size* outAlignment)
-{
- TextureResource::Desc srcDesc = fixupTextureDesc(desc);
- D3D12_RESOURCE_DESC resourceDesc = {};
- initTextureResourceDesc(resourceDesc, srcDesc);
- auto allocInfo = m_device->GetResourceAllocationInfo(0, 1, &resourceDesc);
- *outSize = (Size)allocInfo.SizeInBytes;
- *outAlignment = (Size)allocInfo.Alignment;
- return SLANG_OK;
-}
-
-Result DeviceImpl::getTextureRowAlignment(Size* outAlignment)
-{
- *outAlignment = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
- return SLANG_OK;
-}
-
-Result DeviceImpl::createTextureResource(
- const ITextureResource::Desc& descIn,
- const ITextureResource::SubresourceData* initData,
- ITextureResource** outResource)
-{
- // Description of uploading on Dx12
- // https://msdn.microsoft.com/en-us/library/windows/desktop/dn899215%28v=vs.85%29.aspx
-
- TextureResource::Desc srcDesc = fixupTextureDesc(descIn);
-
- D3D12_RESOURCE_DESC resourceDesc = {};
- initTextureResourceDesc(resourceDesc, srcDesc);
- const int arraySize = calcEffectiveArraySize(srcDesc);
- const int numMipMaps = srcDesc.numMipLevels;
-
- RefPtr<TextureResourceImpl> 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;
-
- D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE;
- if (descIn.isShared)
- flags |= D3D12_HEAP_FLAG_SHARED;
-
- D3D12_CLEAR_VALUE clearValue;
- D3D12_CLEAR_VALUE* clearValuePtr = &clearValue;
- if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET |
- D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 0)
- {
- clearValuePtr = nullptr;
- }
- if (isTypelessDepthFormat(resourceDesc.Format))
- {
- clearValuePtr = nullptr;
- }
- clearValue.Format = resourceDesc.Format;
- memcpy(clearValue.Color, &descIn.optimalClearValue.color, sizeof(clearValue.Color));
- clearValue.DepthStencil.Depth = descIn.optimalClearValue.depthStencil.depth;
- clearValue.DepthStencil.Stencil = descIn.optimalClearValue.depthStencil.stencil;
- SLANG_RETURN_ON_FAIL(texture->m_resource.initCommitted(
- m_device,
- heapProps,
- flags,
- resourceDesc,
- D3D12_RESOURCE_STATE_COPY_DEST,
- clearValuePtr));
-
- texture->m_resource.setDebugName(L"Texture");
- }
-
- // Calculate the layout
- List<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> layouts;
- layouts.setCount(numMipMaps);
- List<UInt64> mipRowSizeInBytes;
- mipRowSizeInBytes.setCount(srcDesc.numMipLevels);
- List<UInt32> mipNumRows;
- mipNumRows.setCount(numMipMaps);
-
- // NOTE! This is just the size for one array upload -> not for the whole texture
- UInt64 requiredSize = 0;
- m_device->GetCopyableFootprints(
- &resourceDesc,
- 0,
- srcDesc.numMipLevels,
- 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
- if (initData)
- {
- // 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<void**>(&p));
-
- for (int j = 0; j < numMipMaps; ++j)
- {
- auto srcSubresource = initData[subResourceIndex + j];
-
- const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& layout = layouts[j];
- const D3D12_SUBRESOURCE_FOOTPRINT& footprint = layout.Footprint;
-
- TextureResource::Extents mipSize = calcMipSize(srcDesc.size, j);
- if (gfxIsCompressedFormat(descIn.format))
- {
- mipSize.width = int(D3DUtil::calcAligned(mipSize.width, 4));
- mipSize.height = int(D3DUtil::calcAligned(mipSize.height, 4));
- }
-
- assert(
- footprint.Width == mipSize.width && footprint.Height == mipSize.height &&
- footprint.Depth == mipSize.depth);
-
- auto mipRowSize = mipRowSizeInBytes[j];
-
- const ptrdiff_t dstMipRowPitch = ptrdiff_t(footprint.RowPitch);
- const ptrdiff_t srcMipRowPitch = ptrdiff_t(srcSubresource.strideY);
-
- const ptrdiff_t dstMipLayerPitch = ptrdiff_t(footprint.RowPitch * footprint.Height);
- const ptrdiff_t srcMipLayerPitch = ptrdiff_t(srcSubresource.strideZ);
-
- // Our outer loop will copy the depth layers one at a time.
- //
- const uint8_t* srcLayer = (const uint8_t*)srcSubresource.data;
- uint8_t* dstLayer = p + layouts[j].Offset;
- for (int l = 0; l < mipSize.depth; l++)
- {
- // Our inner loop will copy the rows one at a time.
- //
- const uint8_t* srcRow = srcLayer;
- uint8_t* dstRow = dstLayer;
- int j = gfxIsCompressedFormat(descIn.format)
- ? 4
- : 1; // BC compressed formats are organized into 4x4 blocks
- for (int k = 0; k < mipSize.height; k += j)
- {
- ::memcpy(dstRow, srcRow, (Size)mipRowSize);
-
- srcRow += srcMipRowPitch;
- dstRow += dstMipRowPitch;
- }
-
- srcLayer += srcMipLayerPitch;
- dstLayer += dstMipLayerPitch;
- }
-
- // assert(srcRow == (const uint8_t*)(srcMip.getBuffer() + srcMip.getCount()));
- }
- uploadResource->Unmap(0, nullptr);
-
- auto encodeInfo = encodeResourceCommands();
- 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;
- encodeInfo.d3dCommandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
-
- subResourceIndex++;
- }
-
- // Block - waiting for copy to complete (so can drop upload texture)
- submitResourceCommandsAndWait(encodeInfo);
- }
- }
- {
- auto encodeInfo = encodeResourceCommands();
- {
- D3D12BarrierSubmitter submitter(encodeInfo.d3dCommandList);
- texture->m_resource.transition(
- D3D12_RESOURCE_STATE_COPY_DEST, texture->m_defaultState, submitter);
- }
- submitResourceCommandsAndWait(encodeInfo);
- }
-
- returnComPtr(outResource, texture);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createTextureFromNativeHandle(
- InteropHandle handle, const ITextureResource::Desc& srcDesc, ITextureResource** outResource)
-{
- RefPtr<TextureResourceImpl> texture(new TextureResourceImpl(srcDesc));
-
- if (handle.api == InteropHandleAPI::D3D12)
- {
- texture->m_resource.setResource((ID3D12Resource*)handle.handleValue);
- }
- else
- {
- return SLANG_FAIL;
- }
-
- returnComPtr(outResource, texture);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createBufferResource(
- const IBufferResource::Desc& descIn, const void* initData, IBufferResource** outResource)
-{
- BufferResource::Desc srcDesc = fixupBufferDesc(descIn);
-
- RefPtr<BufferResourceImpl> buffer(new BufferResourceImpl(srcDesc));
-
- D3D12_RESOURCE_DESC bufferDesc;
- initBufferResourceDesc(descIn.sizeInBytes, bufferDesc);
-
- bufferDesc.Flags |= calcResourceFlags(srcDesc.allowedStates);
-
- const D3D12_RESOURCE_STATES initialState = buffer->m_defaultState;
- SLANG_RETURN_ON_FAIL(createBuffer(
- bufferDesc,
- initData,
- srcDesc.sizeInBytes,
- initialState,
- buffer->m_resource,
- descIn.isShared,
- descIn.memoryType));
-
- returnComPtr(outResource, buffer);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createBufferFromNativeHandle(
- InteropHandle handle, const IBufferResource::Desc& srcDesc, IBufferResource** outResource)
-{
- RefPtr<BufferResourceImpl> buffer(new BufferResourceImpl(srcDesc));
-
- if (handle.api == InteropHandleAPI::D3D12)
- {
- buffer->m_resource.setResource((ID3D12Resource*)handle.handleValue);
- }
- else
- {
- return SLANG_FAIL;
- }
-
- returnComPtr(outResource, buffer);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createSamplerState(ISamplerState::Desc const& desc, ISamplerState** 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;
-
- D3D12Descriptor cpuDescriptor;
- samplerHeap->allocate(&cpuDescriptor);
- m_device->CreateSampler(&dxDesc, cpuDescriptor.cpuHandle);
-
- // 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<SamplerStateImpl> samplerImpl = new SamplerStateImpl();
- samplerImpl->m_allocator = samplerHeap;
- samplerImpl->m_descriptor = cpuDescriptor;
- returnComPtr(outSampler, samplerImpl);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createTextureView(
- ITextureResource* texture, IResourceView::Desc const& desc, IResourceView** outView)
-{
- auto resourceImpl = (TextureResourceImpl*)texture;
-
- RefPtr<ResourceViewImpl> viewImpl = new ResourceViewImpl();
- viewImpl->m_resource = resourceImpl;
- viewImpl->m_desc = desc;
- bool isArray = resourceImpl ? resourceImpl->getDesc()->arraySize != 0 : false;
- bool isMultiSample = resourceImpl ? resourceImpl->getDesc()->sampleDesc.numSamples > 1 : false;
- switch (desc.type)
- {
- default:
- return SLANG_FAIL;
-
- case IResourceView::Type::RenderTarget:
- {
- SLANG_RETURN_ON_FAIL(m_rtvAllocator->allocate(&viewImpl->m_descriptor));
- viewImpl->m_allocator = m_rtvAllocator;
- D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
- rtvDesc.Format = D3DUtil::getMapFormat(desc.format);
- isArray = desc.subresourceRange.layerCount > 1;
- switch (desc.renderTarget.shape)
- {
- case IResource::Type::Texture1D:
- rtvDesc.ViewDimension =
- isArray ? D3D12_RTV_DIMENSION_TEXTURE1DARRAY : D3D12_RTV_DIMENSION_TEXTURE1D;
- rtvDesc.Texture1D.MipSlice = desc.subresourceRange.mipLevel;
- break;
- case IResource::Type::Texture2D:
- if (isMultiSample)
- {
- rtvDesc.ViewDimension = isArray ? D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY
- : D3D12_RTV_DIMENSION_TEXTURE2DMS;
- rtvDesc.Texture2DMSArray.ArraySize = desc.subresourceRange.layerCount;
- rtvDesc.Texture2DMSArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
- }
- else
- {
- rtvDesc.ViewDimension = isArray ? D3D12_RTV_DIMENSION_TEXTURE2DARRAY
- : D3D12_RTV_DIMENSION_TEXTURE2D;
- rtvDesc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel;
- rtvDesc.Texture2DArray.PlaneSlice =
- resourceImpl ? D3DUtil::getPlaneSlice(
- D3DUtil::getMapFormat(resourceImpl->getDesc()->format),
- desc.subresourceRange.aspectMask)
- : 0;
- rtvDesc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount;
- rtvDesc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
- }
- break;
- case IResource::Type::Texture3D:
- rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
- rtvDesc.Texture3D.MipSlice = desc.subresourceRange.mipLevel;
- rtvDesc.Texture3D.FirstWSlice = desc.subresourceRange.baseArrayLayer;
- rtvDesc.Texture3D.WSize = (desc.subresourceRange.layerCount == 0) ? -1 : desc.subresourceRange.layerCount;
- break;
- case IResource::Type::Buffer:
- rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_BUFFER;
- break;
- default:
- return SLANG_FAIL;
- }
- m_device->CreateRenderTargetView(
- resourceImpl ? resourceImpl->m_resource.getResource() : nullptr,
- &rtvDesc,
- viewImpl->m_descriptor.cpuHandle);
- }
- break;
-
- case IResourceView::Type::DepthStencil:
- {
- SLANG_RETURN_ON_FAIL(m_dsvAllocator->allocate(&viewImpl->m_descriptor));
- viewImpl->m_allocator = m_dsvAllocator;
- D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
- dsvDesc.Format = D3DUtil::getMapFormat(desc.format);
- isArray = desc.subresourceRange.layerCount > 1;
- switch (desc.renderTarget.shape)
- {
- case IResource::Type::Texture1D:
- dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1D;
- dsvDesc.Texture1D.MipSlice = desc.subresourceRange.mipLevel;
- break;
- case IResource::Type::Texture2D:
- if (isMultiSample)
- {
- dsvDesc.ViewDimension = isArray ? D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY
- : D3D12_DSV_DIMENSION_TEXTURE2DMS;
- dsvDesc.Texture2DMSArray.ArraySize = desc.subresourceRange.layerCount;
- dsvDesc.Texture2DMSArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
- }
- else
- {
- dsvDesc.ViewDimension = isArray ? D3D12_DSV_DIMENSION_TEXTURE2DARRAY
- : D3D12_DSV_DIMENSION_TEXTURE2D;
- dsvDesc.Texture2DArray.MipSlice = desc.subresourceRange.mipLevel;
- dsvDesc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount;
- dsvDesc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
- }
- break;
- default:
- return SLANG_FAIL;
- }
- m_device->CreateDepthStencilView(
- resourceImpl ? resourceImpl->m_resource.getResource() : nullptr,
- &dsvDesc,
- viewImpl->m_descriptor.cpuHandle);
- }
- break;
-
- case IResourceView::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_cpuViewHeap->allocate(&viewImpl->m_descriptor));
- viewImpl->m_allocator = m_cpuViewHeap;
- D3D12_UNORDERED_ACCESS_VIEW_DESC d3d12desc = {};
- auto& resourceDesc = *resourceImpl->getDesc();
- d3d12desc.Format = gfxIsTypelessFormat(texture->getDesc()->format)
- ? D3DUtil::getMapFormat(desc.format)
- : D3DUtil::getMapFormat(texture->getDesc()->format);
- switch (resourceImpl->getDesc()->type)
- {
- case IResource::Type::Texture1D:
- d3d12desc.ViewDimension = resourceDesc.arraySize == 0
- ? D3D12_UAV_DIMENSION_TEXTURE1D
- : D3D12_UAV_DIMENSION_TEXTURE1DARRAY;
- d3d12desc.Texture1D.MipSlice = desc.subresourceRange.mipLevel;
- d3d12desc.Texture1DArray.ArraySize = desc.subresourceRange.layerCount == 0
- ? resourceDesc.arraySize
- : desc.subresourceRange.layerCount;
- d3d12desc.Texture1DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
-
- break;
- case IResource::Type::Texture2D:
- d3d12desc.ViewDimension = resourceDesc.arraySize == 0
- ? D3D12_UAV_DIMENSION_TEXTURE2D
- : D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
- d3d12desc.Texture2D.MipSlice = desc.subresourceRange.mipLevel;
- d3d12desc.Texture2D.PlaneSlice =
- D3DUtil::getPlaneSlice(d3d12desc.Format, desc.subresourceRange.aspectMask);
- d3d12desc.Texture2DArray.ArraySize = desc.subresourceRange.layerCount == 0
- ? resourceDesc.arraySize
- : desc.subresourceRange.layerCount;
- d3d12desc.Texture2DArray.FirstArraySlice = desc.subresourceRange.baseArrayLayer;
- break;
- case IResource::Type::Texture3D:
- d3d12desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
- d3d12desc.Texture3D.MipSlice = desc.subresourceRange.mipLevel;
- d3d12desc.Texture3D.FirstWSlice = desc.subresourceRange.baseArrayLayer;
- d3d12desc.Texture3D.WSize = resourceDesc.size.depth;
- break;
- default:
- return SLANG_FAIL;
- }
- m_device->CreateUnorderedAccessView(
- resourceImpl->m_resource, nullptr, &d3d12desc, viewImpl->m_descriptor.cpuHandle);
- }
- break;
-
- case IResourceView::Type::ShaderResource:
- {
- SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&viewImpl->m_descriptor));
- viewImpl->m_allocator = m_cpuViewHeap;
-
- // 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 = desc.format == Format::Unknown
- ? resourceDesc.Format
- : D3DUtil::getMapFormat(desc.format);
-
- D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
- initSrvDesc(
- resourceImpl->getType(),
- *resourceImpl->getDesc(),
- resourceDesc,
- pixelFormat,
- desc.subresourceRange,
- srvDesc);
-
- m_device->CreateShaderResourceView(
- resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle);
- }
- break;
- }
-
- returnComPtr(outView, viewImpl);
- return SLANG_OK;
-}
-
-Result DeviceImpl::getFormatSupportedResourceStates(Format format, ResourceStateSet* outStates)
-{
- D3D12_FEATURE_DATA_FORMAT_SUPPORT support;
- support.Format = D3DUtil::getMapFormat(format);
- SLANG_RETURN_ON_FAIL(
- m_device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support)));
-
- ResourceStateSet allowedStates;
-
- auto dxgi1 = support.Support1;
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_BUFFER)
- allowedStates.add(ResourceState::ConstantBuffer);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER)
- allowedStates.add(ResourceState::VertexBuffer);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_IA_INDEX_BUFFER)
- allowedStates.add(ResourceState::IndexBuffer);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_SO_BUFFER)
- allowedStates.add(ResourceState::StreamOutput);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURE1D)
- allowedStates.add(ResourceState::ShaderResource);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D)
- allowedStates.add(ResourceState::ShaderResource);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURE3D)
- allowedStates.add(ResourceState::ShaderResource);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_TEXTURECUBE)
- allowedStates.add(ResourceState::ShaderResource);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_LOAD)
- allowedStates.add(ResourceState::ShaderResource);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE)
- allowedStates.add(ResourceState::ShaderResource);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE_COMPARISON)
- allowedStates.add(ResourceState::ShaderResource);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_GATHER)
- allowedStates.add(ResourceState::ShaderResource);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_SHADER_GATHER_COMPARISON)
- allowedStates.add(ResourceState::ShaderResource);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET)
- allowedStates.add(ResourceState::RenderTarget);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL)
- allowedStates.add(ResourceState::DepthWrite);
- if (dxgi1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW)
- allowedStates.add(ResourceState::UnorderedAccess);
-
- *outStates = allowedStates;
- return SLANG_OK;
-}
-
-Result DeviceImpl::createBufferView(
- IBufferResource* buffer,
- IBufferResource* counterBuffer,
- IResourceView::Desc const& desc,
- IResourceView** outView)
-{
- auto resourceImpl = (BufferResourceImpl*)buffer;
- auto resourceDesc = *resourceImpl->getDesc();
-
- RefPtr<ResourceViewImpl> viewImpl = new ResourceViewImpl();
- viewImpl->m_resource = resourceImpl;
- viewImpl->m_desc = desc;
-
- switch (desc.type)
- {
- default:
- return SLANG_FAIL;
-
- case IResourceView::Type::UnorderedAccess:
- {
- D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
- uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
- uavDesc.Format = D3DUtil::getMapFormat(desc.format);
- uavDesc.Buffer.FirstElement = desc.bufferRange.firstElement;
- uint64_t viewSize = 0;
- if (desc.bufferElementSize)
- {
- uavDesc.Buffer.StructureByteStride = (UINT)desc.bufferElementSize;
- uavDesc.Buffer.NumElements =
- desc.bufferRange.elementCount == 0
- ? UINT(resourceDesc.sizeInBytes / desc.bufferElementSize)
- : (UINT)desc.bufferRange.elementCount;
- viewSize = (uint64_t)desc.bufferElementSize * uavDesc.Buffer.NumElements;
- }
- else if (desc.format == Format::Unknown)
- {
- uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
- uavDesc.Buffer.NumElements = desc.bufferRange.elementCount == 0
- ? UINT(resourceDesc.sizeInBytes / 4)
- : UINT(desc.bufferRange.elementCount / 4);
- uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW;
- viewSize = 4ull * uavDesc.Buffer.NumElements;
- }
- else
- {
- FormatInfo sizeInfo;
- gfxGetFormatInfo(desc.format, &sizeInfo);
- assert(sizeInfo.pixelsPerBlock == 1);
- uavDesc.Buffer.NumElements =
- desc.bufferRange.elementCount == 0
- ? UINT(resourceDesc.sizeInBytes / sizeInfo.blockSizeInBytes)
- : (UINT)desc.bufferRange.elementCount;
- viewSize = (uint64_t)uavDesc.Buffer.NumElements * sizeInfo.blockSizeInBytes;
- }
-
- if (viewSize >= (1ull << 32) - 8)
- {
- // D3D12 does not support view descriptors that has size near 4GB.
- // We will not create actual SRV/UAVs for such large buffers.
- // However, a buffer this large can still be bound as root parameter.
- // So instead of failing, we quietly ignore descriptor creation.
- viewImpl->m_descriptor.cpuHandle.ptr = 0;
- }
- else
- {
- auto counterResourceImpl = static_cast<BufferResourceImpl*>(counterBuffer);
- SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&viewImpl->m_descriptor));
- viewImpl->m_allocator = m_cpuViewHeap;
- m_device->CreateUnorderedAccessView(
- resourceImpl->m_resource,
- counterResourceImpl ? counterResourceImpl->m_resource.getResource() : nullptr,
- &uavDesc,
- viewImpl->m_descriptor.cpuHandle);
- }
- }
- break;
-
- case IResourceView::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 = desc.bufferRange.firstElement;
- srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
- uint64_t viewSize = 0;
- if (desc.bufferElementSize)
- {
- srvDesc.Buffer.StructureByteStride = (UINT)desc.bufferElementSize;
- srvDesc.Buffer.NumElements =
- desc.bufferRange.elementCount == 0
- ? UINT(resourceDesc.sizeInBytes / desc.bufferElementSize)
- : (UINT)desc.bufferRange.elementCount;
- viewSize = (uint64_t)desc.bufferElementSize * srvDesc.Buffer.NumElements;
- }
- else if (desc.format == Format::Unknown)
- {
- srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
- srvDesc.Buffer.NumElements = desc.bufferRange.elementCount == 0
- ? UINT(resourceDesc.sizeInBytes / 4)
- : UINT(desc.bufferRange.elementCount / 4);
- srvDesc.Buffer.Flags |= D3D12_BUFFER_SRV_FLAG_RAW;
- viewSize = 4ull * srvDesc.Buffer.NumElements;
- }
- else
- {
- FormatInfo sizeInfo;
- gfxGetFormatInfo(desc.format, &sizeInfo);
- assert(sizeInfo.pixelsPerBlock == 1);
- srvDesc.Buffer.NumElements =
- desc.bufferRange.elementCount == 0
- ? UINT(resourceDesc.sizeInBytes / sizeInfo.blockSizeInBytes)
- : (UINT)desc.bufferRange.elementCount;
- viewSize = (uint64_t)srvDesc.Buffer.NumElements * sizeInfo.blockSizeInBytes;
- }
- if (viewSize >= (1ull << 32) - 8)
- {
- // D3D12 does not support view descriptors that has size near 4GB.
- // We will not create actual SRV/UAVs for such large buffers.
- // However, a buffer this large can still be bound as root parameter.
- // So instead of failing, we quietly ignore descriptor creation.
- viewImpl->m_descriptor.cpuHandle.ptr = 0;
- }
- else
- {
- SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&viewImpl->m_descriptor));
- viewImpl->m_allocator = m_cpuViewHeap;
- m_device->CreateShaderResourceView(
- resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle);
- }
- }
- break;
- }
-
- returnComPtr(outView, viewImpl);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createFramebuffer(IFramebuffer::Desc const& desc, IFramebuffer** outFb)
-{
- RefPtr<FramebufferImpl> framebuffer = new FramebufferImpl();
- framebuffer->renderTargetViews.setCount(desc.renderTargetCount);
- framebuffer->renderTargetDescriptors.setCount(desc.renderTargetCount);
- framebuffer->renderTargetClearValues.setCount(desc.renderTargetCount);
- for (GfxIndex i = 0; i < desc.renderTargetCount; i++)
- {
- framebuffer->renderTargetViews[i] =
- static_cast<ResourceViewImpl*>(desc.renderTargetViews[i]);
- framebuffer->renderTargetDescriptors[i] =
- framebuffer->renderTargetViews[i]->m_descriptor.cpuHandle;
- if (static_cast<ResourceViewImpl*>(desc.renderTargetViews[i])->m_resource.Ptr())
- {
- auto clearValue =
- static_cast<TextureResourceImpl*>(
- static_cast<ResourceViewImpl*>(desc.renderTargetViews[i])->m_resource.Ptr())
- ->getDesc()
- ->optimalClearValue.color;
- memcpy(&framebuffer->renderTargetClearValues[i], &clearValue, sizeof(ColorClearValue));
- }
- else
- {
- memset(&framebuffer->renderTargetClearValues[i], 0, sizeof(ColorClearValue));
- }
- }
- framebuffer->depthStencilView = static_cast<ResourceViewImpl*>(desc.depthStencilView);
- if (desc.depthStencilView)
- {
- framebuffer->depthStencilClearValue =
- static_cast<TextureResourceImpl*>(
- static_cast<ResourceViewImpl*>(desc.depthStencilView)->m_resource.Ptr())
- ->getDesc()
- ->optimalClearValue.depthStencil;
- framebuffer->depthStencilDescriptor =
- static_cast<ResourceViewImpl*>(desc.depthStencilView)->m_descriptor.cpuHandle;
- }
- else
- {
- framebuffer->depthStencilDescriptor.ptr = 0;
- }
- returnComPtr(outFb, framebuffer);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createFramebufferLayout(
- IFramebufferLayout::Desc const& desc, IFramebufferLayout** outLayout)
-{
- RefPtr<FramebufferLayoutImpl> layout = new FramebufferLayoutImpl();
- layout->m_renderTargets.setCount(desc.renderTargetCount);
- for (GfxIndex i = 0; i < desc.renderTargetCount; i++)
- {
- layout->m_renderTargets[i] = desc.renderTargets[i];
- }
-
- if (desc.depthStencil)
- {
- layout->m_hasDepthStencil = true;
- layout->m_depthStencil = *desc.depthStencil;
- }
- else
- {
- layout->m_hasDepthStencil = false;
- }
- returnComPtr(outLayout, layout);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createRenderPassLayout(
- const IRenderPassLayout::Desc& desc, IRenderPassLayout** outRenderPassLayout)
-{
- RefPtr<RenderPassLayoutImpl> result = new RenderPassLayoutImpl();
- result->init(desc);
- returnComPtr(outRenderPassLayout, result);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createInputLayout(IInputLayout::Desc const& desc, IInputLayout** outLayout)
-{
- RefPtr<InputLayoutImpl> layout(new InputLayoutImpl);
-
- // Work out a buffer size to hold all text
- Size textSize = 0;
- auto inputElementCount = desc.inputElementCount;
- auto inputElements = desc.inputElements;
- auto vertexStreamCount = desc.vertexStreamCount;
- auto vertexStreams = desc.vertexStreams;
- 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<D3D12_INPUT_ELEMENT_DESC>& elements = layout->m_elements;
- elements.setCount(inputElementCount);
-
- for (Int i = 0; i < inputElementCount; ++i)
- {
- const InputElementDesc& srcEle = inputElements[i];
- const auto& srcStream = vertexStreams[srcEle.bufferSlotIndex];
- 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 = (UINT)srcEle.bufferSlotIndex;
- dstEle.AlignedByteOffset = (UINT)srcEle.offset;
- dstEle.InputSlotClass = D3DUtil::getInputSlotClass(srcStream.slotClass);
- dstEle.InstanceDataStepRate = (UINT)srcStream.instanceDataStepRate;
- }
-
- auto& vertexStreamStrides = layout->m_vertexStreamStrides;
- vertexStreamStrides.setCount(vertexStreamCount);
- for (GfxIndex i = 0; i < vertexStreamCount; ++i)
- {
- vertexStreamStrides[i] = (UINT)vertexStreams[i].stride;
- }
-
- returnComPtr(outLayout, layout);
- return SLANG_OK;
-}
-
-const gfx::DeviceInfo& DeviceImpl::getDeviceInfo() const { return m_info; }
-
-Result DeviceImpl::readBufferResource(
- IBufferResource* bufferIn, Offset offset, Size size, ISlangBlob** outBlob)
-{
-
- BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(bufferIn);
-
- const Size bufferSize = buffer->getDesc()->sizeInBytes;
-
- // This will be slow!!! - it blocks CPU on GPU completion
- D3D12Resource& resource = buffer->m_resource;
-
- D3D12Resource stageBuf;
- if (buffer->getDesc()->memoryType != MemoryType::ReadBack)
- {
- auto encodeInfo = encodeResourceCommands();
-
- // 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(size, stagingDesc);
-
- SLANG_RETURN_ON_FAIL(stageBuf.initCommitted(
- m_device,
- heapProps,
- D3D12_HEAP_FLAG_NONE,
- stagingDesc,
- D3D12_RESOURCE_STATE_COPY_DEST,
- nullptr));
-
- // Do the copy
- encodeInfo.d3dCommandList->CopyBufferRegion(stageBuf, 0, resource, offset, size);
-
- // Wait until complete
- submitResourceCommandsAndWait(encodeInfo);
- }
-
- D3D12Resource& stageBufRef =
- buffer->getDesc()->memoryType != MemoryType::ReadBack ? stageBuf : resource;
-
- // Map and copy
- RefPtr<ListBlob> blob = new ListBlob();
- {
- UINT8* data;
- D3D12_RANGE readRange = {0, size};
-
- SLANG_RETURN_ON_FAIL(
- stageBufRef.getResource()->Map(0, &readRange, reinterpret_cast<void**>(&data)));
-
- // Copy to memory buffer
- blob->m_data.setCount(size);
- ::memcpy(blob->m_data.getBuffer(), data, size);
-
- stageBufRef.getResource()->Unmap(0, nullptr);
- }
- returnComPtr(outBlob, blob);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createProgram(
- const IShaderProgram::Desc& desc, IShaderProgram** outProgram, ISlangBlob** outDiagnosticBlob)
-{
- RefPtr<ShaderProgramImpl> shaderProgram = new ShaderProgramImpl();
- shaderProgram->init(desc);
- ComPtr<ID3DBlob> d3dDiagnosticBlob;
- auto rootShaderLayoutResult = RootShaderObjectLayoutImpl::create(
- this,
- shaderProgram->linkedProgram,
- shaderProgram->linkedProgram->getLayout(),
- shaderProgram->m_rootObjectLayout.writeRef(),
- d3dDiagnosticBlob.writeRef());
- if (!SLANG_SUCCEEDED(rootShaderLayoutResult))
- {
- if (outDiagnosticBlob && d3dDiagnosticBlob)
- {
- RefPtr<StringBlob> diagnosticBlob =
- new StringBlob(String((const char*)d3dDiagnosticBlob->GetBufferPointer()));
- returnComPtr(outDiagnosticBlob, diagnosticBlob);
- }
- return rootShaderLayoutResult;
- }
- returnComPtr(outProgram, shaderProgram);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createShaderObjectLayout(
- slang::TypeLayoutReflection* typeLayout, ShaderObjectLayoutBase** outLayout)
-{
- RefPtr<ShaderObjectLayoutImpl> layout;
- SLANG_RETURN_ON_FAIL(
- ShaderObjectLayoutImpl::createForElementType(this, typeLayout, layout.writeRef()));
- returnRefPtrMove(outLayout, layout);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject)
-{
- RefPtr<ShaderObjectImpl> shaderObject;
- SLANG_RETURN_ON_FAIL(ShaderObjectImpl::create(
- this, reinterpret_cast<ShaderObjectLayoutImpl*>(layout), shaderObject.writeRef()));
- returnComPtr(outObject, shaderObject);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createMutableShaderObject(
- ShaderObjectLayoutBase* layout, IShaderObject** outObject)
-{
- auto result = createShaderObject(layout, outObject);
- SLANG_RETURN_ON_FAIL(result);
- static_cast<ShaderObjectImpl*>(*outObject)->m_isMutable = true;
- return result;
-}
-
-Result DeviceImpl::createMutableRootShaderObject(IShaderProgram* program, IShaderObject** outObject)
-{
- RefPtr<MutableRootShaderObjectImpl> result = new MutableRootShaderObjectImpl();
- result->init(this);
- auto programImpl = static_cast<ShaderProgramImpl*>(program);
- result->resetImpl(
- this, programImpl->m_rootObjectLayout, m_cpuViewHeap.Ptr(), m_cpuSamplerHeap.Ptr(), true);
- returnComPtr(outObject, result);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createShaderTable(const IShaderTable::Desc& desc, IShaderTable** outShaderTable)
-{
- RefPtr<ShaderTableImpl> result = new ShaderTableImpl();
- result->m_device = this;
- result->init(desc);
- returnComPtr(outShaderTable, result);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createGraphicsPipelineState(
- const GraphicsPipelineStateDesc& desc, IPipelineState** outState)
-{
- RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(this);
- pipelineStateImpl->init(desc);
- returnComPtr(outState, pipelineStateImpl);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createComputePipelineState(
- const ComputePipelineStateDesc& desc, IPipelineState** outState)
-{
- RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(this);
- pipelineStateImpl->init(desc);
- returnComPtr(outState, pipelineStateImpl);
- return SLANG_OK;
-}
-
-DeviceImpl::ResourceCommandRecordInfo DeviceImpl::encodeResourceCommands()
-{
- ResourceCommandRecordInfo info;
- m_resourceCommandTransientHeap->createCommandBuffer(info.commandBuffer.writeRef());
- info.d3dCommandList = static_cast<CommandBufferImpl*>(info.commandBuffer.get())->m_cmdList;
- return info;
-}
-
-void DeviceImpl::submitResourceCommandsAndWait(const DeviceImpl::ResourceCommandRecordInfo& info)
-{
- info.commandBuffer->close();
- m_resourceCommandQueue->executeCommandBuffer(info.commandBuffer);
- m_resourceCommandTransientHeap->finish();
- m_resourceCommandTransientHeap->synchronizeAndReset();
-}
-
-Result DeviceImpl::createQueryPool(const IQueryPool::Desc& desc, IQueryPool** outState)
-{
- switch (desc.type)
- {
- case QueryType::AccelerationStructureCompactedSize:
- case QueryType::AccelerationStructureSerializedSize:
- case QueryType::AccelerationStructureCurrentSize:
- {
- RefPtr<PlainBufferProxyQueryPoolImpl> queryPoolImpl =
- new PlainBufferProxyQueryPoolImpl();
- uint32_t stride = 8;
- if (desc.type == QueryType::AccelerationStructureSerializedSize)
- stride = 16;
- SLANG_RETURN_ON_FAIL(queryPoolImpl->init(desc, this, stride));
- returnComPtr(outState, queryPoolImpl);
- return SLANG_OK;
- }
- default:
- {
- RefPtr<QueryPoolImpl> queryPoolImpl = new QueryPoolImpl();
- SLANG_RETURN_ON_FAIL(queryPoolImpl->init(desc, this));
- returnComPtr(outState, queryPoolImpl);
- return SLANG_OK;
- }
- }
-}
-
-Result DeviceImpl::createFence(const IFence::Desc& desc, IFence** outFence)
-{
- RefPtr<FenceImpl> fence = new FenceImpl();
- SLANG_RETURN_ON_FAIL(fence->init(this, desc));
- returnComPtr(outFence, fence);
- return SLANG_OK;
-}
-
-Result DeviceImpl::waitForFences(
- GfxCount fenceCount, IFence** fences, uint64_t* fenceValues, bool waitForAll, uint64_t timeout)
-{
- ShortList<HANDLE> waitHandles;
- for (GfxCount i = 0; i < fenceCount; ++i)
- {
- auto fenceImpl = static_cast<FenceImpl*>(fences[i]);
- waitHandles.add(fenceImpl->getWaitEvent());
- SLANG_RETURN_ON_FAIL(
- fenceImpl->m_fence->SetEventOnCompletion(fenceValues[i], fenceImpl->getWaitEvent()));
- }
- auto result = WaitForMultipleObjects(
- fenceCount,
- waitHandles.getArrayView().getBuffer(),
- waitForAll ? TRUE : FALSE,
- timeout == kTimeoutInfinite ? INFINITE : (DWORD)(timeout / 1000000));
- if (result == WAIT_TIMEOUT)
- return SLANG_E_TIME_OUT;
- return result == WAIT_FAILED ? SLANG_FAIL : SLANG_OK;
-}
-
-Result DeviceImpl::getAccelerationStructurePrebuildInfo(
- const IAccelerationStructure::BuildInputs& buildInputs,
- IAccelerationStructure::PrebuildInfo* outPrebuildInfo)
-{
- if (!m_device5)
- return SLANG_E_NOT_AVAILABLE;
-
- D3DAccelerationStructureInputsBuilder inputsBuilder;
- SLANG_RETURN_ON_FAIL(inputsBuilder.build(buildInputs, getDebugCallback()));
-
- D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO prebuildInfo;
- m_device5->GetRaytracingAccelerationStructurePrebuildInfo(&inputsBuilder.desc, &prebuildInfo);
-
- outPrebuildInfo->resultDataMaxSize = (Size)prebuildInfo.ResultDataMaxSizeInBytes;
- outPrebuildInfo->scratchDataSize = (Size)prebuildInfo.ScratchDataSizeInBytes;
- outPrebuildInfo->updateScratchDataSize = (Size)prebuildInfo.UpdateScratchDataSizeInBytes;
- return SLANG_OK;
-}
-
-Result DeviceImpl::createAccelerationStructure(
- const IAccelerationStructure::CreateDesc& desc, IAccelerationStructure** outAS)
-{
-#if SLANG_GFX_HAS_DXR_SUPPORT
- RefPtr<AccelerationStructureImpl> result = new AccelerationStructureImpl();
- result->m_device5 = m_device5;
- result->m_buffer = static_cast<BufferResourceImpl*>(desc.buffer);
- result->m_size = desc.size;
- result->m_offset = desc.offset;
- result->m_allocator = m_cpuViewHeap;
- result->m_desc.type = IResourceView::Type::AccelerationStructure;
- SLANG_RETURN_ON_FAIL(m_cpuViewHeap->allocate(&result->m_descriptor));
- D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
- srvDesc.Format = DXGI_FORMAT_UNKNOWN;
- srvDesc.ViewDimension = D3D12_SRV_DIMENSION_RAYTRACING_ACCELERATION_STRUCTURE;
- srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
- srvDesc.RaytracingAccelerationStructure.Location =
- result->m_buffer->getDeviceAddress() + desc.offset;
- m_device->CreateShaderResourceView(nullptr, &srvDesc, result->m_descriptor.cpuHandle);
- returnComPtr(outAS, result);
- return SLANG_OK;
-#else
- *outAS = nullptr;
- return SLANG_FAIL;
-#endif
-}
-
-Result DeviceImpl::createRayTracingPipelineState(
- const RayTracingPipelineStateDesc& inDesc, IPipelineState** outState)
-{
- if (!m_device5)
- {
- return SLANG_E_NOT_AVAILABLE;
- }
-
- RefPtr<RayTracingPipelineStateImpl> pipelineStateImpl = new RayTracingPipelineStateImpl(this);
- pipelineStateImpl->init(inDesc);
- returnComPtr(outState, pipelineStateImpl);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createTransientResourceHeapImpl(
- ITransientResourceHeap::Flags::Enum flags,
- Size constantBufferSize,
- uint32_t viewDescriptors,
- uint32_t samplerDescriptors,
- TransientResourceHeapImpl** outHeap)
-{
- RefPtr<TransientResourceHeapImpl> result = new TransientResourceHeapImpl();
- ITransientResourceHeap::Desc desc = {};
- desc.flags = flags;
- desc.samplerDescriptorCount = samplerDescriptors;
- desc.constantBufferSize = constantBufferSize;
- desc.constantBufferDescriptorCount = viewDescriptors;
- desc.accelerationStructureDescriptorCount = viewDescriptors;
- desc.srvDescriptorCount = viewDescriptors;
- desc.uavDescriptorCount = viewDescriptors;
- SLANG_RETURN_ON_FAIL(result->init(desc, this, viewDescriptors, samplerDescriptors));
- returnRefPtrMove(outHeap, result);
- return SLANG_OK;
-}
-
-Result DeviceImpl::createCommandQueueImpl(CommandQueueImpl** outQueue)
-{
- int queueIndex = m_queueIndexAllocator.alloc(1);
- // If we run out of queue index space, then the user is requesting too many queues.
- if (queueIndex == -1)
- return SLANG_FAIL;
-
- RefPtr<CommandQueueImpl> queue = new CommandQueueImpl();
- SLANG_RETURN_ON_FAIL(queue->init(this, (uint32_t)queueIndex));
- returnRefPtrMove(outQueue, queue);
- return SLANG_OK;
-}
-
-PROC DeviceImpl::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;
-}
-
-DeviceImpl::~DeviceImpl() { m_shaderObjectLayoutCache = decltype(m_shaderObjectLayoutCache)(); }
-
-struct GraphicsSubmitter : public Submitter
-{
- virtual void setRootConstantBufferView(
- int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override
- {
- m_commandList->SetGraphicsRootConstantBufferView(index, gpuBufferLocation);
- }
- virtual void setRootUAV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override
- {
- m_commandList->SetGraphicsRootUnorderedAccessView(index, gpuBufferLocation);
- }
- virtual void setRootSRV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override
- {
- m_commandList->SetGraphicsRootShaderResourceView(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);
- }
- void setRootConstants(
- Index rootParamIndex,
- Index dstOffsetIn32BitValues,
- Index countOf32BitValues,
- void const* srcData) override
- {
- m_commandList->SetGraphicsRoot32BitConstants(
- UINT(rootParamIndex), UINT(countOf32BitValues), srcData, UINT(dstOffsetIn32BitValues));
- }
- virtual void setPipelineState(PipelineStateBase* pipeline) override
- {
- auto pipelineImpl = static_cast<PipelineStateImpl*>(pipeline);
- m_commandList->SetPipelineState(pipelineImpl->m_pipelineState.get());
- }
-
- 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 setRootUAV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override
- {
- m_commandList->SetComputeRootUnorderedAccessView(index, gpuBufferLocation);
- }
- virtual void setRootSRV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override
- {
- m_commandList->SetComputeRootShaderResourceView(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);
- }
- void setRootConstants(
- Index rootParamIndex,
- Index dstOffsetIn32BitValues,
- Index countOf32BitValues,
- void const* srcData) override
- {
- m_commandList->SetComputeRoot32BitConstants(
- UINT(rootParamIndex), UINT(countOf32BitValues), srcData, UINT(dstOffsetIn32BitValues));
- }
- virtual void setPipelineState(PipelineStateBase* pipeline) override
- {
- auto pipelineImpl = static_cast<PipelineStateImpl*>(pipeline);
- m_commandList->SetPipelineState(pipelineImpl->m_pipelineState.get());
- }
- ComputeSubmitter(ID3D12GraphicsCommandList* commandList)
- : m_commandList(commandList)
- {}
-
- ID3D12GraphicsCommandList* m_commandList;
-};
-
-BufferResourceImpl::BufferResourceImpl(const Desc& desc)
- : Parent(desc)
- , m_defaultState(D3DUtil::getResourceState(desc.defaultState))
-{}
-
-BufferResourceImpl::~BufferResourceImpl()
-{
- if (sharedHandle.handleValue != 0)
- {
- CloseHandle((HANDLE)sharedHandle.handleValue);
- }
-}
-
-DeviceAddress BufferResourceImpl::getDeviceAddress()
-{
- return (DeviceAddress)m_resource.getResource()->GetGPUVirtualAddress();
-}
-
-Result BufferResourceImpl::getNativeResourceHandle(InteropHandle* outHandle)
-{
- outHandle->handleValue = (uint64_t)m_resource.getResource();
- outHandle->api = InteropHandleAPI::D3D12;
- return SLANG_OK;
-}
-
-Result BufferResourceImpl::getSharedHandle(InteropHandle* outHandle)
-{
- // Check if a shared handle already exists for this resource.
- if (sharedHandle.handleValue != 0)
- {
- *outHandle = sharedHandle;
- return SLANG_OK;
- }
-
- // If a shared handle doesn't exist, create one and store it.
- ComPtr<ID3D12Device> pDevice;
- auto pResource = m_resource.getResource();
- pResource->GetDevice(IID_PPV_ARGS(pDevice.writeRef()));
- SLANG_RETURN_ON_FAIL(pDevice->CreateSharedHandle(
- pResource, NULL, GENERIC_ALL, nullptr, (HANDLE*)&outHandle->handleValue));
- outHandle->api = InteropHandleAPI::D3D12;
- sharedHandle = *outHandle;
- return SLANG_OK;
-}
-
-Result BufferResourceImpl::map(MemoryRange* rangeToRead, void** outPointer)
-{
- D3D12_RANGE range = {};
- if (rangeToRead)
- {
- range.Begin = (SIZE_T)rangeToRead->offset;
- range.End = (SIZE_T)(rangeToRead->offset + rangeToRead->size);
- }
- SLANG_RETURN_ON_FAIL(
- m_resource.getResource()->Map(0, rangeToRead ? &range : nullptr, outPointer));
- return SLANG_OK;
-}
-
-Result BufferResourceImpl::unmap(MemoryRange* writtenRange)
-{
- D3D12_RANGE range = {};
- if (writtenRange)
- {
- range.Begin = (SIZE_T)writtenRange->offset;
- range.End = (SIZE_T)(writtenRange->offset + writtenRange->size);
- }
- m_resource.getResource()->Unmap(0, writtenRange ? &range : nullptr);
- return SLANG_OK;
-}
-
-Result BufferResourceImpl::setDebugName(const char* name)
-{
- Parent::setDebugName(name);
- m_resource.setDebugName(name);
- return SLANG_OK;
-}
-
-TextureResourceImpl::TextureResourceImpl(const Desc& desc)
- : Parent(desc)
- , m_defaultState(D3DUtil::getResourceState(desc.defaultState))
-{}
-
-TextureResourceImpl::~TextureResourceImpl()
-{
- if (sharedHandle.handleValue != 0)
- {
- CloseHandle((HANDLE)sharedHandle.handleValue);
- }
-}
-
-Result TextureResourceImpl::getNativeResourceHandle(InteropHandle* outHandle)
-{
- outHandle->handleValue = (uint64_t)m_resource.getResource();
- outHandle->api = InteropHandleAPI::D3D12;
- return SLANG_OK;
-}
-
-Result TextureResourceImpl::getSharedHandle(InteropHandle* outHandle)
-{
- // Check if a shared handle already exists for this resource.
- if (sharedHandle.handleValue != 0)
- {
- *outHandle = sharedHandle;
- return SLANG_OK;
- }
-
- // If a shared handle doesn't exist, create one and store it.
- ComPtr<ID3D12Device> pDevice;
- auto pResource = m_resource.getResource();
- pResource->GetDevice(IID_PPV_ARGS(pDevice.writeRef()));
- SLANG_RETURN_ON_FAIL(pDevice->CreateSharedHandle(
- pResource, NULL, GENERIC_ALL, nullptr, (HANDLE*)&outHandle->handleValue));
- outHandle->api = InteropHandleAPI::D3D12;
- return SLANG_OK;
-}
-
-Result TextureResourceImpl::setDebugName(const char* name)
-{
- Parent::setDebugName(name);
- m_resource.setDebugName(name);
- return SLANG_OK;
-}
-
-SamplerStateImpl::~SamplerStateImpl() { m_allocator->free(m_descriptor); }
-
-Result SamplerStateImpl::getNativeHandle(InteropHandle* outHandle)
-{
- outHandle->api = InteropHandleAPI::D3D12CpuDescriptorHandle;
- outHandle->handleValue = m_descriptor.cpuHandle.ptr;
- return SLANG_OK;
-}
-
-#if SLANG_GFX_HAS_DXR_SUPPORT
-
-DeviceAddress AccelerationStructureImpl::getDeviceAddress()
-{
- return m_buffer->getDeviceAddress() + m_offset;
-}
-
-Result AccelerationStructureImpl::getNativeHandle(InteropHandle* outHandle)
-{
- outHandle->api = InteropHandleAPI::DeviceAddress;
- outHandle->handleValue = getDeviceAddress();
- return SLANG_OK;
-}
-
-#endif // SLANG_GFX_HAS_DXR_SUPPORT
-
-Result TransientResourceHeapImpl::synchronize()
-{
- WaitForMultipleObjects(
- (DWORD)m_waitHandles.getCount(), m_waitHandles.getArrayView().getBuffer(), TRUE, INFINITE);
- m_waitHandles.clear();
- return SLANG_OK;
-}
-
-Result TransientResourceHeapImpl::synchronizeAndReset()
-{
- synchronize();
-
- m_currentViewHeapIndex = -1;
- m_currentSamplerHeapIndex = -1;
- allocateNewViewDescriptorHeap(m_device);
- allocateNewSamplerDescriptorHeap(m_device);
- m_stagingCpuSamplerHeap.freeAll();
- m_stagingCpuViewHeap.freeAll();
- m_commandListAllocId = 0;
- SLANG_RETURN_ON_FAIL(m_commandAllocator->Reset());
- Super::reset();
- return SLANG_OK;
-}
-
-Result TransientResourceHeapImpl::finish()
-{
- for (auto& waitInfo : m_waitInfos)
- {
- if (waitInfo.waitValue == 0)
- continue;
- if (waitInfo.fence)
- {
- waitInfo.queue->Signal(waitInfo.fence, waitInfo.waitValue);
- waitInfo.fence->SetEventOnCompletion(waitInfo.waitValue, waitInfo.fenceEvent);
- m_waitHandles.add(waitInfo.fenceEvent);
- }
- }
- return SLANG_OK;
-}
-
-TransientResourceHeapImpl::QueueWaitInfo& TransientResourceHeapImpl::getQueueWaitInfo(
- uint32_t queueIndex)
-{
- if (queueIndex < (uint32_t)m_waitInfos.getCount())
- {
- return m_waitInfos[queueIndex];
- }
- auto oldCount = m_waitInfos.getCount();
- m_waitInfos.setCount(queueIndex + 1);
- for (auto i = oldCount; i < m_waitInfos.getCount(); i++)
- {
- m_waitInfos[i].waitValue = 0;
- m_waitInfos[i].fenceEvent = CreateEventEx(nullptr, false, 0, EVENT_ALL_ACCESS);
- }
- return m_waitInfos[queueIndex];
-}
-
-D3D12DescriptorHeap& TransientResourceHeapImpl::getCurrentViewHeap()
-{
- return m_viewHeaps[m_currentViewHeapIndex];
-}
-
-D3D12DescriptorHeap& TransientResourceHeapImpl::getCurrentSamplerHeap()
-{
- return m_samplerHeaps[m_currentSamplerHeapIndex];
-}
-
-Result TransientResourceHeapImpl::queryInterface(SlangUUID const& uuid, void** outObject)
-{
- if (uuid == GfxGUID::IID_ID3D12TransientResourceHeap)
- {
- *outObject = static_cast<ID3D12TransientResourceHeap*>(this);
- addRef();
- return SLANG_OK;
- }
- return Super::queryInterface(uuid, outObject);
-}
-
-Result TransientResourceHeapImpl::allocateTransientDescriptorTable(
- DescriptorType type,
- GfxCount count,
- Offset& outDescriptorOffset,
- void** outD3DDescriptorHeapHandle)
-{
- auto& heap =
- (type == DescriptorType::ResourceView) ? getCurrentViewHeap() : getCurrentSamplerHeap();
- int allocResult = heap.allocate((int)count);
- if (allocResult == -1)
- {
- return SLANG_E_OUT_OF_MEMORY;
- }
- outDescriptorOffset = (Offset)allocResult;
- *outD3DDescriptorHeapHandle = heap.getHeap();
- return SLANG_OK;
-}
-
-TransientResourceHeapImpl::~TransientResourceHeapImpl()
-{
- synchronize();
- for (auto& waitInfo : m_waitInfos)
- CloseHandle(waitInfo.fenceEvent);
-}
-
-Result TransientResourceHeapImpl::init(
- const ITransientResourceHeap::Desc& desc,
- DeviceImpl* device,
- uint32_t viewHeapSize,
- uint32_t samplerHeapSize)
-{
- Super::init(desc, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, device);
- m_canResize = (desc.flags & ITransientResourceHeap::Flags::AllowResizing) != 0;
- m_viewHeapSize = viewHeapSize;
- m_samplerHeapSize = samplerHeapSize;
-
- m_stagingCpuViewHeap.init(
- device->m_device,
- 1000000,
- D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
- D3D12_DESCRIPTOR_HEAP_FLAG_NONE);
- m_stagingCpuSamplerHeap.init(
- device->m_device,
- 1000000,
- D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
- D3D12_DESCRIPTOR_HEAP_FLAG_NONE);
-
- auto d3dDevice = device->m_device;
- SLANG_RETURN_ON_FAIL(d3dDevice->CreateCommandAllocator(
- D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_commandAllocator.writeRef())));
-
- allocateNewViewDescriptorHeap(device);
- allocateNewSamplerDescriptorHeap(device);
-
- return SLANG_OK;
-}
-
-Result TransientResourceHeapImpl::allocateNewViewDescriptorHeap(DeviceImpl* device)
-{
- auto nextHeapIndex = m_currentViewHeapIndex + 1;
- if (nextHeapIndex < m_viewHeaps.getCount())
- {
- m_viewHeaps[nextHeapIndex].deallocateAll();
- m_currentViewHeapIndex = nextHeapIndex;
- return SLANG_OK;
- }
- auto d3dDevice = device->m_device;
- D3D12DescriptorHeap viewHeap;
- SLANG_RETURN_ON_FAIL(viewHeap.init(
- d3dDevice,
- m_viewHeapSize,
- D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
- D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE));
- m_currentViewHeapIndex = (int32_t)m_viewHeaps.getCount();
- m_viewHeaps.add(_Move(viewHeap));
- return SLANG_OK;
-}
-
-Result TransientResourceHeapImpl::allocateNewSamplerDescriptorHeap(DeviceImpl* device)
-{
- auto nextHeapIndex = m_currentSamplerHeapIndex + 1;
- if (nextHeapIndex < m_samplerHeaps.getCount())
- {
- m_samplerHeaps[nextHeapIndex].deallocateAll();
- m_currentSamplerHeapIndex = nextHeapIndex;
- return SLANG_OK;
- }
- auto d3dDevice = device->m_device;
- D3D12DescriptorHeap samplerHeap;
- SLANG_RETURN_ON_FAIL(samplerHeap.init(
- d3dDevice,
- m_samplerHeapSize,
- D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
- D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE));
- m_currentSamplerHeapIndex = (int32_t)m_samplerHeaps.getCount();
- m_samplerHeaps.add(_Move(samplerHeap));
- return SLANG_OK;
-}
-
-Result TransientResourceHeapImpl::createCommandBuffer(ICommandBuffer** outCmdBuffer)
-{
- if ((Index)m_commandListAllocId < m_commandBufferPool.getCount())
- {
- auto result =
- static_cast<CommandBufferImpl*>(m_commandBufferPool[m_commandListAllocId].Ptr());
- m_d3dCommandListPool[m_commandListAllocId]->Reset(m_commandAllocator, nullptr);
- result->reinit();
- ++m_commandListAllocId;
- returnComPtr(outCmdBuffer, result);
- return SLANG_OK;
- }
- ComPtr<ID3D12GraphicsCommandList> cmdList;
- m_device->m_device->CreateCommandList(
- 0,
- D3D12_COMMAND_LIST_TYPE_DIRECT,
- m_commandAllocator,
- nullptr,
- IID_PPV_ARGS(cmdList.writeRef()));
-
- m_d3dCommandListPool.add(cmdList);
- RefPtr<CommandBufferImpl> cmdBuffer = new CommandBufferImpl();
- cmdBuffer->init(m_device, cmdList, this);
- m_commandBufferPool.add(cmdBuffer);
- ++m_commandListAllocId;
- returnComPtr(outCmdBuffer, cmdBuffer);
- return SLANG_OK;
-}
-
-int PipelineCommandEncoder::getBindPointIndex(PipelineType type)
-{
- switch (type)
- {
- case PipelineType::Graphics:
- return 0;
- case PipelineType::Compute:
- return 1;
- case PipelineType::RayTracing:
- return 2;
- default:
- assert(!"unknown pipeline type.");
- return -1;
- }
-}
-
-void PipelineCommandEncoder::init(CommandBufferImpl* commandBuffer)
-{
- m_commandBuffer = commandBuffer;
- m_d3dCmdList = m_commandBuffer->m_cmdList;
- m_renderer = commandBuffer->m_renderer;
- m_transientHeap = commandBuffer->m_transientHeap;
- m_device = commandBuffer->m_renderer->m_device;
-}
-
-Result PipelineCommandEncoder::bindPipelineImpl(
- IPipelineState* pipelineState, IShaderObject** outRootObject)
-{
- m_currentPipeline = static_cast<PipelineStateBase*>(pipelineState);
- auto rootObject = &m_commandBuffer->m_rootShaderObject;
- m_commandBuffer->m_mutableRootShaderObject = nullptr;
- SLANG_RETURN_ON_FAIL(rootObject->reset(
- m_renderer,
- m_currentPipeline->getProgram<ShaderProgramImpl>()->m_rootObjectLayout,
- m_commandBuffer->m_transientHeap));
- *outRootObject = rootObject;
- m_bindingDirty = true;
- return SLANG_OK;
-}
-
-Result PipelineCommandEncoder::bindPipelineWithRootObjectImpl(
- IPipelineState* pipelineState, IShaderObject* rootObject)
-{
- m_currentPipeline = static_cast<PipelineStateBase*>(pipelineState);
- m_commandBuffer->m_mutableRootShaderObject =
- static_cast<MutableRootShaderObjectImpl*>(rootObject);
- m_bindingDirty = true;
- return SLANG_OK;
-}
-
-Result PipelineCommandEncoder::_bindRenderState(
- Submitter* submitter, RefPtr<PipelineStateBase>& newPipeline)
-{
- RootShaderObjectImpl* rootObjectImpl = m_commandBuffer->m_mutableRootShaderObject
- ? m_commandBuffer->m_mutableRootShaderObject.Ptr()
- : &m_commandBuffer->m_rootShaderObject;
- SLANG_RETURN_ON_FAIL(
- m_renderer->maybeSpecializePipeline(m_currentPipeline, rootObjectImpl, newPipeline));
- PipelineStateBase* newPipelineImpl = static_cast<PipelineStateBase*>(newPipeline.Ptr());
- auto commandList = m_d3dCmdList;
- auto pipelineTypeIndex = (int)newPipelineImpl->desc.type;
- auto programImpl = static_cast<ShaderProgramImpl*>(newPipelineImpl->m_program.Ptr());
- newPipelineImpl->ensureAPIPipelineStateCreated();
- submitter->setRootSignature(programImpl->m_rootObjectLayout->m_rootSignature);
- submitter->setPipelineState(newPipelineImpl);
- RootShaderObjectLayoutImpl* rootLayoutImpl = programImpl->m_rootObjectLayout;
-
- // We need to set up a context for binding shader objects to the pipeline state.
- // This type mostly exists to bundle together a bunch of parameters that would
- // otherwise need to be tunneled down through all the shader object binding
- // logic.
- //
- BindingContext context = {};
- context.encoder = this;
- context.submitter = submitter;
- context.device = m_renderer;
- context.transientHeap = m_transientHeap;
- context.outOfMemoryHeap = (D3D12_DESCRIPTOR_HEAP_TYPE)(-1);
- // We kick off binding of shader objects at the root object, and the objects
- // themselves will be responsible for allocating, binding, and filling in
- // any descriptor tables or other root parameters needed.
- //
- m_commandBuffer->bindDescriptorHeaps();
- if (rootObjectImpl->bindAsRoot(&context, rootLayoutImpl) == SLANG_E_OUT_OF_MEMORY)
- {
- if (!m_transientHeap->canResize())
- {
- return SLANG_E_OUT_OF_MEMORY;
- }
-
- // If we run out of heap space while binding, allocate new descriptor heaps and try again.
- ID3D12DescriptorHeap* d3dheap = nullptr;
- m_commandBuffer->invalidateDescriptorHeapBinding();
- switch (context.outOfMemoryHeap)
- {
- case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
- SLANG_RETURN_ON_FAIL(m_transientHeap->allocateNewViewDescriptorHeap(m_renderer));
- d3dheap = m_transientHeap->getCurrentViewHeap().getHeap();
- m_commandBuffer->bindDescriptorHeaps();
- break;
- case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
- SLANG_RETURN_ON_FAIL(m_transientHeap->allocateNewSamplerDescriptorHeap(m_renderer));
- d3dheap = m_transientHeap->getCurrentSamplerHeap().getHeap();
- m_commandBuffer->bindDescriptorHeaps();
- break;
- default:
- assert(!"shouldn't be here");
- return SLANG_FAIL;
- }
-
- // Try again.
- SLANG_RETURN_ON_FAIL(rootObjectImpl->bindAsRoot(&context, rootLayoutImpl));
- }
-
- return SLANG_OK;
-}
-
-Result QueryPoolImpl::init(const IQueryPool::Desc& desc, DeviceImpl* device)
-{
- m_desc = desc;
-
- // Translate query type.
- D3D12_QUERY_HEAP_DESC heapDesc = {};
- heapDesc.Count = (UINT)desc.count;
- heapDesc.NodeMask = 1;
- switch (desc.type)
- {
- case QueryType::Timestamp:
- heapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP;
- m_queryType = D3D12_QUERY_TYPE_TIMESTAMP;
- break;
- default:
- return SLANG_E_INVALID_ARG;
- }
-
- // Create query heap.
- auto d3dDevice = device->m_device;
- SLANG_RETURN_ON_FAIL(
- d3dDevice->CreateQueryHeap(&heapDesc, IID_PPV_ARGS(m_queryHeap.writeRef())));
-
- // Create readback buffer.
- 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;
- D3D12_RESOURCE_DESC resourceDesc = {};
- initBufferResourceDesc(sizeof(uint64_t) * desc.count, resourceDesc);
- SLANG_RETURN_ON_FAIL(m_readBackBuffer.initCommitted(
- d3dDevice,
- heapProps,
- D3D12_HEAP_FLAG_NONE,
- resourceDesc,
- D3D12_RESOURCE_STATE_COPY_DEST,
- nullptr));
-
- // Create command allocator.
- SLANG_RETURN_ON_FAIL(d3dDevice->CreateCommandAllocator(
- D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_commandAllocator.writeRef())));
-
- // Create command list.
- SLANG_RETURN_ON_FAIL(d3dDevice->CreateCommandList(
- 0,
- D3D12_COMMAND_LIST_TYPE_DIRECT,
- m_commandAllocator,
- nullptr,
- IID_PPV_ARGS(m_commandList.writeRef())));
- m_commandList->Close();
-
- // Create fence.
- SLANG_RETURN_ON_FAIL(
- d3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef())));
-
- // Get command queue from device.
- m_commandQueue = device->m_resourceCommandQueue->m_d3dQueue;
-
- // Create wait event.
- m_waitEvent = CreateEventEx(nullptr, false, 0, EVENT_ALL_ACCESS);
-
- return SLANG_OK;
-}
-
-Result QueryPoolImpl::getResult(GfxIndex queryIndex, GfxCount count, uint64_t* data)
-{
- m_commandList->Reset(m_commandAllocator, nullptr);
- m_commandList->ResolveQueryData(
- m_queryHeap,
- m_queryType,
- (UINT)queryIndex,
- (UINT)count,
- m_readBackBuffer,
- sizeof(uint64_t) * queryIndex);
- m_commandList->Close();
- ID3D12CommandList* cmdList = m_commandList;
- m_commandQueue->ExecuteCommandLists(1, &cmdList);
- m_eventValue++;
- m_fence->SetEventOnCompletion(m_eventValue, m_waitEvent);
- m_commandQueue->Signal(m_fence, m_eventValue);
- WaitForSingleObject(m_waitEvent, INFINITE);
- m_commandAllocator->Reset();
-
- int8_t* mappedData = nullptr;
- D3D12_RANGE readRange = {
- sizeof(uint64_t) * queryIndex, sizeof(uint64_t) * (queryIndex + count)};
- m_readBackBuffer.getResource()->Map(0, &readRange, (void**)&mappedData);
- memcpy(data, mappedData + sizeof(uint64_t) * queryIndex, sizeof(uint64_t) * count);
- m_readBackBuffer.getResource()->Unmap(0, nullptr);
- return SLANG_OK;
-}
-
-void QueryPoolImpl::writeTimestamp(ID3D12GraphicsCommandList* cmdList, GfxIndex index)
-{
- cmdList->EndQuery(m_queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, (UINT)index);
-}
-
-IQueryPool* PlainBufferProxyQueryPoolImpl::getInterface(const Guid& guid)
-{
- if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IQueryPool)
- return static_cast<IQueryPool*>(this);
- return nullptr;
-}
-
-Result PlainBufferProxyQueryPoolImpl::init(
- const IQueryPool::Desc& desc, DeviceImpl* device, uint32_t stride)
-{
- ComPtr<IBufferResource> bufferResource;
- IBufferResource::Desc bufferDesc = {};
- bufferDesc.defaultState = ResourceState::CopySource;
- bufferDesc.elementSize = 0;
- bufferDesc.type = IResource::Type::Buffer;
- bufferDesc.sizeInBytes = desc.count * stride;
- bufferDesc.format = Format::Unknown;
- bufferDesc.allowedStates.add(ResourceState::UnorderedAccess);
- SLANG_RETURN_ON_FAIL(
- device->createBufferResource(bufferDesc, nullptr, bufferResource.writeRef()));
- m_bufferResource = static_cast<BufferResourceImpl*>(bufferResource.get());
- m_queryType = desc.type;
- m_device = device;
- m_stride = stride;
- m_count = (uint32_t)desc.count;
- m_desc = desc;
- return SLANG_OK;
-}
-
-Result PlainBufferProxyQueryPoolImpl::reset()
-{
- m_resultDirty = true;
- auto encodeInfo = m_device->encodeResourceCommands();
- D3D12_RESOURCE_BARRIER barrier = {};
- barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
- barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
- barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
- barrier.Transition.pResource = m_bufferResource->m_resource.getResource();
- encodeInfo.d3dCommandList->ResourceBarrier(1, &barrier);
- m_device->submitResourceCommandsAndWait(encodeInfo);
- return SLANG_OK;
-}
-
-Result PlainBufferProxyQueryPoolImpl::getResult(GfxIndex queryIndex, GfxCount count, uint64_t* data)
-{
- if (m_resultDirty)
- {
- auto encodeInfo = m_device->encodeResourceCommands();
- D3D12_RESOURCE_BARRIER barrier = {};
- barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
- barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
- barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
- barrier.Transition.pResource = m_bufferResource->m_resource.getResource();
- encodeInfo.d3dCommandList->ResourceBarrier(1, &barrier);
-
- D3D12Resource stageBuf;
-
- auto size = (Size)m_count * m_stride;
- 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;
-
- D3D12_RESOURCE_DESC stagingDesc;
- initBufferResourceDesc(size, stagingDesc);
-
- SLANG_RETURN_ON_FAIL(stageBuf.initCommitted(
- m_device->m_device,
- heapProps,
- D3D12_HEAP_FLAG_NONE,
- stagingDesc,
- D3D12_RESOURCE_STATE_COPY_DEST,
- nullptr));
-
- encodeInfo.d3dCommandList->CopyBufferRegion(
- stageBuf, 0, m_bufferResource->m_resource.getResource(), 0, size);
- m_device->submitResourceCommandsAndWait(encodeInfo);
- void* ptr = nullptr;
- stageBuf.getResource()->Map(0, nullptr, &ptr);
- m_result.setCount(m_count * m_stride);
- memcpy(m_result.getBuffer(), ptr, m_result.getCount());
-
- m_resultDirty = false;
- }
-
- memcpy(data, m_result.getBuffer() + queryIndex * m_stride, count * m_stride);
-
- return SLANG_OK;
-}
-
-void translatePostBuildInfoDescs(
- int propertyQueryCount,
- AccelerationStructureQueryDesc* queryDescs,
- List<D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC>& postBuildInfoDescs)
-{
- postBuildInfoDescs.setCount(propertyQueryCount);
- for (int i = 0; i < propertyQueryCount; i++)
- {
- switch (queryDescs[i].queryType)
- {
- case QueryType::AccelerationStructureCompactedSize:
- postBuildInfoDescs[i].InfoType =
- D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_COMPACTED_SIZE;
- postBuildInfoDescs[i].DestBuffer =
- static_cast<PlainBufferProxyQueryPoolImpl*>(queryDescs[i].queryPool)
- ->m_bufferResource->getDeviceAddress() +
- sizeof(D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_COMPACTED_SIZE_DESC) *
- queryDescs[i].firstQueryIndex;
- break;
- case QueryType::AccelerationStructureCurrentSize:
- postBuildInfoDescs[i].InfoType =
- D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_CURRENT_SIZE;
- postBuildInfoDescs[i].DestBuffer =
- static_cast<PlainBufferProxyQueryPoolImpl*>(queryDescs[i].queryPool)
- ->m_bufferResource->getDeviceAddress() +
- sizeof(D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_COMPACTED_SIZE_DESC) *
- queryDescs[i].firstQueryIndex;
- break;
- case QueryType::AccelerationStructureSerializedSize:
- postBuildInfoDescs[i].InfoType =
- D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_SERIALIZATION;
- postBuildInfoDescs[i].DestBuffer =
- static_cast<PlainBufferProxyQueryPoolImpl*>(queryDescs[i].queryPool)
- ->m_bufferResource->getDeviceAddress() +
- sizeof(D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_SERIALIZATION_DESC) *
- queryDescs[i].firstQueryIndex;
- break;
- }
- }
-}
-
-#if SLANG_GFX_HAS_DXR_SUPPORT
-
-void RayTracingCommandEncoderImpl::buildAccelerationStructure(
- const IAccelerationStructure::BuildDesc& desc,
- GfxCount propertyQueryCount,
- AccelerationStructureQueryDesc* queryDescs)
-{
- if (!m_commandBuffer->m_cmdList4)
- {
- getDebugCallback()->handleMessage(
- DebugMessageType::Error,
- DebugMessageSource::Layer,
- "Ray-tracing is not supported on current system.");
- return;
- }
- AccelerationStructureImpl* destASImpl = nullptr;
- if (desc.dest)
- destASImpl = static_cast<AccelerationStructureImpl*>(desc.dest);
- AccelerationStructureImpl* srcASImpl = nullptr;
- if (desc.source)
- srcASImpl = static_cast<AccelerationStructureImpl*>(desc.source);
-
- D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC buildDesc = {};
- buildDesc.DestAccelerationStructureData = destASImpl->getDeviceAddress();
- buildDesc.SourceAccelerationStructureData = srcASImpl ? srcASImpl->getDeviceAddress() : 0;
- buildDesc.ScratchAccelerationStructureData = desc.scratchData;
- D3DAccelerationStructureInputsBuilder builder;
- builder.build(desc.inputs, getDebugCallback());
- buildDesc.Inputs = builder.desc;
-
- List<D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC> postBuildInfoDescs;
- translatePostBuildInfoDescs(propertyQueryCount, queryDescs, postBuildInfoDescs);
- m_commandBuffer->m_cmdList4->BuildRaytracingAccelerationStructure(
- &buildDesc, (UINT)propertyQueryCount, postBuildInfoDescs.getBuffer());
-}
-
-void RayTracingCommandEncoderImpl::copyAccelerationStructure(
- IAccelerationStructure* dest, IAccelerationStructure* src, AccelerationStructureCopyMode mode)
-{
- auto destASImpl = static_cast<AccelerationStructureImpl*>(dest);
- auto srcASImpl = static_cast<AccelerationStructureImpl*>(src);
- D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE copyMode;
- switch (mode)
- {
- case AccelerationStructureCopyMode::Clone:
- copyMode = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_CLONE;
- break;
- case AccelerationStructureCopyMode::Compact:
- copyMode = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_COMPACT;
- break;
- default:
- getDebugCallback()->handleMessage(
- DebugMessageType::Error,
- DebugMessageSource::Layer,
- "Unsupported AccelerationStructureCopyMode.");
- return;
- }
- m_commandBuffer->m_cmdList4->CopyRaytracingAccelerationStructure(
- destASImpl->getDeviceAddress(), srcASImpl->getDeviceAddress(), copyMode);
-}
-
-void RayTracingCommandEncoderImpl::queryAccelerationStructureProperties(
- GfxCount accelerationStructureCount,
- IAccelerationStructure* const* accelerationStructures,
- GfxCount queryCount,
- AccelerationStructureQueryDesc* queryDescs)
-{
- List<D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC> postBuildInfoDescs;
- List<DeviceAddress> asAddresses;
- asAddresses.setCount(accelerationStructureCount);
- for (GfxIndex i = 0; i < accelerationStructureCount; i++)
- asAddresses[i] = accelerationStructures[i]->getDeviceAddress();
- translatePostBuildInfoDescs(queryCount, queryDescs, postBuildInfoDescs);
- m_commandBuffer->m_cmdList4->EmitRaytracingAccelerationStructurePostbuildInfo(
- postBuildInfoDescs.getBuffer(), (UINT)accelerationStructureCount, asAddresses.getBuffer());
-}
-
-void RayTracingCommandEncoderImpl::serializeAccelerationStructure(
- DeviceAddress dest, IAccelerationStructure* src)
-{
- auto srcASImpl = static_cast<AccelerationStructureImpl*>(src);
- m_commandBuffer->m_cmdList4->CopyRaytracingAccelerationStructure(
- dest,
- srcASImpl->getDeviceAddress(),
- D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_SERIALIZE);
-}
-
-void RayTracingCommandEncoderImpl::deserializeAccelerationStructure(
- IAccelerationStructure* dest, DeviceAddress source)
-{
- auto destASImpl = static_cast<AccelerationStructureImpl*>(dest);
- m_commandBuffer->m_cmdList4->CopyRaytracingAccelerationStructure(
- dest->getDeviceAddress(),
- source,
- D3D12_RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE_DESERIALIZE);
-}
-
-void RayTracingCommandEncoderImpl::bindPipeline(
- IPipelineState* state, IShaderObject** outRootObject)
-{
- bindPipelineImpl(state, outRootObject);
-}
-
-void RayTracingCommandEncoderImpl::dispatchRays(
- GfxIndex rayGenShaderIndex,
- IShaderTable* shaderTable,
- GfxCount width,
- GfxCount height,
- GfxCount depth)
-{
- RefPtr<PipelineStateBase> newPipeline;
- PipelineStateBase* pipeline = m_currentPipeline.Ptr();
- {
- struct RayTracingSubmitter : public ComputeSubmitter
- {
- ID3D12GraphicsCommandList4* m_cmdList4;
- RayTracingSubmitter(ID3D12GraphicsCommandList4* cmdList4)
- : ComputeSubmitter(cmdList4)
- , m_cmdList4(cmdList4)
- {}
- virtual void setPipelineState(PipelineStateBase* pipeline) override
- {
- auto pipelineImpl = static_cast<RayTracingPipelineStateImpl*>(pipeline);
- m_cmdList4->SetPipelineState1(pipelineImpl->m_stateObject.get());
- }
- };
- RayTracingSubmitter submitter(m_commandBuffer->m_cmdList4);
- if (SLANG_FAILED(_bindRenderState(&submitter, newPipeline)))
- {
- assert(!"Failed to bind render state");
- }
- if (newPipeline)
- pipeline = newPipeline.Ptr();
- }
- auto pipelineImpl = static_cast<RayTracingPipelineStateImpl*>(pipeline);
-
- auto shaderTableImpl = static_cast<ShaderTableImpl*>(shaderTable);
-
- auto shaderTableBuffer =
- shaderTableImpl->getOrCreateBuffer(pipelineImpl, m_transientHeap, static_cast<ResourceCommandEncoderImpl*>(this));
- auto shaderTableAddr = shaderTableBuffer->getDeviceAddress();
-
- D3D12_DISPATCH_RAYS_DESC dispatchDesc = {};
-
- dispatchDesc.RayGenerationShaderRecord.StartAddress =
- shaderTableAddr + shaderTableImpl->m_rayGenTableOffset +
- rayGenShaderIndex * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
- dispatchDesc.RayGenerationShaderRecord.SizeInBytes = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
-
- dispatchDesc.MissShaderTable.StartAddress =
- shaderTableAddr + shaderTableImpl->m_missTableOffset;
- dispatchDesc.MissShaderTable.SizeInBytes =
- shaderTableImpl->m_missShaderCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
- dispatchDesc.MissShaderTable.StrideInBytes = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
-
- dispatchDesc.HitGroupTable.StartAddress =
- shaderTableAddr + shaderTableImpl->m_hitGroupTableOffset;
- dispatchDesc.HitGroupTable.SizeInBytes =
- shaderTableImpl->m_hitGroupCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
- dispatchDesc.HitGroupTable.StrideInBytes = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
-
- dispatchDesc.Width = (UINT)width;
- dispatchDesc.Height = (UINT)height;
- dispatchDesc.Depth = (UINT)depth;
- m_commandBuffer->m_cmdList4->DispatchRays(&dispatchDesc);
-}
-
-RayTracingPipelineStateImpl::RayTracingPipelineStateImpl(DeviceImpl* device)
- : m_device(device)
-{}
-
-void RayTracingPipelineStateImpl::init(const RayTracingPipelineStateDesc& inDesc)
-{
- PipelineStateDesc pipelineDesc;
- pipelineDesc.type = PipelineType::RayTracing;
- pipelineDesc.rayTracing.set(inDesc);
- initializeBase(pipelineDesc);
-}
-
-Result RayTracingPipelineStateImpl::getNativeHandle(InteropHandle* outHandle)
-{
- SLANG_RETURN_ON_FAIL(ensureAPIPipelineStateCreated());
- outHandle->api = InteropHandleAPI::D3D12;
- outHandle->handleValue = reinterpret_cast<uint64_t>(m_stateObject.get());
- return SLANG_OK;
-}
-
-Result RayTracingPipelineStateImpl::ensureAPIPipelineStateCreated()
-{
- if (m_stateObject)
- return SLANG_OK;
-
- auto program = static_cast<ShaderProgramImpl*>(m_program.Ptr());
- auto slangGlobalScope = program->linkedProgram;
- auto programLayout = slangGlobalScope->getLayout();
-
- List<D3D12_STATE_SUBOBJECT> subObjects;
- ChunkedList<D3D12_DXIL_LIBRARY_DESC> dxilLibraries;
- ChunkedList<D3D12_HIT_GROUP_DESC> hitGroups;
- ChunkedList<ComPtr<ISlangBlob>> codeBlobs;
- ChunkedList<D3D12_EXPORT_DESC> exports;
- ChunkedList<const wchar_t*> strPtrs;
-
- ComPtr<ISlangBlob> diagnostics;
- ChunkedList<OSString> stringPool;
- auto getWStr = [&](const char* name)
- {
- String str = String(name);
- auto wstr = str.toWString();
- return stringPool.add(wstr)->begin();
- };
- auto compileShader = [&](slang::EntryPointLayout* entryPointInfo,
- slang::IComponentType* component,
- SlangInt entryPointIndex)
- {
- ComPtr<ISlangBlob> codeBlob;
- auto compileResult = component->getEntryPointCode(
- entryPointIndex, 0, codeBlob.writeRef(), diagnostics.writeRef());
- if (diagnostics.get())
- {
- getDebugCallback()->handleMessage(
- compileResult == SLANG_OK ? DebugMessageType::Warning : DebugMessageType::Error,
- DebugMessageSource::Slang,
- (char*)diagnostics->getBufferPointer());
- }
- SLANG_RETURN_ON_FAIL(compileResult);
- codeBlobs.add(codeBlob);
- D3D12_DXIL_LIBRARY_DESC library = {};
- library.DXILLibrary.BytecodeLength = codeBlob->getBufferSize();
- library.DXILLibrary.pShaderBytecode = codeBlob->getBufferPointer();
- library.NumExports = 1;
- D3D12_EXPORT_DESC exportDesc = {};
- exportDesc.Name = getWStr(entryPointInfo->getNameOverride());
- exportDesc.ExportToRename = getWStr(entryPointInfo->getNameOverride());
- exportDesc.Flags = D3D12_EXPORT_FLAG_NONE;
- library.pExports = exports.add(exportDesc);
-
- D3D12_STATE_SUBOBJECT dxilSubObject = {};
- dxilSubObject.Type = D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY;
- dxilSubObject.pDesc = dxilLibraries.add(library);
- subObjects.add(dxilSubObject);
- return SLANG_OK;
- };
- if (program->linkedEntryPoints.getCount() == 0)
- {
- for (SlangUInt i = 0; i < programLayout->getEntryPointCount(); i++)
- {
- SLANG_RETURN_ON_FAIL(compileShader(
- programLayout->getEntryPointByIndex(i), program->linkedProgram, (SlangInt)i));
- }
- }
- else
- {
- for (auto& entryPoint : program->linkedEntryPoints)
- {
- SLANG_RETURN_ON_FAIL(
- compileShader(entryPoint->getLayout()->getEntryPointByIndex(0), entryPoint, 0));
- }
- }
-
- for (Index i = 0; i < desc.rayTracing.hitGroupDescs.getCount(); i++)
- {
- auto& hitGroup = desc.rayTracing.hitGroups[i];
- D3D12_HIT_GROUP_DESC hitGroupDesc = {};
- hitGroupDesc.Type = hitGroup.intersectionEntryPoint.getLength() == 0
- ? D3D12_HIT_GROUP_TYPE_TRIANGLES
- : D3D12_HIT_GROUP_TYPE_PROCEDURAL_PRIMITIVE;
-
- if (hitGroup.anyHitEntryPoint.getLength())
- {
- hitGroupDesc.AnyHitShaderImport = getWStr(hitGroup.anyHitEntryPoint.getBuffer());
- }
- if (hitGroup.closestHitEntryPoint.getLength())
- {
- hitGroupDesc.ClosestHitShaderImport =
- getWStr(hitGroup.closestHitEntryPoint.getBuffer());
- }
- if (hitGroup.intersectionEntryPoint.getLength())
- {
- hitGroupDesc.IntersectionShaderImport =
- getWStr(hitGroup.intersectionEntryPoint.getBuffer());
- }
- hitGroupDesc.HitGroupExport = getWStr(hitGroup.hitGroupName.getBuffer());
-
- D3D12_STATE_SUBOBJECT hitGroupSubObject = {};
- hitGroupSubObject.Type = D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP;
- hitGroupSubObject.pDesc = hitGroups.add(hitGroupDesc);
- subObjects.add(hitGroupSubObject);
- }
-
- D3D12_RAYTRACING_SHADER_CONFIG shaderConfig = {};
- // According to DXR spec, fixed function triangle intersections must use float2 as ray
- // attributes that defines the barycentric coordinates at intersection.
- shaderConfig.MaxAttributeSizeInBytes = (UINT)desc.rayTracing.maxAttributeSizeInBytes;
- shaderConfig.MaxPayloadSizeInBytes = (UINT)desc.rayTracing.maxRayPayloadSize;
- D3D12_STATE_SUBOBJECT shaderConfigSubObject = {};
- shaderConfigSubObject.Type = D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG;
- shaderConfigSubObject.pDesc = &shaderConfig;
- subObjects.add(shaderConfigSubObject);
-
- D3D12_GLOBAL_ROOT_SIGNATURE globalSignatureDesc = {};
- globalSignatureDesc.pGlobalRootSignature = program->m_rootObjectLayout->m_rootSignature.get();
- D3D12_STATE_SUBOBJECT globalSignatureSubobject = {};
- globalSignatureSubobject.Type = D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE;
- globalSignatureSubobject.pDesc = &globalSignatureDesc;
- subObjects.add(globalSignatureSubobject);
-
- D3D12_RAYTRACING_PIPELINE_CONFIG pipelineConfig = {};
- pipelineConfig.MaxTraceRecursionDepth = desc.rayTracing.maxRecursion;
- D3D12_STATE_SUBOBJECT pipelineConfigSubobject = {};
- pipelineConfigSubobject.Type = D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG;
- pipelineConfigSubobject.pDesc = &pipelineConfig;
- subObjects.add(pipelineConfigSubobject);
-
- if (m_device->m_pipelineCreationAPIDispatcher)
- {
- m_device->m_pipelineCreationAPIDispatcher->beforeCreateRayTracingState(
- m_device, slangGlobalScope);
- }
-
- D3D12_STATE_OBJECT_DESC rtpsoDesc = {};
- rtpsoDesc.Type = D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE;
- rtpsoDesc.NumSubobjects = (UINT)subObjects.getCount();
- rtpsoDesc.pSubobjects = subObjects.getBuffer();
- SLANG_RETURN_ON_FAIL(
- m_device->m_device5->CreateStateObject(&rtpsoDesc, IID_PPV_ARGS(m_stateObject.writeRef())));
-
- if (m_device->m_pipelineCreationAPIDispatcher)
- {
- m_device->m_pipelineCreationAPIDispatcher->afterCreateRayTracingState(
- m_device, slangGlobalScope);
- }
- return SLANG_OK;
-}
-
-#endif
-
-GfxCount ShaderObjectImpl::getEntryPointCount() { return 0; }
-
-Result ShaderObjectImpl::getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint)
-{
- *outEntryPoint = nullptr;
- return SLANG_OK;
-}
-
-const void* ShaderObjectImpl::getRawData() { return m_data.getBuffer(); }
-
-Size ShaderObjectImpl::getSize() { return (Size)m_data.getCount(); }
-
-// TODO: Change Index to Offset/Size?
-Result ShaderObjectImpl::setData(ShaderOffset const& inOffset, void const* data, size_t inSize)
-{
- Index offset = inOffset.uniformOffset;
- Index size = inSize;
-
- char* dest = m_data.getBuffer();
- Index availableSize = m_data.getCount();
-
- // TODO: We really should bounds-check access rather than silently ignoring sets
- // that are too large, but we have several test cases that set more data than
- // an object actually stores on several targets...
- //
- if (offset < 0)
- {
- size += offset;
- offset = 0;
- }
- if ((offset + size) >= availableSize)
- {
- size = availableSize - offset;
- }
-
- memcpy(dest + offset, data, size);
-
- m_isConstantBufferDirty = true;
-
- m_version++;
-
- return SLANG_OK;
-}
-
-Result ShaderObjectImpl::setObject(ShaderOffset const& offset, IShaderObject* object)
-{
- SLANG_RETURN_ON_FAIL(Super::setObject(offset, object));
- if (m_isMutable)
- {
- auto subObjectIndex = getSubObjectIndex(offset);
- if (subObjectIndex >= m_subObjectVersions.getCount())
- m_subObjectVersions.setCount(subObjectIndex + 1);
- m_subObjectVersions[subObjectIndex] = static_cast<ShaderObjectImpl*>(object)->m_version;
- m_version++;
- }
- return SLANG_OK;
-}
-
-Result ShaderObjectImpl::setSampler(ShaderOffset const& offset, ISamplerState* sampler)
-{
- if (offset.bindingRangeIndex < 0)
- return SLANG_E_INVALID_ARG;
- auto layout = getLayout();
- if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
- return SLANG_E_INVALID_ARG;
- auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
- auto samplerImpl = static_cast<SamplerStateImpl*>(sampler);
- ID3D12Device* d3dDevice = static_cast<DeviceImpl*>(getDevice())->m_device;
- d3dDevice->CopyDescriptorsSimple(
- 1,
- m_descriptorSet.samplerTable.getCpuHandle(
- bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex),
- samplerImpl->m_descriptor.cpuHandle,
- D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
- m_version++;
- return SLANG_OK;
-}
-
-Result ShaderObjectImpl::setCombinedTextureSampler(
- ShaderOffset const& offset, IResourceView* textureView, ISamplerState* sampler)
-{
-#if 0
- if (offset.bindingRangeIndex < 0)
- return SLANG_E_INVALID_ARG;
- auto layout = getLayout();
- if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
- return SLANG_E_INVALID_ARG;
- auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
- auto resourceViewImpl = static_cast<ResourceViewImpl*>(textureView);
- ID3D12Device* d3dDevice = static_cast<DeviceImpl*>(getDevice())->m_device;
- d3dDevice->CopyDescriptorsSimple(
- 1,
- m_resourceHeap.getCpuHandle(
- m_descriptorSet.m_resourceTable +
- bindingRange.binding.offsetInDescriptorTable.resource +
- (int32_t)offset.bindingArrayIndex),
- resourceViewImpl->m_descriptor.cpuHandle,
- D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
- auto samplerImpl = static_cast<SamplerStateImpl*>(sampler);
- d3dDevice->CopyDescriptorsSimple(
- 1,
- m_samplerHeap.getCpuHandle(
- m_descriptorSet.m_samplerTable +
- bindingRange.binding.offsetInDescriptorTable.sampler +
- (int32_t)offset.bindingArrayIndex),
- samplerImpl->m_descriptor.cpuHandle,
- D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
-#endif
- m_version++;
- return SLANG_OK;
-}
-
-Result ShaderObjectImpl::init(
- DeviceImpl* device,
- ShaderObjectLayoutImpl* layout,
- DescriptorHeapReference viewHeap,
- DescriptorHeapReference samplerHeap)
-{
- m_device = device;
-
- m_layout = layout;
-
- m_cachedTransientHeap = nullptr;
- m_cachedTransientHeapVersion = 0;
- m_isConstantBufferDirty = true;
-
- // If the layout tells us that there is any uniform data,
- // then we will allocate a CPU memory buffer to hold that data
- // while it is being set from the host.
- //
- // Once the user is done setting the parameters/fields of this
- // shader object, we will produce a GPU-memory version of the
- // uniform data (which includes values from this object and
- // any existential-type sub-objects).
- //
- size_t uniformSize = layout->getElementTypeLayout()->getSize();
- if (uniformSize)
- {
- m_data.setCount(uniformSize);
- memset(m_data.getBuffer(), 0, uniformSize);
- }
- m_rootArguments.setCount(layout->getOwnUserRootParameterCount());
- memset(
- m_rootArguments.getBuffer(),
- 0,
- sizeof(D3D12_GPU_VIRTUAL_ADDRESS) * m_rootArguments.getCount());
- // Each shader object will own CPU descriptor heap memory
- // for any resource or sampler descriptors it might store
- // as part of its value.
- //
- // This allocate includes a reservation for any constant
- // buffer descriptor pertaining to the ordinary data,
- // but does *not* include any descriptors that are managed
- // as part of sub-objects.
- //
- if (auto resourceCount = layout->getResourceSlotCount())
- {
- m_descriptorSet.resourceTable.allocate(viewHeap, resourceCount);
-
- // We must also ensure that the memory for any resources
- // referenced by descriptors in this object does not get
- // freed while the object is still live.
- //
- m_boundResources.setCount(resourceCount);
- }
- if (auto samplerCount = layout->getSamplerSlotCount())
- {
- m_descriptorSet.samplerTable.allocate(samplerHeap, samplerCount);
- }
-
- // If the layout specifies that we have any sub-objects, then
- // we need to size the array to account for them.
- //
- Index subObjectCount = layout->getSubObjectSlotCount();
- m_objects.setCount(subObjectCount);
-
- for (auto subObjectRangeInfo : layout->getSubObjectRanges())
- {
- auto subObjectLayout = subObjectRangeInfo.layout;
-
- // In the case where the sub-object range represents an
- // existential-type leaf field (e.g., an `IBar`), we
- // cannot pre-allocate the object(s) to go into that
- // range, since we can't possibly know what to allocate
- // at this point.
- //
- if (!subObjectLayout)
- continue;
- //
- // Otherwise, we will allocate a sub-object to fill
- // in each entry in this range, based on the layout
- // information we already have.
-
- auto& bindingRangeInfo = layout->getBindingRange(subObjectRangeInfo.bindingRangeIndex);
- for (uint32_t i = 0; i < bindingRangeInfo.count; ++i)
- {
- RefPtr<ShaderObjectImpl> subObject;
- SLANG_RETURN_ON_FAIL(
- ShaderObjectImpl::create(device, subObjectLayout, subObject.writeRef()));
- m_objects[bindingRangeInfo.subObjectIndex + i] = subObject;
- }
- }
-
- return SLANG_OK;
-}
-
-/// Write the uniform/ordinary data of this object into the given `dest` buffer at the given
-/// `offset`
-
-Result ShaderObjectImpl::_writeOrdinaryData(
- PipelineCommandEncoder* encoder,
- BufferResourceImpl* buffer,
- Offset offset,
- Size destSize,
- ShaderObjectLayoutImpl* specializedLayout)
-{
- auto src = m_data.getBuffer();
- auto srcSize = Size(m_data.getCount());
-
- SLANG_ASSERT(srcSize <= destSize);
-
- uploadBufferDataImpl(
- encoder->m_device,
- encoder->m_d3dCmdList,
- encoder->m_transientHeap,
- buffer,
- offset,
- srcSize,
- src);
-
- // In the case where this object has any sub-objects of
- // existential/interface type, we need to recurse on those objects
- // that need to write their state into an appropriate "pending" allocation.
- //
- // Note: Any values that could fit into the "payload" included
- // in the existential-type field itself will have already been
- // written as part of `setObject()`. This loop only needs to handle
- // those sub-objects that do not "fit."
- //
- // An implementers looking at this code might wonder if things could be changed
- // so that *all* writes related to sub-objects for interface-type fields could
- // be handled in this one location, rather than having some in `setObject()` and
- // others handled here.
- //
- Index subObjectRangeCounter = 0;
- for (auto const& subObjectRangeInfo : specializedLayout->getSubObjectRanges())
- {
- Index subObjectRangeIndex = subObjectRangeCounter++;
- auto const& bindingRangeInfo =
- specializedLayout->getBindingRange(subObjectRangeInfo.bindingRangeIndex);
-
- // We only need to handle sub-object ranges for interface/existential-type fields,
- // because fields of constant-buffer or parameter-block type are responsible for
- // the ordinary/uniform data of their own existential/interface-type sub-objects.
- //
- if (bindingRangeInfo.bindingType != slang::BindingType::ExistentialValue)
- continue;
-
- // Each sub-object range represents a single "leaf" field, but might be nested
- // under zero or more outer arrays, such that the number of existential values
- // in the same range can be one or more.
- //
- auto count = bindingRangeInfo.count;
-
- // We are not concerned with the case where the existential value(s) in the range
- // git into the payload part of the leaf field.
- //
- // In the case where the value didn't fit, the Slang layout strategy would have
- // considered the requirements of the value as a "pending" allocation, and would
- // allocate storage for the ordinary/uniform part of that pending allocation inside
- // of the parent object's type layout.
- //
- // Here we assume that the Slang reflection API can provide us with a single byte
- // offset and stride for the location of the pending data allocation in the
- // specialized type layout, which will store the values for this sub-object range.
- //
- // TODO: The reflection API functions we are assuming here haven't been implemented
- // yet, so the functions being called here are stubs.
- //
- // TODO: It might not be that a single sub-object range can reliably map to a single
- // contiguous array with a single stride; we need to carefully consider what the
- // layout logic does for complex cases with multiple layers of nested arrays and
- // structures.
- //
- Offset subObjectRangePendingDataOffset = subObjectRangeInfo.offset.pendingOrdinaryData;
- Size subObjectRangePendingDataStride = subObjectRangeInfo.stride.pendingOrdinaryData;
-
- // If the range doesn't actually need/use the "pending" allocation at all, then
- // we need to detect that case and skip such ranges.
- //
- // TODO: This should probably be handled on a per-object basis by caching a "does it
- // fit?" bit as part of the information for bound sub-objects, given that we already
- // compute the "does it fit?" status as part of `setObject()`.
- //
- if (subObjectRangePendingDataOffset == 0)
- continue;
-
- for (uint32_t i = 0; i < count; ++i)
- {
- auto subObject = m_objects[bindingRangeInfo.subObjectIndex + i];
-
- RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
- SLANG_RETURN_ON_FAIL(subObject->getSpecializedLayout(subObjectLayout.writeRef()));
-
- auto subObjectOffset =
- subObjectRangePendingDataOffset + i * subObjectRangePendingDataStride;
-
- subObject->_writeOrdinaryData(
- encoder,
- buffer,
- offset + subObjectOffset,
- destSize - subObjectOffset,
- subObjectLayout);
- }
- }
-
- return SLANG_OK;
-}
-
-bool ShaderObjectImpl::shouldAllocateConstantBuffer(TransientResourceHeapImpl* transientHeap)
-{
- if (m_isConstantBufferDirty || m_cachedTransientHeap != transientHeap ||
- m_cachedTransientHeapVersion != transientHeap->getVersion())
- {
- return true;
- }
- return false;
-}
-
-/// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
-
-Result ShaderObjectImpl::_ensureOrdinaryDataBufferCreatedIfNeeded(
- PipelineCommandEncoder* encoder, ShaderObjectLayoutImpl* specializedLayout)
-{
- // If data has been changed since last allocation/filling of constant buffer,
- // we will need to allocate a new one.
- //
- if (!shouldAllocateConstantBuffer(encoder->m_transientHeap))
- {
- return SLANG_OK;
- }
- m_isConstantBufferDirty = false;
- m_cachedTransientHeap = encoder->m_transientHeap;
- m_cachedTransientHeapVersion = encoder->m_transientHeap->getVersion();
-
- // Computing the size of the ordinary data buffer is *not* just as simple
- // as using the size of the `m_ordinayData` array that we store. The reason
- // for the added complexity is that interface-type fields may lead to the
- // storage being specialized such that it needs extra appended data to
- // store the concrete values that logically belong in those interface-type
- // fields but wouldn't fit in the fixed-size allocation we gave them.
- //
- m_constantBufferSize = specializedLayout->getTotalOrdinaryDataSize();
- if (m_constantBufferSize == 0)
- {
- return SLANG_OK;
- }
-
- // Once we have computed how large the buffer should be, we can allocate
- // it from the transient resource heap.
- //
- auto alignedConstantBufferSize = D3DUtil::calcAligned(m_constantBufferSize, 256);
- SLANG_RETURN_ON_FAIL(encoder->m_commandBuffer->m_transientHeap->allocateConstantBuffer(
- alignedConstantBufferSize, m_constantBufferWeakPtr, m_constantBufferOffset));
-
- // Once the buffer is allocated, we can use `_writeOrdinaryData` to fill it in.
- //
- // Note that `_writeOrdinaryData` is potentially recursive in the case
- // where this object contains interface/existential-type fields, so we
- // don't need or want to inline it into this call site.
- //
- SLANG_RETURN_ON_FAIL(_writeOrdinaryData(
- encoder,
- static_cast<BufferResourceImpl*>(m_constantBufferWeakPtr),
- m_constantBufferOffset,
- m_constantBufferSize,
- specializedLayout));
-
- {
- // We also create and store a descriptor for our root constant buffer
- // into the descriptor table allocation that was reserved for them.
- //
- // We always know that the ordinary data buffer will be the first descriptor
- // in the table of resource views.
- //
- auto descriptorTable = m_descriptorSet.resourceTable;
- D3D12_CONSTANT_BUFFER_VIEW_DESC viewDesc = {};
- viewDesc.BufferLocation = static_cast<BufferResourceImpl*>(m_constantBufferWeakPtr)
- ->m_resource.getResource()
- ->GetGPUVirtualAddress() +
- m_constantBufferOffset;
- viewDesc.SizeInBytes = (UINT)alignedConstantBufferSize;
- encoder->m_device->CreateConstantBufferView(&viewDesc, descriptorTable.getCpuHandle());
- }
-
- return SLANG_OK;
-}
-
-void ShaderObjectImpl::updateSubObjectsRecursive()
-{
- if (!m_isMutable)
- return;
- auto& subObjectRanges = getLayout()->getSubObjectRanges();
- for (Slang::Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRanges.getCount();
- subObjectRangeIndex++)
- {
- auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
- auto const& bindingRange = getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
- Slang::Index count = bindingRange.count;
-
- for (Slang::Index subObjectIndexInRange = 0; subObjectIndexInRange < count;
- subObjectIndexInRange++)
- {
- Slang::Index objectIndex = bindingRange.subObjectIndex + subObjectIndexInRange;
- auto subObject = m_objects[objectIndex].Ptr();
- if (!subObject)
- continue;
- subObject->updateSubObjectsRecursive();
- if (m_subObjectVersions[objectIndex] != m_objects[objectIndex]->m_version)
- {
- ShaderOffset offset;
- offset.bindingRangeIndex = (GfxIndex)subObjectRange.bindingRangeIndex;
- offset.bindingArrayIndex = (GfxIndex)subObjectIndexInRange;
- setObject(offset, subObject);
- }
- }
- }
-}
-
-/// Prepare to bind this object as a parameter block.
-///
-/// This involves allocating and binding any descriptor tables necessary
-/// to to store the state of the object. The function returns a descriptor
-/// set formed from any table(s) allocated. In addition, the `ioOffset`
-/// parameter will be adjusted to be correct for binding values into
-/// the resulting descriptor set.
-///
-/// Returns:
-/// SLANG_OK when successful,
-/// SLANG_E_OUT_OF_MEMORY when descriptor heap is full.
-///
-
-Result ShaderObjectImpl::prepareToBindAsParameterBlock(
- BindingContext* context,
- BindingOffset& ioOffset,
- ShaderObjectLayoutImpl* specializedLayout,
- DescriptorSet& outDescriptorSet)
-{
- auto transientHeap = context->transientHeap;
- auto submitter = context->submitter;
-
- // When writing into the new descriptor set, resource and sampler
- // descriptors will need to start at index zero in the respective
- // tables.
- //
- ioOffset.resource = 0;
- ioOffset.sampler = 0;
-
- // The index of the next root parameter to bind will be maintained,
- // but needs to be incremented by the number of descriptor tables
- // we allocate (zero or one resource table and zero or one sampler
- // table).
- //
- auto& rootParamIndex = ioOffset.rootParam;
-
- if (auto descriptorCount = specializedLayout->getTotalResourceDescriptorCount())
- {
- // There is a non-zero number of resource descriptors needed,
- // so we will allocate a table out of the appropriate heap,
- // and store it into the appropriate part of `descriptorSet`.
- //
- auto descriptorHeap = &transientHeap->getCurrentViewHeap();
- auto& table = outDescriptorSet.resourceTable;
-
- // Allocate the table.
- //
- if (!table.allocate(descriptorHeap, descriptorCount))
- {
- context->outOfMemoryHeap = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
- return SLANG_E_OUT_OF_MEMORY;
- }
-
- // Bind the table to the pipeline, consuming the next available
- // root parameter.
- //
- auto tableRootParamIndex = rootParamIndex++;
- submitter->setRootDescriptorTable(tableRootParamIndex, table.getGpuHandle());
- }
- if (auto descriptorCount = specializedLayout->getTotalSamplerDescriptorCount())
- {
- // There is a non-zero number of sampler descriptors needed,
- // so we will allocate a table out of the appropriate heap,
- // and store it into the appropriate part of `descriptorSet`.
- //
- auto descriptorHeap = &transientHeap->getCurrentSamplerHeap();
- auto& table = outDescriptorSet.samplerTable;
-
- // Allocate the table.
- //
- if (!table.allocate(descriptorHeap, descriptorCount))
- {
- context->outOfMemoryHeap = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
- return SLANG_E_OUT_OF_MEMORY;
- }
-
- // Bind the table to the pipeline, consuming the next available
- // root parameter.
- //
- auto tableRootParamIndex = rootParamIndex++;
- submitter->setRootDescriptorTable(tableRootParamIndex, table.getGpuHandle());
- }
-
- return SLANG_OK;
-}
-
-bool ShaderObjectImpl::checkIfCachedDescriptorSetIsValidRecursive(BindingContext* context)
-{
- if (shouldAllocateConstantBuffer(context->transientHeap))
- return false;
- if (m_isMutable && m_version != m_cachedGPUDescriptorSetVersion)
- return false;
- if (m_cachedGPUDescriptorSet.resourceTable.getDescriptorCount() != 0 &&
- m_cachedGPUDescriptorSet.resourceTable.m_heap.ptr.linearHeap->getHeap() !=
- m_cachedTransientHeap->getCurrentViewHeap().getHeap())
- return false;
- if (m_cachedGPUDescriptorSet.samplerTable.getDescriptorCount() != 0 &&
- m_cachedGPUDescriptorSet.samplerTable.m_heap.ptr.linearHeap->getHeap() !=
- m_cachedTransientHeap->getCurrentSamplerHeap().getHeap())
- return false;
-
- auto& subObjectRanges = getLayout()->getSubObjectRanges();
- for (Slang::Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRanges.getCount();
- subObjectRangeIndex++)
- {
- auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
- auto const& bindingRange = getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
- if (bindingRange.bindingType != slang::BindingType::ParameterBlock)
- continue;
- Slang::Index count = bindingRange.count;
-
- for (Slang::Index subObjectIndexInRange = 0; subObjectIndexInRange < count;
- subObjectIndexInRange++)
- {
- Slang::Index objectIndex = bindingRange.subObjectIndex + subObjectIndexInRange;
- auto subObject = m_objects[objectIndex].Ptr();
- if (!subObject)
- continue;
- if (subObject->checkIfCachedDescriptorSetIsValidRecursive(context))
- return false;
- }
- }
- return true;
-}
-
-/// Bind this object as a `ParameterBlock<X>`
-
-Result ShaderObjectImpl::bindAsParameterBlock(
- BindingContext* context, BindingOffset const& offset, ShaderObjectLayoutImpl* specializedLayout)
-{
- if (checkIfCachedDescriptorSetIsValidRecursive(context))
- {
- // If we already have a valid gpu descriptor table in the current
- // heap, bind it.
- auto rootParamIndex = offset.rootParam;
- if (m_cachedGPUDescriptorSet.resourceTable.getDescriptorCount())
- {
- auto tableRootParamIndex = rootParamIndex++;
- context->submitter->setRootDescriptorTable(
- tableRootParamIndex, m_cachedGPUDescriptorSet.resourceTable.getGpuHandle());
- }
- if (m_cachedGPUDescriptorSet.samplerTable.getDescriptorCount())
- {
- auto tableRootParamIndex = rootParamIndex++;
- context->submitter->setRootDescriptorTable(
- tableRootParamIndex, m_cachedGPUDescriptorSet.samplerTable.getGpuHandle());
- }
- return SLANG_OK;
- }
-
- // The first step to binding an object as a parameter block is to allocate a descriptor
- // set (consisting of zero or one resource descriptor table and zero or one sampler
- // descriptor table) to represent its values.
- //
- BindingOffset subOffset = offset;
- SLANG_RETURN_ON_FAIL(prepareToBindAsParameterBlock(
- context, /* inout */ subOffset, specializedLayout, m_cachedGPUDescriptorSet));
-
- // Next we bind the object into that descriptor set as if it were being used
- // as a `ConstantBuffer<X>`.
- //
- SLANG_RETURN_ON_FAIL(
- bindAsConstantBuffer(context, m_cachedGPUDescriptorSet, subOffset, specializedLayout));
-
- m_cachedGPUDescriptorSetVersion = m_version;
- return SLANG_OK;
-}
-
-/// Bind this object as a `ConstantBuffer<X>`
-
-Result ShaderObjectImpl::bindAsConstantBuffer(
- BindingContext* context,
- DescriptorSet const& descriptorSet,
- BindingOffset const& offset,
- ShaderObjectLayoutImpl* specializedLayout)
-{
- // If we are to bind as a constant buffer we first need to ensure that
- // the ordinary data buffer is created, if this object needs one.
- //
- SLANG_RETURN_ON_FAIL(
- _ensureOrdinaryDataBufferCreatedIfNeeded(context->encoder, specializedLayout));
-
- // Next, we need to bind all of the resource descriptors for this object
- // (including any ordinary data buffer) into the provided `descriptorSet`.
- //
- auto resourceCount = specializedLayout->getResourceSlotCount();
- if (resourceCount)
- {
- auto& dstTable = descriptorSet.resourceTable;
- auto& srcTable = m_descriptorSet.resourceTable;
-
- context->device->m_device->CopyDescriptorsSimple(
- UINT(resourceCount),
- dstTable.getCpuHandle(offset.resource),
- srcTable.getCpuHandle(),
- D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
- }
-
- // Finally, we delegate to `_bindImpl` to bind samplers and sub-objects,
- // since the logic is shared with the `bindAsValue()` case below.
- //
- SLANG_RETURN_ON_FAIL(_bindImpl(context, descriptorSet, offset, specializedLayout));
- return SLANG_OK;
-}
-
-/// Bind this object as a value (for an interface-type parameter)
-
-Result ShaderObjectImpl::bindAsValue(
- BindingContext* context,
- DescriptorSet const& descriptorSet,
- BindingOffset const& offset,
- ShaderObjectLayoutImpl* specializedLayout)
-{
- // When binding a value for an interface-type field we do *not* want
- // to bind a buffer for the ordinary data (if there is any) because
- // ordinary data for interface-type fields gets allocated into the
- // parent object's ordinary data buffer.
- //
- // This CPU-memory descriptor table that holds resource descriptors
- // will have already been allocated to have space for an ordinary data
- // buffer (if needed), so we need to take care to skip over that
- // descriptor when copying descriptors from the CPU-memory set
- // to the GPU-memory `descriptorSet`.
- //
- auto skipResourceCount = specializedLayout->getOrdinaryDataBufferCount();
- auto resourceCount = specializedLayout->getResourceSlotCount() - skipResourceCount;
- if (resourceCount)
- {
- auto& dstTable = descriptorSet.resourceTable;
- auto& srcTable = m_descriptorSet.resourceTable;
-
- context->device->m_device->CopyDescriptorsSimple(
- UINT(resourceCount),
- dstTable.getCpuHandle(offset.resource),
- srcTable.getCpuHandle(skipResourceCount),
- D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
- }
-
- // Finally, we delegate to `_bindImpl` to bind samplers and sub-objects,
- // since the logic is shared with the `bindAsConstantBuffer()` case above.
- //
- // Note: Just like we had to do some subtle handling of the ordinary data buffer
- // above, here we need to contend with the fact that the `offset.resource` fields
- // computed for sub-object ranges were baked to take the ordinary data buffer
- // into account, so that if `skipResourceCount` is non-zero then they are all
- // too high by `skipResourceCount`.
- //
- // We will address the problem here by computing a modified offset that adjusts
- // for the ordinary data buffer that we have not bound after all.
- //
- BindingOffset subOffset = offset;
- subOffset.resource -= skipResourceCount;
- SLANG_RETURN_ON_FAIL(_bindImpl(context, descriptorSet, subOffset, specializedLayout));
- return SLANG_OK;
-}
-
-/// Shared logic for `bindAsConstantBuffer()` and `bindAsValue()`
-
-Result ShaderObjectImpl::_bindImpl(
- BindingContext* context,
- DescriptorSet const& descriptorSet,
- BindingOffset const& offset,
- ShaderObjectLayoutImpl* specializedLayout)
-{
- // We start by binding all the sampler decriptors, if needed.
- //
- // Note: resource descriptors were handled in either `bindAsConstantBuffer()`
- // or `bindAsValue()` before calling into `_bindImpl()`.
- //
- if (auto samplerCount = specializedLayout->getSamplerSlotCount())
- {
- auto& dstTable = descriptorSet.samplerTable;
- auto& srcTable = m_descriptorSet.samplerTable;
-
- context->device->m_device->CopyDescriptorsSimple(
- UINT(samplerCount),
- dstTable.getCpuHandle(offset.sampler),
- srcTable.getCpuHandle(),
- D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
- }
-
- // Next we iterate over the sub-object ranges and bind anything they require.
- //
- auto& subObjectRanges = specializedLayout->getSubObjectRanges();
- auto subObjectRangeCount = subObjectRanges.getCount();
- for (Index i = 0; i < subObjectRangeCount; i++)
- {
- auto& subObjectRange = specializedLayout->getSubObjectRange(i);
- auto& bindingRange = specializedLayout->getBindingRange(subObjectRange.bindingRangeIndex);
- auto subObjectIndex = bindingRange.subObjectIndex;
- auto subObjectLayout = subObjectRange.layout.Ptr();
-
- BindingOffset rangeOffset = offset;
- rangeOffset += subObjectRange.offset;
-
- BindingOffset rangeStride = subObjectRange.stride;
-
- switch (bindingRange.bindingType)
- {
- case slang::BindingType::ConstantBuffer:
- {
- auto objOffset = rangeOffset;
- for (uint32_t j = 0; j < bindingRange.count; j++)
- {
- auto& object = m_objects[subObjectIndex + j];
- SLANG_RETURN_ON_FAIL(object->bindAsConstantBuffer(
- context, descriptorSet, objOffset, subObjectLayout));
- objOffset += rangeStride;
- }
- }
- break;
-
- case slang::BindingType::ParameterBlock:
- {
- auto objOffset = rangeOffset;
- for (uint32_t j = 0; j < bindingRange.count; j++)
- {
- auto& object = m_objects[subObjectIndex + j];
- SLANG_RETURN_ON_FAIL(
- object->bindAsParameterBlock(context, objOffset, subObjectLayout));
- objOffset += rangeStride;
- }
- }
- break;
-
- case slang::BindingType::ExistentialValue:
- if (subObjectLayout)
- {
- auto objOffset = rangeOffset;
- for (uint32_t j = 0; j < bindingRange.count; j++)
- {
- auto& object = m_objects[subObjectIndex + j];
- SLANG_RETURN_ON_FAIL(
- object->bindAsValue(context, descriptorSet, objOffset, subObjectLayout));
- objOffset += rangeStride;
- }
- }
- break;
- }
- }
-
- return SLANG_OK;
-}
-
-Result ShaderObjectImpl::bindRootArguments(BindingContext* context, uint32_t& index)
-{
- auto layoutImpl = getLayout();
- for (Index i = 0; i < m_rootArguments.getCount(); i++)
- {
- switch (layoutImpl->getRootParameterInfo(i).type)
- {
- case IResourceView::Type::ShaderResource:
- case IResourceView::Type::AccelerationStructure:
- context->submitter->setRootSRV(index, m_rootArguments[i]);
- break;
- case IResourceView::Type::UnorderedAccess:
- context->submitter->setRootUAV(index, m_rootArguments[i]);
- break;
- default:
- continue;
- }
- index++;
- }
- for (auto& subObject : m_objects)
- {
- if (subObject)
- {
- SLANG_RETURN_ON_FAIL(subObject->bindRootArguments(context, index));
- }
- }
- return SLANG_OK;
-}
-
-/// Get the layout of this shader object with specialization arguments considered
-///
-/// This operation should only be called after the shader object has been
-/// fully filled in and finalized.
-///
-
-Result ShaderObjectImpl::getSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
-{
- if (!m_specializedLayout)
- {
- SLANG_RETURN_ON_FAIL(_createSpecializedLayout(m_specializedLayout.writeRef()));
- }
- returnRefPtr(outLayout, m_specializedLayout);
- return SLANG_OK;
-}
-
-/// Create the layout for this shader object with specialization arguments considered
-///
-/// This operation is virtual so that it can be customized by `RootShaderObject`.
-///
-
-Result ShaderObjectImpl::_createSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
-{
- ExtendedShaderObjectType extendedType;
- SLANG_RETURN_ON_FAIL(getSpecializedShaderObjectType(&extendedType));
-
- auto renderer = getRenderer();
- RefPtr<ShaderObjectLayoutImpl> layout;
- SLANG_RETURN_ON_FAIL(renderer->getShaderObjectLayout(
- extendedType.slangType,
- m_layout->getContainerType(),
- (ShaderObjectLayoutBase**)layout.writeRef()));
-
- returnRefPtrMove(outLayout, layout);
- return SLANG_OK;
-}
-
-Result ShaderObjectImpl::setResource(ShaderOffset const& offset, IResourceView* resourceView)
-{
- if (offset.bindingRangeIndex < 0)
- return SLANG_E_INVALID_ARG;
- auto layout = getLayout();
- if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
- return SLANG_E_INVALID_ARG;
-
- m_version++;
-
- ID3D12Device* d3dDevice = static_cast<DeviceImpl*>(getDevice())->m_device;
-
- auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
-
- if (bindingRange.isRootParameter && resourceView)
- {
- auto& rootArg = m_rootArguments[bindingRange.baseIndex];
- switch (resourceView->getViewDesc()->type)
- {
- case IResourceView::Type::AccelerationStructure:
- {
- auto resourceViewImpl = static_cast<AccelerationStructureImpl*>(resourceView);
- rootArg = resourceViewImpl->getDeviceAddress();
- }
- break;
- case IResourceView::Type::ShaderResource:
- case IResourceView::Type::UnorderedAccess:
- {
- auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView);
- if (resourceViewImpl->m_resource->isBuffer())
- {
- rootArg = static_cast<BufferResourceImpl*>(resourceViewImpl->m_resource.Ptr())
- ->getDeviceAddress();
- }
- else
- {
- getDebugCallback()->handleMessage(
- DebugMessageType::Error,
- DebugMessageSource::Layer,
- "The shader parameter at the specified offset is a root parameter, and "
- "therefore can only be a buffer view.");
- return SLANG_FAIL;
- }
- }
- break;
- }
- return SLANG_OK;
- }
-
- if (resourceView == nullptr)
- {
- // Create null descriptor for the binding.
- auto destDescriptor = m_descriptorSet.resourceTable.getCpuHandle(
- bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex);
- return createNullDescriptor(d3dDevice, destDescriptor, bindingRange);
- }
-
- ResourceViewInternalImpl* internalResourceView = nullptr;
- switch (resourceView->getViewDesc()->type)
- {
-#if SLANG_GFX_HAS_DXR_SUPPORT
- case IResourceView::Type::AccelerationStructure:
- {
- auto asImpl = static_cast<AccelerationStructureImpl*>(resourceView);
- // Hold a reference to the resource to prevent its destruction.
- m_boundResources[bindingRange.baseIndex + offset.bindingArrayIndex] = asImpl->m_buffer;
- internalResourceView = asImpl;
- }
- break;
-#endif
- default:
- {
- auto resourceViewImpl = static_cast<ResourceViewImpl*>(resourceView);
- // Hold a reference to the resource to prevent its destruction.
- m_boundResources[bindingRange.baseIndex + offset.bindingArrayIndex] =
- resourceViewImpl->m_resource;
- internalResourceView = resourceViewImpl;
- }
- break;
- }
-
- auto descriptorSlotIndex = bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex;
- if (internalResourceView->m_descriptor.cpuHandle.ptr)
- {
- d3dDevice->CopyDescriptorsSimple(
- 1,
- m_descriptorSet.resourceTable.getCpuHandle(
- bindingRange.baseIndex + (int32_t)offset.bindingArrayIndex),
- internalResourceView->m_descriptor.cpuHandle,
- D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
- }
- else
- {
- getDebugCallback()->handleMessage(
- DebugMessageType::Error,
- DebugMessageSource::Layer,
- "IShaderObject::setResource: the resource view cannot be set to this shader parameter. "
- "A possible reason is that the view is too large to be supported by D3D12.");
- return SLANG_FAIL;
- }
- return SLANG_OK;
-}
-
-void PipelineStateImpl::init(const GraphicsPipelineStateDesc& inDesc)
-{
- PipelineStateDesc pipelineDesc;
- pipelineDesc.type = PipelineType::Graphics;
- pipelineDesc.graphics = inDesc;
- initializeBase(pipelineDesc);
-}
-
-void PipelineStateImpl::init(const ComputePipelineStateDesc& inDesc)
-{
- PipelineStateDesc pipelineDesc;
- pipelineDesc.type = PipelineType::Compute;
- pipelineDesc.compute = inDesc;
- initializeBase(pipelineDesc);
-}
-
-Result PipelineStateImpl::getNativeHandle(InteropHandle* outHandle)
-{
- SLANG_RETURN_ON_FAIL(ensureAPIPipelineStateCreated());
- outHandle->api = InteropHandleAPI::D3D12;
- outHandle->handleValue = reinterpret_cast<uint64_t>(m_pipelineState.get());
- return SLANG_OK;
-}
-
-Result PipelineStateImpl::ensureAPIPipelineStateCreated()
-{
- if (m_pipelineState)
- return SLANG_OK;
-
- auto programImpl = static_cast<ShaderProgramImpl*>(m_program.Ptr());
- if (programImpl->m_shaders.getCount() == 0)
- {
- SLANG_RETURN_ON_FAIL(programImpl->compileShaders());
- }
- if (desc.type == PipelineType::Graphics)
- {
- // Only actually create a D3D12 pipeline state if the pipeline is fully specialized.
- auto inputLayoutImpl = (InputLayoutImpl*)desc.graphics.inputLayout;
-
- // Describe and create the graphics pipeline state object (PSO)
- D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
-
- psoDesc.pRootSignature = programImpl->m_rootObjectLayout->m_rootSignature;
-
- for (auto& shaderBin : programImpl->m_shaders)
- {
- switch (shaderBin.stage)
- {
- case SLANG_STAGE_VERTEX:
- psoDesc.VS = {shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount())};
- break;
- case SLANG_STAGE_FRAGMENT:
- psoDesc.PS = {shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount())};
- break;
- case SLANG_STAGE_DOMAIN:
- psoDesc.DS = {shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount())};
- break;
- case SLANG_STAGE_HULL:
- psoDesc.HS = {shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount())};
- break;
- case SLANG_STAGE_GEOMETRY:
- psoDesc.GS = {shaderBin.code.getBuffer(), SIZE_T(shaderBin.code.getCount())};
- break;
- default:
- getDebugCallback()->handleMessage(
- DebugMessageType::Error,
- DebugMessageSource::Layer,
- "Unsupported shader stage.");
- return SLANG_E_NOT_AVAILABLE;
- }
- }
-
- if (inputLayoutImpl)
- {
- psoDesc.InputLayout = {
- inputLayoutImpl->m_elements.getBuffer(),
- UINT(inputLayoutImpl->m_elements.getCount())};
- }
-
- psoDesc.PrimitiveTopologyType = D3DUtil::getPrimitiveType(desc.graphics.primitiveType);
-
- {
- auto framebufferLayout =
- static_cast<FramebufferLayoutImpl*>(desc.graphics.framebufferLayout);
- const int numRenderTargets = int(framebufferLayout->m_renderTargets.getCount());
-
- if (framebufferLayout->m_hasDepthStencil)
- {
- psoDesc.DSVFormat = D3DUtil::getMapFormat(framebufferLayout->m_depthStencil.format);
- psoDesc.SampleDesc.Count = framebufferLayout->m_depthStencil.sampleCount;
- }
- else
- {
- psoDesc.DSVFormat = DXGI_FORMAT_UNKNOWN;
- if (framebufferLayout->m_renderTargets.getCount())
- {
- psoDesc.SampleDesc.Count = framebufferLayout->m_renderTargets[0].sampleCount;
- }
- }
- psoDesc.NumRenderTargets = numRenderTargets;
- for (Int i = 0; i < numRenderTargets; i++)
- {
- psoDesc.RTVFormats[i] =
- D3DUtil::getMapFormat(framebufferLayout->m_renderTargets[i].format);
- }
-
- psoDesc.SampleDesc.Quality = 0;
- psoDesc.SampleMask = UINT_MAX;
- }
-
- {
- auto& rs = psoDesc.RasterizerState;
- rs.FillMode = D3DUtil::getFillMode(desc.graphics.rasterizer.fillMode);
- rs.CullMode = D3DUtil::getCullMode(desc.graphics.rasterizer.cullMode);
- rs.FrontCounterClockwise =
- desc.graphics.rasterizer.frontFace == gfx::FrontFaceMode::CounterClockwise ? TRUE
- : FALSE;
- rs.DepthBias = desc.graphics.rasterizer.depthBias;
- rs.DepthBiasClamp = desc.graphics.rasterizer.depthBiasClamp;
- rs.SlopeScaledDepthBias = desc.graphics.rasterizer.slopeScaledDepthBias;
- rs.DepthClipEnable = desc.graphics.rasterizer.depthClipEnable ? TRUE : FALSE;
- rs.MultisampleEnable = desc.graphics.rasterizer.multisampleEnable ? TRUE : FALSE;
- rs.AntialiasedLineEnable =
- desc.graphics.rasterizer.antialiasedLineEnable ? TRUE : FALSE;
- rs.ForcedSampleCount = desc.graphics.rasterizer.forcedSampleCount;
- rs.ConservativeRaster = desc.graphics.rasterizer.enableConservativeRasterization
- ? D3D12_CONSERVATIVE_RASTERIZATION_MODE_ON
- : D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
- }
-
- {
- D3D12_BLEND_DESC& blend = psoDesc.BlendState;
- blend.IndependentBlendEnable = FALSE;
- blend.AlphaToCoverageEnable = desc.graphics.blend.alphaToCoverageEnable ? TRUE : FALSE;
- blend.RenderTarget[0].RenderTargetWriteMask = (uint8_t)RenderTargetWriteMask::EnableAll;
- for (GfxIndex i = 0; i < desc.graphics.blend.targetCount; i++)
- {
- auto& d3dDesc = blend.RenderTarget[i];
- d3dDesc.BlendEnable = desc.graphics.blend.targets[i].enableBlend ? TRUE : FALSE;
- d3dDesc.BlendOp = D3DUtil::getBlendOp(desc.graphics.blend.targets[i].color.op);
- d3dDesc.BlendOpAlpha = D3DUtil::getBlendOp(desc.graphics.blend.targets[i].alpha.op);
- d3dDesc.DestBlend =
- D3DUtil::getBlendFactor(desc.graphics.blend.targets[i].color.dstFactor);
- d3dDesc.DestBlendAlpha =
- D3DUtil::getBlendFactor(desc.graphics.blend.targets[i].alpha.dstFactor);
- d3dDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
- d3dDesc.LogicOpEnable = FALSE;
- d3dDesc.RenderTargetWriteMask = desc.graphics.blend.targets[i].writeMask;
- d3dDesc.SrcBlend =
- D3DUtil::getBlendFactor(desc.graphics.blend.targets[i].color.srcFactor);
- d3dDesc.SrcBlendAlpha =
- D3DUtil::getBlendFactor(desc.graphics.blend.targets[i].alpha.srcFactor);
- }
- for (GfxIndex i = 1; i < desc.graphics.blend.targetCount; i++)
- {
- if (memcmp(
- &desc.graphics.blend.targets[i],
- &desc.graphics.blend.targets[0],
- sizeof(desc.graphics.blend.targets[0])) != 0)
- {
- blend.IndependentBlendEnable = TRUE;
- break;
- }
- }
- for (uint32_t i = (uint32_t)desc.graphics.blend.targetCount;
- i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT;
- ++i)
- {
- blend.RenderTarget[i] = blend.RenderTarget[0];
- }
- }
-
- {
- auto& ds = psoDesc.DepthStencilState;
-
- ds.DepthEnable = desc.graphics.depthStencil.depthTestEnable;
- ds.DepthWriteMask = desc.graphics.depthStencil.depthWriteEnable
- ? D3D12_DEPTH_WRITE_MASK_ALL
- : D3D12_DEPTH_WRITE_MASK_ZERO;
- ds.DepthFunc = D3DUtil::getComparisonFunc(desc.graphics.depthStencil.depthFunc);
- ds.StencilEnable = desc.graphics.depthStencil.stencilEnable;
- ds.StencilReadMask = (UINT8)desc.graphics.depthStencil.stencilReadMask;
- ds.StencilWriteMask = (UINT8)desc.graphics.depthStencil.stencilWriteMask;
- ds.FrontFace = D3DUtil::translateStencilOpDesc(desc.graphics.depthStencil.frontFace);
- ds.BackFace = D3DUtil::translateStencilOpDesc(desc.graphics.depthStencil.backFace);
- }
-
- psoDesc.PrimitiveTopologyType = D3DUtil::getPrimitiveType(desc.graphics.primitiveType);
-
- if (m_device->m_pipelineCreationAPIDispatcher)
- {
- SLANG_RETURN_ON_FAIL(
- m_device->m_pipelineCreationAPIDispatcher->createGraphicsPipelineState(
- m_device,
- programImpl->linkedProgram.get(),
- &psoDesc,
- (void**)m_pipelineState.writeRef()));
- }
- else
- {
- SLANG_RETURN_ON_FAIL(m_device->m_device->CreateGraphicsPipelineState(
- &psoDesc, IID_PPV_ARGS(m_pipelineState.writeRef())));
- }
- }
- else
- {
-
- // Only actually create a D3D12 pipeline state if the pipeline is fully specialized.
- ComPtr<ID3D12PipelineState> pipelineState;
- if (!programImpl->isSpecializable())
- {
- // Describe and create the compute pipeline state object
- D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {};
- computeDesc.pRootSignature =
- desc.compute.d3d12RootSignatureOverride
- ? static_cast<ID3D12RootSignature*>(desc.compute.d3d12RootSignatureOverride)
- : programImpl->m_rootObjectLayout->m_rootSignature;
- computeDesc.CS = {
- programImpl->m_shaders[0].code.getBuffer(),
- SIZE_T(programImpl->m_shaders[0].code.getCount())};
-
-#ifdef GFX_NVAPI
- if (m_nvapi)
- {
- // Also fill the extension structure.
- // Use the same UAV slot index and register space that are declared in the shader.
-
- // For simplicities sake we just use u0
- NVAPI_D3D12_PSO_SET_SHADER_EXTENSION_SLOT_DESC extensionDesc;
- extensionDesc.baseVersion = NV_PSO_EXTENSION_DESC_VER;
- extensionDesc.version = NV_SET_SHADER_EXTENSION_SLOT_DESC_VER;
- extensionDesc.uavSlot = 0;
- extensionDesc.registerSpace = 0;
-
- // Put the pointer to the extension into an array - there can be multiple extensions
- // enabled at once.
- const NVAPI_D3D12_PSO_EXTENSION_DESC* extensions[] = {&extensionDesc};
-
- // Now create the PSO.
- const NvAPI_Status nvapiStatus = NvAPI_D3D12_CreateComputePipelineState(
- m_device->m_device,
- &computeDesc,
- SLANG_COUNT_OF(extensions),
- extensions,
- m_pipelineState.writeRef());
-
- if (nvapiStatus != NVAPI_OK)
- {
- return SLANG_FAIL;
- }
- }
- else
-#endif
- {
- if (m_device->m_pipelineCreationAPIDispatcher)
- {
- SLANG_RETURN_ON_FAIL(
- m_device->m_pipelineCreationAPIDispatcher->createComputePipelineState(
- m_device,
- programImpl->linkedProgram.get(),
- &computeDesc,
- (void**)m_pipelineState.writeRef()));
- }
- else
- {
- SLANG_RETURN_ON_FAIL(m_device->m_device->CreateComputePipelineState(
- &computeDesc, IID_PPV_ARGS(m_pipelineState.writeRef())));
- }
- }
- }
- }
-
- return SLANG_OK;
-}
-
-// Swapchain Implementation
-
-Result SwapchainImpl::init(
- DeviceImpl* renderer, const ISwapchain::Desc& swapchainDesc, WindowHandle window)
-{
- m_queue = static_cast<CommandQueueImpl*>(swapchainDesc.queue)->m_d3dQueue;
- m_dxgiFactory = renderer->m_deviceInfo.m_dxgiFactory;
- SLANG_RETURN_ON_FAIL(
- D3DSwapchainBase::init(swapchainDesc, window, DXGI_SWAP_EFFECT_FLIP_DISCARD));
- renderer->m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef()));
-
- SLANG_RETURN_ON_FAIL(m_swapChain->QueryInterface(m_swapChain3.writeRef()));
- for (GfxIndex i = 0; i < swapchainDesc.imageCount; i++)
- {
- m_frameEvents.add(CreateEventEx(
- nullptr,
- false,
- CREATE_EVENT_INITIAL_SET | CREATE_EVENT_MANUAL_RESET,
- EVENT_ALL_ACCESS));
- }
- return SLANG_OK;
-}
-
-Result SwapchainImpl::resize(GfxCount width, GfxCount height)
-{
- for (auto evt : m_frameEvents)
- SetEvent(evt);
- SLANG_RETURN_ON_FAIL(D3DSwapchainBase::resize(width, height));
- return SLANG_OK;
-}
-
-void SwapchainImpl::createSwapchainBufferImages()
-{
- m_images.clear();
-
- for (GfxIndex i = 0; i < m_desc.imageCount; i++)
- {
- ComPtr<ID3D12Resource> d3dResource;
- m_swapChain->GetBuffer(i, IID_PPV_ARGS(d3dResource.writeRef()));
- ITextureResource::Desc imageDesc = {};
- imageDesc.allowedStates = ResourceStateSet(
- ResourceState::Present, ResourceState::RenderTarget, ResourceState::CopyDestination);
- imageDesc.type = IResource::Type::Texture2D;
- imageDesc.arraySize = 0;
- imageDesc.format = m_desc.format;
- imageDesc.size.width = m_desc.width;
- imageDesc.size.height = m_desc.height;
- imageDesc.size.depth = 1;
- imageDesc.numMipLevels = 1;
- imageDesc.defaultState = ResourceState::Present;
- RefPtr<TextureResourceImpl> image = new TextureResourceImpl(imageDesc);
- image->m_resource.setResource(d3dResource.get());
- image->m_defaultState = D3D12_RESOURCE_STATE_PRESENT;
- m_images.add(image);
- }
- for (auto evt : m_frameEvents)
- SetEvent(evt);
-}
-
-int SwapchainImpl::acquireNextImage()
-{
- auto result = (int)m_swapChain3->GetCurrentBackBufferIndex();
- WaitForSingleObject(m_frameEvents[result], INFINITE);
- ResetEvent(m_frameEvents[result]);
- return result;
-}
-
-Result SwapchainImpl::present()
-{
- m_fence->SetEventOnCompletion(
- fenceValue, m_frameEvents[m_swapChain3->GetCurrentBackBufferIndex()]);
- SLANG_RETURN_ON_FAIL(D3DSwapchainBase::present());
- fenceValue++;
- m_queue->Signal(m_fence, fenceValue);
- return SLANG_OK;
-}
-
-bool SwapchainImpl::isOccluded()
-{
- return (m_swapChain3->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED);
-}
-
-Result SwapchainImpl::setFullScreenMode(bool mode)
-{
- return m_swapChain3->SetFullscreenState(mode, nullptr);
-}
-
-// CommandQueue implementation.
-
-Result CommandQueueImpl::init(DeviceImpl* device, uint32_t queueIndex)
-{
- m_queueIndex = queueIndex;
- m_renderer = device;
- m_device = device->m_device;
- D3D12_COMMAND_QUEUE_DESC queueDesc = {};
- queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
- SLANG_RETURN_ON_FAIL(
- m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(m_d3dQueue.writeRef())));
- SLANG_RETURN_ON_FAIL(
- m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef())));
- globalWaitHandle = CreateEventEx(
- nullptr, nullptr, CREATE_EVENT_INITIAL_SET | CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
- return SLANG_OK;
-}
-
-CommandQueueImpl::~CommandQueueImpl()
-{
- waitOnHost();
- CloseHandle(globalWaitHandle);
- m_renderer->m_queueIndexAllocator.free((int)m_queueIndex, 1);
-}
-
-void CommandQueueImpl::executeCommandBuffers(
- GfxCount count, ICommandBuffer* const* commandBuffers, IFence* fence, uint64_t valueToSignal)
-{
- ShortList<ID3D12CommandList*> commandLists;
- for (GfxCount i = 0; i < count; i++)
- {
- auto cmdImpl = static_cast<CommandBufferImpl*>(commandBuffers[i]);
- commandLists.add(cmdImpl->m_cmdList);
- }
- if (count > 0)
- {
- m_d3dQueue->ExecuteCommandLists((UINT)count, commandLists.getArrayView().getBuffer());
-
- m_fenceValue++;
-
- for (GfxCount i = 0; i < count; i++)
- {
- if (i > 0 && commandBuffers[i] == commandBuffers[i - 1])
- continue;
- auto cmdImpl = static_cast<CommandBufferImpl*>(commandBuffers[i]);
- auto transientHeap = cmdImpl->m_transientHeap;
- auto& waitInfo = transientHeap->getQueueWaitInfo(m_queueIndex);
- waitInfo.waitValue = m_fenceValue;
- waitInfo.fence = m_fence;
- waitInfo.queue = m_d3dQueue;
- }
- }
-
- if (fence)
- {
- auto fenceImpl = static_cast<FenceImpl*>(fence);
- m_d3dQueue->Signal(fenceImpl->m_fence.get(), valueToSignal);
- }
-}
-
-void CommandQueueImpl::waitOnHost()
-{
- m_fenceValue++;
- m_d3dQueue->Signal(m_fence, m_fenceValue);
- ResetEvent(globalWaitHandle);
- m_fence->SetEventOnCompletion(m_fenceValue, globalWaitHandle);
- WaitForSingleObject(globalWaitHandle, INFINITE);
-}
-
-Result CommandQueueImpl::waitForFenceValuesOnDevice(
- GfxCount fenceCount, IFence** fences, uint64_t* waitValues)
-{
- for (GfxCount i = 0; i < fenceCount; ++i)
- {
- auto fenceImpl = static_cast<FenceImpl*>(fences[i]);
- m_d3dQueue->Wait(fenceImpl->m_fence.get(), waitValues[i]);
- }
- return SLANG_OK;
-}
-
-const CommandQueueImpl::Desc& CommandQueueImpl::getDesc() { return m_desc; }
-
-ICommandQueue* CommandQueueImpl::getInterface(const Guid& guid)
-{
- if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ICommandQueue)
- return static_cast<ICommandQueue*>(this);
- return nullptr;
-}
-
-Result CommandQueueImpl::getNativeHandle(InteropHandle* handle)
-{
- handle->api = InteropHandleAPI::D3D12;
- handle->handleValue = (uint64_t)m_d3dQueue.get();
- return SLANG_OK;
-}
-
-ResourceViewInternalImpl::~ResourceViewInternalImpl()
-{
- if (m_descriptor.cpuHandle.ptr)
- m_allocator->free(m_descriptor);
-}
-
-Result ResourceViewImpl::getNativeHandle(InteropHandle* outHandle)
-{
- outHandle->api = InteropHandleAPI::D3D12CpuDescriptorHandle;
- outHandle->handleValue = m_descriptor.cpuHandle.ptr;
- return SLANG_OK;
-}
-
-void RenderPassLayoutImpl::init(const IRenderPassLayout::Desc& desc)
-{
- SimpleRenderPassLayout::init(desc);
- m_framebufferLayout = static_cast<FramebufferLayoutImpl*>(desc.framebufferLayout);
- m_hasDepthStencil = m_framebufferLayout->m_hasDepthStencil;
-}
-
-ShaderObjectLayoutImpl::SubObjectRangeOffset::SubObjectRangeOffset(
- slang::VariableLayoutReflection* varLayout)
-{
- if (auto pendingLayout = varLayout->getPendingDataLayout())
- {
- pendingOrdinaryData = (uint32_t)pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM);
- }
-}
-
-ShaderObjectLayoutImpl::SubObjectRangeStride::SubObjectRangeStride(
- slang::TypeLayoutReflection* typeLayout)
-{
- if (auto pendingLayout = typeLayout->getPendingDataTypeLayout())
- {
- pendingOrdinaryData = (uint32_t)pendingLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
- }
-}
-
-bool ShaderObjectLayoutImpl::isBindingRangeRootParameter(
- SlangSession* globalSession,
- const char* rootParameterAttributeName,
- slang::TypeLayoutReflection* typeLayout,
- Index bindingRangeIndex)
-{
- bool isRootParameter = false;
- if (rootParameterAttributeName)
- {
- if (auto leafVariable = typeLayout->getBindingRangeLeafVariable(bindingRangeIndex))
- {
- if (leafVariable->findUserAttributeByName(globalSession, rootParameterAttributeName))
- {
- isRootParameter = true;
- }
- }
- }
- return isRootParameter;
-}
-
-Result ShaderObjectLayoutImpl::createForElementType(
- RendererBase* renderer,
- slang::TypeLayoutReflection* elementType,
- ShaderObjectLayoutImpl** outLayout)
-{
- Builder builder(renderer);
- builder.setElementTypeLayout(elementType);
- return builder.build(outLayout);
-}
-
-Result ShaderObjectLayoutImpl::init(Builder* builder)
-{
- auto renderer = builder->m_renderer;
-
- initBase(renderer, builder->m_elementTypeLayout);
-
- m_containerType = builder->m_containerType;
-
- m_bindingRanges = _Move(builder->m_bindingRanges);
- m_subObjectRanges = _Move(builder->m_subObjectRanges);
- m_rootParamsInfo = _Move(builder->m_rootParamsInfo);
-
- m_ownCounts = builder->m_ownCounts;
- m_totalCounts = builder->m_totalCounts;
- m_subObjectCount = builder->m_subObjectCount;
- m_childRootParameterCount = builder->m_childRootParameterCount;
- m_totalOrdinaryDataSize = builder->m_totalOrdinaryDataSize;
-
- return SLANG_OK;
-}
-
-Result ShaderObjectLayoutImpl::Builder::setElementTypeLayout(
- slang::TypeLayoutReflection* typeLayout)
-{
- typeLayout = _unwrapParameterGroups(typeLayout, m_containerType);
- m_elementTypeLayout = typeLayout;
-
- // If the type contains any ordinary data, then we must reserve a buffer
- // descriptor to hold it when binding as a parameter block.
- //
- m_totalOrdinaryDataSize = (uint32_t)typeLayout->getSize();
- if (m_totalOrdinaryDataSize != 0)
- {
- m_ownCounts.resource++;
- }
-
- // We will scan over the reflected Slang binding ranges and add them
- // to our array. There are two main things we compute along the way:
- //
- // * For each binding range we compute a `flatIndex` that can be
- // used to identify where the values for the given range begin
- // in the flattened arrays (e.g., `m_objects`) and descriptor
- // tables that hold the state of a shader object.
- //
- // * We also update the various counters taht keep track of the number
- // of sub-objects, resources, samplers, etc. that are being
- // consumed. These counters will contribute to figuring out
- // the descriptor table(s) that might be needed to represent
- // the object.
- //
- SlangInt bindingRangeCount = typeLayout->getBindingRangeCount();
- for (SlangInt r = 0; r < bindingRangeCount; ++r)
- {
- slang::BindingType slangBindingType = typeLayout->getBindingRangeType(r);
- uint32_t count = (uint32_t)typeLayout->getBindingRangeBindingCount(r);
- slang::TypeLayoutReflection* slangLeafTypeLayout =
- typeLayout->getBindingRangeLeafTypeLayout(r);
- BindingRangeInfo bindingRangeInfo = {};
- bindingRangeInfo.bindingType = slangBindingType;
- bindingRangeInfo.resourceShape = slangLeafTypeLayout->getResourceShape();
- bindingRangeInfo.count = count;
- bindingRangeInfo.isRootParameter = isBindingRangeRootParameter(
- m_renderer->slangContext.globalSession,
- static_cast<DeviceImpl*>(m_renderer)->m_extendedDesc.rootParameterShaderAttributeName,
- typeLayout,
- r);
- if (bindingRangeInfo.isRootParameter)
- {
- RootParameterInfo rootInfo = {};
- switch (slangBindingType)
- {
- case slang::BindingType::RayTracingAccelerationStructure:
- rootInfo.type = IResourceView::Type::AccelerationStructure;
- break;
- case slang::BindingType::RawBuffer:
- case slang::BindingType::TypedBuffer:
- rootInfo.type = IResourceView::Type::ShaderResource;
- break;
- case slang::BindingType::MutableRawBuffer:
- case slang::BindingType::MutableTypedBuffer:
- rootInfo.type = IResourceView::Type::UnorderedAccess;
- break;
- }
- bindingRangeInfo.baseIndex = (uint32_t)m_rootParamsInfo.getCount();
- for (uint32_t i = 0; i < count; i++)
- {
- m_rootParamsInfo.add(rootInfo);
- }
- }
- else
- {
- switch (slangBindingType)
- {
- case slang::BindingType::ConstantBuffer:
- case slang::BindingType::ParameterBlock:
- case slang::BindingType::ExistentialValue:
- bindingRangeInfo.baseIndex = m_subObjectCount;
- bindingRangeInfo.subObjectIndex = m_subObjectCount;
- m_subObjectCount += count;
- break;
- case slang::BindingType::RawBuffer:
- case slang::BindingType::MutableRawBuffer:
- if (slangLeafTypeLayout->getType()->getElementType() != nullptr)
- {
- // A structured buffer occupies both a resource slot and
- // a sub-object slot.
- bindingRangeInfo.subObjectIndex = m_subObjectCount;
- m_subObjectCount += count;
- }
- bindingRangeInfo.baseIndex = m_ownCounts.resource;
- m_ownCounts.resource += count;
- break;
- case slang::BindingType::Sampler:
- bindingRangeInfo.baseIndex = m_ownCounts.sampler;
- m_ownCounts.sampler += count;
- break;
-
- case slang::BindingType::CombinedTextureSampler:
- // TODO: support this case...
- break;
-
- case slang::BindingType::VaryingInput:
- case slang::BindingType::VaryingOutput:
- break;
-
- default:
- bindingRangeInfo.baseIndex = m_ownCounts.resource;
- m_ownCounts.resource += count;
- break;
- }
- }
- m_bindingRanges.add(bindingRangeInfo);
- }
-
- // At this point we've computed the number of resources/samplers that
- // the type needs to represent its *own* state, and stored those counts
- // in `m_ownCounts`. Next we need to consider any resources/samplers
- // and root parameters needed to represent the state of the transitive
- // sub-objects of this objet, so that we can compute the total size
- // of the object when bound to the pipeline.
-
- m_totalCounts = m_ownCounts;
-
- SlangInt subObjectRangeCount = typeLayout->getSubObjectRangeCount();
- for (SlangInt r = 0; r < subObjectRangeCount; ++r)
- {
- SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r);
- auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
- auto count = (uint32_t)typeLayout->getBindingRangeBindingCount(bindingRangeIndex);
- slang::TypeLayoutReflection* slangLeafTypeLayout =
- typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
-
- // A sub-object range can either represent a sub-object of a known
- // type, like a `ConstantBuffer<Foo>` or `ParameterBlock<Foo>`
- // (in which case we can pre-compute a layout to use, based on
- // the type `Foo`) *or* it can represent a sub-object of some
- // existential type (e.g., `IBar`) in which case we cannot
- // know the appropraite type/layout of sub-object to allocate.
- //
- RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
- if (slangBindingType == slang::BindingType::ExistentialValue)
- {
- if (auto pendingTypeLayout = slangLeafTypeLayout->getPendingDataTypeLayout())
- {
- createForElementType(m_renderer, pendingTypeLayout, subObjectLayout.writeRef());
- }
- }
- else
- {
- createForElementType(
- m_renderer,
- slangLeafTypeLayout->getElementTypeLayout(),
- subObjectLayout.writeRef());
- }
-
- SubObjectRangeInfo subObjectRange;
- subObjectRange.bindingRangeIndex = bindingRangeIndex;
- subObjectRange.layout = subObjectLayout;
-
- // The Slang reflection API stors offset information for sub-object ranges,
- // and we care about *some* of that information: in particular, we need
- // the offset of sub-objects in terms of uniform/ordinary data for the
- // cases where we need to fill in "pending" data in our ordinary buffer.
- //
- subObjectRange.offset = SubObjectRangeOffset(typeLayout->getSubObjectRangeOffset(r));
- subObjectRange.stride = SubObjectRangeStride(slangLeafTypeLayout);
-
- // The remaining offset information is computed based on the counters
- // we are generating here, which depend only on the in-memory layout
- // decisions being made in our implementation. Remember that the
- // `register` and `space` values coming from DXBC/DXIL do *not*
- // dictate the in-memory layout we use.
- //
- // Note: One subtle point here is that the `.rootParam` offset we are computing
- // here does *not* include any root parameters that would be allocated
- // for the parent object type itself (e.g., for descriptor tables
- // used if it were bound as a parameter block). The later logic when
- // we actually go to bind things will need to apply those offsets.
- //
- // Note: An even *more* subtle point is that the `.resource` offset
- // being computed here *does* include the resource descriptor allocated
- // for holding the ordinary data buffer, if any. The implications of
- // this for later offset math is subtle.
- //
- subObjectRange.offset.rootParam = m_childRootParameterCount;
- subObjectRange.offset.resource = m_totalCounts.resource;
- subObjectRange.offset.sampler = m_totalCounts.sampler;
-
- // Along with the offset information, we also need to compute the
- // "stride" between consecutive sub-objects in the range. The actual
- // size/stride of a single object depends on the type of range we
- // are dealing with.
- //
- BindingOffset objectCounts;
- switch (slangBindingType)
- {
- default:
- {
- // We only treat buffers of interface types as actual sub-object binding
- // range.
- auto bindingRangeTypeLayout =
- typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
- if (!bindingRangeTypeLayout)
- continue;
- auto elementType = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex)
- ->getElementTypeLayout();
- if (!elementType)
- continue;
- if (elementType->getKind() != slang::TypeReflection::Kind::Interface)
- {
- continue;
- }
- }
- break;
-
- case slang::BindingType::ConstantBuffer:
- {
- SLANG_ASSERT(subObjectLayout);
-
- // The resource and sampler descriptors of a nested
- // constant buffer will "leak" into those of the
- // parent type, and we need to account for them
- // whenever we allocate storage.
- //
- objectCounts.resource = subObjectLayout->getTotalResourceDescriptorCount();
- objectCounts.sampler = subObjectLayout->getTotalSamplerDescriptorCount();
- objectCounts.rootParam = subObjectRange.layout->getChildRootParameterCount();
- }
- break;
-
- case slang::BindingType::ParameterBlock:
- {
- SLANG_ASSERT(subObjectLayout);
-
- // In contrast to a constant buffer, a parameter block can hide
- // the resource and sampler descriptor allocation it uses (since they
- // are allocated into the tables that make up the parameter block.
- //
- // The only resource usage that leaks into the surrounding context
- // is the number of root parameters consumed.
- //
- objectCounts.rootParam = subObjectRange.layout->getTotalRootTableParameterCount();
- }
- break;
-
- case slang::BindingType::ExistentialValue:
- // An unspecialized existential/interface value cannot consume any resources
- // as part of the parent object (it needs to fit inside the fixed-size
- // represnetation of existential types).
- //
- // However, if we are statically specializing to a type that doesn't "fit"
- // we may need to account for additional information that needs to be
- // allocaated.
- //
- if (subObjectLayout)
- {
- // The ordinary data for an existential-type value is allocated into
- // the same buffer as the parent object, so we only want to consider
- // the resource descriptors *other than* the ordinary data buffer.
- //
- // Otherwise the logic here is identical to the constant buffer case.
- //
- objectCounts.resource =
- subObjectLayout->getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer();
- objectCounts.sampler = subObjectLayout->getTotalSamplerDescriptorCount();
- objectCounts.rootParam = subObjectRange.layout->getChildRootParameterCount();
-
- // Note: In the implementation for some other graphics API (e.g.,
- // Vulkan) there needs to be more work done to handle the fact that
- // "pending" data from interface-type sub-objects get allocated to a
- // distinct offset after all the "primary" data. We are consciously
- // ignoring that issue here, and the physical layout of a shader object
- // into the D3D12 binding state may end up interleaving
- // resources/samplers for "primary" and "pending" data.
- //
- // If this choice ever causes issues, we can revisit the approach here.
-
- // An interface-type range that includes ordinary data can
- // increase the size of the ordinary data buffer we need to
- // allocate for the parent object.
- //
- uint32_t ordinaryDataEnd =
- subObjectRange.offset.pendingOrdinaryData +
- (uint32_t)count * subObjectRange.stride.pendingOrdinaryData;
-
- if (ordinaryDataEnd > m_totalOrdinaryDataSize)
- {
- m_totalOrdinaryDataSize = ordinaryDataEnd;
- }
- }
- break;
- }
-
- // Once we've computed the usage for each object in the range, we can
- // easily compute the usage for the entire range.
- //
- auto rangeResourceCount = count * objectCounts.resource;
- auto rangeSamplerCount = count * objectCounts.sampler;
- auto rangeRootParamCount = count * objectCounts.rootParam;
-
- m_totalCounts.resource += rangeResourceCount;
- m_totalCounts.sampler += rangeSamplerCount;
- m_childRootParameterCount += rangeRootParamCount;
-
- m_subObjectRanges.add(subObjectRange);
- }
-
- // Once we have added up the resource usage from all the sub-objects
- // we can look at the total number of resources and samplers that
- // need to be bound as part of this objects descriptor tables and
- // that will allow us to decide whether we need to allocate a root
- // parameter for a resource table or not, ans similarly for a
- // sampler table.
- //
- if (m_totalCounts.resource)
- m_ownCounts.rootParam++;
- if (m_totalCounts.sampler)
- m_ownCounts.rootParam++;
-
- m_totalCounts.rootParam = m_ownCounts.rootParam + m_childRootParameterCount;
-
- return SLANG_OK;
-}
-
-Result ShaderObjectLayoutImpl::Builder::build(ShaderObjectLayoutImpl** outLayout)
-{
- auto layout = RefPtr<ShaderObjectLayoutImpl>(new ShaderObjectLayoutImpl());
- SLANG_RETURN_ON_FAIL(layout->init(this));
-
- returnRefPtrMove(outLayout, layout);
- return SLANG_OK;
-}
-
-Result RootShaderObjectLayoutImpl::Builder::build(RootShaderObjectLayoutImpl** outLayout)
-{
- RefPtr<RootShaderObjectLayoutImpl> layout = new RootShaderObjectLayoutImpl();
- SLANG_RETURN_ON_FAIL(layout->init(this));
-
- returnRefPtrMove(outLayout, layout);
- return SLANG_OK;
-}
-
-void RootShaderObjectLayoutImpl::Builder::addGlobalParams(
- slang::VariableLayoutReflection* globalsLayout)
-{
- setElementTypeLayout(globalsLayout->getTypeLayout());
-}
-
-void RootShaderObjectLayoutImpl::Builder::addEntryPoint(
- SlangStage stage, ShaderObjectLayoutImpl* entryPointLayout)
-{
- EntryPointInfo info;
- info.layout = entryPointLayout;
-
- info.offset.resource = m_totalCounts.resource;
- info.offset.sampler = m_totalCounts.sampler;
- info.offset.rootParam = m_childRootParameterCount;
-
- m_totalCounts.resource += entryPointLayout->getTotalResourceDescriptorCount();
- m_totalCounts.sampler += entryPointLayout->getTotalSamplerDescriptorCount();
-
- // TODO(tfoley): Check this to make sure it is reasonable...
- m_childRootParameterCount += entryPointLayout->getChildRootParameterCount();
-
- m_entryPoints.add(info);
-}
-
-Result RootShaderObjectLayoutImpl::RootSignatureDescBuilder::translateDescriptorRangeType(
- slang::BindingType c, D3D12_DESCRIPTOR_RANGE_TYPE* outType)
-{
- switch (c)
- {
- case slang::BindingType::ConstantBuffer:
- *outType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
- return SLANG_OK;
- case slang::BindingType::RawBuffer:
- case slang::BindingType::Texture:
- case slang::BindingType::TypedBuffer:
- case slang::BindingType::RayTracingAccelerationStructure:
- *outType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
- return SLANG_OK;
- case slang::BindingType::MutableRawBuffer:
- case slang::BindingType::MutableTexture:
- case slang::BindingType::MutableTypedBuffer:
- *outType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
- return SLANG_OK;
- case slang::BindingType::Sampler:
- *outType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
- return SLANG_OK;
- default:
- return SLANG_FAIL;
- }
-}
-
-/// Add a new descriptor set to the layout being computed.
-///
-/// Note that a "descriptor set" in the layout may amount to
-/// zero, one, or two different descriptor *tables* in the
-/// final D3D12 root signature. Each descriptor set may
-/// contain zero or more view ranges (CBV/SRV/UAV) and zero
-/// or more sampler ranges. It maps to a view descriptor table
-/// if the number of view ranges is non-zero and to a sampler
-/// descriptor table if the number of sampler ranges is non-zero.
-///
-
-uint32_t RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addDescriptorSet()
-{
- auto result = (uint32_t)m_descriptorSets.getCount();
- m_descriptorSets.add(DescriptorSetLayout{});
- return result;
-}
-
-Result RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addDescriptorRange(
- Index physicalDescriptorSetIndex,
- D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
- UINT registerIndex,
- UINT spaceIndex,
- UINT count,
- bool isRootParameter)
-{
- if (isRootParameter)
- {
- D3D12_ROOT_PARAMETER rootParam = {};
- switch (rangeType)
- {
- case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
- rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
- break;
- case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
- rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
- break;
- default:
- getDebugCallback()->handleMessage(
- DebugMessageType::Error,
- DebugMessageSource::Layer,
- "A shader parameter marked as root parameter is neither SRV nor UAV.");
- return SLANG_FAIL;
- }
- rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
- rootParam.Descriptor.RegisterSpace = spaceIndex;
- rootParam.Descriptor.ShaderRegister = registerIndex;
- m_rootParameters.add(rootParam);
- return SLANG_OK;
- }
-
- auto& descriptorSet = m_descriptorSets[physicalDescriptorSetIndex];
-
- D3D12_DESCRIPTOR_RANGE range = {};
- range.RangeType = rangeType;
- range.NumDescriptors = count;
- range.BaseShaderRegister = registerIndex;
- range.RegisterSpace = spaceIndex;
- range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
-
- if (range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER)
- {
- descriptorSet.m_samplerRanges.add(range);
- descriptorSet.m_samplerCount += range.NumDescriptors;
- }
- else
- {
- descriptorSet.m_resourceRanges.add(range);
- descriptorSet.m_resourceCount += range.NumDescriptors;
- }
-
- return SLANG_OK;
-}
-
-/// Add one descriptor range as specified in Slang reflection information to the layout.
-///
-/// The layout information is taken from `typeLayout` for the descriptor
-/// range with the given `descriptorRangeIndex` within the logical
-/// descriptor set (reflected by Slang) with the given `logicalDescriptorSetIndex`.
-///
-/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
-/// the descriptor set that the range should be added to.
-///
-/// The `offset` encodes information about space and/or register offsets that
-/// should be applied to descrptor ranges.
-///
-/// This operation can fail if the given descriptor range encodes a range that
-/// doesn't map to anything directly supported by D3D12. Higher-level routines
-/// will often want to ignore such failures.
-///
-
-Result RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addDescriptorRange(
- slang::TypeLayoutReflection* typeLayout,
- Index physicalDescriptorSetIndex,
- BindingRegisterOffset const& containerOffset,
- BindingRegisterOffset const& elementOffset,
- Index logicalDescriptorSetIndex,
- Index descriptorRangeIndex,
- bool isRootParameter)
-{
- auto bindingType = typeLayout->getDescriptorSetDescriptorRangeType(
- logicalDescriptorSetIndex, descriptorRangeIndex);
- auto count = typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(
- logicalDescriptorSetIndex, descriptorRangeIndex);
- auto index = typeLayout->getDescriptorSetDescriptorRangeIndexOffset(
- logicalDescriptorSetIndex, descriptorRangeIndex);
- auto space = typeLayout->getDescriptorSetSpaceOffset(logicalDescriptorSetIndex);
-
- D3D12_DESCRIPTOR_RANGE_TYPE rangeType;
- SLANG_RETURN_ON_FAIL(translateDescriptorRangeType(bindingType, &rangeType));
-
- return addDescriptorRange(
- physicalDescriptorSetIndex,
- rangeType,
- (UINT)index + elementOffset[rangeType],
- (UINT)space + containerOffset.spaceOffset,
- (UINT)count,
- isRootParameter);
-}
-
-/// Add one binding range to the computed layout.
-///
-/// The layout information is taken from `typeLayout` for the binding
-/// range with the given `bindingRangeIndex`.
-///
-/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
-/// the descriptor set that the range should be added to.
-///
-/// The `offset` encodes information about space and/or register offsets that
-/// should be applied to descrptor ranges.
-///
-/// Note that a single binding range may encompass zero or more descriptor ranges.
-///
-
-void RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addBindingRange(
- slang::TypeLayoutReflection* typeLayout,
- Index physicalDescriptorSetIndex,
- BindingRegisterOffset const& containerOffset,
- BindingRegisterOffset const& elementOffset,
- Index bindingRangeIndex)
-{
- auto logicalDescriptorSetIndex =
- typeLayout->getBindingRangeDescriptorSetIndex(bindingRangeIndex);
- auto firstDescriptorRangeIndex =
- typeLayout->getBindingRangeFirstDescriptorRangeIndex(bindingRangeIndex);
- Index descriptorRangeCount = typeLayout->getBindingRangeDescriptorRangeCount(bindingRangeIndex);
- bool isRootParameter = isBindingRangeRootParameter(
- m_device->slangContext.globalSession,
- m_device->m_extendedDesc.rootParameterShaderAttributeName,
- typeLayout,
- bindingRangeIndex);
- for (Index i = 0; i < descriptorRangeCount; ++i)
- {
- auto descriptorRangeIndex = firstDescriptorRangeIndex + i;
-
- // Note: we ignore the `Result` returned by `addDescriptorRange()` because we
- // want to silently skip any ranges that represent kinds of bindings that
- // don't actually exist in D3D12.
- //
- addDescriptorRange(
- typeLayout,
- physicalDescriptorSetIndex,
- containerOffset,
- elementOffset,
- logicalDescriptorSetIndex,
- descriptorRangeIndex,
- isRootParameter);
- }
-}
-
-void RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addAsValue(
- slang::VariableLayoutReflection* varLayout, Index physicalDescriptorSetIndex)
-{
- BindingRegisterOffsetPair offset(varLayout);
- addAsValue(varLayout->getTypeLayout(), physicalDescriptorSetIndex, offset, offset);
-}
-
-/// Add binding ranges and parameter blocks to the root signature.
-///
-/// The layout information is taken from `typeLayout` which should
-/// be a layout for either a program or an entry point.
-///
-/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
-/// the descriptor set that binding ranges not belonging to nested
-/// parameter blocks should be added to.
-///
-/// The `offset` encodes information about space and/or register offsets that
-/// should be applied to descrptor ranges.
-///
-
-void RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addAsConstantBuffer(
- slang::TypeLayoutReflection* typeLayout,
- Index physicalDescriptorSetIndex,
- BindingRegisterOffsetPair const& containerOffset,
- BindingRegisterOffsetPair const& elementOffset)
-{
- if (typeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0)
- {
- auto descriptorRangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
- auto& offsetForRangeType = containerOffset.primary.offsetForRangeType[descriptorRangeType];
- addDescriptorRange(
- physicalDescriptorSetIndex,
- descriptorRangeType,
- offsetForRangeType,
- containerOffset.primary.spaceOffset,
- 1,
- false);
- }
-
- addAsValue(typeLayout, physicalDescriptorSetIndex, containerOffset, elementOffset);
-}
-
-void RootShaderObjectLayoutImpl::RootSignatureDescBuilder::addAsValue(
- slang::TypeLayoutReflection* typeLayout,
- Index physicalDescriptorSetIndex,
- BindingRegisterOffsetPair const& containerOffset,
- BindingRegisterOffsetPair const& elementOffset)
-{
- // Our first task is to add the binding ranges for stuff that is
- // directly contained in `typeLayout` rather than via sub-objects.
- //
- // Our goal is to have the descriptors for directly-contained views/samplers
- // always be contiguous in CPU and GPU memory, so that we can write
- // to them easily with a single operaiton.
- //
- Index bindingRangeCount = typeLayout->getBindingRangeCount();
- for (Index bindingRangeIndex = 0; bindingRangeIndex < bindingRangeCount; bindingRangeIndex++)
- {
- // We will look at the type of each binding range and intentionally
- // skip those that represent sub-objects.
- //
- auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
- switch (bindingType)
- {
- case slang::BindingType::ConstantBuffer:
- case slang::BindingType::ParameterBlock:
- case slang::BindingType::ExistentialValue:
- continue;
-
- default:
- break;
- }
-
- // For binding ranges that don't represent sub-objects, we will add
- // all of the descriptor ranges they encompass to the root signature.
- //
- addBindingRange(
- typeLayout,
- physicalDescriptorSetIndex,
- containerOffset.primary,
- elementOffset.primary,
- bindingRangeIndex);
- }
-
- // Next we need to recursively include everything bound via sub-objects
- Index subObjectRangeCount = typeLayout->getSubObjectRangeCount();
- for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount;
- subObjectRangeIndex++)
- {
- auto bindingRangeIndex =
- typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex);
- auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
-
- auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
-
- BindingRegisterOffsetPair subObjectRangeContainerOffset = containerOffset;
- subObjectRangeContainerOffset +=
- BindingRegisterOffsetPair(typeLayout->getSubObjectRangeOffset(subObjectRangeIndex));
- BindingRegisterOffsetPair subObjectRangeElementOffset = elementOffset;
- subObjectRangeElementOffset +=
- BindingRegisterOffsetPair(typeLayout->getSubObjectRangeOffset(subObjectRangeIndex));
-
- switch (bindingType)
- {
- case slang::BindingType::ConstantBuffer:
- {
- auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
- SLANG_ASSERT(containerVarLayout);
-
- auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
- SLANG_ASSERT(elementVarLayout);
-
- auto elementTypeLayout = elementVarLayout->getTypeLayout();
- SLANG_ASSERT(elementTypeLayout);
-
- BindingRegisterOffsetPair containerOffset = subObjectRangeContainerOffset;
- containerOffset += BindingRegisterOffsetPair(containerVarLayout);
-
- BindingRegisterOffsetPair elementOffset = subObjectRangeElementOffset;
- elementOffset += BindingRegisterOffsetPair(elementVarLayout);
-
- addAsConstantBuffer(
- elementTypeLayout, physicalDescriptorSetIndex, containerOffset, elementOffset);
- }
- break;
-
- case slang::BindingType::ParameterBlock:
- {
- auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
- SLANG_ASSERT(containerVarLayout);
-
- auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
- SLANG_ASSERT(elementVarLayout);
-
- auto elementTypeLayout = elementVarLayout->getTypeLayout();
- SLANG_ASSERT(elementTypeLayout);
-
- BindingRegisterOffsetPair subDescriptorSetOffset;
- subDescriptorSetOffset.primary.spaceOffset =
- subObjectRangeElementOffset.primary.spaceOffset;
- subDescriptorSetOffset.pending.spaceOffset =
- subObjectRangeElementOffset.pending.spaceOffset;
-
- auto subPhysicalDescriptorSetIndex = addDescriptorSet();
-
- BindingRegisterOffsetPair containerOffset = subDescriptorSetOffset;
- containerOffset += BindingRegisterOffsetPair(containerVarLayout);
-
- BindingRegisterOffsetPair elementOffset = subDescriptorSetOffset;
- elementOffset += BindingRegisterOffsetPair(elementVarLayout);
-
- addAsConstantBuffer(
- elementTypeLayout,
- subPhysicalDescriptorSetIndex,
- containerOffset,
- elementOffset);
- }
- break;
-
- case slang::BindingType::ExistentialValue:
- {
- // Any nested binding ranges in the sub-object will "leak" into the
- // binding ranges for the surrounding context.
- //
- auto specializedTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout();
- if (specializedTypeLayout)
- {
- BindingRegisterOffsetPair pendingOffset;
- pendingOffset.primary = subObjectRangeElementOffset.pending;
-
- addAsValue(
- specializedTypeLayout,
- physicalDescriptorSetIndex,
- pendingOffset,
- pendingOffset);
- }
- }
- break;
- }
- }
-}
-
-D3D12_ROOT_SIGNATURE_DESC& RootShaderObjectLayoutImpl::RootSignatureDescBuilder::build()
-{
- for (Index i = 0; i < m_descriptorSets.getCount(); i++)
- {
- auto& descriptorSet = m_descriptorSets[i];
- if (descriptorSet.m_resourceRanges.getCount())
- {
- D3D12_ROOT_PARAMETER rootParam = {};
- rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
- rootParam.DescriptorTable.NumDescriptorRanges =
- (UINT)descriptorSet.m_resourceRanges.getCount();
- rootParam.DescriptorTable.pDescriptorRanges =
- descriptorSet.m_resourceRanges.getBuffer();
- m_rootParameters.add(rootParam);
- }
- if (descriptorSet.m_samplerRanges.getCount())
- {
- D3D12_ROOT_PARAMETER rootParam = {};
- rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
- rootParam.DescriptorTable.NumDescriptorRanges =
- (UINT)descriptorSet.m_samplerRanges.getCount();
- rootParam.DescriptorTable.pDescriptorRanges = descriptorSet.m_samplerRanges.getBuffer();
- m_rootParameters.add(rootParam);
- }
- }
-
- m_rootSignatureDesc.NumParameters = UINT(m_rootParameters.getCount());
- m_rootSignatureDesc.pParameters = m_rootParameters.getBuffer();
-
- // TODO: static samplers should be reasonably easy to support...
- m_rootSignatureDesc.NumStaticSamplers = 0;
- m_rootSignatureDesc.pStaticSamplers = nullptr;
-
- // TODO: only set this flag if needed (requires creating root
- // signature at same time as pipeline state...).
- //
- m_rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
-
- return m_rootSignatureDesc;
-}
-
-Result RootShaderObjectLayoutImpl::createRootSignatureFromSlang(
- DeviceImpl* device,
- RootShaderObjectLayoutImpl* rootLayout,
- slang::IComponentType* program,
- ID3D12RootSignature** outRootSignature,
- ID3DBlob** outError)
-{
- // We are going to build up the root signature by adding
- // binding/descritpor ranges and nested parameter blocks
- // based on the computed layout information for `program`.
- //
- RootSignatureDescBuilder builder(device);
- auto layout = program->getLayout();
-
- // The layout information computed by Slang breaks up shader
- // parameters into what we can think of as "logical" descriptor
- // sets based on whether or not parameters have the same `space`.
- //
- // We want to basically ignore that decomposition and generate a
- // single descriptor set to hold all top-level parameters, and only
- // generate distinct descriptor sets when the shader has opted in
- // via explicit parameter blocks.
- //
- // To achieve this goal, we will manually allocate a default descriptor
- // set for root parameters in our signature, and then recursively
- // add all the binding/descriptor ranges implied by the global-scope
- // parameters.
- //
- auto rootDescriptorSetIndex = builder.addDescriptorSet();
- builder.addAsValue(layout->getGlobalParamsVarLayout(), rootDescriptorSetIndex);
-
- for (SlangUInt i = 0; i < layout->getEntryPointCount(); i++)
- {
- // Entry-point parameters should also be added to the default root
- // descriptor set.
- //
- // We add the parameters using the "variable layout" for the entry point
- // and not just its type layout, to ensure that any offset information is
- // applied correctly to the `register` and `space` information for entry-point
- // parameters.
- //
- // Note: When we start to support DXR we will need to handle entry-point parameters
- // differently because they will need to map to local root signatures rather than
- // being included in the global root signature as is being done here.
- //
- auto entryPoint = layout->getEntryPointByIndex(i);
- builder.addAsValue(entryPoint->getVarLayout(), rootDescriptorSetIndex);
- }
-
- auto& rootSignatureDesc = builder.build();
-
- ComPtr<ID3DBlob> signature;
- ComPtr<ID3DBlob> error;
- if (SLANG_FAILED(device->m_D3D12SerializeRootSignature(
- &rootSignatureDesc,
- D3D_ROOT_SIGNATURE_VERSION_1,
- signature.writeRef(),
- error.writeRef())))
- {
- getDebugCallback()->handleMessage(
- DebugMessageType::Error,
- DebugMessageSource::Layer,
- "error: D3D12SerializeRootSignature failed");
- if (error)
- {
- getDebugCallback()->handleMessage(
- DebugMessageType::Error,
- DebugMessageSource::Driver,
- (const char*)error->GetBufferPointer());
- if (outError)
- returnComPtr(outError, error);
- }
- return SLANG_FAIL;
- }
-
- SLANG_RETURN_ON_FAIL(device->m_device->CreateRootSignature(
- 0,
- signature->GetBufferPointer(),
- signature->GetBufferSize(),
- IID_PPV_ARGS(outRootSignature)));
- return SLANG_OK;
-}
-
-Result RootShaderObjectLayoutImpl::create(
- DeviceImpl* device,
- slang::IComponentType* program,
- slang::ProgramLayout* programLayout,
- RootShaderObjectLayoutImpl** outLayout,
- ID3DBlob** outError)
-{
- RootShaderObjectLayoutImpl::Builder builder(device, program, programLayout);
- builder.addGlobalParams(programLayout->getGlobalParamsVarLayout());
-
- SlangInt entryPointCount = programLayout->getEntryPointCount();
- for (SlangInt e = 0; e < entryPointCount; ++e)
- {
- auto slangEntryPoint = programLayout->getEntryPointByIndex(e);
- RefPtr<ShaderObjectLayoutImpl> entryPointLayout;
- SLANG_RETURN_ON_FAIL(ShaderObjectLayoutImpl::createForElementType(
- device, slangEntryPoint->getTypeLayout(), entryPointLayout.writeRef()));
- builder.addEntryPoint(slangEntryPoint->getStage(), entryPointLayout);
- }
-
- RefPtr<RootShaderObjectLayoutImpl> layout;
- SLANG_RETURN_ON_FAIL(builder.build(layout.writeRef()));
-
- if (program->getSpecializationParamCount() == 0)
- {
- // For root object, we would like know the union of all binding slots
- // including all sub-objects in the shader-object hierarchy, so at
- // parameter binding time we can easily know how many GPU descriptor tables
- // to create without walking through the shader-object hierarchy again.
- // We build out this array along with root signature construction and store
- // it in `m_gpuDescriptorSetInfos`.
- SLANG_RETURN_ON_FAIL(createRootSignatureFromSlang(
- device, layout, program, layout->m_rootSignature.writeRef(), outError));
- }
-
- *outLayout = layout.detach();
-
- return SLANG_OK;
-}
-
-Result RootShaderObjectLayoutImpl::init(Builder* builder)
-{
- auto renderer = builder->m_renderer;
-
- SLANG_RETURN_ON_FAIL(Super::init(builder));
-
- m_program = builder->m_program;
- m_programLayout = builder->m_programLayout;
- m_entryPoints = builder->m_entryPoints;
- return SLANG_OK;
-}
-
-Result ShaderProgramImpl::createShaderModule(
- slang::EntryPointReflection* entryPointInfo, ComPtr<ISlangBlob> kernelCode)
-{
- ShaderBinary shaderBin;
- shaderBin.stage = entryPointInfo->getStage();
- shaderBin.entryPointInfo = entryPointInfo;
- shaderBin.code.addRange(
- reinterpret_cast<const uint8_t*>(kernelCode->getBufferPointer()),
- (Index)kernelCode->getBufferSize());
- m_shaders.add(_Move(shaderBin));
- return SLANG_OK;
-}
-
-Result ShaderObjectImpl::create(
- DeviceImpl* device, ShaderObjectLayoutImpl* layout, ShaderObjectImpl** outShaderObject)
-{
- auto object = RefPtr<ShaderObjectImpl>(new ShaderObjectImpl());
- SLANG_RETURN_ON_FAIL(
- object->init(device, layout, device->m_cpuViewHeap.Ptr(), device->m_cpuSamplerHeap.Ptr()));
- returnRefPtrMove(outShaderObject, object);
- return SLANG_OK;
-}
-
-ShaderObjectImpl::~ShaderObjectImpl() { m_descriptorSet.freeIfSupported(); }
-
-RootShaderObjectLayoutImpl* RootShaderObjectImpl::getLayout()
-{
- return static_cast<RootShaderObjectLayoutImpl*>(m_layout.Ptr());
-}
-
-GfxCount RootShaderObjectImpl::getEntryPointCount() { return (GfxCount)m_entryPoints.getCount(); }
-
-SlangResult RootShaderObjectImpl::getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint)
-{
- returnComPtr(outEntryPoint, m_entryPoints[index]);
- return SLANG_OK;
-}
-
-Result RootShaderObjectImpl::collectSpecializationArgs(ExtendedShaderObjectTypeList& args)
-{
- SLANG_RETURN_ON_FAIL(ShaderObjectImpl::collectSpecializationArgs(args));
- for (auto& entryPoint : m_entryPoints)
- {
- SLANG_RETURN_ON_FAIL(entryPoint->collectSpecializationArgs(args));
- }
- return SLANG_OK;
-}
-
-Result RootShaderObjectImpl::_createSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
-{
- ExtendedShaderObjectTypeList specializationArgs;
- SLANG_RETURN_ON_FAIL(collectSpecializationArgs(specializationArgs));
-
- // Note: There is an important policy decision being made here that we need
- // to approach carefully.
- //
- // We are doing two different things that affect the layout of a program:
- //
- // 1. We are *composing* one or more pieces of code (notably the shared global/module
- // stuff and the per-entry-point stuff).
- //
- // 2. We are *specializing* code that includes generic/existential parameters
- // to concrete types/values.
- //
- // We need to decide the relative *order* of these two steps, because of how it impacts
- // layout. The layout for `specialize(compose(A,B), X, Y)` is potentially different
- // form that of `compose(specialize(A,X), speciealize(B,Y))`, even when both are
- // semantically equivalent programs.
- //
- // Right now we are using the first option: we are first generating a full composition
- // of all the code we plan to use (global scope plus all entry points), and then
- // specializing it to the concatenated specialization argumenst for all of that.
- //
- // In some cases, though, this model isn't appropriate. For example, when dealing with
- // ray-tracing shaders and local root signatures, we really want the parameters of each
- // entry point (actually, each entry-point *group*) to be allocated distinct storage,
- // which really means we want to compute something like:
- //
- // SpecializedGlobals = specialize(compose(ModuleA, ModuleB, ...), X, Y, ...)
- //
- // SpecializedEP1 = compose(SpecializedGlobals, specialize(EntryPoint1, T, U, ...))
- // SpecializedEP2 = compose(SpecializedGlobals, specialize(EntryPoint2, A, B, ...))
- //
- // Note how in this case all entry points agree on the layout for the shared/common
- // parmaeters, but their layouts are also independent of one another.
- //
- // Furthermore, in this example, loading another entry point into the system would not
- // rquire re-computing the layouts (or generated kernel code) for any of the entry
- // points that had already been loaded (in contrast to a compose-then-specialize
- // approach).
- //
- ComPtr<slang::IComponentType> specializedComponentType;
- ComPtr<slang::IBlob> diagnosticBlob;
- auto result = getLayout()->getSlangProgram()->specialize(
- specializationArgs.components.getArrayView().getBuffer(),
- specializationArgs.getCount(),
- specializedComponentType.writeRef(),
- diagnosticBlob.writeRef());
-
- if (diagnosticBlob && diagnosticBlob->getBufferSize())
- {
- getDebugCallback()->handleMessage(
- SLANG_FAILED(result) ? DebugMessageType::Error : DebugMessageType::Info,
- DebugMessageSource::Layer,
- (const char*)diagnosticBlob->getBufferPointer());
- }
-
- if (SLANG_FAILED(result))
- return result;
-
- ComPtr<ID3DBlob> d3dDiagnosticBlob;
- auto slangSpecializedLayout = specializedComponentType->getLayout();
- RefPtr<RootShaderObjectLayoutImpl> specializedLayout;
- auto rootLayoutResult = RootShaderObjectLayoutImpl::create(
- static_cast<DeviceImpl*>(getRenderer()),
- specializedComponentType,
- slangSpecializedLayout,
- specializedLayout.writeRef(),
- d3dDiagnosticBlob.writeRef());
-
- if (SLANG_FAILED(rootLayoutResult))
- {
- return rootLayoutResult;
- }
-
- // Note: Computing the layout for the specialized program will have also computed
- // the layouts for the entry points, and we really need to attach that information
- // to them so that they don't go and try to compute their own specializations.
- //
- // TODO: Well, if we move to the specialization model described above then maybe
- // we *will* want entry points to do their own specialization work...
- //
- auto entryPointCount = m_entryPoints.getCount();
- for (Index i = 0; i < entryPointCount; ++i)
- {
- auto entryPointInfo = specializedLayout->getEntryPoint(i);
- auto entryPointVars = m_entryPoints[i];
-
- entryPointVars->m_specializedLayout = entryPointInfo.layout;
- }
-
- returnRefPtrMove(outLayout, specializedLayout);
- return SLANG_OK;
-}
-
-Result RootShaderObjectImpl::copyFrom(IShaderObject* object, ITransientResourceHeap* transientHeap)
-{
- if (auto srcObj = dynamic_cast<MutableRootShaderObjectImpl*>(object))
- {
- *this = *srcObj;
- return SLANG_OK;
- }
- return SLANG_FAIL;
-}
-
-Result RootShaderObjectImpl::bindAsRoot(
- BindingContext* context, RootShaderObjectLayoutImpl* specializedLayout)
-{
- // Pull updates from sub-objects when this is a mutable root shader object.
- updateSubObjectsRecursive();
-
- // A root shader object always binds as if it were a parameter block,
- // insofar as it needs to allocate a descriptor set to hold the bindings
- // for its own state and any sub-objects.
- //
- // Note: We do not direclty use `bindAsParameterBlock` here because we also
- // need to bind the entry points into the same descriptor set that is
- // being used for the root object.
-
- BindingOffset rootOffset;
-
- // Bind all root parameters first.
- Super::bindRootArguments(context, rootOffset.rootParam);
-
- DescriptorSet descriptorSet;
- SLANG_RETURN_ON_FAIL(prepareToBindAsParameterBlock(
- context, /* inout */ rootOffset, specializedLayout, descriptorSet));
-
- SLANG_RETURN_ON_FAIL(
- Super::bindAsConstantBuffer(context, descriptorSet, rootOffset, specializedLayout));
-
- auto entryPointCount = m_entryPoints.getCount();
- for (Index i = 0; i < entryPointCount; ++i)
- {
- auto entryPoint = m_entryPoints[i];
- auto& entryPointInfo = specializedLayout->getEntryPoint(i);
-
- auto entryPointOffset = rootOffset;
- entryPointOffset += entryPointInfo.offset;
-
- entryPoint->updateSubObjectsRecursive();
-
- SLANG_RETURN_ON_FAIL(entryPoint->bindAsConstantBuffer(
- context, descriptorSet, entryPointOffset, entryPointInfo.layout));
- }
-
- return SLANG_OK;
-}
-
-Result RootShaderObjectImpl::resetImpl(
- DeviceImpl* device,
- RootShaderObjectLayoutImpl* layout,
- DescriptorHeapReference viewHeap,
- DescriptorHeapReference samplerHeap,
- bool isMutable)
-{
- SLANG_RETURN_ON_FAIL(Super::init(device, layout, viewHeap, samplerHeap));
- m_isMutable = isMutable;
- m_specializedLayout = nullptr;
- m_entryPoints.clear();
- for (auto entryPointInfo : layout->getEntryPoints())
- {
- RefPtr<ShaderObjectImpl> entryPoint;
- SLANG_RETURN_ON_FAIL(
- ShaderObjectImpl::create(device, entryPointInfo.layout, entryPoint.writeRef()));
- entryPoint->m_isMutable = isMutable;
- m_entryPoints.add(entryPoint);
- }
- return SLANG_OK;
-}
-
-Result RootShaderObjectImpl::reset(
- DeviceImpl* device, RootShaderObjectLayoutImpl* layout, TransientResourceHeapImpl* heap)
-{
- return resetImpl(
- device, layout, &heap->m_stagingCpuViewHeap, &heap->m_stagingCpuSamplerHeap, false);
-}
-
-RefPtr<BufferResource> ShaderTableImpl::createDeviceBuffer(
- PipelineStateBase* pipeline,
- TransientResourceHeapBase* transientHeap,
- IResourceCommandEncoder* encoder)
-{
- uint32_t raygenTableSize = m_rayGenShaderCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
- uint32_t missTableSize = m_missShaderCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
- uint32_t hitgroupTableSize = m_hitGroupCount * D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
- m_rayGenTableOffset = 0;
- m_missTableOffset = (uint32_t)D3DUtil::calcAligned(
- raygenTableSize, D3D12_RAYTRACING_SHADER_TABLE_BYTE_ALIGNMENT);
- m_hitGroupTableOffset = (uint32_t)D3DUtil::calcAligned(
- m_missTableOffset + missTableSize, D3D12_RAYTRACING_SHADER_TABLE_BYTE_ALIGNMENT);
- uint32_t tableSize = m_hitGroupTableOffset + hitgroupTableSize;
-
- auto pipelineImpl = static_cast<RayTracingPipelineStateImpl*>(pipeline);
- ComPtr<IBufferResource> bufferResource;
- IBufferResource::Desc bufferDesc = {};
- bufferDesc.memoryType = gfx::MemoryType::DeviceLocal;
- bufferDesc.defaultState = ResourceState::General;
- bufferDesc.type = IResource::Type::Buffer;
- bufferDesc.sizeInBytes = tableSize;
- m_device->createBufferResource(bufferDesc, nullptr, bufferResource.writeRef());
-
- ComPtr<ID3D12StateObjectProperties> stateObjectProperties;
- pipelineImpl->m_stateObject->QueryInterface(stateObjectProperties.writeRef());
-
- TransientResourceHeapImpl* transientHeapImpl =
- static_cast<TransientResourceHeapImpl*>(transientHeap);
-
- IBufferResource* stagingBuffer = nullptr;
- Offset stagingBufferOffset = 0;
- transientHeapImpl->allocateStagingBuffer(
- tableSize, stagingBuffer, stagingBufferOffset, MemoryType::Upload);
-
- assert(stagingBuffer);
- void* stagingPtr = nullptr;
- stagingBuffer->map(nullptr, &stagingPtr);
-
- auto copyShaderIdInto = [&](void* dest, String& name, const ShaderRecordOverwrite& overwrite)
- {
- if (name.getLength())
- {
- void* shaderId = stateObjectProperties->GetShaderIdentifier(name.toWString().begin());
- memcpy(dest, shaderId, D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES);
- }
- else
- {
- memset(dest, 0, D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES);
- }
- if (overwrite.size)
- {
- memcpy((uint8_t*)dest + overwrite.offset, overwrite.data, overwrite.size);
- }
- };
-
- uint8_t* stagingBufferPtr = (uint8_t*)stagingPtr + stagingBufferOffset;
- for (uint32_t i = 0; i < m_rayGenShaderCount; i++)
- {
- copyShaderIdInto(
- stagingBufferPtr + m_rayGenTableOffset + D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES * i,
- m_shaderGroupNames[i],
- m_recordOverwrites[i]);
- }
- for (uint32_t i = 0; i < m_missShaderCount; i++)
- {
- copyShaderIdInto(
- stagingBufferPtr + m_missTableOffset + D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES * i,
- m_shaderGroupNames[m_rayGenShaderCount + i],
- m_recordOverwrites[m_rayGenShaderCount + i]);
- }
- for (uint32_t i = 0; i < m_hitGroupCount; i++)
- {
- copyShaderIdInto(
- stagingBufferPtr + m_hitGroupTableOffset + D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES * i,
- m_shaderGroupNames[m_rayGenShaderCount + m_missShaderCount + i],
- m_recordOverwrites[m_rayGenShaderCount + m_missShaderCount + i]);
- }
-
- stagingBuffer->unmap(nullptr);
- encoder->copyBuffer(bufferResource, 0, stagingBuffer, stagingBufferOffset, tableSize);
- encoder->bufferBarrier(
- 1,
- bufferResource.readRef(),
- gfx::ResourceState::CopyDestination,
- gfx::ResourceState::ShaderResource);
- RefPtr<BufferResource> resultPtr = static_cast<BufferResource*>(bufferResource.get());
- return _Move(resultPtr);
-}
-
-// There are a pair of cyclic references between a `TransientResourceHeap` and
-// a `CommandBuffer` created from the heap. We need to break the cycle upon
-// the public reference count of a command buffer dropping to 0.
-
-ICommandBufferD3D12* CommandBufferImpl::getInterface(const Guid& guid)
-{
- if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_ICommandBuffer ||
- guid == GfxGUID::IID_ICommandBufferD3D12)
- return static_cast<ICommandBufferD3D12*>(this);
- return nullptr;
-}
-
-Result CommandBufferImpl::getNativeHandle(InteropHandle* handle)
-{
- handle->api = InteropHandleAPI::D3D12;
- handle->handleValue = (uint64_t)m_cmdList.get();
- return SLANG_OK;
-}
-
-void CommandBufferImpl::bindDescriptorHeaps()
-{
- if (!m_descriptorHeapsBound)
- {
- ID3D12DescriptorHeap* heaps[] = {
- m_transientHeap->getCurrentViewHeap().getHeap(),
- m_transientHeap->getCurrentSamplerHeap().getHeap(),
- };
- m_cmdList->SetDescriptorHeaps(SLANG_COUNT_OF(heaps), heaps);
- m_descriptorHeapsBound = true;
- }
-}
-
-void CommandBufferImpl::reinit()
-{
- invalidateDescriptorHeapBinding();
- m_rootShaderObject.init(m_renderer);
-}
-
-void CommandBufferImpl::init(
- DeviceImpl* renderer,
- ID3D12GraphicsCommandList* d3dCommandList,
- TransientResourceHeapImpl* transientHeap)
-{
- m_transientHeap = transientHeap;
- m_renderer = renderer;
- m_cmdList = d3dCommandList;
-
- reinit();
-
-#if SLANG_GFX_HAS_DXR_SUPPORT
- m_cmdList->QueryInterface<ID3D12GraphicsCommandList4>(m_cmdList4.writeRef());
- if (m_cmdList4)
- {
- m_cmdList1 = m_cmdList4;
- return;
- }
-#endif
- m_cmdList->QueryInterface<ID3D12GraphicsCommandList1>(m_cmdList1.writeRef());
-}
-
-void CommandBufferImpl::encodeResourceCommands(IResourceCommandEncoder** outEncoder)
-{
- m_resourceCommandEncoder.init(this);
- *outEncoder = &m_resourceCommandEncoder;
-}
-
-void CommandBufferImpl::encodeRenderCommands(
- IRenderPassLayout* renderPass, IFramebuffer* framebuffer, IRenderCommandEncoder** outEncoder)
-{
- m_renderCommandEncoder.init(
- m_renderer,
- m_transientHeap,
- this,
- static_cast<RenderPassLayoutImpl*>(renderPass),
- static_cast<FramebufferImpl*>(framebuffer));
- *outEncoder = &m_renderCommandEncoder;
-}
-
-void CommandBufferImpl::encodeComputeCommands(IComputeCommandEncoder** outEncoder)
-{
- m_computeCommandEncoder.init(m_renderer, m_transientHeap, this);
- *outEncoder = &m_computeCommandEncoder;
-}
-
-void CommandBufferImpl::encodeRayTracingCommands(IRayTracingCommandEncoder** outEncoder)
-{
-#if SLANG_GFX_HAS_DXR_SUPPORT
- m_rayTracingCommandEncoder.init(this);
- *outEncoder = &m_rayTracingCommandEncoder;
-#else
- *outEncoder = nullptr;
-#endif
-}
-
-void CommandBufferImpl::close() { m_cmdList->Close(); }
-
-void ResourceCommandEncoderImpl::copyBuffer(
- IBufferResource* dst, Offset dstOffset, IBufferResource* src, Offset srcOffset, Size size)
-{
- auto dstBuffer = static_cast<BufferResourceImpl*>(dst);
- auto srcBuffer = static_cast<BufferResourceImpl*>(src);
-
- m_commandBuffer->m_cmdList->CopyBufferRegion(
- dstBuffer->m_resource.getResource(),
- dstOffset,
- srcBuffer->m_resource.getResource(),
- srcOffset,
- size);
-}
-
-void ResourceCommandEncoderImpl::uploadBufferData(
- IBufferResource* dst, Offset offset, Size size, void* data)
-{
- uploadBufferDataImpl(
- m_commandBuffer->m_renderer->m_device,
- m_commandBuffer->m_cmdList,
- m_commandBuffer->m_transientHeap,
- static_cast<BufferResourceImpl*>(dst),
- offset,
- size,
- data);
-}
-
-void ResourceCommandEncoderImpl::textureBarrier(
- GfxCount count, ITextureResource* const* textures, ResourceState src, ResourceState dst)
-{
- ShortList<D3D12_RESOURCE_BARRIER> barriers;
-
- for (GfxIndex i = 0; i < count; i++)
- {
- auto textureImpl = static_cast<TextureResourceImpl*>(textures[i]);
- auto d3dFormat = D3DUtil::getMapFormat(textureImpl->getDesc()->format);
- auto textureDesc = textureImpl->getDesc();
- D3D12_RESOURCE_BARRIER barrier;
- barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
- if (src == dst && src == ResourceState::UnorderedAccess)
- {
- barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
- barrier.UAV.pResource = textureImpl->m_resource.getResource();
- }
- else
- {
- barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
- barrier.Transition.StateBefore = D3DUtil::getResourceState(src);
- barrier.Transition.StateAfter = D3DUtil::getResourceState(dst);
- if (barrier.Transition.StateBefore == barrier.Transition.StateAfter)
- continue;
- barrier.Transition.pResource = textureImpl->m_resource.getResource();
- auto planeCount =
- D3DUtil::getPlaneSliceCount(D3DUtil::getMapFormat(textureImpl->getDesc()->format));
- auto arraySize = textureDesc->arraySize;
- if (arraySize == 0)
- arraySize = 1;
- barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
- }
- barriers.add(barrier);
- }
- if (barriers.getCount())
- {
- m_commandBuffer->m_cmdList->ResourceBarrier(
- (UINT)barriers.getCount(), barriers.getArrayView().getBuffer());
- }
-}
-
-void ResourceCommandEncoderImpl::bufferBarrier(
- GfxCount count, IBufferResource* const* buffers, ResourceState src, ResourceState dst)
-{
- ShortList<D3D12_RESOURCE_BARRIER, 16> barriers;
- for (GfxIndex i = 0; i < count; i++)
- {
- auto bufferImpl = static_cast<BufferResourceImpl*>(buffers[i]);
-
- D3D12_RESOURCE_BARRIER barrier = {};
- // If the src == dst, it must be a UAV barrier.
- barrier.Type = (src == dst && dst == ResourceState::UnorderedAccess)
- ? D3D12_RESOURCE_BARRIER_TYPE_UAV
- : D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
- barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
-
- if (barrier.Type == D3D12_RESOURCE_BARRIER_TYPE_UAV)
- {
- barrier.UAV.pResource = bufferImpl->m_resource;
- }
- else
- {
- barrier.Transition.pResource = bufferImpl->m_resource;
- barrier.Transition.StateBefore = D3DUtil::getResourceState(src);
- barrier.Transition.StateAfter = D3DUtil::getResourceState(dst);
- barrier.Transition.Subresource = 0;
- if (barrier.Transition.StateAfter == barrier.Transition.StateBefore)
- continue;
- }
- barriers.add(barrier);
- }
- if (barriers.getCount())
- {
- m_commandBuffer->m_cmdList4->ResourceBarrier(
- (UINT)barriers.getCount(), barriers.getArrayView().getBuffer());
- }
-}
-
-void ResourceCommandEncoderImpl::writeTimestamp(IQueryPool* pool, GfxIndex index)
-{
- static_cast<QueryPoolImpl*>(pool)->writeTimestamp(m_commandBuffer->m_cmdList, index);
-}
-
-void ResourceCommandEncoderImpl::copyTexture(
- ITextureResource* dst,
- ResourceState dstState,
- SubresourceRange dstSubresource,
- ITextureResource::Offset3D dstOffset,
- ITextureResource* src,
- ResourceState srcState,
- SubresourceRange srcSubresource,
- ITextureResource::Offset3D srcOffset,
- ITextureResource::Extents extent)
-{
- auto dstTexture = static_cast<TextureResourceImpl*>(dst);
- auto srcTexture = static_cast<TextureResourceImpl*>(src);
-
- if (dstSubresource.layerCount == 0 && dstSubresource.mipLevelCount == 0 &&
- srcSubresource.layerCount == 0 && srcSubresource.mipLevelCount == 0)
- {
- m_commandBuffer->m_cmdList->CopyResource(
- dstTexture->m_resource.getResource(), srcTexture->m_resource.getResource());
- return;
- }
-
- auto d3dFormat = D3DUtil::getMapFormat(dstTexture->getDesc()->format);
- auto aspectMask = (int32_t)dstSubresource.aspectMask;
- if (dstSubresource.aspectMask == TextureAspect::Default)
- aspectMask = (int32_t)TextureAspect::Color;
- while (aspectMask)
- {
- auto aspect = Math::getLowestBit((int32_t)aspectMask);
- aspectMask &= ~aspect;
- auto planeIndex = D3DUtil::getPlaneSlice(d3dFormat, (TextureAspect)aspect);
- for (GfxIndex layer = 0; layer < dstSubresource.layerCount; layer++)
- {
- for (GfxIndex mipLevel = 0; mipLevel < dstSubresource.mipLevelCount; mipLevel++)
- {
- D3D12_TEXTURE_COPY_LOCATION dstRegion = {};
-
- dstRegion.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
- dstRegion.pResource = dstTexture->m_resource.getResource();
- dstRegion.SubresourceIndex = D3DUtil::getSubresourceIndex(
- dstSubresource.mipLevel + mipLevel,
- dstSubresource.baseArrayLayer + layer,
- planeIndex,
- dstTexture->getDesc()->numMipLevels,
- dstTexture->getDesc()->arraySize);
-
- D3D12_TEXTURE_COPY_LOCATION srcRegion = {};
- srcRegion.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
- srcRegion.pResource = srcTexture->m_resource.getResource();
- srcRegion.SubresourceIndex = D3DUtil::getSubresourceIndex(
- srcSubresource.mipLevel + mipLevel,
- srcSubresource.baseArrayLayer + layer,
- planeIndex,
- srcTexture->getDesc()->numMipLevels,
- srcTexture->getDesc()->arraySize);
-
- D3D12_BOX srcBox = {};
- srcBox.left = srcOffset.x;
- srcBox.top = srcOffset.y;
- srcBox.front = srcOffset.z;
- srcBox.right = srcBox.left + extent.width;
- srcBox.bottom = srcBox.top + extent.height;
- srcBox.back = srcBox.front + extent.depth;
-
- m_commandBuffer->m_cmdList->CopyTextureRegion(
- &dstRegion, dstOffset.x, dstOffset.y, dstOffset.z, &srcRegion, &srcBox);
- }
- }
- }
-}
-
-void ResourceCommandEncoderImpl::uploadTextureData(
- ITextureResource* dst,
- SubresourceRange subResourceRange,
- ITextureResource::Offset3D offset,
- ITextureResource::Extents extent,
- ITextureResource::SubresourceData* subResourceData,
- GfxCount subResourceDataCount)
-{
- auto dstTexture = static_cast<TextureResourceImpl*>(dst);
- auto baseSubresourceIndex = D3DUtil::getSubresourceIndex(
- subResourceRange.mipLevel,
- subResourceRange.baseArrayLayer,
- 0,
- dstTexture->getDesc()->numMipLevels,
- dstTexture->getDesc()->arraySize);
- auto textureSize = dstTexture->getDesc()->size;
- FormatInfo formatInfo = {};
- gfxGetFormatInfo(dstTexture->getDesc()->format, &formatInfo);
- for (GfxCount i = 0; i < subResourceDataCount; i++)
- {
- auto subresourceIndex = baseSubresourceIndex + i;
- // Get the footprint
- D3D12_RESOURCE_DESC texDesc = dstTexture->m_resource.getResource()->GetDesc();
-
- D3D12_TEXTURE_COPY_LOCATION dstRegion = {};
-
- dstRegion.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
- dstRegion.SubresourceIndex = subresourceIndex;
- dstRegion.pResource = dstTexture->m_resource.getResource();
-
- D3D12_TEXTURE_COPY_LOCATION srcRegion = {};
- srcRegion.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
- D3D12_PLACED_SUBRESOURCE_FOOTPRINT& footprint = srcRegion.PlacedFootprint;
- footprint.Offset = 0;
- footprint.Footprint.Format = texDesc.Format;
- uint32_t mipLevel =
- D3DUtil::getSubresourceMipLevel(subresourceIndex, dstTexture->getDesc()->numMipLevels);
- if (extent.width != ITextureResource::kRemainingTextureSize)
- {
- footprint.Footprint.Width = extent.width;
- }
- else
- {
- footprint.Footprint.Width = Math::Max(1, (textureSize.width >> mipLevel)) - offset.x;
- }
- if (extent.height != ITextureResource::kRemainingTextureSize)
- {
- footprint.Footprint.Height = extent.height;
- }
- else
- {
- footprint.Footprint.Height = Math::Max(1, (textureSize.height >> mipLevel)) - offset.y;
- }
- if (extent.depth != ITextureResource::kRemainingTextureSize)
- {
- footprint.Footprint.Depth = extent.depth;
- }
- else
- {
- footprint.Footprint.Depth = Math::Max(1, (textureSize.depth >> mipLevel)) - offset.z;
- }
- auto rowSize = (footprint.Footprint.Width + formatInfo.blockWidth - 1) /
- formatInfo.blockWidth * formatInfo.blockSizeInBytes;
- auto rowCount =
- (footprint.Footprint.Height + formatInfo.blockHeight - 1) / formatInfo.blockHeight;
- footprint.Footprint.RowPitch =
- (UINT)D3DUtil::calcAligned(rowSize, (uint32_t)D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
-
- auto bufferSize = footprint.Footprint.RowPitch * rowCount * footprint.Footprint.Depth;
-
- IBufferResource* stagingBuffer;
- Offset stagingBufferOffset = 0;
- m_commandBuffer->m_transientHeap->allocateStagingBuffer(
- bufferSize, stagingBuffer, stagingBufferOffset, MemoryType::Upload, true);
- assert(stagingBufferOffset == 0);
- BufferResourceImpl* bufferImpl = static_cast<BufferResourceImpl*>(stagingBuffer);
- uint8_t* bufferData = nullptr;
- D3D12_RANGE mapRange = {0, 0};
- bufferImpl->m_resource.getResource()->Map(0, &mapRange, (void**)&bufferData);
- for (uint32_t z = 0; z < footprint.Footprint.Depth; z++)
- {
- auto imageStart = bufferData + footprint.Footprint.RowPitch * rowCount * (Size)z;
- auto srcData = (uint8_t*)subResourceData->data + subResourceData->strideZ * z;
- for (uint32_t row = 0; row < rowCount; row++)
- {
- memcpy(
- imageStart + row * (Size)footprint.Footprint.RowPitch,
- srcData + subResourceData->strideY * row,
- rowSize);
- }
- }
- bufferImpl->m_resource.getResource()->Unmap(0, nullptr);
- srcRegion.pResource = bufferImpl->m_resource.getResource();
- m_commandBuffer->m_cmdList->CopyTextureRegion(
- &dstRegion, offset.x, offset.y, offset.z, &srcRegion, nullptr);
- }
-}
-
-void ResourceCommandEncoderImpl::clearResourceView(
- IResourceView* view, ClearValue* clearValue, ClearResourceViewFlags::Enum flags)
-{
- auto viewImpl = static_cast<ResourceViewImpl*>(view);
- switch (view->getViewDesc()->type)
- {
- case IResourceView::Type::RenderTarget:
- m_commandBuffer->m_cmdList->ClearRenderTargetView(
- viewImpl->m_descriptor.cpuHandle, clearValue->color.floatValues, 0, nullptr);
- break;
- case IResourceView::Type::DepthStencil:
- {
- D3D12_CLEAR_FLAGS clearFlags = (D3D12_CLEAR_FLAGS)0;
- if (flags & ClearResourceViewFlags::ClearDepth)
- {
- clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
- }
- if (flags & ClearResourceViewFlags::ClearStencil)
- {
- clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
- }
- m_commandBuffer->m_cmdList->ClearDepthStencilView(
- viewImpl->m_descriptor.cpuHandle,
- clearFlags,
- clearValue->depthStencil.depth,
- (UINT8)clearValue->depthStencil.stencil,
- 0,
- nullptr);
- break;
- }
- case IResourceView::Type::UnorderedAccess:
- {
- ID3D12Resource* d3dResource = nullptr;
- switch (viewImpl->m_resource->getType())
- {
- case IResource::Type::Buffer:
- d3dResource = static_cast<BufferResourceImpl*>(viewImpl->m_resource.Ptr())
- ->m_resource.getResource();
- break;
- default:
- d3dResource = static_cast<TextureResourceImpl*>(viewImpl->m_resource.Ptr())
- ->m_resource.getResource();
- break;
- }
- auto gpuHandleIndex =
- m_commandBuffer->m_transientHeap->getCurrentViewHeap().allocate(1);
- if (gpuHandleIndex == -1)
- {
- m_commandBuffer->m_transientHeap->allocateNewViewDescriptorHeap(
- m_commandBuffer->m_renderer);
- gpuHandleIndex = m_commandBuffer->m_transientHeap->getCurrentViewHeap().allocate(1);
- m_commandBuffer->bindDescriptorHeaps();
- }
- this->m_commandBuffer->m_renderer->m_device->CopyDescriptorsSimple(
- 1,
- m_commandBuffer->m_transientHeap->getCurrentViewHeap().getCpuHandle(gpuHandleIndex),
- viewImpl->m_descriptor.cpuHandle,
- D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
-
- if (flags & ClearResourceViewFlags::FloatClearValues)
- {
- m_commandBuffer->m_cmdList->ClearUnorderedAccessViewFloat(
- m_commandBuffer->m_transientHeap->getCurrentViewHeap().getGpuHandle(
- gpuHandleIndex),
- viewImpl->m_descriptor.cpuHandle,
- d3dResource,
- clearValue->color.floatValues,
- 0,
- nullptr);
- }
- else
- {
- m_commandBuffer->m_cmdList->ClearUnorderedAccessViewUint(
- m_commandBuffer->m_transientHeap->getCurrentViewHeap().getGpuHandle(
- gpuHandleIndex),
- viewImpl->m_descriptor.cpuHandle,
- d3dResource,
- clearValue->color.uintValues,
- 0,
- nullptr);
- }
- break;
- }
- default:
- break;
- }
-}
-
-void ResourceCommandEncoderImpl::resolveResource(
- ITextureResource* source,
- ResourceState sourceState,
- SubresourceRange sourceRange,
- ITextureResource* dest,
- ResourceState destState,
- SubresourceRange destRange)
-{
- auto srcTexture = static_cast<TextureResourceImpl*>(source);
- auto srcDesc = srcTexture->getDesc();
- auto dstTexture = static_cast<TextureResourceImpl*>(dest);
- auto dstDesc = dstTexture->getDesc();
-
- for (GfxIndex layer = 0; layer < sourceRange.layerCount; ++layer)
- {
- for (GfxIndex mip = 0; mip < sourceRange.mipLevelCount; ++mip)
- {
- auto srcSubresourceIndex = D3DUtil::getSubresourceIndex(
- mip + sourceRange.mipLevel,
- layer + sourceRange.baseArrayLayer,
- 0,
- srcDesc->numMipLevels,
- srcDesc->arraySize);
- auto dstSubresourceIndex = D3DUtil::getSubresourceIndex(
- mip + destRange.mipLevel,
- layer + destRange.baseArrayLayer,
- 0,
- dstDesc->numMipLevels,
- dstDesc->arraySize);
-
- DXGI_FORMAT format = D3DUtil::getMapFormat(srcDesc->format);
-
- m_commandBuffer->m_cmdList->ResolveSubresource(
- dstTexture->m_resource.getResource(),
- dstSubresourceIndex,
- srcTexture->m_resource.getResource(),
- srcSubresourceIndex,
- format);
- }
- }
-}
-
-void ResourceCommandEncoderImpl::resolveQuery(
- IQueryPool* queryPool, GfxIndex index, GfxCount count, IBufferResource* buffer, Offset offset)
-{
- auto queryBase = static_cast<QueryPoolBase*>(queryPool);
- switch (queryBase->m_desc.type)
- {
- case QueryType::AccelerationStructureCompactedSize:
- case QueryType::AccelerationStructureCurrentSize:
- case QueryType::AccelerationStructureSerializedSize:
- {
- auto queryPoolImpl = static_cast<PlainBufferProxyQueryPoolImpl*>(queryPool);
- auto bufferImpl = static_cast<BufferResourceImpl*>(buffer);
- auto srcQueryBuffer = queryPoolImpl->m_bufferResource->m_resource.getResource();
-
- D3D12_RESOURCE_BARRIER barrier = {};
- barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
- barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
- barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
- barrier.Transition.pResource = srcQueryBuffer;
- m_commandBuffer->m_cmdList->ResourceBarrier(1, &barrier);
-
- m_commandBuffer->m_cmdList->CopyBufferRegion(
- bufferImpl->m_resource.getResource(),
- (uint64_t)offset,
- srcQueryBuffer,
- index * sizeof(uint64_t),
- count * sizeof(uint64_t));
-
- barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
- barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
- barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
- barrier.Transition.pResource = srcQueryBuffer;
- m_commandBuffer->m_cmdList->ResourceBarrier(1, &barrier);
- }
- break;
- default:
- {
- auto queryPoolImpl = static_cast<QueryPoolImpl*>(queryPool);
- auto bufferImpl = static_cast<BufferResourceImpl*>(buffer);
- m_commandBuffer->m_cmdList->ResolveQueryData(
- queryPoolImpl->m_queryHeap.get(),
- queryPoolImpl->m_queryType,
- index,
- count,
- bufferImpl->m_resource.getResource(),
- offset);
- }
- break;
- }
-}
-
-void ResourceCommandEncoderImpl::copyTextureToBuffer(
- IBufferResource* dst,
- Offset dstOffset,
- Size dstSize,
- Size dstRowStride,
- ITextureResource* src,
- ResourceState srcState,
- SubresourceRange srcSubresource,
- ITextureResource::Offset3D srcOffset,
- ITextureResource::Extents extent)
-{
- assert(srcSubresource.mipLevelCount <= 1);
-
- auto srcTexture = static_cast<TextureResourceImpl*>(src);
- auto dstBuffer = static_cast<BufferResourceImpl*>(dst);
- auto baseSubresourceIndex = D3DUtil::getSubresourceIndex(
- srcSubresource.mipLevel,
- srcSubresource.baseArrayLayer,
- 0,
- srcTexture->getDesc()->numMipLevels,
- srcTexture->getDesc()->arraySize);
- auto textureSize = srcTexture->getDesc()->size;
- FormatInfo formatInfo = {};
- gfxGetFormatInfo(srcTexture->getDesc()->format, &formatInfo);
- if (srcSubresource.mipLevelCount == 0)
- srcSubresource.mipLevelCount = srcTexture->getDesc()->numMipLevels;
- if (srcSubresource.layerCount == 0)
- srcSubresource.layerCount = srcTexture->getDesc()->arraySize;
-
- for (GfxCount layer = 0; layer < srcSubresource.layerCount; layer++)
- {
- // Get the footprint
- D3D12_RESOURCE_DESC texDesc = srcTexture->m_resource.getResource()->GetDesc();
-
- D3D12_TEXTURE_COPY_LOCATION dstRegion = {};
- dstRegion.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
- dstRegion.pResource = dstBuffer->m_resource.getResource();
- D3D12_PLACED_SUBRESOURCE_FOOTPRINT& footprint = dstRegion.PlacedFootprint;
-
- D3D12_TEXTURE_COPY_LOCATION srcRegion = {};
- srcRegion.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
- srcRegion.SubresourceIndex = D3DUtil::getSubresourceIndex(
- srcSubresource.mipLevel,
- layer + srcSubresource.baseArrayLayer,
- 0,
- srcTexture->getDesc()->numMipLevels,
- srcTexture->getDesc()->arraySize);
- srcRegion.pResource = srcTexture->m_resource.getResource();
-
- footprint.Offset = dstOffset;
- footprint.Footprint.Format = texDesc.Format;
- uint32_t mipLevel = srcSubresource.mipLevel;
- if (extent.width != 0xFFFFFFFF)
- {
- footprint.Footprint.Width = extent.width;
- }
- else
- {
- footprint.Footprint.Width = Math::Max(1, (textureSize.width >> mipLevel)) - srcOffset.x;
- }
- if (extent.height != 0xFFFFFFFF)
- {
- footprint.Footprint.Height = extent.height;
- }
- else
- {
- footprint.Footprint.Height =
- Math::Max(1, (textureSize.height >> mipLevel)) - srcOffset.y;
- }
- if (extent.depth != 0xFFFFFFFF)
- {
- footprint.Footprint.Depth = extent.depth;
- }
- else
- {
- footprint.Footprint.Depth = Math::Max(1, (textureSize.depth >> mipLevel)) - srcOffset.z;
- }
-
- assert(dstRowStride % D3D12_TEXTURE_DATA_PITCH_ALIGNMENT == 0);
- footprint.Footprint.RowPitch = (UINT)dstRowStride;
-
- auto bufferSize =
- footprint.Footprint.RowPitch * footprint.Footprint.Height * footprint.Footprint.Depth;
-
- D3D12_BOX srcBox = {};
- srcBox.left = srcOffset.x;
- srcBox.top = srcOffset.y;
- srcBox.front = srcOffset.z;
- srcBox.right = srcOffset.x + extent.width;
- srcBox.bottom = srcOffset.y + extent.height;
- srcBox.back = srcOffset.z + extent.depth;
- m_commandBuffer->m_cmdList->CopyTextureRegion(&dstRegion, 0, 0, 0, &srcRegion, &srcBox);
- }
-}
-
-void ResourceCommandEncoderImpl::textureSubresourceBarrier(
- ITextureResource* texture,
- SubresourceRange subresourceRange,
- ResourceState src,
- ResourceState dst)
-{
- auto textureImpl = static_cast<TextureResourceImpl*>(texture);
-
- ShortList<D3D12_RESOURCE_BARRIER> barriers;
- D3D12_RESOURCE_BARRIER barrier;
- barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
- if (src == dst && src == ResourceState::UnorderedAccess)
- {
- barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
- barrier.UAV.pResource = textureImpl->m_resource.getResource();
- barriers.add(barrier);
- }
- else
- {
- barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
- barrier.Transition.StateBefore = D3DUtil::getResourceState(src);
- barrier.Transition.StateAfter = D3DUtil::getResourceState(dst);
- if (barrier.Transition.StateBefore == barrier.Transition.StateAfter)
- return;
- barrier.Transition.pResource = textureImpl->m_resource.getResource();
- auto d3dFormat = D3DUtil::getMapFormat(textureImpl->getDesc()->format);
- auto aspectMask = (int32_t)subresourceRange.aspectMask;
- if (subresourceRange.aspectMask == TextureAspect::Default)
- aspectMask = (int32_t)TextureAspect::Color;
- while (aspectMask)
- {
- auto aspect = Math::getLowestBit((int32_t)aspectMask);
- aspectMask &= ~aspect;
- auto planeIndex = D3DUtil::getPlaneSlice(d3dFormat, (TextureAspect)aspect);
- for (GfxCount layer = 0; layer < subresourceRange.layerCount; layer++)
- {
- for (GfxCount mip = 0; mip < subresourceRange.mipLevelCount; mip++)
- {
- barrier.Transition.Subresource = D3DUtil::getSubresourceIndex(
- mip + subresourceRange.mipLevel,
- layer + subresourceRange.baseArrayLayer,
- planeIndex,
- textureImpl->getDesc()->numMipLevels,
- textureImpl->getDesc()->arraySize);
- barriers.add(barrier);
- }
- }
- }
- }
- m_commandBuffer->m_cmdList->ResourceBarrier(
- (UINT)barriers.getCount(), barriers.getArrayView().getBuffer());
-}
-
-void ResourceCommandEncoderImpl::beginDebugEvent(const char* name, float rgbColor[3])
-{
- auto beginEvent = m_commandBuffer->m_renderer->m_BeginEventOnCommandList;
- if (beginEvent)
- {
- beginEvent(
- m_commandBuffer->m_cmdList,
- 0xff000000 | (uint8_t(rgbColor[0] * 255.0f) << 16) |
- (uint8_t(rgbColor[1] * 255.0f) << 8) | uint8_t(rgbColor[2] * 255.0f),
- name);
- }
-}
-
-void ResourceCommandEncoderImpl::endDebugEvent()
-{
- auto endEvent = m_commandBuffer->m_renderer->m_EndEventOnCommandList;
- if (endEvent)
- {
- endEvent(m_commandBuffer->m_cmdList);
- }
-}
-
-void RenderCommandEncoderImpl::init(
- DeviceImpl* renderer,
- TransientResourceHeapImpl* transientHeap,
- CommandBufferImpl* cmdBuffer,
- RenderPassLayoutImpl* renderPass,
- FramebufferImpl* framebuffer)
-{
- PipelineCommandEncoder::init(cmdBuffer);
- m_preCmdList = nullptr;
- m_renderPass = renderPass;
- m_framebuffer = framebuffer;
- m_transientHeap = transientHeap;
- m_boundVertexBuffers.clear();
- m_boundIndexBuffer = nullptr;
- m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
- m_primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
- m_boundIndexFormat = DXGI_FORMAT_UNKNOWN;
- m_boundIndexOffset = 0;
- m_currentPipeline = nullptr;
-
- // Set render target states.
- if (!framebuffer)
- {
- return;
- }
- m_d3dCmdList->OMSetRenderTargets(
- (UINT)framebuffer->renderTargetViews.getCount(),
- framebuffer->renderTargetDescriptors.getArrayView().getBuffer(),
- FALSE,
- framebuffer->depthStencilView ? &framebuffer->depthStencilDescriptor : nullptr);
-
- // Issue clear commands based on render pass set up.
- for (Index i = 0; i < framebuffer->renderTargetViews.getCount(); i++)
- {
- if (i >= renderPass->m_renderTargetAccesses.getCount())
- continue;
-
- auto& access = renderPass->m_renderTargetAccesses[i];
-
- // Transit resource states.
- {
- D3D12BarrierSubmitter submitter(m_d3dCmdList);
- auto resourceViewImpl = framebuffer->renderTargetViews[i].Ptr();
- if (resourceViewImpl)
- {
- auto textureResource =
- static_cast<TextureResourceImpl*>(resourceViewImpl->m_resource.Ptr());
- if (textureResource)
- {
- D3D12_RESOURCE_STATES initialState;
- if (access.initialState == ResourceState::Undefined)
- {
- initialState = textureResource->m_defaultState;
- }
- else
- {
- initialState = D3DUtil::getResourceState(access.initialState);
- }
- textureResource->m_resource.transition(
- initialState, D3D12_RESOURCE_STATE_RENDER_TARGET, submitter);
- }
- }
- }
- // Clear.
- if (access.loadOp == IRenderPassLayout::TargetLoadOp::Clear)
- {
- m_d3dCmdList->ClearRenderTargetView(
- framebuffer->renderTargetDescriptors[i],
- framebuffer->renderTargetClearValues[i].values,
- 0,
- nullptr);
- }
- }
-
- if (renderPass->m_hasDepthStencil)
- {
- // Transit resource states.
- {
- D3D12BarrierSubmitter submitter(m_d3dCmdList);
- auto resourceViewImpl = framebuffer->depthStencilView.Ptr();
- auto textureResource =
- static_cast<TextureResourceImpl*>(resourceViewImpl->m_resource.Ptr());
- D3D12_RESOURCE_STATES initialState;
- if (renderPass->m_depthStencilAccess.initialState == ResourceState::Undefined)
- {
- initialState = textureResource->m_defaultState;
- }
- else
- {
- initialState =
- D3DUtil::getResourceState(renderPass->m_depthStencilAccess.initialState);
- }
- textureResource->m_resource.transition(
- initialState, D3D12_RESOURCE_STATE_DEPTH_WRITE, submitter);
- }
- // Clear.
- uint32_t clearFlags = 0;
- if (renderPass->m_depthStencilAccess.loadOp == IRenderPassLayout::TargetLoadOp::Clear)
- {
- clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
- }
- if (renderPass->m_depthStencilAccess.stencilLoadOp ==
- IRenderPassLayout::TargetLoadOp::Clear)
- {
- clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
- }
- if (clearFlags)
- {
- m_d3dCmdList->ClearDepthStencilView(
- framebuffer->depthStencilDescriptor,
- (D3D12_CLEAR_FLAGS)clearFlags,
- framebuffer->depthStencilClearValue.depth,
- framebuffer->depthStencilClearValue.stencil,
- 0,
- nullptr);
- }
- }
-}
-
-Result RenderCommandEncoderImpl::bindPipeline(IPipelineState* state, IShaderObject** outRootObject)
-{
- return bindPipelineImpl(state, outRootObject);
-}
-
-Result RenderCommandEncoderImpl::bindPipelineWithRootObject(
- IPipelineState* state, IShaderObject* rootObject)
-{
- return bindPipelineWithRootObjectImpl(state, rootObject);
-}
-
-void RenderCommandEncoderImpl::setViewports(GfxCount count, const Viewport* viewports)
-{
- static const int kMaxViewports = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
- assert(count <= kMaxViewports && count <= kMaxRTVCount);
- for (GfxIndex ii = 0; ii < count; ++ii)
- {
- auto& inViewport = viewports[ii];
- auto& dxViewport = m_viewports[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_d3dCmdList->RSSetViewports(UINT(count), m_viewports);
-}
-
-void RenderCommandEncoderImpl::setScissorRects(GfxCount count, const ScissorRect* rects)
-{
- static const int kMaxScissorRects = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
- assert(count <= kMaxScissorRects && count <= kMaxRTVCount);
-
- for (GfxIndex ii = 0; ii < count; ++ii)
- {
- auto& inRect = rects[ii];
- auto& dxRect = m_scissorRects[ii];
-
- dxRect.left = LONG(inRect.minX);
- dxRect.top = LONG(inRect.minY);
- dxRect.right = LONG(inRect.maxX);
- dxRect.bottom = LONG(inRect.maxY);
- }
-
- m_d3dCmdList->RSSetScissorRects(UINT(count), m_scissorRects);
-}
-
-void RenderCommandEncoderImpl::setPrimitiveTopology(PrimitiveTopology topology)
-{
- m_primitiveTopologyType = D3DUtil::getPrimitiveType(topology);
- m_primitiveTopology = D3DUtil::getPrimitiveTopology(topology);
-}
-
-void RenderCommandEncoderImpl::setVertexBuffers(
- GfxIndex startSlot,
- GfxCount slotCount,
- IBufferResource* const* buffers,
- const Offset* offsets)
-{
- {
- const Index num = startSlot + slotCount;
- if (num > m_boundVertexBuffers.getCount())
- {
- m_boundVertexBuffers.setCount(num);
- }
- }
-
- for (GfxIndex i = 0; i < slotCount; i++)
- {
- BufferResourceImpl* buffer = static_cast<BufferResourceImpl*>(buffers[i]);
-
- BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i];
- boundBuffer.m_buffer = buffer;
- boundBuffer.m_offset = int(offsets[i]);
- }
-}
-
-void RenderCommandEncoderImpl::setIndexBuffer(
- IBufferResource* buffer, Format indexFormat, Offset offset)
-{
- m_boundIndexBuffer = (BufferResourceImpl*)buffer;
- m_boundIndexFormat = D3DUtil::getMapFormat(indexFormat);
- m_boundIndexOffset = (UINT)offset;
-}
-
-void RenderCommandEncoderImpl::prepareDraw()
-{
- auto pipelineState = m_currentPipeline.Ptr();
- if (!pipelineState || (pipelineState->desc.type != PipelineType::Graphics))
- {
- assert(!"No graphics pipeline state set");
- return;
- }
-
- // Submit - setting for graphics
- {
- GraphicsSubmitter submitter(m_d3dCmdList);
- RefPtr<PipelineStateBase> newPipeline;
- if (SLANG_FAILED(_bindRenderState(&submitter, newPipeline)))
- {
- assert(!"Failed to bind render state");
- }
- }
-
- m_d3dCmdList->IASetPrimitiveTopology(m_primitiveTopology);
-
- // Set up vertex buffer views
- {
- auto inputLayout = (InputLayoutImpl*)pipelineState->inputLayout.Ptr();
- if (inputLayout)
- {
- 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 = inputLayout->m_vertexStreamStrides[i];
- }
- }
- m_d3dCmdList->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;
-
- m_d3dCmdList->IASetIndexBuffer(&indexBufferView);
- }
-}
-
-void RenderCommandEncoderImpl::draw(GfxCount vertexCount, GfxIndex startVertex)
-{
- prepareDraw();
- m_d3dCmdList->DrawInstanced((uint32_t)vertexCount, 1, (uint32_t)startVertex, 0);
-}
-
-void RenderCommandEncoderImpl::drawIndexed(
- GfxCount indexCount, GfxIndex startIndex, GfxIndex baseVertex)
-{
- prepareDraw();
- m_d3dCmdList->DrawIndexedInstanced((uint32_t)indexCount, 1, (uint32_t)startIndex, (uint32_t)baseVertex, 0);
-}
-
-void RenderCommandEncoderImpl::endEncoding()
-{
- PipelineCommandEncoder::endEncodingImpl();
- if (!m_framebuffer)
- return;
- // Issue clear commands based on render pass set up.
- for (Index i = 0; i < m_renderPass->m_renderTargetAccesses.getCount(); i++)
- {
- auto& access = m_renderPass->m_renderTargetAccesses[i];
-
- // Transit resource states.
- {
- D3D12BarrierSubmitter submitter(m_d3dCmdList);
- auto resourceViewImpl = m_framebuffer->renderTargetViews[i].Ptr();
- if (!resourceViewImpl)
- continue;
- auto textureResource =
- static_cast<TextureResourceImpl*>(resourceViewImpl->m_resource.Ptr());
- if (textureResource)
- {
- textureResource->m_resource.transition(
- D3D12_RESOURCE_STATE_RENDER_TARGET,
- D3DUtil::getResourceState(access.finalState),
- submitter);
- }
- }
- }
-
- if (m_renderPass->m_hasDepthStencil)
- {
- // Transit resource states.
- D3D12BarrierSubmitter submitter(m_d3dCmdList);
- auto resourceViewImpl = m_framebuffer->depthStencilView.Ptr();
- auto textureResource =
- static_cast<TextureResourceImpl*>(resourceViewImpl->m_resource.Ptr());
- textureResource->m_resource.transition(
- D3D12_RESOURCE_STATE_DEPTH_WRITE,
- D3DUtil::getResourceState(m_renderPass->m_depthStencilAccess.finalState),
- submitter);
- }
- m_framebuffer = nullptr;
-}
-
-void RenderCommandEncoderImpl::setStencilReference(uint32_t referenceValue)
-{
- m_d3dCmdList->OMSetStencilRef((UINT)referenceValue);
-}
-
-void RenderCommandEncoderImpl::drawIndirect(
- GfxCount maxDrawCount,
- IBufferResource* argBuffer,
- Offset argOffset,
- IBufferResource* countBuffer,
- Offset countOffset)
-{
- prepareDraw();
-
- auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer);
- auto countBufferImpl = static_cast<BufferResourceImpl*>(countBuffer);
-
- m_d3dCmdList->ExecuteIndirect(
- m_renderer->drawIndirectCmdSignature,
- (uint32_t)maxDrawCount,
- argBufferImpl->m_resource,
- (uint64_t)argOffset,
- countBufferImpl ? countBufferImpl->m_resource.getResource() : nullptr,
- (uint64_t)countOffset);
-}
-
-void RenderCommandEncoderImpl::drawIndexedIndirect(
- GfxCount maxDrawCount,
- IBufferResource* argBuffer,
- Offset argOffset,
- IBufferResource* countBuffer,
- Offset countOffset)
-{
- prepareDraw();
-
- auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer);
- auto countBufferImpl = static_cast<BufferResourceImpl*>(countBuffer);
-
- m_d3dCmdList->ExecuteIndirect(
- m_renderer->drawIndexedIndirectCmdSignature,
- (uint32_t)maxDrawCount,
- argBufferImpl->m_resource,
- (uint64_t)argOffset,
- countBufferImpl ? countBufferImpl->m_resource.getResource() : nullptr,
- (uint64_t)countOffset);
-}
-
-Result RenderCommandEncoderImpl::setSamplePositions(
- GfxCount samplesPerPixel, GfxCount pixelCount, const SamplePosition* samplePositions)
-{
- if (m_commandBuffer->m_cmdList1)
- {
- m_commandBuffer->m_cmdList1->SetSamplePositions(
- (uint32_t)samplesPerPixel, (uint32_t)pixelCount, (D3D12_SAMPLE_POSITION*)samplePositions);
- return SLANG_OK;
- }
- return SLANG_E_NOT_AVAILABLE;
-}
-
-void RenderCommandEncoderImpl::drawInstanced(
- GfxCount vertexCount,
- GfxCount instanceCount,
- GfxIndex startVertex,
- GfxIndex startInstanceLocation)
-{
- prepareDraw();
- m_d3dCmdList->DrawInstanced(
- (uint32_t)vertexCount,
- (uint32_t)instanceCount,
- (uint32_t)startVertex,
- (uint32_t)startInstanceLocation);
-}
-
-void RenderCommandEncoderImpl::drawIndexedInstanced(
- GfxCount indexCount,
- GfxCount instanceCount,
- GfxIndex startIndexLocation,
- GfxIndex baseVertexLocation,
- GfxIndex startInstanceLocation)
-{
- prepareDraw();
- m_d3dCmdList->DrawIndexedInstanced(
- (uint32_t)indexCount,
- (uint32_t)instanceCount,
- (uint32_t)startIndexLocation,
- baseVertexLocation,
- (uint32_t)startInstanceLocation);
-}
-
-void ComputeCommandEncoderImpl::endEncoding() { PipelineCommandEncoder::endEncodingImpl(); }
-
-void ComputeCommandEncoderImpl::init(
- DeviceImpl* renderer, TransientResourceHeapImpl* transientHeap, CommandBufferImpl* cmdBuffer)
-{
- PipelineCommandEncoder::init(cmdBuffer);
- m_preCmdList = nullptr;
- m_transientHeap = transientHeap;
- m_currentPipeline = nullptr;
-}
-
-Result ComputeCommandEncoderImpl::bindPipeline(IPipelineState* state, IShaderObject** outRootObject)
-{
- return bindPipelineImpl(state, outRootObject);
-}
-
-Result ComputeCommandEncoderImpl::bindPipelineWithRootObject(
- IPipelineState* state, IShaderObject* rootObject)
-{
- return bindPipelineWithRootObjectImpl(state, rootObject);
-}
-
-void ComputeCommandEncoderImpl::dispatchCompute(int x, int y, int z)
-{
- // Submit binding for compute
- {
- ComputeSubmitter submitter(m_d3dCmdList);
- RefPtr<PipelineStateBase> newPipeline;
- if (SLANG_FAILED(_bindRenderState(&submitter, newPipeline)))
- {
- assert(!"Failed to bind render state");
- }
- }
- m_d3dCmdList->Dispatch(x, y, z);
-}
-
-void ComputeCommandEncoderImpl::dispatchComputeIndirect(IBufferResource* argBuffer, Offset offset)
-{
- // Submit binding for compute
- {
- ComputeSubmitter submitter(m_d3dCmdList);
- RefPtr<PipelineStateBase> newPipeline;
- if (SLANG_FAILED(_bindRenderState(&submitter, newPipeline)))
- {
- assert(!"Failed to bind render state");
- }
- }
- auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer);
-
- m_d3dCmdList->ExecuteIndirect(
- m_renderer->dispatchIndirectCmdSignature, 1, argBufferImpl->m_resource, (uint64_t)offset, nullptr, 0);
-}
-
-FenceImpl::~FenceImpl()
-{
- if (m_waitEvent)
- CloseHandle(m_waitEvent);
-}
-
-HANDLE FenceImpl::getWaitEvent()
-{
- if (m_waitEvent)
- return m_waitEvent;
- m_waitEvent = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
- return m_waitEvent;
-}
-
-Result FenceImpl::init(DeviceImpl* device, const IFence::Desc& desc)
-{
- SLANG_RETURN_ON_FAIL(device->m_device->CreateFence(
- desc.initialValue,
- desc.isShared ? D3D12_FENCE_FLAG_SHARED : D3D12_FENCE_FLAG_NONE,
- IID_PPV_ARGS(m_fence.writeRef())));
- return SLANG_OK;
-}
-
-Result FenceImpl::getCurrentValue(uint64_t* outValue)
-{
- *outValue = m_fence->GetCompletedValue();
- return SLANG_OK;
-}
-
-Result FenceImpl::setCurrentValue(uint64_t value)
-{
- SLANG_RETURN_ON_FAIL(m_fence->Signal(value));
- return SLANG_OK;
-}
-
-Result FenceImpl::getSharedHandle(InteropHandle* outHandle)
-{
- // Check if a shared handle already exists.
- if (sharedHandle.handleValue != 0)
- {
- *outHandle = sharedHandle;
- return SLANG_OK;
- }
-
- ComPtr<ID3D12Device> devicePtr;
- m_fence->GetDevice(IID_PPV_ARGS(devicePtr.writeRef()));
- SLANG_RETURN_ON_FAIL(devicePtr->CreateSharedHandle(
- m_fence, NULL, GENERIC_ALL, nullptr, (HANDLE*)&outHandle->handleValue));
- outHandle->api = InteropHandleAPI::D3D12;
- sharedHandle = *outHandle;
- return SLANG_OK;
-}
-
-Result FenceImpl::getNativeHandle(InteropHandle* outNativeHandle)
-{
- outNativeHandle->api = gfx::InteropHandleAPI::D3D12;
- outNativeHandle->handleValue = (uint64_t)m_fence.get();
- return SLANG_OK;
-}
-
-} // namespace d3d12
-
-Result SLANG_MCALL createD3D12Device(const IDevice::Desc* desc, IDevice** outDevice)
-{
- RefPtr<d3d12::DeviceImpl> result = new d3d12::DeviceImpl();
- SLANG_RETURN_ON_FAIL(result->initialize(*desc));
- returnComPtr(outDevice, result);
- return SLANG_OK;
-}
-} // namespace gfx
diff --git a/tools/gfx/d3d12/render-d3d12.h b/tools/gfx/d3d12/render-d3d12.h
deleted file mode 100644
index 95a982104..000000000
--- a/tools/gfx/d3d12/render-d3d12.h
+++ /dev/null
@@ -1,1806 +0,0 @@
-// render-d3d12.h
-#pragma once
-
-#include "../command-encoder-com-forward.h"
-#include "../d3d/d3d-swapchain.h"
-#include "../mutable-shader-object.h"
-#include "../renderer-shared.h"
-#include "../simple-render-pass-layout.h"
-#include "../transient-resource-heap-base.h"
-#include "core/slang-basic.h"
-#include "core/slang-blob.h"
-#include "core/slang-chunked-list.h"
-#include "descriptor-heap-d3d12.h"
-#include "resource-d3d12.h"
-
-#define _CRT_SECURE_NO_WARNINGS
-#define WIN32_LEAN_AND_MEAN
-#define NOMINMAX
-#include <Windows.h>
-#undef WIN32_LEAN_AND_MEAN
-#undef NOMINMAX
-
-#include <d3d12.h>
-#include <dxgi1_4.h>
-
-#ifndef __ID3D12GraphicsCommandList1_FWD_DEFINED__
-// If can't find a definition of CommandList1, just use an empty definition
-struct ID3D12GraphicsCommandList1
-{};
-#endif
-
-namespace gfx
-{
-namespace d3d12
-{
-
-using namespace Slang;
-
-// Define function pointer types for PIX library.
-typedef HRESULT(WINAPI* PFN_BeginEventOnCommandList)(
- ID3D12GraphicsCommandList* commandList, UINT64 color, _In_ PCSTR formatString);
-typedef HRESULT(WINAPI* PFN_EndEventOnCommandList)(ID3D12GraphicsCommandList* commandList);
-
-static const Int kMaxNumRenderFrames = 4;
-static const Int kMaxRTVCount = 8;
-
-struct D3D12DeviceInfo
-{
- void clear()
- {
- m_dxgiFactory.setNull();
- m_device.setNull();
- m_adapter.setNull();
- m_desc = {};
- m_desc1 = {};
- m_isWarp = false;
- m_isSoftware = false;
- }
-
- bool m_isWarp;
- bool m_isSoftware;
- ComPtr<IDXGIFactory> m_dxgiFactory;
- ComPtr<ID3D12Device> m_device;
- ComPtr<ID3D12Device5> m_device5;
- ComPtr<IDXGIAdapter> m_adapter;
- DXGI_ADAPTER_DESC m_desc;
- DXGI_ADAPTER_DESC1 m_desc1;
-};
-
-class CommandQueueImpl;
-class TransientResourceHeapImpl;
-class TextureResourceImpl;
-class CommandBufferImpl;
-
-class DeviceImpl : public RendererBase
-{
-public:
- Desc m_desc;
- D3D12DeviceExtendedDesc m_extendedDesc;
-
- DeviceInfo m_info;
- String m_adapterName;
-
- bool m_isInitialized = false;
-
- ComPtr<ID3D12Debug> m_dxDebug;
-
- D3D12DeviceInfo m_deviceInfo;
- ID3D12Device* m_device = nullptr;
- ID3D12Device5* m_device5 = nullptr;
-
- VirtualObjectPool m_queueIndexAllocator;
-
- RefPtr<CommandQueueImpl> m_resourceCommandQueue;
- RefPtr<TransientResourceHeapImpl> m_resourceCommandTransientHeap;
-
- RefPtr<D3D12GeneralExpandingDescriptorHeap> m_rtvAllocator;
- RefPtr<D3D12GeneralExpandingDescriptorHeap> m_dsvAllocator;
-
- // Space in the GPU-visible heaps is precious, so we will also keep
- // around CPU-visible heaps for storing shader-objects' descriptors in a format
- // that is ready for copying into the GPU-visible heaps as needed.
- //
- RefPtr<D3D12GeneralExpandingDescriptorHeap> m_cpuViewHeap; ///< Cbv, Srv, Uav
- RefPtr<D3D12GeneralExpandingDescriptorHeap> m_cpuSamplerHeap; ///< Heap for samplers
-
- // 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;
-
- PFN_BeginEventOnCommandList m_BeginEventOnCommandList = nullptr;
- PFN_EndEventOnCommandList m_EndEventOnCommandList = nullptr;
-
- bool m_nvapi = false;
-
- // Command signatures required for indirect draws. These indicate the format of the indirect
- // as well as the command type to be used (DrawInstanced and DrawIndexedInstanced, in this
- // case).
- ComPtr<ID3D12CommandSignature> drawIndirectCmdSignature;
- ComPtr<ID3D12CommandSignature> drawIndexedIndirectCmdSignature;
- ComPtr<ID3D12CommandSignature> dispatchIndirectCmdSignature;
-
-public:
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL initialize(const Desc& desc) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getFormatSupportedResourceStates(Format format, ResourceStateSet* outStates) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createTransientResourceHeap(
- const ITransientResourceHeap::Desc& desc, ITransientResourceHeap** outHeap) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createSwapchain(
- const ISwapchain::Desc& desc, WindowHandle window, ISwapchain** outSwapchain) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL getTextureAllocationInfo(
- const ITextureResource::Desc& desc, Size* outSize, Size* outAlignment) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL getTextureRowAlignment(Size* outAlignment) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createTextureResource(
- const ITextureResource::Desc& desc,
- const ITextureResource::SubresourceData* initData,
- ITextureResource** outResource) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createTextureFromNativeHandle(
- InteropHandle handle,
- const ITextureResource::Desc& srcDesc,
- ITextureResource** outResource) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createBufferResource(
- const IBufferResource::Desc& desc,
- const void* initData,
- IBufferResource** outResource) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createBufferFromNativeHandle(
- InteropHandle handle,
- const IBufferResource::Desc& srcDesc,
- IBufferResource** outResource) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createSamplerState(ISamplerState::Desc const& desc, ISamplerState** outSampler) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL createTextureView(
- ITextureResource* texture,
- IResourceView::Desc const& desc,
- IResourceView** outView) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createBufferView(
- IBufferResource* buffer,
- IBufferResource* counterBuffer,
- IResourceView::Desc const& desc,
- IResourceView** outView) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createFramebuffer(IFramebuffer::Desc const& desc, IFramebuffer** outFrameBuffer) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL createFramebufferLayout(
- IFramebufferLayout::Desc const& desc, IFramebufferLayout** outLayout) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL createRenderPassLayout(
- const IRenderPassLayout::Desc& desc, IRenderPassLayout** outRenderPassLayout) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createInputLayout(IInputLayout::Desc const& desc, IInputLayout** outLayout) override;
-
- virtual Result createShaderObjectLayout(
- slang::TypeLayoutReflection* typeLayout, ShaderObjectLayoutBase** outLayout) override;
- virtual Result createShaderObject(
- ShaderObjectLayoutBase* layout, IShaderObject** outObject) override;
- virtual Result createMutableShaderObject(
- ShaderObjectLayoutBase* layout, IShaderObject** outObject) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createMutableRootShaderObject(IShaderProgram* program, IShaderObject** outObject) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createShaderTable(const IShaderTable::Desc& desc, IShaderTable** outShaderTable) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createProgram(
- const IShaderProgram::Desc& desc,
- IShaderProgram** outProgram,
- ISlangBlob** outDiagnostics) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createGraphicsPipelineState(
- const GraphicsPipelineStateDesc& desc, IPipelineState** outState) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createComputePipelineState(
- const ComputePipelineStateDesc& desc, IPipelineState** outState) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createQueryPool(const IQueryPool::Desc& desc, IQueryPool** outState) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createFence(const IFence::Desc& desc, IFence** outFence) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL waitForFences(
- GfxCount fenceCount,
- IFence** fences,
- uint64_t* fenceValues,
- bool waitForAll,
- uint64_t timeout) override;
-
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL readTextureResource(
- ITextureResource* resource,
- ResourceState state,
- ISlangBlob** outBlob,
- Size* outRowPitch,
- Size* outPixelSize) override;
-
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL readBufferResource(
- IBufferResource* resource, Offset offset, Size size, ISlangBlob** outBlob) override;
-
- virtual SLANG_NO_THROW const gfx::DeviceInfo& SLANG_MCALL getDeviceInfo() const override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getNativeDeviceHandles(InteropHandles* outHandles) override;
-
- ~DeviceImpl();
-
- virtual SLANG_NO_THROW Result SLANG_MCALL getAccelerationStructurePrebuildInfo(
- const IAccelerationStructure::BuildInputs& buildInputs,
- IAccelerationStructure::PrebuildInfo* outPrebuildInfo) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createAccelerationStructure(
- const IAccelerationStructure::CreateDesc& desc, IAccelerationStructure** outView) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createRayTracingPipelineState(
- const RayTracingPipelineStateDesc& desc, IPipelineState** outState) override;
-
-public:
- static PROC loadProc(HMODULE module, char const* name);
-
- Result createCommandQueueImpl(CommandQueueImpl** outQueue);
-
- Result createTransientResourceHeapImpl(
- ITransientResourceHeap::Flags::Enum flags,
- Size constantBufferSize,
- uint32_t viewDescriptors,
- uint32_t samplerDescriptors,
- TransientResourceHeapImpl** outHeap);
-
- Result createBuffer(
- const D3D12_RESOURCE_DESC& resourceDesc,
- const void* srcData,
- Size srcDataSize,
- D3D12_RESOURCE_STATES finalState,
- D3D12Resource& resourceOut,
- bool isShared,
- MemoryType access = MemoryType::DeviceLocal);
-
- Result captureTextureToSurface(
- TextureResourceImpl* resource,
- ResourceState state,
- ISlangBlob** blob,
- Size* outRowPitch,
- Size* outPixelSize);
-
- Result _createDevice(
- DeviceCheckFlags deviceCheckFlags,
- const UnownedStringSlice& nameMatch,
- D3D_FEATURE_LEVEL featureLevel,
- D3D12DeviceInfo& outDeviceInfo);
-
- struct ResourceCommandRecordInfo
- {
- ComPtr<ICommandBuffer> commandBuffer;
- ID3D12GraphicsCommandList* d3dCommandList;
- };
- ResourceCommandRecordInfo encodeResourceCommands();
- void submitResourceCommandsAndWait(const ResourceCommandRecordInfo& info);
-};
-
-class BufferResourceImpl : public gfx::BufferResource
-{
-public:
- typedef BufferResource Parent;
-
- BufferResourceImpl(const Desc& desc);
-
- ~BufferResourceImpl();
-
- D3D12Resource m_resource; ///< The resource in gpu memory, allocated on the correct heap
- ///< relative to the cpu access flag
-
- D3D12_RESOURCE_STATES m_defaultState;
-
- virtual SLANG_NO_THROW DeviceAddress SLANG_MCALL getDeviceAddress() override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getNativeResourceHandle(InteropHandle* outHandle) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- map(MemoryRange* rangeToRead, void** outPointer) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL unmap(MemoryRange* writtenRange) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL setDebugName(const char* name) override;
-};
-
-class TextureResourceImpl : public TextureResource
-{
-public:
- typedef TextureResource Parent;
-
- TextureResourceImpl(const Desc& desc);
-
- ~TextureResourceImpl();
-
- D3D12Resource m_resource;
- D3D12_RESOURCE_STATES m_defaultState;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getNativeResourceHandle(InteropHandle* outHandle) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL setDebugName(const char* name) override;
-};
-
-class SamplerStateImpl : public SamplerStateBase
-{
-public:
- D3D12Descriptor m_descriptor;
- RefPtr<D3D12GeneralExpandingDescriptorHeap> m_allocator;
- ~SamplerStateImpl();
- virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
-};
-
-class ResourceViewInternalImpl
-{
-public:
- D3D12Descriptor m_descriptor;
- RefPtr<D3D12GeneralExpandingDescriptorHeap> m_allocator;
- ~ResourceViewInternalImpl();
-};
-
-class ResourceViewImpl
- : public ResourceViewBase
- , public ResourceViewInternalImpl
-{
-public:
- Slang::RefPtr<Resource> m_resource;
- virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
-};
-
-class FramebufferLayoutImpl : public FramebufferLayoutBase
-{
-public:
- ShortList<IFramebufferLayout::TargetLayout> m_renderTargets;
- bool m_hasDepthStencil = false;
- IFramebufferLayout::TargetLayout m_depthStencil;
-};
-
-class FramebufferImpl : public FramebufferBase
-{
-public:
- ShortList<RefPtr<ResourceViewImpl>> renderTargetViews;
- RefPtr<ResourceViewImpl> depthStencilView;
- ShortList<D3D12_CPU_DESCRIPTOR_HANDLE> renderTargetDescriptors;
- struct Color4f
- {
- float values[4];
- };
- ShortList<Color4f> renderTargetClearValues;
- D3D12_CPU_DESCRIPTOR_HANDLE depthStencilDescriptor;
- DepthStencilClearValue depthStencilClearValue;
-};
-
-class RenderPassLayoutImpl : public SimpleRenderPassLayout
-{
-public:
- RefPtr<FramebufferLayoutImpl> m_framebufferLayout;
- void init(const IRenderPassLayout::Desc& desc);
-};
-
-class InputLayoutImpl : public InputLayoutBase
-{
-public:
- List<D3D12_INPUT_ELEMENT_DESC> m_elements;
- List<UINT> m_vertexStreamStrides;
- List<char> m_text; ///< Holds all strings to keep in scope
-};
-
-class PipelineStateImpl : public PipelineStateBase
-{
-public:
- PipelineStateImpl(DeviceImpl* device)
- : m_device(device)
- {}
- DeviceImpl* m_device;
- ComPtr<ID3D12PipelineState> m_pipelineState;
- void init(const GraphicsPipelineStateDesc& inDesc);
- void init(const ComputePipelineStateDesc& inDesc);
- virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
- virtual Result ensureAPIPipelineStateCreated() override;
-};
-
-#if SLANG_GFX_HAS_DXR_SUPPORT
-class RayTracingPipelineStateImpl : public PipelineStateBase
-{
-public:
- ComPtr<ID3D12StateObject> m_stateObject;
- DeviceImpl* m_device;
- RayTracingPipelineStateImpl(DeviceImpl* device);
- void init(const RayTracingPipelineStateDesc& inDesc);
- virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
- virtual Result ensureAPIPipelineStateCreated() override;
-};
-#endif
-
-class QueryPoolImpl : public QueryPoolBase
-{
-public:
- Result init(const IQueryPool::Desc& desc, DeviceImpl* device);
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getResult(GfxIndex queryIndex, GfxCount count, uint64_t* data) override;
-
- void writeTimestamp(ID3D12GraphicsCommandList* cmdList, GfxIndex index);
-
-public:
- D3D12_QUERY_TYPE m_queryType;
- ComPtr<ID3D12QueryHeap> m_queryHeap;
- D3D12Resource m_readBackBuffer;
- ComPtr<ID3D12CommandAllocator> m_commandAllocator;
- ComPtr<ID3D12GraphicsCommandList> m_commandList;
- ComPtr<ID3D12Fence> m_fence;
- ComPtr<ID3D12CommandQueue> m_commandQueue;
- HANDLE m_waitEvent;
- UINT64 m_eventValue = 0;
-};
-
-/// Implements the IQueryPool interface with a plain buffer.
-/// Used for query types that does not correspond to a D3D query,
-/// such as ray-tracing acceleration structure post-build info.
-class PlainBufferProxyQueryPoolImpl : public QueryPoolBase
-{
-public:
- SLANG_COM_OBJECT_IUNKNOWN_ALL
- IQueryPool* getInterface(const Guid& guid);
-
-public:
- Result init(const IQueryPool::Desc& desc, DeviceImpl* device, uint32_t stride);
-
- virtual SLANG_NO_THROW Result SLANG_MCALL reset() override;
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getResult(GfxIndex queryIndex, GfxCount count, uint64_t* data) override;
-
-public:
- QueryType m_queryType;
- RefPtr<BufferResourceImpl> m_bufferResource;
- RefPtr<DeviceImpl> m_device;
- List<uint8_t> m_result;
- bool m_resultDirty = true;
- uint32_t m_stride = 0;
- uint32_t m_count = 0;
-};
-
-struct BoundVertexBuffer
-{
- RefPtr<BufferResourceImpl> m_buffer;
- int m_offset;
-};
-
-class TransientResourceHeapImpl
- : public TransientResourceHeapBaseImpl<DeviceImpl, BufferResourceImpl>
- , public ID3D12TransientResourceHeap
-{
-private:
- typedef TransientResourceHeapBaseImpl<DeviceImpl, BufferResourceImpl> Super;
-
-public:
- ComPtr<ID3D12CommandAllocator> m_commandAllocator;
- List<ComPtr<ID3D12GraphicsCommandList>> m_d3dCommandListPool;
- List<RefPtr<RefObject>> m_commandBufferPool;
- uint32_t m_commandListAllocId = 0;
- // Wait values for each command queue.
- struct QueueWaitInfo
- {
- uint64_t waitValue;
- HANDLE fenceEvent;
- ComPtr<ID3D12CommandQueue> queue;
- ComPtr<ID3D12Fence> fence = nullptr;
- };
- ShortList<QueueWaitInfo, 4> m_waitInfos;
- ShortList<HANDLE, 4> m_waitHandles;
-
- QueueWaitInfo& getQueueWaitInfo(uint32_t queueIndex);
- // 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.
- ShortList<D3D12DescriptorHeap, 4> m_viewHeaps; // Cbv, Srv, Uav
- ShortList<D3D12DescriptorHeap, 4> m_samplerHeaps; // Heap for samplers
- int32_t m_currentViewHeapIndex = -1;
- int32_t m_currentSamplerHeapIndex = -1;
- bool m_canResize = false;
-
- uint32_t m_viewHeapSize;
- uint32_t m_samplerHeapSize;
-
- D3D12DescriptorHeap& getCurrentViewHeap();
- D3D12DescriptorHeap& getCurrentSamplerHeap();
-
- D3D12LinearExpandingDescriptorHeap m_stagingCpuViewHeap;
- D3D12LinearExpandingDescriptorHeap m_stagingCpuSamplerHeap;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- queryInterface(SlangUUID const& uuid, void** outObject) override;
-
- virtual SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return Super::addRef(); }
- virtual SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return Super::release(); }
-
- virtual SLANG_NO_THROW Result SLANG_MCALL allocateTransientDescriptorTable(
- DescriptorType type,
- GfxCount count,
- Offset& outDescriptorOffset,
- void** outD3DDescriptorHeapHandle) override;
-
- ~TransientResourceHeapImpl();
-
- bool canResize() { return m_canResize; }
-
- Result init(
- const ITransientResourceHeap::Desc& desc,
- DeviceImpl* device,
- uint32_t viewHeapSize,
- uint32_t samplerHeapSize);
-
- Result allocateNewViewDescriptorHeap(DeviceImpl* device);
-
- Result allocateNewSamplerDescriptorHeap(DeviceImpl* device);
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createCommandBuffer(ICommandBuffer** outCommandBuffer) override;
-
- Result synchronize();
-
- virtual SLANG_NO_THROW Result SLANG_MCALL synchronizeAndReset() override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL finish() override;
-};
-
-struct Submitter
-{
- virtual void setRootConstantBufferView(
- int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0;
- virtual void setRootUAV(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0;
- virtual void setRootSRV(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;
- virtual void setRootConstants(
- Index rootParamIndex,
- Index dstOffsetIn32BitValues,
- Index countOf32BitValues,
- void const* srcData) = 0;
- virtual void setPipelineState(PipelineStateBase* pipelineState) = 0;
-};
-
-class PipelineCommandEncoder
-{
-public:
- bool m_isOpen = false;
- bool m_bindingDirty = true;
- CommandBufferImpl* m_commandBuffer;
- TransientResourceHeapImpl* m_transientHeap;
- DeviceImpl* m_renderer;
- ID3D12Device* m_device;
- ID3D12GraphicsCommandList* m_d3dCmdList;
- ID3D12GraphicsCommandList* m_preCmdList = nullptr;
-
- RefPtr<PipelineStateBase> m_currentPipeline;
-
- static int getBindPointIndex(PipelineType type);
-
- void init(CommandBufferImpl* commandBuffer);
-
- void endEncodingImpl() { m_isOpen = false; }
-
- Result bindPipelineImpl(IPipelineState* pipelineState, IShaderObject** outRootObject);
-
- Result bindPipelineWithRootObjectImpl(IPipelineState* pipelineState, IShaderObject* rootObject);
-
- /// Specializes the pipeline according to current root-object argument values,
- /// applys the root object bindings and binds the pipeline state.
- /// The newly specialized pipeline is held alive by the pipeline cache so users of
- /// `newPipeline` do not need to maintain its lifespan.
- Result _bindRenderState(Submitter* submitter, RefPtr<PipelineStateBase>& newPipeline);
-};
-
-struct DescriptorTable
-{
- DescriptorHeapReference m_heap;
- uint32_t m_offset = 0;
- uint32_t m_count = 0;
-
- SLANG_FORCE_INLINE uint32_t getDescriptorCount() const { return m_count; }
-
- /// Get the GPU handle at the specified index
- SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(uint32_t index = 0) const
- {
- SLANG_ASSERT(index < getDescriptorCount());
- return m_heap.getGpuHandle(m_offset + index);
- }
-
- /// Get the CPU handle at the specified index
- SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(uint32_t index = 0) const
- {
- SLANG_ASSERT(index < getDescriptorCount());
- return m_heap.getCpuHandle(m_offset + index);
- }
-
- void freeIfSupported()
- {
- if (m_count)
- {
- m_heap.freeIfSupported(m_offset, m_count);
- m_offset = 0;
- m_count = 0;
- }
- }
-
- bool allocate(uint32_t count)
- {
- auto allocatedOffset = m_heap.allocate(count);
- if (allocatedOffset == -1)
- return false;
- m_offset = allocatedOffset;
- m_count = count;
- return true;
- }
-
- bool allocate(DescriptorHeapReference heap, uint32_t count)
- {
- auto allocatedOffset = heap.allocate(count);
- if (allocatedOffset == -1)
- return false;
- m_heap = heap;
- m_offset = allocatedOffset;
- m_count = count;
- return true;
- }
-};
-
-/// Contextual data and operations required when binding shader objects to the pipeline state
-struct BindingContext
-{
- PipelineCommandEncoder* encoder;
- Submitter* submitter;
- TransientResourceHeapImpl* transientHeap;
- DeviceImpl* device;
- D3D12_DESCRIPTOR_HEAP_TYPE
- outOfMemoryHeap; // The type of descriptor heap that is OOM during binding.
-};
-
-/// A representation of the offset at which to bind a shader parameter or sub-object
-struct BindingOffset
-{
- // Note: When we actually bind a shader object to the pipeline we do not care about
- // HLSL-specific notions like `t` registers and `space`s. Those concepts are all
- // mediated by the root signature.
- //
- // Instead, we need to consider the offsets at which the object will be bound
- // into the actual D3D12 API state, which consists of the index of the current
- // root parameter to bind from, as well as indices into the current descriptor
- // tables (for resource views and samplers).
-
- uint32_t rootParam = 0;
- uint32_t resource = 0;
- uint32_t sampler = 0;
-
- void operator+=(BindingOffset const& offset)
- {
- rootParam += offset.rootParam;
- resource += offset.resource;
- sampler += offset.sampler;
- }
-};
-
-/// A reprsentation of an allocated descriptor set, consisting of an option resource table and
-/// an optional sampler table
-struct DescriptorSet
-{
- DescriptorTable resourceTable;
- DescriptorTable samplerTable;
-
- void freeIfSupported()
- {
- resourceTable.freeIfSupported();
- samplerTable.freeIfSupported();
- }
-};
-
-// Provides information on how binding ranges are stored in descriptor tables for
-// a shader object.
-// We allocate one CPU descriptor table for each descriptor heap type for the shader
-// object. In `ShaderObjectLayoutImpl`, we store the offset into the descriptor tables
-// for each binding, so we know where to write the descriptor when the user sets
-// a resource or sampler binding.
-class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
-{
-public:
- /// Information about a single logical binding range
- struct BindingRangeInfo
- {
- // Some of the information we store on binding ranges is redundant with
- // the information that Slang's reflection information stores, but having
- // it here can make the code more compact and obvious.
-
- /// The type of binding in this range.
- slang::BindingType bindingType;
-
- /// The shape of the resource
- SlangResourceShape resourceShape;
-
- /// The number of distinct bindings in this range.
- uint32_t count;
-
- /// A "flat" index for this range in whatever array provides backing storage for it
- uint32_t baseIndex;
-
- /// An index into the sub-object array if this binding range is treated
- /// as a sub-object.
- uint32_t subObjectIndex;
-
- bool isRootParameter;
- };
-
- /// Offset information for a sub-object range
- struct SubObjectRangeOffset : BindingOffset
- {
- SubObjectRangeOffset() {}
-
- SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout);
-
- /// The offset for "pending" ordinary data related to this range
- uint32_t pendingOrdinaryData = 0;
- };
-
- /// Stride information for a sub-object range
- struct SubObjectRangeStride : BindingOffset
- {
- SubObjectRangeStride() {}
-
- SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout);
-
- /// The strid for "pending" ordinary data related to this range
- uint32_t pendingOrdinaryData = 0;
- };
-
- /// Information about a sub-objecrt range
- struct SubObjectRangeInfo
- {
- /// The index of the binding range corresponding to this sub-object range
- Index bindingRangeIndex = 0;
-
- /// Layout information for the type of sub-object expected to be bound, if known
- RefPtr<ShaderObjectLayoutImpl> layout;
-
- /// The offset to use when binding the first object in this range
- SubObjectRangeOffset offset;
-
- /// Stride between consecutive objects in this range
- SubObjectRangeStride stride;
- };
-
- struct RootParameterInfo
- {
- IResourceView::Type type;
- };
-
- static bool isBindingRangeRootParameter(
- SlangSession* globalSession,
- const char* rootParameterAttributeName,
- slang::TypeLayoutReflection* typeLayout,
- Index bindingRangeIndex);
-
- struct Builder
- {
- public:
- Builder(RendererBase* renderer)
- : m_renderer(renderer)
- {}
-
- RendererBase* m_renderer;
- slang::TypeLayoutReflection* m_elementTypeLayout;
- List<BindingRangeInfo> m_bindingRanges;
- List<SubObjectRangeInfo> m_subObjectRanges;
- List<RootParameterInfo> m_rootParamsInfo;
-
- /// The number of sub-objects (not just sub-object *ranges*) stored in instances of this
- /// layout
- uint32_t m_subObjectCount = 0;
-
- /// Counters for the number of root parameters, resources, and samplers in this object
- /// itself
- BindingOffset m_ownCounts;
-
- /// Counters for the number of root parameters, resources, and sampler in this object
- /// and transitive sub-objects
- BindingOffset m_totalCounts;
-
- /// The number of root parameter consumed by (transitive) sub-objects
- uint32_t m_childRootParameterCount = 0;
-
- /// The total size in bytes of the ordinary data for this object and transitive
- /// sub-object.
- uint32_t m_totalOrdinaryDataSize = 0;
-
- /// The container type of this shader object. When `m_containerType` is
- /// `StructuredBuffer` or `UnsizedArray`, this shader object represents a collection
- /// instead of a single object.
- ShaderObjectContainerType m_containerType = ShaderObjectContainerType::None;
-
- Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout);
-
- Result build(ShaderObjectLayoutImpl** outLayout);
- };
-
- static Result createForElementType(
- RendererBase* renderer,
- slang::TypeLayoutReflection* elementType,
- ShaderObjectLayoutImpl** outLayout);
-
- List<BindingRangeInfo> const& getBindingRanges() { return m_bindingRanges; }
-
- Index getBindingRangeCount() { return m_bindingRanges.getCount(); }
-
- BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; }
-
- uint32_t getResourceSlotCount() { return m_ownCounts.resource; }
- uint32_t getSamplerSlotCount() { return m_ownCounts.sampler; }
- Index getSubObjectSlotCount() { return m_subObjectCount; }
- Index getSubObjectCount() { return m_subObjectCount; }
-
- uint32_t getTotalResourceDescriptorCount() { return m_totalCounts.resource; }
- uint32_t getTotalSamplerDescriptorCount() { return m_totalCounts.sampler; }
-
- uint32_t getOrdinaryDataBufferCount() { return m_totalOrdinaryDataSize ? 1 : 0; }
- bool hasOrdinaryDataBuffer() { return m_totalOrdinaryDataSize != 0; }
-
- uint32_t getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer()
- {
- return m_totalCounts.resource - getOrdinaryDataBufferCount();
- }
-
- uint32_t getOwnUserRootParameterCount() { return (uint32_t)m_rootParamsInfo.getCount(); }
- uint32_t getTotalRootTableParameterCount() { return m_totalCounts.rootParam; }
- uint32_t getChildRootParameterCount() { return m_childRootParameterCount; }
-
- uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
-
- SubObjectRangeInfo const& getSubObjectRange(Index index) { return m_subObjectRanges[index]; }
- List<SubObjectRangeInfo> const& getSubObjectRanges() { return m_subObjectRanges; }
-
- RendererBase* getRenderer() { return m_renderer; }
-
- slang::TypeReflection* getType() { return m_elementTypeLayout->getType(); }
-
- const RootParameterInfo& getRootParameterInfo(Index index) { return m_rootParamsInfo[index]; }
-
-protected:
- Result init(Builder* builder);
-
- List<BindingRangeInfo> m_bindingRanges;
- List<SubObjectRangeInfo> m_subObjectRanges;
- List<RootParameterInfo> m_rootParamsInfo;
-
- BindingOffset m_ownCounts;
- BindingOffset m_totalCounts;
-
- uint32_t m_subObjectCount = 0;
- uint32_t m_childRootParameterCount = 0;
-
- uint32_t m_totalOrdinaryDataSize = 0;
-};
-
-class RootShaderObjectLayoutImpl : public ShaderObjectLayoutImpl
-{
- typedef ShaderObjectLayoutImpl Super;
-
-public:
- struct EntryPointInfo
- {
- RefPtr<ShaderObjectLayoutImpl> layout;
- BindingOffset offset;
- };
-
- struct Builder : Super::Builder
- {
- Builder(
- RendererBase* renderer,
- slang::IComponentType* program,
- slang::ProgramLayout* programLayout)
- : Super::Builder(renderer)
- , m_program(program)
- , m_programLayout(programLayout)
- {}
-
- Result build(RootShaderObjectLayoutImpl** outLayout);
-
- void addGlobalParams(slang::VariableLayoutReflection* globalsLayout);
-
- void addEntryPoint(SlangStage stage, ShaderObjectLayoutImpl* entryPointLayout);
-
- slang::IComponentType* m_program;
- slang::ProgramLayout* m_programLayout;
- List<EntryPointInfo> m_entryPoints;
- };
-
- EntryPointInfo& getEntryPoint(Index index) { return m_entryPoints[index]; }
-
- List<EntryPointInfo>& getEntryPoints() { return m_entryPoints; }
-
- struct DescriptorSetLayout
- {
- List<D3D12_DESCRIPTOR_RANGE> m_resourceRanges;
- List<D3D12_DESCRIPTOR_RANGE> m_samplerRanges;
- uint32_t m_resourceCount = 0;
- uint32_t m_samplerCount = 0;
- };
-
- struct RootSignatureDescBuilder
- {
- DeviceImpl* m_device;
-
- RootSignatureDescBuilder(DeviceImpl* device)
- : m_device(device)
- {}
-
- // We will use one descriptor set for the global scope and one additional
- // descriptor set for each `ParameterBlock` binding range in the shader object
- // hierarchy, regardless of the shader's `space` indices.
- List<DescriptorSetLayout> m_descriptorSets;
- List<D3D12_ROOT_PARAMETER> m_rootParameters;
- D3D12_ROOT_SIGNATURE_DESC m_rootSignatureDesc = {};
-
- static Result translateDescriptorRangeType(
- slang::BindingType c, D3D12_DESCRIPTOR_RANGE_TYPE* outType);
-
- /// Stores offset information to apply to the reflected register/space for a descriptor
- /// range.
- ///
- struct BindingRegisterOffset
- {
- uint32_t spaceOffset = 0; // The `space` index as specified in shader.
-
- enum
- {
- kRangeTypeCount = 4
- };
-
- /// An offset to apply for each D3D12 register class, as given
- /// by a `D3D12_DESCRIPTOR_RANGE_TYPE`.
- ///
- /// Note that the `D3D12_DESCRIPTOR_RANGE_TYPE` enumeration has
- /// values between 0 and 3, inclusive.
- ///
- uint32_t offsetForRangeType[kRangeTypeCount] = {0, 0, 0, 0};
-
- uint32_t& operator[](D3D12_DESCRIPTOR_RANGE_TYPE type)
- {
- return offsetForRangeType[int(type)];
- }
-
- uint32_t operator[](D3D12_DESCRIPTOR_RANGE_TYPE type) const
- {
- return offsetForRangeType[int(type)];
- }
-
- BindingRegisterOffset() {}
-
- BindingRegisterOffset(slang::VariableLayoutReflection* varLayout)
- {
- if (varLayout)
- {
- spaceOffset =
- (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE);
- offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] =
- (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER);
- offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SRV] =
- (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE);
- offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_UAV] =
- (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS);
- offsetForRangeType[D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER] =
- (UINT)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE);
- }
- }
-
- void operator+=(BindingRegisterOffset const& other)
- {
- spaceOffset += other.spaceOffset;
- for (int i = 0; i < kRangeTypeCount; ++i)
- {
- offsetForRangeType[i] += other.offsetForRangeType[i];
- }
- }
- };
-
- struct BindingRegisterOffsetPair
- {
- BindingRegisterOffset primary;
- BindingRegisterOffset pending;
-
- BindingRegisterOffsetPair() {}
-
- BindingRegisterOffsetPair(slang::VariableLayoutReflection* varLayout)
- : primary(varLayout)
- , pending(varLayout->getPendingDataLayout())
- {}
-
- void operator+=(BindingRegisterOffsetPair const& other)
- {
- primary += other.primary;
- pending += other.pending;
- }
- };
- /// Add a new descriptor set to the layout being computed.
- ///
- /// Note that a "descriptor set" in the layout may amount to
- /// zero, one, or two different descriptor *tables* in the
- /// final D3D12 root signature. Each descriptor set may
- /// contain zero or more view ranges (CBV/SRV/UAV) and zero
- /// or more sampler ranges. It maps to a view descriptor table
- /// if the number of view ranges is non-zero and to a sampler
- /// descriptor table if the number of sampler ranges is non-zero.
- ///
- uint32_t addDescriptorSet();
-
- Result addDescriptorRange(
- Index physicalDescriptorSetIndex,
- D3D12_DESCRIPTOR_RANGE_TYPE rangeType,
- UINT registerIndex,
- UINT spaceIndex,
- UINT count,
- bool isRootParameter);
- /// Add one descriptor range as specified in Slang reflection information to the layout.
- ///
- /// The layout information is taken from `typeLayout` for the descriptor
- /// range with the given `descriptorRangeIndex` within the logical
- /// descriptor set (reflected by Slang) with the given `logicalDescriptorSetIndex`.
- ///
- /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
- /// the descriptor set that the range should be added to.
- ///
- /// The `offset` encodes information about space and/or register offsets that
- /// should be applied to descrptor ranges.
- ///
- /// This operation can fail if the given descriptor range encodes a range that
- /// doesn't map to anything directly supported by D3D12. Higher-level routines
- /// will often want to ignore such failures.
- ///
- Result addDescriptorRange(
- slang::TypeLayoutReflection* typeLayout,
- Index physicalDescriptorSetIndex,
- BindingRegisterOffset const& containerOffset,
- BindingRegisterOffset const& elementOffset,
- Index logicalDescriptorSetIndex,
- Index descriptorRangeIndex,
- bool isRootParameter);
-
- /// Add one binding range to the computed layout.
- ///
- /// The layout information is taken from `typeLayout` for the binding
- /// range with the given `bindingRangeIndex`.
- ///
- /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
- /// the descriptor set that the range should be added to.
- ///
- /// The `offset` encodes information about space and/or register offsets that
- /// should be applied to descrptor ranges.
- ///
- /// Note that a single binding range may encompass zero or more descriptor ranges.
- ///
- void addBindingRange(
- slang::TypeLayoutReflection* typeLayout,
- Index physicalDescriptorSetIndex,
- BindingRegisterOffset const& containerOffset,
- BindingRegisterOffset const& elementOffset,
- Index bindingRangeIndex);
-
- void addAsValue(
- slang::VariableLayoutReflection* varLayout, Index physicalDescriptorSetIndex);
-
- /// Add binding ranges and parameter blocks to the root signature.
- ///
- /// The layout information is taken from `typeLayout` which should
- /// be a layout for either a program or an entry point.
- ///
- /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of
- /// the descriptor set that binding ranges not belonging to nested
- /// parameter blocks should be added to.
- ///
- /// The `offset` encodes information about space and/or register offsets that
- /// should be applied to descrptor ranges.
- ///
- void addAsConstantBuffer(
- slang::TypeLayoutReflection* typeLayout,
- Index physicalDescriptorSetIndex,
- BindingRegisterOffsetPair const& containerOffset,
- BindingRegisterOffsetPair const& elementOffset);
-
- void addAsValue(
- slang::TypeLayoutReflection* typeLayout,
- Index physicalDescriptorSetIndex,
- BindingRegisterOffsetPair const& containerOffset,
- BindingRegisterOffsetPair const& elementOffset);
-
- D3D12_ROOT_SIGNATURE_DESC& build();
- };
-
- static Result createRootSignatureFromSlang(
- DeviceImpl* device,
- RootShaderObjectLayoutImpl* rootLayout,
- slang::IComponentType* program,
- ID3D12RootSignature** outRootSignature,
- ID3DBlob** outError);
-
- static Result create(
- DeviceImpl* device,
- slang::IComponentType* program,
- slang::ProgramLayout* programLayout,
- RootShaderObjectLayoutImpl** outLayout,
- ID3DBlob** outError);
-
- slang::IComponentType* getSlangProgram() const { return m_program; }
- slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
-
-protected:
- Result init(Builder* builder);
-
- ComPtr<slang::IComponentType> m_program;
- slang::ProgramLayout* m_programLayout = nullptr;
-
- List<EntryPointInfo> m_entryPoints;
-
-public:
- ComPtr<ID3D12RootSignature> m_rootSignature;
-};
-
-struct ShaderBinary
-{
- SlangStage stage;
- slang::EntryPointReflection* entryPointInfo;
- String actualEntryPointNameInAPI;
- List<uint8_t> code;
-};
-
-class ShaderProgramImpl : public ShaderProgramBase
-{
-public:
- RefPtr<RootShaderObjectLayoutImpl> m_rootObjectLayout;
- List<ShaderBinary> m_shaders;
-
- virtual Result createShaderModule(
- slang::EntryPointReflection* entryPointInfo, ComPtr<ISlangBlob> kernelCode) override;
-};
-
-class ShaderObjectImpl
- : public ShaderObjectBaseImpl<ShaderObjectImpl, ShaderObjectLayoutImpl, SimpleShaderObjectData>
-{
- typedef ShaderObjectBaseImpl<ShaderObjectImpl, ShaderObjectLayoutImpl, SimpleShaderObjectData>
- Super;
-
-public:
- static Result create(
- DeviceImpl* device, ShaderObjectLayoutImpl* layout, ShaderObjectImpl** outShaderObject);
-
- ~ShaderObjectImpl();
-
- RendererBase* getDevice() { return m_device.get(); }
-
- virtual SLANG_NO_THROW GfxCount SLANG_MCALL getEntryPointCount() override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint) override;
-
- virtual SLANG_NO_THROW const void* SLANG_MCALL getRawData() override;
-
- virtual SLANG_NO_THROW Size SLANG_MCALL getSize() override;
-
- // TODO: What to do with size_t?
- virtual SLANG_NO_THROW Result SLANG_MCALL
- setData(ShaderOffset const& inOffset, void const* data, size_t inSize) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL
- setObject(ShaderOffset const& offset, IShaderObject* object) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- setResource(ShaderOffset const& offset, IResourceView* resourceView) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- setSampler(ShaderOffset const& offset, ISamplerState* sampler) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL setCombinedTextureSampler(
- ShaderOffset const& offset, IResourceView* textureView, ISamplerState* sampler) override;
-
-protected:
- Result init(
- DeviceImpl* device,
- ShaderObjectLayoutImpl* layout,
- DescriptorHeapReference viewHeap,
- DescriptorHeapReference samplerHeap);
-
- /// Write the uniform/ordinary data of this object into the given `dest` buffer at the given
- /// `offset`
- Result _writeOrdinaryData(
- PipelineCommandEncoder* encoder,
- BufferResourceImpl* buffer,
- Offset offset,
- Size destSize,
- ShaderObjectLayoutImpl* specializedLayout);
-
- bool shouldAllocateConstantBuffer(TransientResourceHeapImpl* transientHeap);
-
- /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
- Result _ensureOrdinaryDataBufferCreatedIfNeeded(
- PipelineCommandEncoder* encoder, ShaderObjectLayoutImpl* specializedLayout);
-
-public:
- void updateSubObjectsRecursive();
- /// Prepare to bind this object as a parameter block.
- ///
- /// This involves allocating and binding any descriptor tables necessary
- /// to to store the state of the object. The function returns a descriptor
- /// set formed from any table(s) allocated. In addition, the `ioOffset`
- /// parameter will be adjusted to be correct for binding values into
- /// the resulting descriptor set.
- ///
- /// Returns:
- /// SLANG_OK when successful,
- /// SLANG_E_OUT_OF_MEMORY when descriptor heap is full.
- ///
- Result prepareToBindAsParameterBlock(
- BindingContext* context,
- BindingOffset& ioOffset,
- ShaderObjectLayoutImpl* specializedLayout,
- DescriptorSet& outDescriptorSet);
-
- bool checkIfCachedDescriptorSetIsValidRecursive(BindingContext* context);
-
- /// Bind this object as a `ParameterBlock<X>`
- Result bindAsParameterBlock(
- BindingContext* context,
- BindingOffset const& offset,
- ShaderObjectLayoutImpl* specializedLayout);
-
- /// Bind this object as a `ConstantBuffer<X>`
- Result bindAsConstantBuffer(
- BindingContext* context,
- DescriptorSet const& descriptorSet,
- BindingOffset const& offset,
- ShaderObjectLayoutImpl* specializedLayout);
-
- /// Bind this object as a value (for an interface-type parameter)
- Result bindAsValue(
- BindingContext* context,
- DescriptorSet const& descriptorSet,
- BindingOffset const& offset,
- ShaderObjectLayoutImpl* specializedLayout);
-
- /// Shared logic for `bindAsConstantBuffer()` and `bindAsValue()`
- Result _bindImpl(
- BindingContext* context,
- DescriptorSet const& descriptorSet,
- BindingOffset const& offset,
- ShaderObjectLayoutImpl* specializedLayout);
-
- Result bindRootArguments(BindingContext* context, uint32_t& index);
- /// A CPU-memory descriptor set holding any descriptors used to represent the
- /// resources/samplers in this object's state
- DescriptorSet m_descriptorSet;
- /// A cached descriptor set on GPU heap.
- DescriptorSet m_cachedGPUDescriptorSet;
-
- ShortList<RefPtr<Resource>, 8> m_boundResources;
- List<D3D12_GPU_VIRTUAL_ADDRESS> m_rootArguments;
- /// A constant buffer used to stored ordinary data for this object
- /// and existential-type sub-objects.
- ///
- /// Allocated from transient heap on demand with `_createOrdinaryDataBufferIfNeeded()`
- IBufferResource* m_constantBufferWeakPtr = nullptr;
- Offset m_constantBufferOffset = 0;
- Size m_constantBufferSize = 0;
-
- /// Dirty bit tracking whether the constant buffer needs to be updated.
- bool m_isConstantBufferDirty = true;
- /// The transient heap from which the constant buffer and descriptor set is allocated.
- TransientResourceHeapImpl* m_cachedTransientHeap;
- /// The version of the transient heap when the constant buffer and descriptor set is
- /// allocated.
- uint64_t m_cachedTransientHeapVersion;
-
- /// Whether this shader object is allowed to be mutable.
- bool m_isMutable = false;
- /// The version of a mutable shader object.
- uint32_t m_version = 0;
- /// The version of this mutable shader object when the gpu descriptor table is cached.
- uint32_t m_cachedGPUDescriptorSetVersion = -1;
- /// The versions of bound subobjects.
- List<uint32_t> m_subObjectVersions;
-
- /// Get the layout of this shader object with specialization arguments considered
- ///
- /// This operation should only be called after the shader object has been
- /// fully filled in and finalized.
- ///
- Result getSpecializedLayout(ShaderObjectLayoutImpl** outLayout);
-
- /// Create the layout for this shader object with specialization arguments considered
- ///
- /// This operation is virtual so that it can be customized by `RootShaderObject`.
- ///
- virtual Result _createSpecializedLayout(ShaderObjectLayoutImpl** outLayout);
-
- RefPtr<ShaderObjectLayoutImpl> m_specializedLayout;
-};
-
-class RootShaderObjectImpl : public ShaderObjectImpl
-{
- typedef ShaderObjectImpl Super;
-
-public:
- // Override default reference counting behavior to disable lifetime management via ComPtr.
- // Root objects are managed by command buffer and does not need to be freed by the user.
- SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return 1; }
- SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return 1; }
-
-public:
- RootShaderObjectLayoutImpl* getLayout();
-
- virtual SLANG_NO_THROW GfxCount SLANG_MCALL getEntryPointCount() override;
- virtual SLANG_NO_THROW SlangResult SLANG_MCALL
- getEntryPoint(GfxIndex index, IShaderObject** outEntryPoint) override;
- virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL
- copyFrom(IShaderObject* object, ITransientResourceHeap* transientHeap) override;
-
-public:
- Result bindAsRoot(BindingContext* context, RootShaderObjectLayoutImpl* specializedLayout);
-
-public:
- Result init(DeviceImpl* device) { return SLANG_OK; }
-
- Result resetImpl(
- DeviceImpl* device,
- RootShaderObjectLayoutImpl* layout,
- DescriptorHeapReference viewHeap,
- DescriptorHeapReference samplerHeap,
- bool isMutable);
-
- Result reset(
- DeviceImpl* device, RootShaderObjectLayoutImpl* layout, TransientResourceHeapImpl* heap);
-
-protected:
- virtual Result _createSpecializedLayout(ShaderObjectLayoutImpl** outLayout) override;
-
- List<RefPtr<ShaderObjectImpl>> m_entryPoints;
-};
-
-class MutableRootShaderObjectImpl : public RootShaderObjectImpl
-{
-public:
- // Override default reference counting behavior to disable lifetime management via ComPtr.
- // Root objects are managed by command buffer and does not need to be freed by the user.
- SLANG_NO_THROW uint32_t SLANG_MCALL addRef() override { return ShaderObjectBase::addRef(); }
- SLANG_NO_THROW uint32_t SLANG_MCALL release() override { return ShaderObjectBase::release(); }
-};
-
-class ShaderTableImpl : public ShaderTableBase
-{
-public:
- uint32_t m_rayGenTableOffset;
- uint32_t m_missTableOffset;
- uint32_t m_hitGroupTableOffset;
-
- DeviceImpl* m_device;
-
- virtual RefPtr<BufferResource> createDeviceBuffer(
- PipelineStateBase* pipeline,
- TransientResourceHeapBase* transientHeap,
- IResourceCommandEncoder* encoder) override;
-};
-
-class ResourceCommandEncoderImpl
- : public IResourceCommandEncoder
- , public PipelineCommandEncoder
-{
-public:
- virtual SLANG_NO_THROW void SLANG_MCALL copyBuffer(
- IBufferResource* dst,
- Offset dstOffset,
- IBufferResource* src,
- Offset srcOffset,
- Size size) override;
- virtual SLANG_NO_THROW void SLANG_MCALL
- uploadBufferData(IBufferResource* dst, Offset offset, Size size, void* data) override;
- virtual SLANG_NO_THROW void SLANG_MCALL textureBarrier(
- GfxCount count,
- ITextureResource* const* textures,
- ResourceState src,
- ResourceState dst) override;
- virtual SLANG_NO_THROW void SLANG_MCALL bufferBarrier(
- GfxCount count,
- IBufferResource* const* buffers,
- ResourceState src,
- ResourceState dst) override;
- virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() {}
- virtual SLANG_NO_THROW void SLANG_MCALL
- writeTimestamp(IQueryPool* pool, GfxIndex index) override;
- virtual SLANG_NO_THROW void SLANG_MCALL copyTexture(
- ITextureResource* dst,
- ResourceState dstState,
- SubresourceRange dstSubresource,
- ITextureResource::Offset3D dstOffset,
- ITextureResource* src,
- ResourceState srcState,
- SubresourceRange srcSubresource,
- ITextureResource::Offset3D srcOffset,
- ITextureResource::Extents extent) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL uploadTextureData(
- ITextureResource* dst,
- SubresourceRange subResourceRange,
- ITextureResource::Offset3D offset,
- ITextureResource::Extents extent,
- ITextureResource::SubresourceData* subResourceData,
- GfxCount subResourceDataCount) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL clearResourceView(
- IResourceView* view, ClearValue* clearValue, ClearResourceViewFlags::Enum flags) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL resolveResource(
- ITextureResource* source,
- ResourceState sourceState,
- SubresourceRange sourceRange,
- ITextureResource* dest,
- ResourceState destState,
- SubresourceRange destRange) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL resolveQuery(
- IQueryPool* queryPool,
- GfxIndex index,
- GfxCount count,
- IBufferResource* buffer,
- Offset offset) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL copyTextureToBuffer(
- IBufferResource* dst,
- Offset dstOffset,
- Size dstSize,
- Size dstRowStride,
- ITextureResource* src,
- ResourceState srcState,
- SubresourceRange srcSubresource,
- ITextureResource::Offset3D srcOffset,
- ITextureResource::Extents extent) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL textureSubresourceBarrier(
- ITextureResource* texture,
- SubresourceRange subresourceRange,
- ResourceState src,
- ResourceState dst) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL
- beginDebugEvent(const char* name, float rgbColor[3]) override;
- virtual SLANG_NO_THROW void SLANG_MCALL endDebugEvent() override;
-};
-
-class ComputeCommandEncoderImpl
- : public IComputeCommandEncoder
- , public ResourceCommandEncoderImpl
-{
-public:
- SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoderImpl)
-public:
- virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() override;
- void init(
- DeviceImpl* renderer,
- TransientResourceHeapImpl* transientHeap,
- CommandBufferImpl* cmdBuffer);
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- bindPipeline(IPipelineState* state, IShaderObject** outRootObject) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- bindPipelineWithRootObject(IPipelineState* state, IShaderObject* rootObject) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL dispatchCompute(int x, int y, int z) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL
- dispatchComputeIndirect(IBufferResource* argBuffer, Offset offset) override;
-};
-class RenderCommandEncoderImpl
- : public IRenderCommandEncoder
- , public ResourceCommandEncoderImpl
-{
-public:
- SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoderImpl)
-public:
- RefPtr<RenderPassLayoutImpl> m_renderPass;
- RefPtr<FramebufferImpl> m_framebuffer;
-
- List<BoundVertexBuffer> m_boundVertexBuffers;
-
- RefPtr<BufferResourceImpl> m_boundIndexBuffer;
-
- D3D12_VIEWPORT m_viewports[kMaxRTVCount];
- D3D12_RECT m_scissorRects[kMaxRTVCount];
-
- DXGI_FORMAT m_boundIndexFormat;
- UINT m_boundIndexOffset;
-
- D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType;
- D3D12_PRIMITIVE_TOPOLOGY m_primitiveTopology;
-
- void init(
- DeviceImpl* renderer,
- TransientResourceHeapImpl* transientHeap,
- CommandBufferImpl* cmdBuffer,
- RenderPassLayoutImpl* renderPass,
- FramebufferImpl* framebuffer);
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- bindPipeline(IPipelineState* state, IShaderObject** outRootObject) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- bindPipelineWithRootObject(IPipelineState* state, IShaderObject* rootObject) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL
- setViewports(GfxCount count, const Viewport* viewports) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL
- setScissorRects(GfxCount count, const ScissorRect* rects) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL
- setPrimitiveTopology(PrimitiveTopology topology) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL setVertexBuffers(
- GfxIndex startSlot,
- GfxCount slotCount,
- IBufferResource* const* buffers,
- const Offset* offsets) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL
- setIndexBuffer(IBufferResource* buffer, Format indexFormat, Offset offset = 0) override;
-
- void prepareDraw();
- virtual SLANG_NO_THROW void SLANG_MCALL
- draw(GfxCount vertexCount, GfxIndex startVertex = 0) override;
- virtual SLANG_NO_THROW void SLANG_MCALL
- drawIndexed(GfxCount indexCount, GfxIndex startIndex = 0, GfxIndex baseVertex = 0) override;
- virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL setStencilReference(uint32_t referenceValue) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL drawIndirect(
- GfxCount maxDrawCount,
- IBufferResource* argBuffer,
- Offset argOffset,
- IBufferResource* countBuffer,
- Offset countOffset) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL drawIndexedIndirect(
- GfxCount maxDrawCount,
- IBufferResource* argBuffer,
- Offset argOffset,
- IBufferResource* countBuffer,
- Offset countOffset) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL setSamplePositions(
- GfxCount samplesPerPixel,
- GfxCount pixelCount,
- const SamplePosition* samplePositions) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL drawInstanced(
- GfxCount vertexCount,
- GfxCount instanceCount,
- GfxIndex startVertex,
- GfxIndex startInstanceLocation) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL drawIndexedInstanced(
- GfxCount indexCount,
- GfxCount instanceCount,
- GfxIndex startIndexLocation,
- GfxIndex baseVertexLocation,
- GfxIndex startInstanceLocation) override;
-};
-
-#if SLANG_GFX_HAS_DXR_SUPPORT
-class RayTracingCommandEncoderImpl
- : public IRayTracingCommandEncoder
- , public ResourceCommandEncoderImpl
-{
-public:
- SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoderImpl)
-public:
- virtual SLANG_NO_THROW void SLANG_MCALL buildAccelerationStructure(
- const IAccelerationStructure::BuildDesc& desc,
- GfxCount propertyQueryCount,
- AccelerationStructureQueryDesc* queryDescs) override;
- virtual SLANG_NO_THROW void SLANG_MCALL copyAccelerationStructure(
- IAccelerationStructure* dest,
- IAccelerationStructure* src,
- AccelerationStructureCopyMode mode) override;
- virtual SLANG_NO_THROW void SLANG_MCALL queryAccelerationStructureProperties(
- GfxCount accelerationStructureCount,
- IAccelerationStructure* const* accelerationStructures,
- GfxCount queryCount,
- AccelerationStructureQueryDesc* queryDescs) override;
- virtual SLANG_NO_THROW void SLANG_MCALL
- serializeAccelerationStructure(DeviceAddress dest, IAccelerationStructure* source) override;
- virtual SLANG_NO_THROW void SLANG_MCALL deserializeAccelerationStructure(
- IAccelerationStructure* dest, DeviceAddress source) override;
- virtual SLANG_NO_THROW void SLANG_MCALL
- bindPipeline(IPipelineState* state, IShaderObject** outRootObject) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL
- bindPipelineWithRootObject(IPipelineState* state, IShaderObject* rootObject) override
- {
- return bindPipelineWithRootObjectImpl(state, rootObject);
- }
- virtual SLANG_NO_THROW void SLANG_MCALL dispatchRays(
- GfxIndex rayGenShaderIndex,
- IShaderTable* shaderTable,
- GfxCount width,
- GfxCount height,
- GfxCount depth) override;
- virtual SLANG_NO_THROW void SLANG_MCALL endEncoding() {}
-};
-#endif
-
-class CommandBufferImpl
- : public ICommandBufferD3D12
- , public ComObject
-{
-public:
- // There are a pair of cyclic references between a `TransientResourceHeap` and
- // a `CommandBuffer` created from the heap. We need to break the cycle upon
- // the public reference count of a command buffer dropping to 0.
- SLANG_COM_OBJECT_IUNKNOWN_ALL
-
- ICommandBufferD3D12* getInterface(const Guid& guid);
- virtual void comFree() override { m_transientHeap.breakStrongReference(); }
-
- virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* handle) override;
-
-public:
- ComPtr<ID3D12GraphicsCommandList> m_cmdList;
- ComPtr<ID3D12GraphicsCommandList1> m_cmdList1;
- ComPtr<ID3D12GraphicsCommandList4> m_cmdList4;
-
- BreakableReference<TransientResourceHeapImpl> m_transientHeap;
- // Weak reference is fine here since `m_transientHeap` already holds strong reference to
- // device.
- DeviceImpl* m_renderer;
- RootShaderObjectImpl m_rootShaderObject;
- RefPtr<MutableRootShaderObjectImpl> m_mutableRootShaderObject;
- bool m_descriptorHeapsBound = false;
-
- void bindDescriptorHeaps();
-
- virtual SLANG_NO_THROW void SLANG_MCALL invalidateDescriptorHeapBinding() override { m_descriptorHeapsBound = false; }
-
- void reinit();
-
- void init(
- DeviceImpl* renderer,
- ID3D12GraphicsCommandList* d3dCommandList,
- TransientResourceHeapImpl* transientHeap);
-
- ResourceCommandEncoderImpl m_resourceCommandEncoder;
-
- virtual SLANG_NO_THROW void SLANG_MCALL
- encodeResourceCommands(IResourceCommandEncoder** outEncoder) override;
-
- RenderCommandEncoderImpl m_renderCommandEncoder;
- virtual SLANG_NO_THROW void SLANG_MCALL encodeRenderCommands(
- IRenderPassLayout* renderPass,
- IFramebuffer* framebuffer,
- IRenderCommandEncoder** outEncoder) override;
-
- ComputeCommandEncoderImpl m_computeCommandEncoder;
- virtual SLANG_NO_THROW void SLANG_MCALL
- encodeComputeCommands(IComputeCommandEncoder** outEncoder) override;
-
-#if SLANG_GFX_HAS_DXR_SUPPORT
- RayTracingCommandEncoderImpl m_rayTracingCommandEncoder;
-#endif
- virtual SLANG_NO_THROW void SLANG_MCALL
- encodeRayTracingCommands(IRayTracingCommandEncoder** outEncoder) override;
- virtual SLANG_NO_THROW void SLANG_MCALL close() override;
-};
-
-class FenceImpl : public FenceBase
-{
-public:
- ComPtr<ID3D12Fence> m_fence;
- HANDLE m_waitEvent = 0;
-
- ~FenceImpl();
-
- HANDLE getWaitEvent();
-
- Result init(DeviceImpl* device, const IFence::Desc& desc);
-
- virtual SLANG_NO_THROW Result SLANG_MCALL getCurrentValue(uint64_t* outValue) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL setCurrentValue(uint64_t value) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getNativeHandle(InteropHandle* outNativeHandle) override;
-};
-
-class CommandQueueImpl
- : public ICommandQueue
- , public ComObject
-{
-public:
- SLANG_COM_OBJECT_IUNKNOWN_ALL
- ICommandQueue* getInterface(const Guid& guid);
- void breakStrongReferenceToDevice() { m_renderer.breakStrongReference(); }
-
- virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* handle) override;
-
-public:
- BreakableReference<DeviceImpl> m_renderer;
- ComPtr<ID3D12Device> m_device;
- ComPtr<ID3D12CommandQueue> m_d3dQueue;
- ComPtr<ID3D12Fence> m_fence;
- uint64_t m_fenceValue = 0;
- HANDLE globalWaitHandle;
- Desc m_desc;
- uint32_t m_queueIndex = 0;
-
- Result init(DeviceImpl* device, uint32_t queueIndex);
- ~CommandQueueImpl();
- virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL executeCommandBuffers(
- GfxCount count,
- ICommandBuffer* const* commandBuffers,
- IFence* fence,
- uint64_t valueToSignal) override;
-
- virtual SLANG_NO_THROW void SLANG_MCALL waitOnHost() override;
-
- virtual SLANG_NO_THROW Result SLANG_MCALL waitForFenceValuesOnDevice(
- GfxCount fenceCount, IFence** fences, uint64_t* waitValues) override;
-};
-
-class SwapchainImpl : public D3DSwapchainBase
-{
-public:
- ComPtr<ID3D12CommandQueue> m_queue;
- ComPtr<IDXGIFactory> m_dxgiFactory;
- ComPtr<IDXGISwapChain3> m_swapChain3;
- ComPtr<ID3D12Fence> m_fence;
- ShortList<HANDLE, kMaxNumRenderFrames> m_frameEvents;
- uint64_t fenceValue = 0;
- Result init(DeviceImpl* renderer, const ISwapchain::Desc& swapchainDesc, WindowHandle window);
-
- virtual SLANG_NO_THROW Result SLANG_MCALL resize(GfxCount width, GfxCount height) override;
-
- virtual void createSwapchainBufferImages() override;
- virtual IDXGIFactory* getDXGIFactory() override { return m_dxgiFactory; }
- virtual IUnknown* getOwningDevice() override { return m_queue; }
- virtual SLANG_NO_THROW int SLANG_MCALL acquireNextImage() override;
- virtual SLANG_NO_THROW Result SLANG_MCALL present() override;
- virtual SLANG_NO_THROW bool SLANG_MCALL isOccluded() override;
- virtual SLANG_NO_THROW Result SLANG_MCALL setFullScreenMode(bool mode) override;
-};
-
-#if SLANG_GFX_HAS_DXR_SUPPORT
-
-class AccelerationStructureImpl
- : public AccelerationStructureBase
- , public ResourceViewInternalImpl
-{
-public:
- RefPtr<BufferResourceImpl> m_buffer;
- uint64_t m_offset;
- uint64_t m_size;
- ComPtr<ID3D12Device5> m_device5;
-
-public:
- virtual SLANG_NO_THROW DeviceAddress SLANG_MCALL getDeviceAddress() override;
- virtual SLANG_NO_THROW Result SLANG_MCALL getNativeHandle(InteropHandle* outHandle) override;
-};
-
-#endif
-
-} // namespace d3d12
-} // namespace gfx