From 5a3aa6159e0ef0241b528812e1d138f0d7055f22 Mon Sep 17 00:00:00 2001
From: lucy96chen <47800040+lucy96chen@users.noreply.github.com>
Date: Tue, 17 May 2022 10:56:14 -0700
Subject: 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>
---
build/visual-studio/gfx/gfx.vcxproj | 53 +-
build/visual-studio/gfx/gfx.vcxproj.filters | 143 +-
external/glslang | 2 +-
external/spirv-headers | 2 +-
external/spirv-tools | 2 +-
tools/gfx/d3d12/d3d12-base.h | 73 +
tools/gfx/d3d12/d3d12-buffer.cpp | 89 +
tools/gfx/d3d12/d3d12-buffer.h | 44 +
tools/gfx/d3d12/d3d12-command-buffer.cpp | 110 +
tools/gfx/d3d12/d3d12-command-buffer.h | 84 +
tools/gfx/d3d12/d3d12-command-encoder.cpp | 1406 +++++
tools/gfx/d3d12/d3d12-command-encoder.h | 305 +
tools/gfx/d3d12/d3d12-command-queue.cpp | 110 +
tools/gfx/d3d12/d3d12-command-queue.h | 51 +
tools/gfx/d3d12/d3d12-descriptor-heap.cpp | 50 +
tools/gfx/d3d12/d3d12-descriptor-heap.h | 514 ++
tools/gfx/d3d12/d3d12-device.cpp | 2036 +++++++
tools/gfx/d3d12/d3d12-device.h | 253 +
tools/gfx/d3d12/d3d12-fence.cpp | 74 +
tools/gfx/d3d12/d3d12-fence.h | 37 +
tools/gfx/d3d12/d3d12-framebuffer.h | 38 +
tools/gfx/d3d12/d3d12-helper-functions.cpp | 590 ++
tools/gfx/d3d12/d3d12-helper-functions.h | 81 +
tools/gfx/d3d12/d3d12-pipeline-state.cpp | 469 ++
tools/gfx/d3d12/d3d12-pipeline-state.h | 41 +
tools/gfx/d3d12/d3d12-query.cpp | 207 +
tools/gfx/d3d12/d3d12-query.h | 64 +
tools/gfx/d3d12/d3d12-render-pass.cpp | 19 +
tools/gfx/d3d12/d3d12-render-pass.h | 22 +
tools/gfx/d3d12/d3d12-resource-views.cpp | 41 +
tools/gfx/d3d12/d3d12-resource-views.h | 52 +
tools/gfx/d3d12/d3d12-resource.cpp | 146 +
tools/gfx/d3d12/d3d12-resource.h | 116 +
tools/gfx/d3d12/d3d12-sampler.cpp | 21 +
tools/gfx/d3d12/d3d12-sampler.h | 23 +
tools/gfx/d3d12/d3d12-shader-object-layout.cpp | 1004 ++++
tools/gfx/d3d12/d3d12-shader-object-layout.h | 485 ++
tools/gfx/d3d12/d3d12-shader-object.cpp | 1179 ++++
tools/gfx/d3d12/d3d12-shader-object.h | 294 +
tools/gfx/d3d12/d3d12-shader-program.cpp | 25 +
tools/gfx/d3d12/d3d12-shader-program.h | 33 +
tools/gfx/d3d12/d3d12-shader-table.cpp | 106 +
tools/gfx/d3d12/d3d12-shader-table.h | 29 +
tools/gfx/d3d12/d3d12-submitter.cpp | 100 +
tools/gfx/d3d12/d3d12-submitter.h | 75 +
tools/gfx/d3d12/d3d12-swap-chain.cpp | 101 +
tools/gfx/d3d12/d3d12-swap-chain.h | 38 +
tools/gfx/d3d12/d3d12-texture.cpp | 58 +
tools/gfx/d3d12/d3d12-texture.h | 34 +
tools/gfx/d3d12/d3d12-transient-heap.cpp | 222 +
tools/gfx/d3d12/d3d12-transient-heap.h | 94 +
tools/gfx/d3d12/d3d12-vertex-layout.h | 23 +
tools/gfx/d3d12/descriptor-heap-d3d12.cpp | 50 -
tools/gfx/d3d12/descriptor-heap-d3d12.h | 514 --
tools/gfx/d3d12/render-d3d12.cpp | 7667 ------------------------
tools/gfx/d3d12/render-d3d12.h | 1806 ------
tools/gfx/d3d12/resource-d3d12.cpp | 146 -
tools/gfx/d3d12/resource-d3d12.h | 116 -
58 files changed, 11249 insertions(+), 10318 deletions(-)
create mode 100644 tools/gfx/d3d12/d3d12-base.h
create mode 100644 tools/gfx/d3d12/d3d12-buffer.cpp
create mode 100644 tools/gfx/d3d12/d3d12-buffer.h
create mode 100644 tools/gfx/d3d12/d3d12-command-buffer.cpp
create mode 100644 tools/gfx/d3d12/d3d12-command-buffer.h
create mode 100644 tools/gfx/d3d12/d3d12-command-encoder.cpp
create mode 100644 tools/gfx/d3d12/d3d12-command-encoder.h
create mode 100644 tools/gfx/d3d12/d3d12-command-queue.cpp
create mode 100644 tools/gfx/d3d12/d3d12-command-queue.h
create mode 100644 tools/gfx/d3d12/d3d12-descriptor-heap.cpp
create mode 100644 tools/gfx/d3d12/d3d12-descriptor-heap.h
create mode 100644 tools/gfx/d3d12/d3d12-device.cpp
create mode 100644 tools/gfx/d3d12/d3d12-device.h
create mode 100644 tools/gfx/d3d12/d3d12-fence.cpp
create mode 100644 tools/gfx/d3d12/d3d12-fence.h
create mode 100644 tools/gfx/d3d12/d3d12-framebuffer.h
create mode 100644 tools/gfx/d3d12/d3d12-helper-functions.cpp
create mode 100644 tools/gfx/d3d12/d3d12-helper-functions.h
create mode 100644 tools/gfx/d3d12/d3d12-pipeline-state.cpp
create mode 100644 tools/gfx/d3d12/d3d12-pipeline-state.h
create mode 100644 tools/gfx/d3d12/d3d12-query.cpp
create mode 100644 tools/gfx/d3d12/d3d12-query.h
create mode 100644 tools/gfx/d3d12/d3d12-render-pass.cpp
create mode 100644 tools/gfx/d3d12/d3d12-render-pass.h
create mode 100644 tools/gfx/d3d12/d3d12-resource-views.cpp
create mode 100644 tools/gfx/d3d12/d3d12-resource-views.h
create mode 100644 tools/gfx/d3d12/d3d12-resource.cpp
create mode 100644 tools/gfx/d3d12/d3d12-resource.h
create mode 100644 tools/gfx/d3d12/d3d12-sampler.cpp
create mode 100644 tools/gfx/d3d12/d3d12-sampler.h
create mode 100644 tools/gfx/d3d12/d3d12-shader-object-layout.cpp
create mode 100644 tools/gfx/d3d12/d3d12-shader-object-layout.h
create mode 100644 tools/gfx/d3d12/d3d12-shader-object.cpp
create mode 100644 tools/gfx/d3d12/d3d12-shader-object.h
create mode 100644 tools/gfx/d3d12/d3d12-shader-program.cpp
create mode 100644 tools/gfx/d3d12/d3d12-shader-program.h
create mode 100644 tools/gfx/d3d12/d3d12-shader-table.cpp
create mode 100644 tools/gfx/d3d12/d3d12-shader-table.h
create mode 100644 tools/gfx/d3d12/d3d12-submitter.cpp
create mode 100644 tools/gfx/d3d12/d3d12-submitter.h
create mode 100644 tools/gfx/d3d12/d3d12-swap-chain.cpp
create mode 100644 tools/gfx/d3d12/d3d12-swap-chain.h
create mode 100644 tools/gfx/d3d12/d3d12-texture.cpp
create mode 100644 tools/gfx/d3d12/d3d12-texture.h
create mode 100644 tools/gfx/d3d12/d3d12-transient-heap.cpp
create mode 100644 tools/gfx/d3d12/d3d12-transient-heap.h
create mode 100644 tools/gfx/d3d12/d3d12-vertex-layout.h
delete mode 100644 tools/gfx/d3d12/descriptor-heap-d3d12.cpp
delete mode 100644 tools/gfx/d3d12/descriptor-heap-d3d12.h
delete mode 100644 tools/gfx/d3d12/render-d3d12.cpp
delete mode 100644 tools/gfx/d3d12/render-d3d12.h
delete mode 100644 tools/gfx/d3d12/resource-d3d12.cpp
delete mode 100644 tools/gfx/d3d12/resource-d3d12.h
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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
@@ -333,12 +355,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
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 @@
Header Files
-
+
Header Files
-
- Header Files
-
-
+
Header Files
@@ -99,6 +96,75 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
@@ -116,13 +182,10 @@
Source Files
-
+
Source Files
-
- Source Files
-
-
+
Source Files
@@ -170,5 +233,65 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
\ No newline at end of file
diff --git a/external/glslang b/external/glslang
index dc703d952..418694e6c 160000
--- a/external/glslang
+++ b/external/glslang
@@ -1 +1 @@
-Subproject commit dc703d95289e5376de457af0ed9b3dd0f0f97567
+Subproject commit 418694e6cd2e2e84052f77bb8306d560c7d8fe3a
diff --git a/external/spirv-headers b/external/spirv-headers
index b765c355f..1380cbbec 160000
--- a/external/spirv-headers
+++ b/external/spirv-headers
@@ -1 +1 @@
-Subproject commit b765c355f488837ca4c77980ba69484f3ff277f5
+Subproject commit 1380cbbec10756b492e9397d03c4250887e15090
diff --git a/external/spirv-tools b/external/spirv-tools
index ffc8f2d45..d997c83b1 160000
--- a/external/spirv-tools
+++ b/external/spirv-tools
@@ -1 +1 @@
-Subproject commit ffc8f2d45566329bb9e21170f9e2a34ed8a65e0a
+Subproject commit d997c83b103ed1f3af09ed65e1cbf89fbc6d9451
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
+#undef WIN32_LEAN_AND_MEAN
+#undef NOMINMAX
+
+#include
+#include
+
+#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 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(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(m_cmdList4.writeRef());
+ if (m_cmdList4)
+ {
+ m_cmdList1 = m_cmdList4;
+ return;
+ }
+#endif
+ m_cmdList->QueryInterface(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(renderPass),
+ static_cast(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 m_cmdList;
+ ComPtr m_cmdList1;
+ ComPtr m_cmdList4;
+
+ BreakableReference m_transientHeap;
+ // Weak reference is fine here since `m_transientHeap` already holds strong reference to
+ // device.
+ DeviceImpl* m_renderer;
+ RootShaderObjectImpl m_rootShaderObject;
+ RefPtr 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(pipelineState);
+ auto rootObject = &m_commandBuffer->m_rootShaderObject;
+ m_commandBuffer->m_mutableRootShaderObject = nullptr;
+ SLANG_RETURN_ON_FAIL(rootObject->reset(
+ m_renderer,
+ m_currentPipeline->getProgram()->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(pipelineState);
+ m_commandBuffer->m_mutableRootShaderObject =
+ static_cast(rootObject);
+ m_bindingDirty = true;
+ return SLANG_OK;
+}
+
+Result PipelineCommandEncoder::_bindRenderState(
+ Submitter* submitter, RefPtr& 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(newPipeline.Ptr());
+ auto commandList = m_d3dCmdList;
+ auto pipelineTypeIndex = (int)newPipelineImpl->desc.type;
+ auto programImpl = static_cast(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 barriers;
+ for (GfxIndex i = 0; i < count; i++)
+ {
+ auto bufferImpl = static_cast(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(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(dst);
+ auto srcTexture = static_cast(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(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(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(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(viewImpl->m_resource.Ptr())
+ ->m_resource.getResource();
+ break;
+ default:
+ d3dResource = static_cast(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(source);
+ auto srcDesc = srcTexture->getDesc();
+ auto dstTexture = static_cast(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(queryPool);
+ switch (queryBase->m_desc.type)
+ {
+ case QueryType::AccelerationStructureCompactedSize:
+ case QueryType::AccelerationStructureCurrentSize:
+ case QueryType::AccelerationStructureSerializedSize:
+ {
+ auto queryPoolImpl = static_cast(queryPool);
+ auto bufferImpl = static_cast(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(queryPool);
+ auto bufferImpl = static_cast(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(src);
+ auto dstBuffer = static_cast(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(texture);
+
+ ShortList 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(dst);
+ auto srcBuffer = static_cast(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(dst),
+ offset,
+ size,
+ data);
+}
+
+void ResourceCommandEncoderImpl::textureBarrier(
+ GfxCount count, ITextureResource* const* textures, ResourceState src, ResourceState dst)
+{
+ ShortList barriers;
+
+ for (GfxIndex i = 0; i < count; i++)
+ {
+ auto textureImpl = static_cast(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(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(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(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 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(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(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(argBuffer);
+ auto countBufferImpl = static_cast(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(argBuffer);
+ auto countBufferImpl = static_cast(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 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 newPipeline;
+ if (SLANG_FAILED(_bindRenderState(&submitter, newPipeline)))
+ {
+ assert(!"Failed to bind render state");
+ }
+ }
+ auto argBufferImpl = static_cast(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(desc.dest);
+ AccelerationStructureImpl* srcASImpl = nullptr;
+ if (desc.source)
+ srcASImpl = static_cast(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 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(dest);
+ auto srcASImpl = static_cast(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 postBuildInfoDescs;
+ List 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(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(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 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(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(pipeline);
+
+ auto shaderTableImpl = static_cast(shaderTable);
+
+ auto shaderTableBuffer =
+ shaderTableImpl->getOrCreateBuffer(pipelineImpl, m_transientHeap, static_cast(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 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& 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 m_buffer;
+ int m_offset;
+};
+
+class RenderCommandEncoderImpl
+ : public IRenderCommandEncoder
+ , public ResourceCommandEncoderImpl
+{
+public:
+ SLANG_GFX_FORWARD_RESOURCE_COMMAND_ENCODER_IMPL(ResourceCommandEncoderImpl)
+public:
+ RefPtr m_renderPass;
+ RefPtr m_framebuffer;
+
+ List m_boundVertexBuffers;
+
+ RefPtr 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 commandLists;
+ for (GfxCount i = 0; i < count; i++)
+ {
+ auto cmdImpl = static_cast(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(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(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(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(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 m_renderer;
+ ComPtr m_device;
+ ComPtr m_d3dQueue;
+ ComPtr 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/d3d12-descriptor-heap.cpp b/tools/gfx/d3d12/d3d12-descriptor-heap.cpp
new file mode 100644
index 000000000..b9c0d28ba
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-descriptor-heap.cpp
@@ -0,0 +1,50 @@
+
+#include "d3d12-descriptor-heap.h"
+
+namespace gfx {
+using namespace Slang;
+
+D3D12DescriptorHeap::D3D12DescriptorHeap():
+ m_totalSize(0),
+ m_currentIndex(0),
+ m_descriptorSize(0)
+{
+}
+
+Result D3D12DescriptorHeap::init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags)
+{
+ m_device = device;
+
+ D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
+ srvHeapDesc.NumDescriptors = size;
+ srvHeapDesc.Flags = flags;
+ srvHeapDesc.Type = type;
+ SLANG_RETURN_ON_FAIL(device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(m_heap.writeRef())));
+
+ m_descriptorSize = device->GetDescriptorHandleIncrementSize(type);
+ m_totalSize = size;
+ m_heapFlags = flags;
+
+ return SLANG_OK;
+}
+
+Result D3D12DescriptorHeap::init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags)
+{
+ SLANG_RETURN_ON_FAIL(init(device, numHandles, type, flags));
+ D3D12_CPU_DESCRIPTOR_HANDLE dst = m_heap->GetCPUDescriptorHandleForHeapStart();
+
+ // Copy them all
+ for (int i = 0; i < numHandles; i++, dst.ptr += m_descriptorSize)
+ {
+ D3D12_CPU_DESCRIPTOR_HANDLE src = handles[i];
+ if (src.ptr != 0)
+ {
+ device->CopyDescriptorsSimple(1, dst, src, type);
+ }
+ }
+
+ return SLANG_OK;
+}
+
+} // namespace gfx
+
diff --git a/tools/gfx/d3d12/d3d12-descriptor-heap.h b/tools/gfx/d3d12/d3d12-descriptor-heap.h
new file mode 100644
index 000000000..6f82a3f42
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-descriptor-heap.h
@@ -0,0 +1,514 @@
+#pragma once
+
+
+#include
+#include
+
+#include "slang-com-ptr.h"
+#include "core/slang-virtual-object-pool.h"
+#include "core/slang-short-list.h"
+#include "core/slang-basic.h"
+
+namespace gfx {
+
+/*! \brief A simple class to manage an underlying Dx12 Descriptor Heap. Allocations are made linearly in order. It is not possible to free
+individual allocations, but all allocations can be deallocated with 'deallocateAll'. */
+class D3D12DescriptorHeap
+{
+public:
+ typedef D3D12DescriptorHeap ThisType;
+
+ /// Initialize
+ Slang::Result init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags);
+ /// Initialize with an array of handles copying over the representation
+ Slang::Result init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags);
+
+ /// Returns the number of slots that have been used
+ SLANG_FORCE_INLINE int getUsedSize() const { return m_currentIndex; }
+
+ /// Get the total amount of descriptors possible on the heap
+ SLANG_FORCE_INLINE int getTotalSize() const { return m_totalSize; }
+ /// Allocate a descriptor. Returns the index, or -1 if none left.
+ SLANG_FORCE_INLINE int allocate();
+ /// Allocate a number of descriptors. Returns the start index (or -1 if not possible)
+ SLANG_FORCE_INLINE int allocate(int numDescriptors);
+
+ ///
+ SLANG_FORCE_INLINE int placeAt(int index);
+
+ /// Deallocates all allocations, and starts allocation from the start of the underlying heap again
+ SLANG_FORCE_INLINE void deallocateAll() { m_currentIndex = 0; }
+
+ /// Get the size of each
+ SLANG_FORCE_INLINE int getDescriptorSize() const { return m_descriptorSize; }
+
+ /// Get the GPU heap start
+ SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuStart() const { return m_heap->GetGPUDescriptorHandleForHeapStart(); }
+ /// Get the CPU heap start
+ SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuStart() const { return m_heap->GetCPUDescriptorHandleForHeapStart(); }
+
+ /// Get the GPU handle at the specified index
+ SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(int index) const;
+ /// Get the CPU handle at the specified index
+ SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(int index) const;
+
+ /// Get the underlying heap
+ SLANG_FORCE_INLINE ID3D12DescriptorHeap* getHeap() const { return m_heap; }
+
+ /// Ctor
+ D3D12DescriptorHeap();
+
+protected:
+ Slang::ComPtr m_device;
+ Slang::ComPtr m_heap; ///< The underlying heap being allocated from
+ int m_totalSize; ///< Total amount of allocations available on the heap
+ int m_currentIndex; ///< The current descriptor
+ int m_descriptorSize; ///< The size of each descriptor
+ D3D12_DESCRIPTOR_HEAP_FLAGS m_heapFlags; ///< The flags of the heap
+};
+
+/// A d3d12 descriptor, used as "backing storage" for a view.
+///
+/// This type is intended to be used to represent descriptors that
+/// are allocated and freed through a `D3D12GeneralDescriptorHeap`.
+struct D3D12Descriptor
+{
+ D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle;
+};
+
+/// An allocator for host-visible descriptors.
+///
+/// Unlike the `D3D12DescriptorHeap` type, this class allows for both
+/// allocation and freeing of descriptors, by maintaining a free list.
+///
+class D3D12GeneralDescriptorHeap : public Slang::RefObject
+{
+ ID3D12Device* m_device;
+ int m_chunkSize;
+ D3D12_DESCRIPTOR_HEAP_TYPE m_type;
+
+ D3D12DescriptorHeap m_heap;
+ Slang::VirtualObjectPool m_allocator;
+
+public:
+ int getSize() { return m_chunkSize; }
+
+ Slang::Result init(ID3D12Device* device, int chunkSize, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flag)
+ {
+ m_device = device;
+ m_chunkSize = chunkSize;
+ m_type = type;
+
+ SLANG_RETURN_ON_FAIL(m_heap.init(m_device, m_chunkSize, m_type, flag));
+ m_allocator.initPool(m_chunkSize);
+ return SLANG_OK;
+ }
+
+ SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(int index) const
+ {
+ return m_heap.getCpuHandle(index);
+ }
+
+ SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(int index) const
+ {
+ return m_heap.getGpuHandle(index);
+ }
+
+ int allocate(int count)
+ {
+ return m_allocator.alloc(count);
+ }
+
+ Slang::Result allocate(D3D12Descriptor* outDescriptor)
+ {
+ // TODO: this allocator would take some work to make thread-safe
+
+ int index = m_allocator.alloc(1);
+ if(index < 0)
+ {
+ assert(!"descriptor allocation failed");
+ return SLANG_FAIL;
+ }
+
+ D3D12Descriptor descriptor;
+ descriptor.cpuHandle = m_heap.getCpuHandle(index);
+
+ *outDescriptor = descriptor;
+ return SLANG_OK;
+ }
+
+ void free(int index, int count)
+ {
+ m_allocator.free(index, count);
+ }
+
+ void free(D3D12Descriptor descriptor)
+ {
+ auto index =
+ (int)(descriptor.cpuHandle.ptr - m_heap.getCpuStart().ptr) / m_heap.getDescriptorSize();
+ free(index, 1);
+ }
+};
+
+class D3D12GeneralExpandingDescriptorHeap : public Slang::RefObject
+{
+ ID3D12Device* m_device;
+ D3D12_DESCRIPTOR_HEAP_TYPE m_type;
+ D3D12_DESCRIPTOR_HEAP_FLAGS m_flag;
+ int m_chunkSize;
+ Slang::List> m_subHeaps;
+ Slang::List m_subHeapStartingIndex;
+
+public:
+ Slang::Result newSubHeap()
+ {
+ Slang::RefPtr subHeap = new D3D12GeneralDescriptorHeap();
+ SLANG_RETURN_ON_FAIL(subHeap->init(m_device, m_chunkSize, m_type, m_flag));
+ m_subHeaps.add(subHeap);
+ if (m_subHeapStartingIndex.getCount())
+ {
+ m_subHeapStartingIndex.add(m_subHeapStartingIndex.getLast() + m_subHeaps.getLast()->getSize());
+ }
+ else
+ {
+ m_subHeapStartingIndex.add(0);
+ }
+ return SLANG_OK;
+ }
+
+ int getSubHeapIndex(int descriptorIndex) const
+ {
+ Slang::Index l = 0;
+ Slang::Index r = m_subHeapStartingIndex.getCount();
+ while (l < r - 1)
+ {
+ Slang::Index m = l + (r - l) / 2;
+ if (m_subHeapStartingIndex[m] < descriptorIndex)
+ l = m;
+ else if (m_subHeapStartingIndex[m] > descriptorIndex)
+ r = m;
+ else
+ return (int)m;
+ }
+ assert(
+ m_subHeapStartingIndex[l] <= descriptorIndex &&
+ m_subHeapStartingIndex[l] + m_subHeaps[l]->getSize() > descriptorIndex);
+ return (int)l;
+ }
+
+ Slang::Result init(
+ ID3D12Device* device,
+ int chunkSize,
+ D3D12_DESCRIPTOR_HEAP_TYPE type,
+ D3D12_DESCRIPTOR_HEAP_FLAGS flag)
+ {
+ m_device = device;
+ m_chunkSize = chunkSize;
+ m_type = type;
+ m_flag = flag;
+
+ return newSubHeap();
+ }
+
+ SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(int index) const
+ {
+ auto subHeapIndex = getSubHeapIndex(index);
+ return m_subHeaps[subHeapIndex]->getCpuHandle(index - m_subHeapStartingIndex[subHeapIndex]);
+ }
+
+ SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(int index) const
+ {
+ auto subHeapIndex = getSubHeapIndex(index);
+ return m_subHeaps[subHeapIndex]->getGpuHandle(index - m_subHeapStartingIndex[subHeapIndex]);
+ }
+
+ int allocate(int count)
+ {
+ auto result = m_subHeaps.getLast()->allocate(count);
+ if (result == -1)
+ {
+ newSubHeap();
+ return allocate(count);
+ }
+ return result + m_subHeapStartingIndex.getLast();
+ }
+
+ Slang::Result allocate(D3D12Descriptor* outDescriptor)
+ {
+ int index = allocate(1);
+ if (index < 0)
+ {
+ assert(!"descriptor allocation failed");
+ return SLANG_FAIL;
+ }
+
+ D3D12Descriptor descriptor;
+ descriptor.cpuHandle = getCpuHandle(index);
+
+ *outDescriptor = descriptor;
+ return SLANG_OK;
+ }
+
+ void free(int index, int count)
+ {
+ auto subHeapIndex = getSubHeapIndex(index);
+ m_subHeaps[subHeapIndex]->free(index - m_subHeapStartingIndex[subHeapIndex], count);
+ }
+
+ void free(D3D12Descriptor descriptor)
+ {
+ for (auto& subHeap : m_subHeaps)
+ {
+ if (descriptor.cpuHandle.ptr >= subHeap->getCpuHandle(0).ptr)
+ {
+ auto subIndex = descriptor.cpuHandle.ptr - subHeap->getCpuHandle(0).ptr;
+ if (subIndex < (SIZE_T)subHeap->getSize())
+ {
+ subHeap->free(descriptor);
+ break;
+ }
+ }
+
+ }
+ }
+};
+
+class D3D12LinearExpandingDescriptorHeap : public Slang::RefObject
+{
+ ID3D12Device* m_device;
+ D3D12_DESCRIPTOR_HEAP_TYPE m_type;
+ D3D12_DESCRIPTOR_HEAP_FLAGS m_flag;
+ int m_chunkSize;
+ Slang::ShortList m_subHeaps;
+ int32_t m_subHeapIndex;
+
+public:
+ Slang::Result newSubHeap()
+ {
+ m_subHeapIndex++;
+ if (m_subHeapIndex <= m_subHeaps.getCount())
+ {
+ D3D12DescriptorHeap subHeap;
+ SLANG_RETURN_ON_FAIL(subHeap.init(m_device, m_chunkSize, m_type, m_flag));
+ m_subHeaps.add(Slang::_Move(subHeap));
+ }
+ return SLANG_OK;
+ }
+
+ Slang::Result init(
+ ID3D12Device* device,
+ int chunkSize,
+ D3D12_DESCRIPTOR_HEAP_TYPE type,
+ D3D12_DESCRIPTOR_HEAP_FLAGS flag)
+ {
+ m_device = device;
+ m_chunkSize = chunkSize;
+ m_type = type;
+ m_flag = flag;
+ m_subHeapIndex = -1;
+ return newSubHeap();
+ }
+
+ int allocate(int count)
+ {
+ auto result = m_subHeaps[m_subHeapIndex].allocate(count);
+ if (result == -1)
+ {
+ newSubHeap();
+ return allocate(count);
+ }
+ assert(result <= 0xFFFFFF);
+ assert(m_subHeapIndex <= 255);
+ return (m_subHeapIndex << 24) + result;
+ }
+
+ SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(int index) const
+ {
+ auto subHeapIndex = ((uint32_t)(index >> 24) & 0xFF);
+ return m_subHeaps[subHeapIndex].getCpuHandle(index & 0xFFFFFF);
+ }
+
+ void free(int index, int count) { assert(0 && "not supported"); }
+
+ void free(D3D12Descriptor descriptor) { assert(0 && "not supported"); }
+
+ void freeAll()
+ {
+ for (auto& subHeap : m_subHeaps)
+ subHeap.deallocateAll();
+ m_subHeapIndex = 0;
+ }
+};
+
+struct DescriptorHeapReference
+{
+ enum class Type
+ {
+ Linear, General, ExpandingGeneral, ExpandingLinear
+ };
+ union Ptr
+ {
+ D3D12DescriptorHeap* linearHeap;
+ D3D12GeneralDescriptorHeap* generalHeap;
+ D3D12GeneralExpandingDescriptorHeap* generalExpandingHeap;
+ D3D12LinearExpandingDescriptorHeap* linearExpandingHeap;
+ };
+ Type type;
+ Ptr ptr;
+ DescriptorHeapReference() = default;
+ DescriptorHeapReference(D3D12DescriptorHeap* heap)
+ {
+ type = Type::Linear;
+ ptr.linearHeap = heap;
+ }
+ DescriptorHeapReference(D3D12GeneralDescriptorHeap* heap)
+ {
+ type = Type::General;
+ ptr.generalHeap = heap;
+ }
+ DescriptorHeapReference(D3D12GeneralExpandingDescriptorHeap* heap)
+ {
+ type = Type::ExpandingGeneral;
+ ptr.generalExpandingHeap = heap;
+ }
+ DescriptorHeapReference(D3D12LinearExpandingDescriptorHeap* heap)
+ {
+ type = Type::ExpandingLinear;
+ ptr.linearExpandingHeap = heap;
+ }
+ D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(int index) const
+ {
+ switch (type)
+ {
+ case Type::Linear:
+ return ptr.linearHeap->getCpuHandle(index);
+ case Type::General:
+ return ptr.generalHeap->getCpuHandle(index);
+ case Type::ExpandingGeneral:
+ return ptr.generalExpandingHeap->getCpuHandle(index);
+ case Type::ExpandingLinear:
+ return ptr.linearExpandingHeap->getCpuHandle(index);
+ default:
+ return D3D12_CPU_DESCRIPTOR_HANDLE();
+ }
+ }
+ D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(int index) const
+ {
+ switch (type)
+ {
+ case Type::Linear:
+ return ptr.linearHeap->getGpuHandle(index);
+ case Type::General:
+ return ptr.generalHeap->getGpuHandle(index);
+ case Type::ExpandingGeneral:
+ return ptr.generalExpandingHeap->getGpuHandle(index);
+ default:
+ return D3D12_GPU_DESCRIPTOR_HANDLE();
+ }
+ }
+ int allocate(int numDescriptors)
+ {
+ switch (type)
+ {
+ case Type::Linear:
+ return ptr.linearHeap->allocate(numDescriptors);
+ case Type::General:
+ return ptr.generalHeap->allocate(numDescriptors);
+ case Type::ExpandingGeneral:
+ return ptr.generalExpandingHeap->allocate(numDescriptors);
+ default:
+ return ptr.linearExpandingHeap->allocate(numDescriptors);
+ }
+ }
+ void free(int index, int count)
+ {
+ switch (type)
+ {
+ default:
+ case Type::Linear:
+ SLANG_ASSERT(!"Linear heap does not support free().");
+ break;
+ case Type::General:
+ return ptr.generalHeap->free(index, count);
+ case Type::ExpandingGeneral:
+ return ptr.generalExpandingHeap->free(index, count);
+ }
+ }
+ void freeIfSupported(int index, int count)
+ {
+ switch (type)
+ {
+ case Type::Linear:
+ return;
+ case Type::General:
+ return ptr.generalHeap->free(index, count);
+ case Type::ExpandingGeneral:
+ return ptr.generalExpandingHeap->free(index, count);
+ default:
+ break;
+ }
+ }
+};
+
+// ---------------------------------------------------------------------------
+int D3D12DescriptorHeap::allocate() { return allocate(1); }
+// ---------------------------------------------------------------------------
+int D3D12DescriptorHeap::allocate(int numDescriptors)
+{
+ if (m_currentIndex + numDescriptors <= m_totalSize)
+ {
+ const int index = m_currentIndex;
+ m_currentIndex += numDescriptors;
+ return index;
+ }
+ if (m_heapFlags & D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)
+ {
+ // No automatic resizing for GPU visible heaps.
+ return -1;
+ }
+ // We don't have enough heap size, resize the heap.
+ auto oldHeap = m_heap;
+ auto oldSize = m_totalSize;
+ auto currentIndex = m_currentIndex;
+ auto desc = m_heap->GetDesc();
+ this->init(m_device, (int)desc.NumDescriptors * 2, desc.Type, desc.Flags);
+ m_device->CopyDescriptorsSimple(
+ (UINT)currentIndex,
+ m_heap->GetCPUDescriptorHandleForHeapStart(),
+ oldHeap->GetCPUDescriptorHandleForHeapStart(),
+ desc.Type);
+ m_currentIndex = currentIndex;
+ // Now allocate again.
+ const int index = m_currentIndex;
+ m_currentIndex += numDescriptors;
+ return index;
+}
+// ---------------------------------------------------------------------------
+SLANG_FORCE_INLINE int D3D12DescriptorHeap::placeAt(int index)
+{
+ assert(index >= 0 && index < m_totalSize);
+ m_currentIndex = index + 1;
+ return index;
+}
+
+// ---------------------------------------------------------------------------
+SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getCpuHandle(int index) const
+{
+ assert(index >= 0 && index < m_totalSize);
+ D3D12_CPU_DESCRIPTOR_HANDLE start = m_heap->GetCPUDescriptorHandleForHeapStart();
+ D3D12_CPU_DESCRIPTOR_HANDLE dst;
+ dst.ptr = start.ptr + m_descriptorSize * index;
+ return dst;
+}
+// ---------------------------------------------------------------------------
+SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getGpuHandle(int index) const
+{
+ assert(index >= 0 && index < m_totalSize);
+ D3D12_GPU_DESCRIPTOR_HANDLE start = m_heap->GetGPUDescriptorHandleForHeapStart();
+ D3D12_GPU_DESCRIPTOR_HANDLE dst;
+ dst.ptr = start.ptr + m_descriptorSize * index;
+ return dst;
+}
+
+} // namespace gfx
+
diff --git a/tools/gfx/d3d12/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(&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(&data)));
+
+ RefPtr 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 dxgiFactory;
+ SLANG_RETURN_ON_FAIL(D3DUtil::createFactory(deviceCheckFlags, dxgiFactory));
+
+ List> dxgiAdapters;
+ SLANG_RETURN_ON_FAIL(
+ D3DUtil::findAdapters(deviceCheckFlags, nameMatch, dxgiFactory, dxgiAdapters));
+
+ ComPtr device;
+ ComPtr adapter;
+
+ for (Index i = 0; i < dxgiAdapters.getCount(); ++i)
+ {
+ IDXGIAdapter* dxgiAdapter = dxgiAdapters[i];
+ if (SLANG_SUCCEEDED(
+ m_D3D12CreateDevice(dxgiAdapter, featureLevel, IID_PPV_ARGS(device.writeRef()))))
+ {
+ adapter = dxgiAdapter;
+ break;
+ }
+ }
+
+ if (!device)
+ {
+ return SLANG_FAIL;
+ }
+
+ if (m_dxDebug && (deviceCheckFlags & DeviceCheckFlag::UseDebug))
+ {
+ ComPtr 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 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 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 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(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 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 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 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(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 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 layouts;
+ layouts.setCount(numMipMaps);
+ List mipRowSizeInBytes;
+ mipRowSizeInBytes.setCount(srcDesc.numMipLevels);
+ List 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(&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 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 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 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 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 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 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(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 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(desc.renderTargetViews[i]);
+ framebuffer->renderTargetDescriptors[i] =
+ framebuffer->renderTargetViews[i]->m_descriptor.cpuHandle;
+ if (static_cast(desc.renderTargetViews[i])->m_resource.Ptr())
+ {
+ auto clearValue =
+ static_cast(
+ static_cast(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(desc.depthStencilView);
+ if (desc.depthStencilView)
+ {
+ framebuffer->depthStencilClearValue =
+ static_cast(
+ static_cast(desc.depthStencilView)->m_resource.Ptr())
+ ->getDesc()
+ ->optimalClearValue.depthStencil;
+ framebuffer->depthStencilDescriptor =
+ static_cast(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 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 result = new RenderPassLayoutImpl();
+ result->init(desc);
+ returnComPtr(outRenderPassLayout, result);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createInputLayout(IInputLayout::Desc const& desc, IInputLayout** outLayout)
+{
+ RefPtr 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& 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(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 blob = new ListBlob();
+ {
+ UINT8* data;
+ D3D12_RANGE readRange = { 0, size };
+
+ SLANG_RETURN_ON_FAIL(
+ stageBufRef.getResource()->Map(0, &readRange, reinterpret_cast(&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 shaderProgram = new ShaderProgramImpl();
+ shaderProgram->init(desc);
+ ComPtr 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 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 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 shaderObject;
+ SLANG_RETURN_ON_FAIL(ShaderObjectImpl::create(
+ this, reinterpret_cast(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(*outObject)->m_isMutable = true;
+ return result;
+}
+
+Result DeviceImpl::createMutableRootShaderObject(IShaderProgram* program, IShaderObject** outObject)
+{
+ RefPtr result = new MutableRootShaderObjectImpl();
+ result->init(this);
+ auto programImpl = static_cast(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 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 = new PipelineStateImpl(this);
+ pipelineStateImpl->init(desc);
+ returnComPtr(outState, pipelineStateImpl);
+ return SLANG_OK;
+}
+
+Result DeviceImpl::createComputePipelineState(
+ const ComputePipelineStateDesc& desc, IPipelineState** outState)
+{
+ RefPtr 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(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 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 = 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 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 waitHandles;
+ for (GfxCount i = 0; i < fenceCount; ++i)
+ {
+ auto fenceImpl = static_cast(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 result = new AccelerationStructureImpl();
+ result->m_device5 = m_device5;
+ result->m_buffer = static_cast(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 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 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 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
+
+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 m_dxgiFactory;
+ ComPtr m_device;
+ ComPtr m_device5;
+ ComPtr 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 m_dxDebug;
+
+ D3D12DeviceInfo m_deviceInfo;
+ ID3D12Device* m_device = nullptr;
+ ID3D12Device5* m_device5 = nullptr;
+
+ VirtualObjectPool m_queueIndexAllocator;
+
+ RefPtr m_resourceCommandQueue;
+ RefPtr m_resourceCommandTransientHeap;
+
+ RefPtr m_rtvAllocator;
+ RefPtr 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 m_cpuViewHeap; ///< Cbv, Srv, Uav
+ RefPtr 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 drawIndirectCmdSignature;
+ ComPtr drawIndexedIndirectCmdSignature;
+ ComPtr 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 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 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 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 m_renderTargets;
+ bool m_hasDepthStencil = false;
+ IFramebufferLayout::TargetLayout m_depthStencil;
+};
+
+class FramebufferImpl : public FramebufferBase
+{
+public:
+ ShortList> renderTargetViews;
+ RefPtr depthStencilView;
+ ShortList renderTargetDescriptors;
+ struct Color4f
+ {
+ float values[4];
+ };
+ ShortList 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(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(&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& 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(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(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(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 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& 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(m_pipelineState.get());
+ return SLANG_OK;
+}
+
+Result PipelineStateImpl::ensureAPIPipelineStateCreated()
+{
+ if (m_pipelineState)
+ return SLANG_OK;
+
+ auto programImpl = static_cast(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(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 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(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(m_stateObject.get());
+ return SLANG_OK;
+}
+
+Result RayTracingPipelineStateImpl::ensureAPIPipelineStateCreated()
+{
+ if (m_stateObject)
+ return SLANG_OK;
+
+ auto program = static_cast(m_program.Ptr());
+ auto slangGlobalScope = program->linkedProgram;
+ auto programLayout = slangGlobalScope->getLayout();
+
+ List subObjects;
+ ChunkedList dxilLibraries;
+ ChunkedList hitGroups;
+ ChunkedList> codeBlobs;
+ ChunkedList exports;
+ ChunkedList strPtrs;
+
+ ComPtr diagnostics;
+ ChunkedList 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 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 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 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(this);
+ return nullptr;
+}
+
+Result PlainBufferProxyQueryPoolImpl::init(
+ const IQueryPool::Desc& desc, DeviceImpl* device, uint32_t stride)
+{
+ ComPtr 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(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 m_queryHeap;
+ D3D12Resource m_readBackBuffer;
+ ComPtr m_commandAllocator;
+ ComPtr m_commandList;
+ ComPtr m_fence;
+ ComPtr 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 m_bufferResource;
+ RefPtr m_device;
+ List 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(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 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 m_allocator;
+ ~ResourceViewInternalImpl();
+};
+
+class ResourceViewImpl
+ : public ResourceViewBase
+ , public ResourceViewInternalImpl
+{
+public:
+ Slang::RefPtr 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 m_buffer;
+ uint64_t m_offset;
+ uint64_t m_size;
+ ComPtr 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/d3d12-resource.cpp b/tools/gfx/d3d12/d3d12-resource.cpp
new file mode 100644
index 000000000..8975f5825
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-resource.cpp
@@ -0,0 +1,146 @@
+// d3d12-resource.cpp
+#include "d3d12-resource.h"
+
+namespace gfx {
+using namespace Slang;
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12BarrierSubmitter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void D3D12BarrierSubmitter::_flush()
+{
+ assert(m_numBarriers > 0);
+
+ if (m_commandList)
+ {
+ m_commandList->ResourceBarrier(UINT(m_numBarriers), m_barriers);
+ }
+ m_numBarriers = 0;
+}
+
+D3D12_RESOURCE_BARRIER& D3D12BarrierSubmitter::_expandOne()
+{
+ _flush();
+ return m_barriers[m_numBarriers++];
+}
+
+void D3D12BarrierSubmitter::transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState)
+{
+ if (nextState != prevState)
+ {
+ D3D12_RESOURCE_BARRIER& barrier = expandOne();
+
+ const UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ const D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+
+ ::memset(&barrier, 0, sizeof(barrier));
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = flags;
+ barrier.Transition.pResource = resource;
+ barrier.Transition.StateBefore = prevState;
+ barrier.Transition.StateAfter = nextState;
+ barrier.Transition.Subresource = subresource;
+ }
+ else
+ {
+ if (nextState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS)
+ {
+ D3D12_RESOURCE_BARRIER& barrier = expandOne();
+
+ ::memset(&barrier, 0, sizeof(barrier));
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
+ barrier.UAV.pResource = resource;
+ }
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12ResourceBase !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+/* static */DXGI_FORMAT D3D12ResourceBase::calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource)
+{
+ return resource ? D3DUtil::calcFormat(usage, resource->GetDesc().Format) : DXGI_FORMAT_UNKNOWN;
+}
+
+void D3D12ResourceBase::transition(
+ D3D12_RESOURCE_STATES oldState,
+ D3D12_RESOURCE_STATES nextState,
+ D3D12BarrierSubmitter& submitter)
+{
+ // Transition only if there is a resource
+ if (m_resource && oldState != nextState)
+ {
+ submitter.transition(m_resource, oldState, nextState);
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!! D3D12Resource !!!!!!!!!!!!!!!!!!!!!!!! */
+
+/* static */void D3D12Resource::setDebugName(ID3D12Resource* resource, const char* name)
+{
+ if (resource)
+ {
+ resource->SetName(String(name).toWString().begin());
+ }
+}
+
+void D3D12Resource::setDebugName(const char* name)
+{
+ setDebugName(m_resource, name);
+}
+
+void D3D12Resource::setDebugName(const wchar_t* name)
+{
+ if (m_resource)
+ {
+ m_resource->SetName(name);
+ }
+}
+
+void D3D12Resource::setResource(ID3D12Resource* resource)
+{
+ if (resource != m_resource)
+ {
+ if (resource)
+ {
+ resource->AddRef();
+ }
+ if (m_resource)
+ {
+ m_resource->Release();
+ }
+ m_resource = resource;
+ }
+}
+
+void D3D12Resource::setResourceNull()
+{
+ if (m_resource)
+ {
+ m_resource->Release();
+ m_resource = nullptr;
+ }
+}
+
+Result D3D12Resource::initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue)
+{
+ setResourceNull();
+ ComPtr resource;
+ SLANG_RETURN_ON_FAIL(device->CreateCommittedResource(&heapProps, heapFlags, &resourceDesc, initState, clearValue, IID_PPV_ARGS(resource.writeRef())));
+ setResource(resource);
+ return SLANG_OK;
+}
+
+ID3D12Resource* D3D12Resource::detach()
+{
+ ID3D12Resource* resource = m_resource;
+ m_resource = nullptr;
+ return resource;
+}
+
+void D3D12Resource::swap(ComPtr& resourceInOut)
+{
+ ID3D12Resource* tmp = m_resource;
+ m_resource = resourceInOut.detach();
+ resourceInOut.attach(tmp);
+}
+
+} // renderer_test
diff --git a/tools/gfx/d3d12/d3d12-resource.h b/tools/gfx/d3d12/d3d12-resource.h
new file mode 100644
index 000000000..ab9244470
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-resource.h
@@ -0,0 +1,116 @@
+// d3d12-resource.h
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include
+#undef WIN32_LEAN_AND_MEAN
+#undef NOMINMAX
+
+#include
+#include
+
+#include "slang-com-ptr.h"
+#include "../d3d/d3d-util.h"
+
+namespace gfx {
+
+// Enables more conservative barriers - restoring the state of resources after they are used.
+// Should not need to be enabled in normal builds, as the barriers should correctly sync resources
+// If enabling fixes an issue it implies regular barriers are not correctly used.
+#define SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS 0
+
+struct D3D12BarrierSubmitter
+{
+ enum { MAX_BARRIERS = 8 };
+
+ /// Expand one space to hold a barrier
+ SLANG_FORCE_INLINE D3D12_RESOURCE_BARRIER& expandOne() { return (m_numBarriers < MAX_BARRIERS) ? m_barriers[m_numBarriers++] : _expandOne(); }
+ /// Flush barriers to command list
+ SLANG_FORCE_INLINE void flush() { if (m_numBarriers > 0) _flush(); }
+
+ /// Transition resource from prevState to nextState
+ void transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState);
+
+ /// Ctor
+ SLANG_FORCE_INLINE D3D12BarrierSubmitter(ID3D12GraphicsCommandList* commandList) : m_numBarriers(0), m_commandList(commandList) { }
+ /// Dtor
+ SLANG_FORCE_INLINE ~D3D12BarrierSubmitter() { flush(); }
+
+protected:
+ D3D12_RESOURCE_BARRIER& _expandOne();
+ void _flush();
+
+ ID3D12GraphicsCommandList* m_commandList;
+ int m_numBarriers;
+ D3D12_RESOURCE_BARRIER m_barriers[MAX_BARRIERS];
+};
+
+/** The base class for resource types allows for tracking of state. It does not allow for setting of the resource though, such that
+an interface can return a D3D12ResourceBase, and a client cant manipulate it's state, but it cannot replace/change the actual resource */
+struct D3D12ResourceBase
+{
+ /// Add a transition if necessary to the list
+ void transition(
+ D3D12_RESOURCE_STATES currentState,
+ D3D12_RESOURCE_STATES nextState,
+ D3D12BarrierSubmitter& submitter);
+ /// Get the associated resource
+ SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_resource; }
+
+ /// True if a resource is set
+ SLANG_FORCE_INLINE bool isSet() const { return m_resource != nullptr; }
+
+ /// Coercible into ID3D12Resource
+ SLANG_FORCE_INLINE operator ID3D12Resource*() const { return m_resource; }
+
+ /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible
+ static DXGI_FORMAT calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource);
+
+ /// Ctor
+ SLANG_FORCE_INLINE D3D12ResourceBase() :
+ m_resource(nullptr)
+ {}
+
+protected:
+ /// This is protected so as clients cannot slice the class, and so state tracking is lost
+ ~D3D12ResourceBase() {}
+
+ ID3D12Resource* m_resource; ///< The resource (ref counted)
+};
+
+struct D3D12Resource : public D3D12ResourceBase
+{
+
+ /// Dtor
+ ~D3D12Resource()
+ {
+ if (m_resource)
+ {
+ m_resource->Release();
+ }
+ }
+
+ /// Initialize as committed resource
+ Slang::Result initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue);
+
+ /// Set a resource.
+ void setResource(ID3D12Resource* resource);
+ /// Make the resource null
+ void setResourceNull();
+ /// Returns the attached resource (with any ref counts) and sets to nullptr on this.
+ ID3D12Resource* detach();
+
+ /// Swaps the resource contents with the contents of the smart pointer
+ void swap(Slang::ComPtr& resourceInOut);
+
+ /// Set the debug name on a resource
+ static void setDebugName(ID3D12Resource* resource, const char* name);
+
+ /// Set the the debug name on the resource
+ void setDebugName(const wchar_t* name);
+ /// Set the debug name
+ void setDebugName(const char* name);
+};
+
+} // renderer_test
diff --git a/tools/gfx/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 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(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` or `ParameterBlock`
+ // (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 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(new ShaderObjectLayoutImpl());
+ SLANG_RETURN_ON_FAIL(layout->init(this));
+
+ returnRefPtrMove(outLayout, layout);
+ return SLANG_OK;
+}
+
+Result RootShaderObjectLayoutImpl::Builder::build(RootShaderObjectLayoutImpl** outLayout)
+{
+ RefPtr 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 signature;
+ ComPtr 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 entryPointLayout;
+ SLANG_RETURN_ON_FAIL(ShaderObjectLayoutImpl::createForElementType(
+ device, slangEntryPoint->getTypeLayout(), entryPointLayout.writeRef()));
+ builder.addEntryPoint(slangEntryPoint->getStage(), entryPointLayout);
+ }
+
+ RefPtr 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 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 m_bindingRanges;
+ List m_subObjectRanges;
+ List 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 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