summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/core/core.vcxproj2
-rw-r--r--source/core/slang-com-ptr.h19
-rw-r--r--source/core/slang-free-list.cpp216
-rw-r--r--source/core/slang-free-list.h142
-rw-r--r--tools/render-test/circular-resource-heap-d3d12.cpp221
-rw-r--r--tools/render-test/circular-resource-heap-d3d12.h206
-rw-r--r--tools/render-test/d3d-util.cpp280
-rw-r--r--tools/render-test/d3d-util.h61
-rw-r--r--tools/render-test/descriptor-heap-d3d12.cpp47
-rw-r--r--tools/render-test/descriptor-heap-d3d12.h115
-rw-r--r--tools/render-test/main.cpp12
-rw-r--r--tools/render-test/render-d3d11.cpp168
-rw-r--r--tools/render-test/render-d3d12.cpp2248
-rw-r--r--tools/render-test/render-gl.cpp2
-rw-r--r--tools/render-test/render-test.vcxproj8
-rw-r--r--tools/render-test/render-test.vcxproj.filters24
-rw-r--r--tools/render-test/render-vk.cpp2
-rw-r--r--tools/render-test/render.h6
-rw-r--r--tools/render-test/resource-d3d12.cpp214
-rw-r--r--tools/render-test/resource-d3d12.h178
-rw-r--r--tools/slang-test/README.md4
21 files changed, 3934 insertions, 241 deletions
diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj
index 2a9a9a4fe..77e91b32f 100644
--- a/source/core/core.vcxproj
+++ b/source/core/core.vcxproj
@@ -32,6 +32,7 @@
<ClInclude Include="secure-crt.h" />
<ClInclude Include="slang-com-ptr.h" />
<ClInclude Include="slang-defines.h" />
+ <ClInclude Include="slang-free-list.h" />
<ClInclude Include="slang-io.h" />
<ClInclude Include="slang-math.h" />
<ClInclude Include="slang-result.h" />
@@ -44,6 +45,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="platform.cpp" />
+ <ClCompile Include="slang-free-list.cpp" />
<ClCompile Include="slang-io.cpp" />
<ClCompile Include="slang-string.cpp" />
<ClCompile Include="stream.cpp" />
diff --git a/source/core/slang-com-ptr.h b/source/core/slang-com-ptr.h
index 729b6b266..9f6651306 100644
--- a/source/core/slang-com-ptr.h
+++ b/source/core/slang-com-ptr.h
@@ -51,21 +51,18 @@ struct Guid
SLANG_FORCE_INLINE bool operator==(const Guid& aIn, const Guid& bIn)
{
+ // Use the largest type the honors the alignment of Guid
+ typedef uint32_t CmpType;
struct GuidCompare
{
- enum { kNum = sizeof(Guid) / sizeof(size_t) };
Guid guid;
- size_t data[kNum];
+ CmpType data[sizeof(Guid) / sizeof(CmpType)];
};
- const GuidCompare& a = reinterpret_cast<const GuidCompare&>(aIn);
- const GuidCompare& b = reinterpret_cast<const GuidCompare&>(bIn);
-
- switch (GuidCompare::kNum)
- {
- case 2: return ((a.data[0] ^ b.data[0]) | (a.data[1] ^ b.data[1])) == 0;
- case 4: return ((a.data[0] ^ b.data[0]) | (a.data[1] ^ b.data[1]) | (a.data[2] ^ b.data[2]) | (a.data[3] ^ b.data[3]) ) == 0;
- default: return false;
- }
+ // Type pun - so compiler can 'see' the pun and not break aliasing rules
+ const CmpType* a = reinterpret_cast<const GuidCompare&>(aIn).data;
+ const CmpType* b = reinterpret_cast<const GuidCompare&>(bIn).data;
+ // Make the guid comparison a single branch, by not using short circuit
+ return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]) | (a[3] ^ b[3])) == 0;
}
SLANG_FORCE_INLINE bool operator!=(const Guid& a, const Guid& b)
diff --git a/source/core/slang-free-list.cpp b/source/core/slang-free-list.cpp
new file mode 100644
index 000000000..0701558aa
--- /dev/null
+++ b/source/core/slang-free-list.cpp
@@ -0,0 +1,216 @@
+#include "slang-free-list.h"
+
+//#include "list.h"
+
+namespace Slang {
+
+FreeList::~FreeList()
+{
+ _deallocateBlocks(m_activeBlocks);
+ _deallocateBlocks(m_freeBlocks);
+}
+
+void FreeList::_init()
+{
+ m_top = nullptr;
+ m_end = nullptr;
+
+ m_activeBlocks = nullptr;
+ m_freeBlocks = nullptr;
+
+ m_freeElements = nullptr;
+
+ m_elementSize = 0;
+ m_alignment = 1;
+ m_blockSize = 0;
+ m_blockAllocationSize = 0;
+}
+
+void FreeList::_init(size_t elementSize, size_t alignment, size_t elemsPerBlock)
+{
+ alignment = (alignment < sizeof(void*)) ? sizeof(void*) : alignment;
+
+ // Alignment must be a power of 2
+ assert(((alignment - 1) & alignment) == 0);
+
+ // The elementSize must at least be
+ elementSize = (elementSize >= alignment) ? elementSize : alignment;
+ m_blockSize = elementSize * elemsPerBlock;
+ m_elementSize = elementSize;
+ m_alignment = alignment;
+
+ // Calculate the block size need, correcting for alignment
+ const size_t alignedBlockSize = (alignment <= DEFAULT_ALIGNMENT) ?
+ _calcAlignedBlockSize(DEFAULT_ALIGNMENT) :
+ _calcAlignedBlockSize(alignment);
+
+ // Make the block struct size aligned
+ m_blockAllocationSize = m_blockSize + alignedBlockSize;
+
+ m_top = nullptr;
+ m_end = nullptr;
+
+ m_activeBlocks = nullptr;
+ m_freeBlocks = nullptr; ///< Blocks that there are no allocations in
+
+ m_freeElements = nullptr;
+}
+
+void FreeList::init(size_t elementSize, size_t alignment, size_t elemsPerBlock)
+{
+ _deallocateBlocks(m_activeBlocks);
+ _deallocateBlocks(m_freeBlocks);
+ _init(elementSize, alignment, elemsPerBlock);
+}
+
+void FreeList::_deallocateBlocks(Block* block)
+{
+ while (block)
+ {
+ Block* next = block->m_next;
+
+#ifdef SLANG_FREE_LIST_INIT_MEM
+ Memory::set(block, 0xfd, m_blockAllocationSize);
+#endif
+
+ ::free(block); // deallocate(block, m_blockAllocationSize);
+ block = next;
+ }
+}
+
+bool FreeList::isValidAllocation(const void* dataIn) const
+{
+ uint8_t* data = (uint8_t*)dataIn;
+
+ Block* block = m_activeBlocks;
+ while (block)
+ {
+ uint8_t* start = block->m_data;
+ uint8_t* end = start + m_blockSize;
+
+ if (data >= start && data < end)
+ {
+ // Check it's aligned correctly
+ if ((data - start) % m_elementSize)
+ {
+ return false;
+ }
+
+ // Non allocated data is between top and end
+ if (data >= m_top && data < m_end)
+ {
+ return false;
+ }
+
+ // It can't be in the free list
+ Element* ele = m_freeElements;
+ while (ele)
+ {
+ if (ele == (Element*)data)
+ {
+ return false;
+ }
+
+ ele = ele->m_next;
+ }
+ return true;
+ }
+
+ block = block->m_next;
+ }
+ // It's not in an active block -> it cannot be a valid allocation
+ return false;
+}
+
+void* FreeList::_allocate()
+{
+ Block* block = m_freeBlocks;
+ if (block)
+ {
+ /// Remove from the free blocks
+ m_freeBlocks = block->m_next;
+ }
+ else
+ {
+ //block = (Block*)m_allocator->allocate(m_blockAllocationSize);
+ block = (Block*)::malloc(m_blockAllocationSize);
+ if (!block)
+ {
+ // Allocation failed... doh
+ return nullptr;
+ }
+ // Do the alignment
+ {
+ size_t fix = (size_t(block) + sizeof(Block) + m_alignment - 1) & ~(m_alignment - 1);
+ block->m_data = (uint8_t*)fix;
+ }
+ }
+
+ // Attach to the active blocks
+ block->m_next = m_activeBlocks;
+ m_activeBlocks = block;
+
+ // Set up top and end
+ m_end = block->m_data + m_blockSize;
+
+ // Return the first element
+ uint8_t* element = block->m_data;
+ m_top = element + m_elementSize;
+
+ SLANG_FREE_LIST_INIT_ALLOCATE(element)
+
+ return element;
+}
+
+void FreeList::deallocateAll()
+{
+ Block* block = m_activeBlocks;
+ if (block)
+ {
+ // Find the end block
+ while (block->m_next)
+ {
+#ifdef SLANG_FREE_LIST_INIT_MEM
+ Memory::set(block->m_data, 0xfd, m_blockSize);
+#endif
+ block = block->m_next;
+ }
+ // Attach to the freeblocks
+ block->m_next = m_freeBlocks;
+ // The list is now all freelists
+ m_freeBlocks = m_activeBlocks;
+ // There are no active blocks
+ m_activeBlocks = nullptr;
+ }
+
+ m_top = nullptr;
+ m_end = nullptr;
+}
+
+void FreeList::reset()
+{
+ _deallocateBlocks(m_activeBlocks);
+ _deallocateBlocks(m_freeBlocks);
+
+ m_top = nullptr;
+ m_end = nullptr;
+
+ m_activeBlocks = nullptr;
+ m_freeBlocks = nullptr;
+
+ m_freeElements = nullptr;
+}
+
+
+void FreeList::_initAllocate(void* mem)
+{
+ ::memset(mem, 0xcd, m_elementSize);
+}
+
+void FreeList::_initDeallocate(void* mem)
+{
+ ::memset(mem, 0xfd, m_elementSize);
+}
+
+} // namespace Slang
+
diff --git a/source/core/slang-free-list.h b/source/core/slang-free-list.h
new file mode 100644
index 000000000..10f0a39ba
--- /dev/null
+++ b/source/core/slang-free-list.h
@@ -0,0 +1,142 @@
+#ifndef SLANG_FREE_LIST_H
+#define SLANG_FREE_LIST_H
+
+#include "slang-defines.h"
+#include "slang-result.h"
+#include <stdlib.h>
+#include <string.h>
+
+namespace Slang {
+
+#if SLANG_DEBUG
+# define SLANG_FREE_LIST_INIT_MEM
+#endif
+
+#ifdef SLANG_FREE_LIST_INIT_MEM
+# define SLANG_FREE_LIST_INIT_ALLOCATE(ptr) _initAllocate(ptr);
+# define SLANG_FREE_LIST_INIT_DEALLOCATE(ptr) _initDeallocate(ptr);
+#else
+# define SLANG_FREE_LIST_INIT_ALLOCATE(ptr)
+# define SLANG_FREE_LIST_INIT_DEALLOCATE(ptr)
+#endif
+
+/*! \brief A freelist is a simple and fast memory allocator that can allocate and free in any order identically sized blocks.
+
+\details A free list is a memory allocation system that performs allocations/deallocations very quickly for elements which are
+all the same size.
+In a freelist all elements are the same size, and elements can be allocated and freed in any order, as long as every deallocation
+matches every allocation. Both allocation and deallocation are O(1), and generally just a few instructions. The underlying
+memory allocator will allocate in large blocks, with multiple elements amortizing a more costly large allocation against lots
+of fast small element allocations. */
+class FreeList
+{
+ public:
+ typedef FreeList ThisType;
+
+ enum { DEFAULT_ALIGNMENT = sizeof(void*) };
+
+ /// Free elements are held in a singly linked list. The minimum size of an element is therefore a pointer
+ struct Element
+ {
+ Element* m_next;
+ };
+ struct Block
+ {
+ Block* m_next; ///< The next block
+ uint8_t* m_data; ///< The list of the elements each m_elementSize in size
+ };
+
+ /// Allocate a single element
+ SLANG_FORCE_INLINE void* allocate();
+ /// Deallocate a block that was previously allocated with allocate
+ SLANG_FORCE_INLINE void deallocate(void* data);
+
+ /// Returns true if this is from a valid allocation
+ bool isValidAllocation(const void* dataIn) const;
+
+ /// Get the element size
+ SLANG_FORCE_INLINE size_t getElementSize() const { return m_elementSize; }
+ /// Get the total size of each individual block allocation in bytes
+ SLANG_FORCE_INLINE size_t getBlockSize() const { return m_blockSize; }
+
+ /// Deallocates all elements
+ void deallocateAll();
+ /// Deallocates all, and frees any backing memory (put in initial state)
+ void reset();
+
+ /// Initialize. If called on an already initialized heap, the heap will be deallocated.
+ void init(size_t elementSize, size_t alignment, size_t elemsPerBlock);
+
+ /// Default Ctor
+ FreeList() { _init(); }
+ /// Ctor
+ FreeList(size_t elementSize, size_t alignment, size_t elemsPerBlock) { _init(elementSize, alignment, elemsPerBlock); }
+ /// Dtor
+ ~FreeList();
+
+ protected:
+ /// Initializes assuming freelist is not constructed
+ void _init(size_t elementSize, size_t alignment, size_t elemsPerBlock);
+ void* _allocate();
+ void _deallocateBlocks(Block* block);
+ /// Initializes setting everything to empty (doesn't free anything if already allocated)
+ void _init();
+
+ SLANG_FORCE_INLINE static size_t _calcAlignedBlockSize(size_t align) { return (sizeof(Block) + align - 1) & ~(align - 1); }
+
+ void _initAllocate(void* mem);
+ void _initDeallocate(void* mem);
+
+ uint8_t* m_top; ///< The top position of the current block
+ uint8_t* m_end; ///< The end of the current block
+
+ Block* m_activeBlocks; ///< The blocks there are potentially allocations from
+ Block* m_freeBlocks; ///< Blocks that there are no allocations in
+
+ Element* m_freeElements; ///< A singly linked list of elements available
+
+ size_t m_elementSize;
+ size_t m_alignment;
+ size_t m_blockSize;
+ size_t m_blockAllocationSize; ///< The actual allocation size. Maybe bigger than m_blockSize if alignment requires it.
+};
+
+// --------------------------------------------------------------------------
+SLANG_FORCE_INLINE void* FreeList::allocate()
+{
+ // First see if there are any freeElements ready to go
+ {
+ Element* element = m_freeElements;
+ if (element)
+ {
+ m_freeElements = element->m_next;
+ SLANG_FREE_LIST_INIT_ALLOCATE(element)
+ return element;
+ }
+ }
+ if (m_top >= m_end)
+ {
+ return _allocate();
+ }
+ void* data = (void*)m_top;
+ SLANG_FREE_LIST_INIT_ALLOCATE(data)
+
+ m_top += m_elementSize;
+ return data;
+}
+// --------------------------------------------------------------------------
+SLANG_FORCE_INLINE void FreeList::deallocate(void* data)
+{
+ assert(isValidAllocation(data));
+
+ SLANG_FREE_LIST_INIT_DEALLOCATE(data)
+
+ // Put onto the singly linked free element list
+ Element* ele = (Element*)data;
+ ele->m_next = m_freeElements;
+ m_freeElements = ele;
+}
+
+} // namespace Slang
+
+#endif // SLANG_FREE_LIST_H \ No newline at end of file
diff --git a/tools/render-test/circular-resource-heap-d3d12.cpp b/tools/render-test/circular-resource-heap-d3d12.cpp
new file mode 100644
index 000000000..7336c3e09
--- /dev/null
+++ b/tools/render-test/circular-resource-heap-d3d12.cpp
@@ -0,0 +1,221 @@
+#include "circular-resource-heap-d3d12.h"
+
+namespace renderer_test {
+using namespace Slang;
+
+D3D12CircularResourceHeap::D3D12CircularResourceHeap():
+ m_fence(nullptr),
+ m_device(nullptr),
+ m_blockFreeList(sizeof(Block), SLANG_ALIGN_OF(Block), 16),
+ m_blocks(nullptr)
+{
+ m_back.m_block = nullptr;
+ m_back.m_position = nullptr;
+ m_front.m_block = nullptr;
+ m_front.m_position = nullptr;
+}
+
+D3D12CircularResourceHeap::~D3D12CircularResourceHeap()
+{
+ _freeBlockListResources(m_blocks);
+}
+
+void D3D12CircularResourceHeap::_freeBlockListResources(const Block* start)
+{
+ if (start)
+ {
+ {
+ ID3D12Resource* resource = start->m_resource;
+ resource->Unmap(0, nullptr);
+ resource->Release();
+ }
+ for (Block* block = start->m_next; block != start; block = block->m_next)
+ {
+ ID3D12Resource* resource = block->m_resource;
+ resource->Unmap(0, nullptr);
+ resource->Release();
+ }
+ }
+}
+
+Result D3D12CircularResourceHeap::init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence)
+{
+ assert(m_blocks == nullptr);
+ assert(desc.m_blockSize > 0);
+
+ m_fence = fence;
+ m_desc = desc;
+ m_device = device;
+
+ return SLANG_OK;
+}
+
+void D3D12CircularResourceHeap::addSync(uint64_t signalValue)
+{
+ assert(signalValue == m_fence->getCurrentValue());
+ PendingEntry entry;
+ entry.m_completedValue = signalValue;
+ entry.m_cursor = m_front;
+ m_pendingQueue.Add(entry);
+}
+
+void D3D12CircularResourceHeap::updateCompleted()
+{
+ const uint64_t completedValue = m_fence->getCompletedValue();
+
+#if 0
+ while (m_pendingQueue.Count() != 0)
+ {
+ const PendingEntry& entry = m_pendingQueue[0];
+ if (entry.m_completedValue <= completedValue)
+ {
+ m_back = entry.m_cursor;
+ m_pendingQueue.RemoveAt(0);
+ }
+ else
+ {
+ break;
+ }
+ }
+#else
+ // A more efficient implementation is m_pendingQueue is implemented as a vector like type
+ const int size = int(m_pendingQueue.Count());
+ int end = 0;
+ while (end < size && m_pendingQueue[end].m_completedValue <= completedValue)
+ {
+ end++;
+ }
+
+ if (end > 0)
+ {
+ // Set the back position
+ m_back = m_pendingQueue[end - 1].m_cursor;
+ if (end == size)
+ {
+ m_pendingQueue.Clear();
+ }
+ else
+ {
+ m_pendingQueue.RemoveRange(0, size);
+ }
+ }
+#endif
+}
+
+D3D12CircularResourceHeap::Block* D3D12CircularResourceHeap::_newBlock()
+{
+ D3D12_RESOURCE_DESC desc;
+
+ desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ desc.Alignment = 0;
+ desc.Width = m_desc.m_blockSize;
+ desc.Height = 1;
+ desc.DepthOrArraySize = 1;
+ desc.MipLevels = 1;
+ desc.Format = DXGI_FORMAT_UNKNOWN;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+ desc.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+ ComPtr<ID3D12Resource> resource;
+ Result res = m_device->CreateCommittedResource(&m_desc.m_heapProperties, m_desc.m_heapFlags, &desc, m_desc.m_initialState, nullptr, IID_PPV_ARGS(resource.writeRef()));
+ if (SLANG_FAILED(res))
+ {
+ assert(!"Resource allocation failed");
+ return nullptr;
+ }
+
+ uint8_t* data = nullptr;
+ if (m_desc.m_heapProperties.Type == D3D12_HEAP_TYPE_READBACK)
+ {
+ }
+ else
+ {
+ // Map it, and keep it mapped
+ resource->Map(0, nullptr, (void**)&data);
+ }
+
+ // We have no blocks -> so lets allocate the first
+ Block* block = (Block*)m_blockFreeList.allocate();
+ block->m_next = nullptr;
+
+ block->m_resource = resource.detach();
+ block->m_start = data;
+ return block;
+}
+
+D3D12CircularResourceHeap::Cursor D3D12CircularResourceHeap::allocate(size_t size, size_t alignment)
+{
+ const size_t blockSize = getBlockSize();
+
+ assert(size <= blockSize);
+
+ // If nothing is allocated add the first block
+ if (m_blocks == nullptr)
+ {
+ Block* block = _newBlock();
+ if (!block)
+ {
+ Cursor cursor = {};
+ return cursor;
+ }
+ m_blocks = block;
+ // Make circular
+ block->m_next = block;
+
+ // Point front and back to same position, as currently it is all free
+ m_back = { block, block->m_start };
+ m_front = m_back;
+ }
+
+ // If front and back are in the same block then front MUST be ahead of back (as that defined as
+ // an invariant and is required for block insertion to be possible
+ Block* block = m_front.m_block;
+
+ // Check the invariant
+ assert(block != m_back.m_block || m_front.m_position >= m_back.m_position);
+
+ {
+ uint8_t* cur = (uint8_t*)((size_t(m_front.m_position) + alignment - 1) & ~(alignment - 1));
+ // Does the the allocation fit?
+ if (cur + size <= block->m_start + blockSize)
+ {
+ // It fits
+ // Move the front forward
+ m_front.m_position = cur + size;
+ Cursor cursor = { block, cur };
+ return cursor;
+ }
+ }
+
+ // Okay I can't fit into current block...
+
+ // If the next block contains front, we need to add a block, else we can use that block
+ if (block->m_next == m_back.m_block)
+ {
+ Block* newBlock = _newBlock();
+ // Insert into the list
+ newBlock->m_next = block->m_next;
+ block->m_next = newBlock;
+ }
+
+ // Use the block we are going to add to
+ block = block->m_next;
+ uint8_t* cur = (uint8_t*)((size_t(block->m_start) + alignment - 1) & ~(alignment - 1));
+ // Does the the allocation fit?
+ if (cur + size > block->m_start + blockSize)
+ {
+ assert(!"Couldn't fit into a free block(!) Alignment breaks it?");
+ Cursor cursor = {};
+ return cursor;
+ }
+ // It fits
+ // Move the front forward
+ m_front.m_block = block;
+ m_front.m_position = cur + size;
+ Cursor cursor = { block, cur };
+ return cursor;
+}
+
+} // namespace renderer_test
diff --git a/tools/render-test/circular-resource-heap-d3d12.h b/tools/render-test/circular-resource-heap-d3d12.h
new file mode 100644
index 000000000..6f251c476
--- /dev/null
+++ b/tools/render-test/circular-resource-heap-d3d12.h
@@ -0,0 +1,206 @@
+#pragma once
+
+#include "../../source/core/slang-com-ptr.h"
+#include "../../source/core/list.h"
+#include "../../source/core/slang-free-list.h"
+
+#include "resource-d3d12.h"
+
+namespace renderer_test {
+
+/*! \brief The D3D12CircularResourceHeap is a heap that is suited for size constrained real-time resources allocation that
+is transitory in nature. It is designed to allocate resources which are used and discarded, often used where in
+previous versions of DirectX the 'DISCARD' flag was used.
+
+The idea is to have a heap which chunks of resource can be allocated, and used for GPU execution,
+and that the heap is able through the addSync/updateCompleted idiom is able to track when the usage of the resources is
+completed allowing them to be reused. The heap is arranged as circularly, with new allocations made from the front, and the back
+being updated as the GPU updating the back when it is informed anything using prior parts of the heap have completed. In this
+arrangement all the heap between the back and the front can be thought of as in use or potentially in use by the GPU. All the heap
+from the front back around to the back, is free and can be allocated from. It is the responsibility of the user of the Heap to make
+sure the invariant holds, but in most normal usage it does so simply.
+
+Another feature of the heap is that it does not require upfront knowledge of how big a heap is needed. The backing resources will be expanded
+dynamically with requests as needed. The only requirement is that know single request can be larger than m_blockSize specified in the Desc
+used to initialize the heap. This is because all the backing resources are allocated to a single size. This limitation means the D3D12CircularResourceHeap
+may not be the best use for example for uploading a texture - because it's design is really around transitory uploads or write backs, and so more suited
+to constant buffers, vertex buffer, index buffers and the like.
+
+To upload a texture at program startup it is most likely better to use a D3D12ResourceScopeManager.
+
+\code{.cpp}
+
+typedef D3D12CircularResourceHeap Heap;
+
+Heap::Cursor cursor = heap.allocateVertexBuffer(sizeof(Vertex) * numVerts);
+Memory:copy(cursor.m_position, verts, sizeof(Vertex) * numVerts);
+
+// Do a command using the GPU handle
+m_commandList->...
+// Do another command using the GPU handle
+
+m_commandList->...
+
+// Execute the command list on the command queue
+{
+ ID3D12CommandList* lists[] = { m_commandList };
+ m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(lists), lists);
+}
+
+// Add a sync point
+const uint64_t signalValue = m_fence.nextSignal(m_commandQueue);
+heap.addSync(signalValue)
+
+// The cursors cannot be used anymore
+
+// At some later point call updateCompleted. This will see where the GPU is at, and make resources available that the GPU no longer accesses.
+heap.updateCompleted();
+
+\endcode
+
+### Implementation
+
+Front and back can be in the same block, but ONLY if back is behind front, because we have to always be able to insert
+new blocks in front of front. So it must be possible to do an block insertion between the two of them.
+
+|--B---F-----| |----------|
+
+When B and F are on top of one another it means there is nothing in the list. NOTE this also means that a move of front can never place it
+top of the back.
+
+https://msdn.microsoft.com/en-us/library/windows/desktop/dn899125%28v=vs.85%29.aspx
+https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx
+*/
+
+class D3D12CircularResourceHeap
+{
+ protected:
+ struct Block;
+ public:
+ typedef D3D12CircularResourceHeap ThisType;
+
+ /// The alignment used for VERTEX_BUFFER allocations
+ /// Strictly speaking it seems the hardware can handle 4 byte alignment, but since often in use
+ /// data will be copied from CPU memory to the allocation, using 16 byte alignment is superior as allows
+ /// significantly faster memcpy.
+ /// The sample that shows sizeof(float) - 4 bytes is appropriate is at the link below.
+ /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx
+ enum
+ {
+ VERTEX_BUFFER_ALIGNMENT = 16,
+ };
+
+ struct Desc
+ {
+ void init()
+ {
+ {
+ D3D12_HEAP_PROPERTIES& props = m_heapProperties;
+
+ props.Type = D3D12_HEAP_TYPE_UPLOAD;
+ props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ props.CreationNodeMask = 1;
+ props.VisibleNodeMask = 1;
+ }
+ m_heapFlags = D3D12_HEAP_FLAG_NONE;
+ m_initialState = D3D12_RESOURCE_STATE_GENERIC_READ;
+ m_blockSize = 0;
+ }
+
+ D3D12_HEAP_PROPERTIES m_heapProperties;
+ D3D12_HEAP_FLAGS m_heapFlags;
+ D3D12_RESOURCE_STATES m_initialState;
+ size_t m_blockSize;
+ };
+
+ /// Cursor position
+ struct Cursor
+ {
+ /// Get GpuHandle
+ SLANG_FORCE_INLINE D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle() const { return m_block->m_resource->GetGPUVirtualAddress() + size_t(m_position - m_block->m_start); }
+ /// Must have a block and position
+ SLANG_FORCE_INLINE bool isValid() const { return m_block != nullptr; }
+ /// Calculate the offset into the underlying resource
+ SLANG_FORCE_INLINE size_t getOffset() const { return size_t(m_position - m_block->m_start); }
+ /// Get the underlying resource
+ SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_block->m_resource; }
+
+ Block* m_block; ///< The block index
+ uint8_t* m_position; ///< The current position
+ };
+
+ /// Get the desc used to initialize the heap
+ SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; }
+
+ /// Must be called before used
+ /// Block size must be at least as large as the _largest_ thing allocated
+ /// Also note depending on alignment of a resource allocation, the block size might also need to take into account the
+ /// maximum alignment use. It is a REQUIREMENT that a newly allocated resource block is large enough to hold any
+ /// allocation taking into account the alignment used.
+ Slang::Result init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence);
+
+ /// Get the block size
+ SLANG_FORCE_INLINE size_t getBlockSize() const { return m_desc.m_blockSize; }
+
+ /// Allocate constant buffer of specified size
+ Cursor allocate(size_t size, size_t alignment);
+
+ /// Allocate a constant buffer
+ SLANG_FORCE_INLINE Cursor allocateConstantBuffer(size_t size) { return allocate(size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); }
+ /// Allocate a vertex buffer
+ SLANG_FORCE_INLINE Cursor allocateVertexBuffer(size_t size) { return allocate(size, VERTEX_BUFFER_ALIGNMENT); }
+
+ /// Create filled in constant buffer
+ SLANG_FORCE_INLINE Cursor newConstantBuffer(const void* data, size_t size) { Cursor cursor = allocateConstantBuffer(size); ::memcpy(cursor.m_position, data, size); return cursor; }
+ /// Create in filled in constant buffer
+ template <typename T>
+ SLANG_FORCE_INLINE Cursor newConstantBuffer(const T& in) { return newConstantBuffer(&in, sizeof(T)); }
+
+ /// Look where the GPU has got to and release anything not currently used
+ void updateCompleted();
+ /// Add a sync point - meaning that when this point is hit in the queue
+ /// all of the resources up to this point will no longer be used.
+ void addSync(uint64_t signalValue);
+
+ /// Get the gpu address of this cursor
+ D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle(const Cursor& cursor) const { return cursor.m_block->m_resource->GetGPUVirtualAddress() + size_t(cursor.m_position - cursor.m_block->m_start); }
+
+ /// Ctor
+ D3D12CircularResourceHeap();
+ /// Dtor
+ ~D3D12CircularResourceHeap();
+
+ protected:
+
+ struct Block
+ {
+ ID3D12Resource* m_resource; ///< The mapped resource
+ uint8_t* m_start; ///< Once created the resource is mapped to here
+ Block* m_next; ///< Points to next block in the list
+ };
+ struct PendingEntry
+ {
+ uint64_t m_completedValue; ///< The value when this is completed
+ Cursor m_cursor; ///< the cursor at that point
+ };
+ void _freeBlockListResources(const Block* block);
+ /// Create a new block (with associated resource), do not add the block list
+ Block* _newBlock();
+
+ Block* m_blocks; ///< Circular singly linked list of block. nullptr initially
+ Slang::FreeList m_blockFreeList; ///< Free list of actual allocations of blocks
+ Slang::List<PendingEntry> m_pendingQueue; ///< Holds the list of pending positions. When the fence value is greater than the value on the queue entry, the entry is done.
+
+ // Allocation is made from the front, and freed from the back.
+ Cursor m_back; ///< Current back position.
+ Cursor m_front; ///< Current front position.
+
+ Desc m_desc; ///< Describes the heap
+
+ D3D12CounterFence* m_fence; ///< The fence to use
+ ID3D12Device* m_device; ///< The device that resources will be constructed on
+};
+
+} // namespace renderer_test
+
diff --git a/tools/render-test/d3d-util.cpp b/tools/render-test/d3d-util.cpp
new file mode 100644
index 000000000..ff07fc2ea
--- /dev/null
+++ b/tools/render-test/d3d-util.cpp
@@ -0,0 +1,280 @@
+// d3d-util.cpp
+#include "d3d-util.h"
+
+#include <d3dcompiler.h>
+
+// We will use the C standard library just for printing error messages.
+#include <stdio.h>
+
+namespace renderer_test {
+using namespace Slang;
+
+/* static */D3D_PRIMITIVE_TOPOLOGY D3DUtil::getPrimitiveTopology(PrimitiveTopology topology)
+{
+ switch (topology)
+ {
+ case PrimitiveTopology::TriangleList:
+ {
+ return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ }
+ default: break;
+ }
+ return D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
+}
+
+/* static */DXGI_FORMAT D3DUtil::getMapFormat(Format format)
+{
+ switch (format)
+ {
+ case Format::RGB_Float32:
+ return DXGI_FORMAT_R32G32B32_FLOAT;
+ case Format::RG_Float32:
+ return DXGI_FORMAT_R32G32_FLOAT;
+ default:
+ return DXGI_FORMAT_UNKNOWN;
+ }
+}
+
+/* static */DXGI_FORMAT D3DUtil::calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format)
+{
+ SLANG_UNUSED(usage);
+ if (usageFlags)
+ {
+ switch (format)
+ {
+ case DXGI_FORMAT_R32_FLOAT:
+ case DXGI_FORMAT_D32_FLOAT: return DXGI_FORMAT_R32_TYPELESS;
+ case DXGI_FORMAT_D24_UNORM_S8_UINT: return DXGI_FORMAT_R24G8_TYPELESS;
+ default: break;
+ }
+ return format;
+ }
+ return format;
+}
+
+/* static */DXGI_FORMAT D3DUtil::calcFormat(UsageType usage, DXGI_FORMAT format)
+{
+ switch (usage)
+ {
+ case USAGE_COUNT_OF:
+ case USAGE_UNKNOWN:
+ {
+ return DXGI_FORMAT_UNKNOWN;
+ }
+ case USAGE_DEPTH_STENCIL:
+ {
+ switch (format)
+ {
+ case DXGI_FORMAT_D32_FLOAT:
+ case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_D32_FLOAT;
+ case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT;
+ case DXGI_FORMAT_R24G8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT;
+ default: break;
+ }
+ return format;
+ }
+ case USAGE_TARGET:
+ {
+ switch (format)
+ {
+ case DXGI_FORMAT_D32_FLOAT:
+ case DXGI_FORMAT_D24_UNORM_S8_UINT: return DXGI_FORMAT_UNKNOWN;
+ case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT;
+ default: break;
+ }
+ return format;
+ }
+ case USAGE_SRV:
+ {
+ switch (format)
+ {
+ case DXGI_FORMAT_D32_FLOAT:
+ case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT;
+ case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+ default: break;
+ }
+
+ return format;
+ }
+ }
+
+ assert(!"Not reachable");
+ return DXGI_FORMAT_UNKNOWN;
+}
+
+bool D3DUtil::isTypeless(DXGI_FORMAT format)
+{
+ switch (format)
+ {
+ case DXGI_FORMAT_R32G32B32A32_TYPELESS:
+ case DXGI_FORMAT_R32G32B32_TYPELESS:
+ case DXGI_FORMAT_R16G16B16A16_TYPELESS:
+ case DXGI_FORMAT_R32G32_TYPELESS:
+ case DXGI_FORMAT_R32G8X24_TYPELESS:
+ case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
+ case DXGI_FORMAT_R10G10B10A2_TYPELESS:
+ case DXGI_FORMAT_R8G8B8A8_TYPELESS:
+ case DXGI_FORMAT_R16G16_TYPELESS:
+ case DXGI_FORMAT_R32_TYPELESS:
+ case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
+ case DXGI_FORMAT_R24G8_TYPELESS:
+ case DXGI_FORMAT_R8G8_TYPELESS:
+ case DXGI_FORMAT_R16_TYPELESS:
+ case DXGI_FORMAT_R8_TYPELESS:
+ case DXGI_FORMAT_BC1_TYPELESS:
+ case DXGI_FORMAT_BC2_TYPELESS:
+ case DXGI_FORMAT_BC3_TYPELESS:
+ case DXGI_FORMAT_BC4_TYPELESS:
+ case DXGI_FORMAT_BC5_TYPELESS:
+ case DXGI_FORMAT_B8G8R8A8_TYPELESS:
+ case DXGI_FORMAT_BC6H_TYPELESS:
+ case DXGI_FORMAT_BC7_TYPELESS:
+ {
+ return true;
+ }
+ default: break;
+ }
+ return false;
+}
+
+/* static */Int D3DUtil::getNumColorChannelBits(DXGI_FORMAT fmt)
+{
+ switch (fmt)
+ {
+ case DXGI_FORMAT_R32G32B32A32_TYPELESS:
+ case DXGI_FORMAT_R32G32B32A32_FLOAT:
+ case DXGI_FORMAT_R32G32B32A32_UINT:
+ case DXGI_FORMAT_R32G32B32A32_SINT:
+ case DXGI_FORMAT_R32G32B32_TYPELESS:
+ case DXGI_FORMAT_R32G32B32_FLOAT:
+ case DXGI_FORMAT_R32G32B32_UINT:
+ case DXGI_FORMAT_R32G32B32_SINT:
+ return 32;
+
+ case DXGI_FORMAT_R16G16B16A16_TYPELESS:
+ case DXGI_FORMAT_R16G16B16A16_FLOAT:
+ case DXGI_FORMAT_R16G16B16A16_UNORM:
+ case DXGI_FORMAT_R16G16B16A16_UINT:
+ case DXGI_FORMAT_R16G16B16A16_SNORM:
+ case DXGI_FORMAT_R16G16B16A16_SINT:
+ return 16;
+
+ case DXGI_FORMAT_R10G10B10A2_TYPELESS:
+ case DXGI_FORMAT_R10G10B10A2_UNORM:
+ case DXGI_FORMAT_R10G10B10A2_UINT:
+ case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
+ return 10;
+
+ case DXGI_FORMAT_R8G8B8A8_TYPELESS:
+ case DXGI_FORMAT_R8G8B8A8_UNORM:
+ case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+ case DXGI_FORMAT_R8G8B8A8_UINT:
+ case DXGI_FORMAT_R8G8B8A8_SNORM:
+ case DXGI_FORMAT_R8G8B8A8_SINT:
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ case DXGI_FORMAT_B8G8R8X8_UNORM:
+ case DXGI_FORMAT_B8G8R8A8_TYPELESS:
+ case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
+ case DXGI_FORMAT_B8G8R8X8_TYPELESS:
+ case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
+ return 8;
+
+ case DXGI_FORMAT_B5G6R5_UNORM:
+ case DXGI_FORMAT_B5G5R5A1_UNORM:
+ return 5;
+
+ case DXGI_FORMAT_B4G4R4A4_UNORM:
+ return 4;
+
+ default:
+ return 0;
+ }
+}
+
+/* static */Result D3DUtil::compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, ComPtr<ID3DBlob>& shaderBlobOut)
+{
+ // Rather than statically link against the `d3dcompile` library, we
+ // dynamically load it.
+ //
+ // Note: A more realistic application would compile from HLSL text to D3D
+ // shader bytecode as part of an offline process, rather than doing it
+ // on-the-fly like this
+ //
+ static pD3DCompile compileFunc = nullptr;
+ if (!compileFunc)
+ {
+ // TODO(tfoley): maybe want to search for one of a few versions of the DLL
+ HMODULE compilerModule = LoadLibraryA("d3dcompiler_47.dll");
+ if (!compilerModule)
+ {
+ fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n");
+ return SLANG_FAIL;
+ }
+
+ compileFunc = (pD3DCompile)GetProcAddress(compilerModule, "D3DCompile");
+ if (!compileFunc)
+ {
+ fprintf(stderr, "error: failed load symbol 'D3DCompile'\n");
+ return SLANG_FAIL;
+ }
+ }
+
+ // For this example, we turn on debug output, and turn off all
+ // optimization. A real application would only use these flags
+ // when shader debugging is needed.
+ UINT flags = 0;
+ flags |= D3DCOMPILE_DEBUG;
+ flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION;
+
+ // We will always define `__HLSL__` when compiling here, so that
+ // input code can react differently to being compiled as pure HLSL.
+ D3D_SHADER_MACRO defines[] = {
+ { "__HLSL__", "1" },
+ { nullptr, nullptr },
+ };
+
+ // The `D3DCompile` entry point takes a bunch of parameters, but we
+ // don't really need most of them for Slang-generated code.
+ ComPtr<ID3DBlob> shaderBlob;
+ ComPtr<ID3DBlob> errorBlob;
+
+ HRESULT hr = compileFunc(source, strlen(source), sourcePath, &defines[0], nullptr, entryPointName, dxProfileName, flags, 0,
+ shaderBlob.writeRef(), errorBlob.writeRef());
+
+ // If the HLSL-to-bytecode compilation produced any diagnostic messages
+ // then we will print them out (whether or not the compilation failed).
+ if (errorBlob)
+ {
+ ::fputs((const char*)errorBlob->GetBufferPointer(), stderr);
+ ::fflush(stderr);
+ ::OutputDebugStringA((const char*)errorBlob->GetBufferPointer());
+ return SLANG_FAIL;
+ }
+
+ SLANG_RETURN_ON_FAIL(hr);
+ shaderBlobOut.swap(shaderBlob);
+ return SLANG_OK;
+}
+
+/* static */void D3DUtil::appendWideChars(const char* in, List<wchar_t>& out)
+{
+ size_t len = ::strlen(in);
+
+ const DWORD dwFlags = 0;
+ int outSize = ::MultiByteToWideChar(CP_UTF8, dwFlags, in, int(len), nullptr, 0);
+
+ if (outSize > 0)
+ {
+ const UInt prevSize = out.Count();
+ out.SetSize(prevSize + len + 1);
+
+ WCHAR* dst = out.Buffer() + prevSize;
+ ::MultiByteToWideChar(CP_UTF8, dwFlags, in, int(len), dst, outSize);
+ // Make null terminated
+ dst[outSize] = 0;
+ // Remove terminating 0 from array
+ out.UnsafeShrinkToSize(prevSize + outSize);
+ }
+}
+
+} // renderer_test
diff --git a/tools/render-test/d3d-util.h b/tools/render-test/d3d-util.h
new file mode 100644
index 000000000..95ccf4b18
--- /dev/null
+++ b/tools/render-test/d3d-util.h
@@ -0,0 +1,61 @@
+// d3d-util.h
+#pragma once
+
+#include <stdint.h>
+#include "../../source/core/slang-result.h"
+#include "../../source/core/slang-com-ptr.h"
+#include "../../source/core/list.h"
+
+#include "render.h"
+
+#include <D3Dcommon.h>
+#include <DXGIFormat.h>
+
+namespace renderer_test {
+
+class D3DUtil
+{
+ public:
+ enum UsageType
+ {
+ USAGE_UNKNOWN, ///< Generally used to mark an error
+ USAGE_TARGET, ///< Format should be used when written as target
+ USAGE_DEPTH_STENCIL, ///< Format should be used when written as depth stencil
+ USAGE_SRV, ///< Format if being read as srv
+ USAGE_COUNT_OF,
+ };
+ enum UsageFlag
+ {
+ USAGE_FLAG_MULTI_SAMPLE = 0x1,
+ USAGE_FLAG_SRV = 0x2,
+ };
+
+ /// Get primitive topology as D3D primitive topology
+ static D3D_PRIMITIVE_TOPOLOGY getPrimitiveTopology(PrimitiveTopology prim);
+
+ /// Calculate size taking into account alignment. Alignment must be a power of 2
+ static UInt calcAligned(UInt size, UInt alignment) { return (size + alignment - 1) & ~(alignment - 1); }
+
+ /// The Slang compiler currently generates HLSL source, so we'll need a utility
+ /// routine (defined later) to translate that into D3D11 shader bytecode.
+ /// Definition of the HLSL-to-bytecode compilation logic.
+ static Slang::Result compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, Slang::ComPtr<ID3DBlob>& shaderBlobOut);
+
+ static DXGI_FORMAT getMapFormat(Format format);
+
+
+ /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible
+ static DXGI_FORMAT calcFormat(UsageType usage, DXGI_FORMAT format);
+ /// Calculate appropriate format for creating a buffer for usage and flags
+ static DXGI_FORMAT calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format);
+ /// True if the type is 'typeless'
+ static bool isTypeless(DXGI_FORMAT format);
+
+ /// Returns number of bits used for color channel for format (for channels with multiple sizes, returns smallest ie RGB565 -> 5)
+ static Int getNumColorChannelBits(DXGI_FORMAT fmt);
+
+ /// Append text in in, into wide char array
+ static void appendWideChars(const char* in, Slang::List<wchar_t>& out);
+};
+
+} // renderer_test
diff --git a/tools/render-test/descriptor-heap-d3d12.cpp b/tools/render-test/descriptor-heap-d3d12.cpp
new file mode 100644
index 000000000..5bd238528
--- /dev/null
+++ b/tools/render-test/descriptor-heap-d3d12.cpp
@@ -0,0 +1,47 @@
+
+#include "descriptor-heap-d3d12.h"
+
+namespace renderer_test {
+using namespace Slang;
+
+D3D12DescriptorHeap::D3D12DescriptorHeap():
+ m_totalSize(0),
+ m_currentIndex(0),
+ m_descriptorSize(0)
+{
+}
+
+Result D3D12DescriptorHeap::init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags)
+{
+ D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
+ srvHeapDesc.NumDescriptors = size;
+ srvHeapDesc.Flags = flags;
+ srvHeapDesc.Type = type;
+ SLANG_RETURN_ON_FAIL(device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(m_heap.writeRef())));
+
+ m_descriptorSize = device->GetDescriptorHandleIncrementSize(type);
+ m_totalSize = size;
+
+ return SLANG_OK;
+}
+
+Result D3D12DescriptorHeap::init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags)
+{
+ SLANG_RETURN_ON_FAIL(init(device, numHandles, type, flags));
+ D3D12_CPU_DESCRIPTOR_HANDLE dst = m_heap->GetCPUDescriptorHandleForHeapStart();
+
+ // Copy them all
+ for (int i = 0; i < numHandles; i++, dst.ptr += m_descriptorSize)
+ {
+ D3D12_CPU_DESCRIPTOR_HANDLE src = handles[i];
+ if (src.ptr != 0)
+ {
+ device->CopyDescriptorsSimple(1, dst, src, type);
+ }
+ }
+
+ return SLANG_OK;
+}
+
+} // namespace renderer_test
+
diff --git a/tools/render-test/descriptor-heap-d3d12.h b/tools/render-test/descriptor-heap-d3d12.h
new file mode 100644
index 000000000..11fb40810
--- /dev/null
+++ b/tools/render-test/descriptor-heap-d3d12.h
@@ -0,0 +1,115 @@
+#pragma once
+
+
+#include <dxgi.h>
+#include <d3d12.h>
+
+#include "../../source/core/slang-com-ptr.h"
+
+namespace renderer_test {
+
+/*! \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<ID3D12DescriptorHeap> 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
+};
+
+// ---------------------------------------------------------------------------
+int D3D12DescriptorHeap::allocate()
+{
+ assert(m_currentIndex < m_totalSize);
+ if (m_currentIndex < m_totalSize)
+ {
+ return m_currentIndex++;
+ }
+ return -1;
+}
+// ---------------------------------------------------------------------------
+int D3D12DescriptorHeap::allocate(int numDescriptors)
+{
+ assert(m_currentIndex + numDescriptors <= m_totalSize);
+ if (m_currentIndex + numDescriptors <= m_totalSize)
+ {
+ const int index = m_currentIndex;
+ m_currentIndex += numDescriptors;
+ return index;
+ }
+ return -1;
+}
+// ---------------------------------------------------------------------------
+SLANG_FORCE_INLINE int D3D12DescriptorHeap::placeAt(int index)
+{
+ assert(index >= 0 && index < m_totalSize);
+ m_currentIndex = index + 1;
+ return index;
+}
+
+// ---------------------------------------------------------------------------
+SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getCpuHandle(int index) const
+{
+ assert(index >= 0 && index < m_totalSize);
+ D3D12_CPU_DESCRIPTOR_HANDLE start = m_heap->GetCPUDescriptorHandleForHeapStart();
+ D3D12_CPU_DESCRIPTOR_HANDLE dst;
+ dst.ptr = start.ptr + m_descriptorSize * index;
+ return dst;
+}
+// ---------------------------------------------------------------------------
+SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getGpuHandle(int index) const
+{
+ assert(index >= 0 && index < m_totalSize);
+ D3D12_GPU_DESCRIPTOR_HANDLE start = m_heap->GetGPUDescriptorHandleForHeapStart();
+ D3D12_GPU_DESCRIPTOR_HANDLE dst;
+ dst.ptr = start.ptr + m_descriptorSize * index;
+ return dst;
+}
+
+} // namespace renderer_test
+
diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp
index b333e57bd..6233b7617 100644
--- a/tools/render-test/main.cpp
+++ b/tools/render-test/main.cpp
@@ -3,6 +3,7 @@
#include "options.h"
#include "render.h"
#include "render-d3d11.h"
+#include "render-d3d12.h"
#include "render-gl.h"
#include "render-vk.h"
@@ -334,7 +335,11 @@ SlangResult innerMain(int argc, char** argv)
nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL;
break;
- // TODO: `RendererID::D3D12`
+ case RendererID::D3D12:
+ renderer = createD3D12Renderer();
+ slangTarget = SLANG_HLSL;
+ nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL;
+ break;
case RendererID::GL:
renderer = createGLRenderer();
@@ -412,6 +417,11 @@ SlangResult innerMain(int argc, char** argv)
// If we are in a mode where output is requested, we need to snapshot the back buffer here
if (gOptions.outputPath)
{
+ // Submit the work
+ renderer->submitGpuWork();
+ // Wait until everything is complete
+ renderer->waitForGpu();
+
if (gOptions.shaderType == ShaderProgramType::Compute || gOptions.shaderType == ShaderProgramType::GraphicsCompute)
renderer->serializeOutput(app.getBindingState(), gOptions.outputPath);
else
diff --git a/tools/render-test/render-d3d11.cpp b/tools/render-test/render-d3d11.cpp
index fb3d44f4d..e65b12b2c 100644
--- a/tools/render-test/render-d3d11.cpp
+++ b/tools/render-test/render-d3d11.cpp
@@ -3,6 +3,7 @@
#include "options.h"
#include "render.h"
+#include "d3d-util.h"
// In order to use the Slang API, we need to include its header
@@ -66,7 +67,9 @@ public:
virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer*const* buffers, const UInt* offsets) override;
virtual void draw(UInt vertexCount, UInt startVertex) override;
virtual void dispatchCompute(int x, int y, int z) override;
-
+ virtual void submitGpuWork() override {}
+ virtual void waitForGpu() override {}
+
// ShaderCompiler implementation
virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override;
@@ -111,22 +114,13 @@ public:
ComPtr<ID3D11InputLayout> m_layout;
};
- /// Calculate size taking into account alignment. Alignment must be a power of 2
- static UInt calcAligned(UInt size, UInt alignment) { return (size + alignment - 1) & ~(alignment - 1); }
-
- static DXGI_FORMAT getMapFormat(Format format);
-
- /// The Slang compiler currently generates HLSL source, so we'll need a utility
- /// routine (defined later) to translate that into D3D11 shader bytecode.
- /// Definition of the HLSL-to-bytecode compilation logic.
- static Result compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, ComPtr<ID3DBlob>& shaderBlobOut);
/// Capture a texture to a file
static HRESULT captureTextureToFile(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, char const* outputPath);
void* map(ID3D11Buffer* buffer, MapFlavor flavor);
void unmap(ID3D11Buffer* buffer);
- Result createInputBuffer(InputBufferDesc& bufferDesc, const List<unsigned int>& bufferData,
+ Result createInputBuffer(const InputBufferDesc& bufferDesc, const List<unsigned int>& bufferData,
ComPtr<ID3D11Buffer>& bufferOut, ComPtr<ID3D11UnorderedAccessView>& viewOut, ComPtr<ID3D11ShaderResourceView>& srvOut);
Result createInputTexture(const InputTextureDesc& inputDesc, ComPtr<ID3D11ShaderResourceView>& viewOut);
@@ -153,84 +147,6 @@ Renderer* createD3D11Renderer()
return new D3D11Renderer();
}
-/* static */DXGI_FORMAT D3D11Renderer::getMapFormat(Format format)
-{
- switch (format)
- {
- case Format::RGB_Float32:
- return DXGI_FORMAT_R32G32B32_FLOAT;
- case Format::RG_Float32:
- return DXGI_FORMAT_R32G32_FLOAT;
- default:
- return DXGI_FORMAT_UNKNOWN;
- }
-}
-
-/* static */Result D3D11Renderer::compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, ComPtr<ID3DBlob>& shaderBlobOut)
-{
- // Rather than statically link against the `d3dcompile` library, we
- // dynamically load it.
- //
- // Note: A more realistic application would compile from HLSL text to D3D
- // shader bytecode as part of an offline process, rather than doing it
- // on-the-fly like this
- //
- static pD3DCompile compileFunc = nullptr;
- if (!compileFunc)
- {
- // TODO(tfoley): maybe want to search for one of a few versions of the DLL
- HMODULE compilerModule = LoadLibraryA("d3dcompiler_47.dll");
- if (!compilerModule)
- {
- fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n");
- return SLANG_FAIL;
- }
-
- compileFunc = (pD3DCompile)GetProcAddress(compilerModule, "D3DCompile");
- if (!compileFunc)
- {
- fprintf(stderr, "error: failed load symbol 'D3DCompile'\n");
- return SLANG_FAIL;
- }
- }
-
- // For this example, we turn on debug output, and turn off all
- // optimization. A real application would only use these flags
- // when shader debugging is needed.
- UINT flags = 0;
- flags |= D3DCOMPILE_DEBUG;
- flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION;
-
- // We will always define `__HLSL__` when compiling here, so that
- // input code can react differently to being compiled as pure HLSL.
- D3D_SHADER_MACRO defines[] = {
- { "__HLSL__", "1" },
- { nullptr, nullptr },
- };
-
- // The `D3DCompile` entry point takes a bunch of parameters, but we
- // don't really need most of them for Slang-generated code.
- ComPtr<ID3DBlob> shaderBlob;
- ComPtr<ID3DBlob> errorBlob;
-
- HRESULT hr = compileFunc(source, strlen(source), sourcePath, &defines[0], nullptr, entryPointName, dxProfileName, flags, 0,
- shaderBlob.writeRef(), errorBlob.writeRef());
-
- // If the HLSL-to-bytecode compilation produced any diagnostic messages
- // then we will print them out (whether or not the compilation failed).
- if (errorBlob)
- {
- ::fputs((const char*)errorBlob->GetBufferPointer(), stderr);
- ::fflush(stderr);
- ::OutputDebugStringA((const char*)errorBlob->GetBufferPointer());
- return SLANG_FAIL;
- }
-
- SLANG_RETURN_ON_FAIL(hr);
- shaderBlobOut.swap(shaderBlob);
- return SLANG_OK;
-}
-
/* static */HRESULT D3D11Renderer::captureTextureToFile(ID3D11Device* device, ID3D11DeviceContext* context,
ID3D11Texture2D* texture, char const* outputPath)
{
@@ -474,7 +390,7 @@ ShaderCompiler* D3D11Renderer::getShaderCompiler()
Buffer* D3D11Renderer::createBuffer(const BufferDesc& desc)
{
D3D11_BUFFER_DESC bufferDesc = { 0 };
- bufferDesc.ByteWidth = (UINT)calcAligned(desc.size, 256);
+ bufferDesc.ByteWidth = (UINT)D3DUtil::calcAligned(desc.size, 256);
switch (desc.flavor)
{
@@ -517,7 +433,7 @@ InputLayout* D3D11Renderer::createInputLayout(const InputElementDesc* inputEleme
{
inputElements[ii].SemanticName = inputElementsIn[ii].semanticName;
inputElements[ii].SemanticIndex = (UINT)inputElementsIn[ii].semanticIndex;
- inputElements[ii].Format = getMapFormat(inputElementsIn[ii].format);
+ inputElements[ii].Format = D3DUtil::getMapFormat(inputElementsIn[ii].format);
inputElements[ii].InputSlot = 0;
inputElements[ii].AlignedByteOffset = (UINT)inputElementsIn[ii].offset;
inputElements[ii].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
@@ -551,10 +467,10 @@ InputLayout* D3D11Renderer::createInputLayout(const InputElementDesc* inputEleme
hlslCursor += sprintf(hlslCursor, "\n) : SV_Position { return 0; }");
ComPtr<ID3DBlob> vertexShaderBlob;
- SLANG_RETURN_NULL_ON_FAIL(compileHLSLShader("inputLayout", hlslBuffer, "main", "vs_5_0", vertexShaderBlob));
+ SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader("inputLayout", hlslBuffer, "main", "vs_5_0", vertexShaderBlob));
ComPtr<ID3D11InputLayout> inputLayout;
- SLANG_RETURN_NULL_ON_FAIL(m_device->CreateInputLayout(&inputElements[0], (UINT)inputElementCount, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(),
+ SLANG_RETURN_NULL_ON_FAIL(m_device->CreateInputLayout(&inputElements[0], (UINT)inputElementCount, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(),
inputLayout.writeRef()));
InputLayoutImpl* impl = new InputLayoutImpl;
@@ -613,18 +529,7 @@ void D3D11Renderer::setInputLayout(InputLayout* inputLayoutIn)
void D3D11Renderer::setPrimitiveTopology(PrimitiveTopology topology)
{
- D3D11_PRIMITIVE_TOPOLOGY primTopology;
- switch (topology)
- {
- case PrimitiveTopology::TriangleList:
- primTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
- break;
-
- default:
- return;
- }
-
- m_immediateContext->IASetPrimitiveTopology(primTopology);
+ m_immediateContext->IASetPrimitiveTopology(D3DUtil::getPrimitiveTopology(topology));
}
void D3D11Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, Buffer*const* buffersIn, const UInt* stridesIn, const UInt* offsetsIn)
@@ -687,7 +592,7 @@ ShaderProgram* D3D11Renderer::compileProgram(const ShaderCompileRequest& request
if (request.computeShader.name)
{
ComPtr<ID3DBlob> computeShaderBlob;
- SLANG_RETURN_NULL_ON_FAIL(compileHLSLShader(request.computeShader.source.path, request.computeShader.source.dataBegin, request.computeShader.name, request.computeShader.profile, computeShaderBlob));
+ SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader(request.computeShader.source.path, request.computeShader.source.dataBegin, request.computeShader.name, request.computeShader.profile, computeShaderBlob));
ComPtr<ID3D11ComputeShader> computeShader;
SLANG_RETURN_NULL_ON_FAIL(m_device->CreateComputeShader(computeShaderBlob->GetBufferPointer(), computeShaderBlob->GetBufferSize(), nullptr, computeShader.writeRef()));
@@ -699,8 +604,8 @@ ShaderProgram* D3D11Renderer::compileProgram(const ShaderCompileRequest& request
else
{
ComPtr<ID3DBlob> vertexShaderBlob, fragmentShaderBlob;
- SLANG_RETURN_NULL_ON_FAIL(compileHLSLShader(request.vertexShader.source.path, request.vertexShader.source.dataBegin, request.vertexShader.name, request.vertexShader.profile, vertexShaderBlob));
- SLANG_RETURN_NULL_ON_FAIL(compileHLSLShader(request.fragmentShader.source.path, request.fragmentShader.source.dataBegin, request.fragmentShader.name, request.fragmentShader.profile, fragmentShaderBlob));
+ SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader(request.vertexShader.source.path, request.vertexShader.source.dataBegin, request.vertexShader.name, request.vertexShader.profile, vertexShaderBlob));
+ SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader(request.fragmentShader.source.path, request.fragmentShader.source.dataBegin, request.fragmentShader.name, request.fragmentShader.profile, fragmentShaderBlob));
ComPtr<ID3D11VertexShader> vertexShader;
ComPtr<ID3D11PixelShader> pixelShader;
@@ -721,12 +626,12 @@ void D3D11Renderer::dispatchCompute(int x, int y, int z)
m_immediateContext->Dispatch(x, y, z);
}
-Result D3D11Renderer::createInputBuffer(InputBufferDesc& bufferDesc, const List<unsigned int>& bufferData,
+Result D3D11Renderer::createInputBuffer(const InputBufferDesc& bufferDesc, const List<unsigned int>& bufferData,
ComPtr<ID3D11Buffer>& bufferOut, ComPtr<ID3D11UnorderedAccessView>& viewOut, ComPtr<ID3D11ShaderResourceView>& srvOut)
{
D3D11_BUFFER_DESC desc = { 0 };
List<unsigned int> newBuffer;
- desc.ByteWidth = (UINT)calcAligned((bufferData.Count() * sizeof(unsigned int)), 256);
+ desc.ByteWidth = (UINT)D3DUtil::calcAligned((bufferData.Count() * sizeof(unsigned int)), 256);
newBuffer.SetSize(desc.ByteWidth / sizeof(unsigned int));
for (UInt i = 0; i < bufferData.Count(); i++)
newBuffer[i] = bufferData[i];
@@ -939,32 +844,42 @@ Result D3D11Renderer::createInputSampler(const InputSamplerDesc& inputDesc, ComP
BindingState* D3D11Renderer::createBindingState(const ShaderInputLayout& layout)
{
- List<Binding> bindings;
+ RefPtr<BindingStateImpl> bindingState(new BindingStateImpl);
+
+ const List<ShaderInputLayoutEntry>& srcBindings = layout.entries;
+ const int numBindings = int(srcBindings.Count());
- for (auto & entry : layout.entries)
+ List<Binding>& dstBindings = bindingState->m_bindings;
+ dstBindings.SetSize(numBindings);
+
+ bindingState->m_numRenderTargets = layout.numRenderTargets;
+
+ for (int i = 0; i < numBindings; ++i)
{
- Binding rsEntry;
- rsEntry.type = entry.type;
- rsEntry.binding = entry.hlslBinding;
- rsEntry.isOutput = entry.isOutput;
- switch (entry.type)
+ Binding& dstBinding = dstBindings[i];
+ const ShaderInputLayoutEntry& srcBinding = srcBindings[i];
+
+ dstBinding.type = srcBinding.type;
+ dstBinding.binding = srcBinding.hlslBinding;
+ dstBinding.isOutput = srcBinding.isOutput;
+ switch (srcBinding.type)
{
case ShaderInputType::Buffer:
{
- SLANG_RETURN_NULL_ON_FAIL(createInputBuffer(entry.bufferDesc, entry.bufferData, rsEntry.buffer, rsEntry.uav, rsEntry.srv));
+ SLANG_RETURN_NULL_ON_FAIL(createInputBuffer(srcBinding.bufferDesc, srcBinding.bufferData, dstBinding.buffer, dstBinding.uav, dstBinding.srv));
- rsEntry.bufferLength = (int)(entry.bufferData.Count() * sizeof(unsigned int));
- rsEntry.bufferType = entry.bufferDesc.type;
+ dstBinding.bufferLength = (int)(srcBinding.bufferData.Count() * sizeof(unsigned int));
+ dstBinding.bufferType = srcBinding.bufferDesc.type;
break;
}
case ShaderInputType::Texture:
{
- SLANG_RETURN_NULL_ON_FAIL(createInputTexture(entry.textureDesc, rsEntry.srv));
+ SLANG_RETURN_NULL_ON_FAIL(createInputTexture(srcBinding.textureDesc, dstBinding.srv));
break;
}
case ShaderInputType::Sampler:
{
- SLANG_RETURN_NULL_ON_FAIL(createInputSampler(entry.samplerDesc, rsEntry.samplerState));
+ SLANG_RETURN_NULL_ON_FAIL(createInputSampler(srcBinding.samplerDesc, dstBinding.samplerState));
break;
}
case ShaderInputType::CombinedTextureSampler:
@@ -975,14 +890,9 @@ BindingState* D3D11Renderer::createBindingState(const ShaderInputLayout& layout)
break;
}
}
- bindings.Add(rsEntry);
}
- BindingStateImpl* rs = new BindingStateImpl;
- rs->m_numRenderTargets = layout.numRenderTargets;
- rs->m_bindings.SwapWith(bindings);
-
- return rs;
+ return bindingState.detach();
}
void D3D11Renderer::applyBindingState(bool isCompute)
@@ -1079,7 +989,7 @@ void D3D11Renderer::serializeOutput(BindingState* stateIn, const char* fileName)
D3D11_BUFFER_DESC bufDesc;
memset(&bufDesc, 0, sizeof(bufDesc));
bufDesc.BindFlags = 0;
- bufDesc.ByteWidth = (UINT)calcAligned(binding.bufferLength, 256);
+ bufDesc.ByteWidth = (UINT)D3DUtil::calcAligned(binding.bufferLength, 256);
bufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
bufDesc.Usage = D3D11_USAGE_STAGING;
diff --git a/tools/render-test/render-d3d12.cpp b/tools/render-test/render-d3d12.cpp
index ed0ae93cb..50ef6e575 100644
--- a/tools/render-test/render-d3d12.cpp
+++ b/tools/render-test/render-d3d12.cpp
@@ -21,6 +21,21 @@
#include <d3d12.h>
#include <d3dcompiler.h>
+#include "../../source/core/slang-com-ptr.h"
+
+#include "resource-d3d12.h"
+#include "descriptor-heap-d3d12.h"
+#include "circular-resource-heap-d3d12.h"
+
+#include "d3d-util.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4996)
+#endif
+
+//#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "external/stb/stb_image_write.h"
+
// We will use the C standard library just for printing error messages.
#include <stdio.h>
@@ -31,25 +46,12 @@
#endif
#endif
//
-using namespace Slang;
#define ENABLE_DEBUG_LAYER 1
namespace renderer_test {
+using namespace Slang;
-// The Slang compiler currently generates HLSL source, so we'll need a utility
-// routine (defined later) to translate that into D3D11 shader bytecode.
-// Returns nullptr if compilation fails.
-/* ID3DBlob* compileHLSLShader(
- char const* sourcePath,
- char const* source,
- char const* entryPointName,
- char const* dxProfileName); */
-
-//static char const* vertexProfileName = "vs_4_0";
-//static char const* fragmentProfileName = "ps_4_0";
-
-//
class D3D12Renderer : public Renderer, public ShaderCompiler
{
public:
@@ -62,7 +64,7 @@ public:
virtual void serializeOutput(BindingState* state, const char* fileName) override;
virtual Buffer* createBuffer(const BufferDesc& desc) override;
virtual InputLayout* createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) override;
- virtual BindingState * createBindingState(const ShaderInputLayout& layout) override;
+ virtual BindingState* createBindingState(const ShaderInputLayout& layout) override;
virtual ShaderCompiler* getShaderCompiler() override;
virtual void* map(Buffer* buffer, MapFlavor flavor) override;
virtual void unmap(Buffer* buffer) override;
@@ -74,17 +76,310 @@ public:
virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer*const* buffers, const UInt* offsets) override;
virtual void draw(UInt vertexCount, UInt startVertex) override;
virtual void dispatchCompute(int x, int y, int z) override;
+ virtual void submitGpuWork() override;
+ virtual void waitForGpu() override;
// ShaderCompiler implementation
virtual ShaderProgram* compileProgram(const ShaderCompileRequest& request) override;
- protected:
- PROC loadProc(HMODULE module, char const* name);
- static DXGI_FORMAT mapFormat(Format format);
+ ~D3D12Renderer();
+
+protected:
+ static const Int kMaxNumRenderFrames = 4;
+ static const Int kMaxNumRenderTargets = 3;
+
+ enum class ProgramType
+ {
+ kCompute,
+ kGraphics,
+ };
+
+ struct FrameInfo
+ {
+ FrameInfo() :m_fenceValue(0) {}
+ void reset()
+ {
+ m_commandAllocator.setNull();
+ }
+ ComPtr<ID3D12CommandAllocator> m_commandAllocator; ///< The command allocator for this frame
+ UINT64 m_fenceValue; ///< The fence value when rendering this Frame is complete
+ };
+
+ class ShaderProgramImpl: public ShaderProgram
+ {
+ public:
+ ProgramType m_programType;
+ List<uint8_t> m_vertexShader;
+ List<uint8_t> m_pixelShader;
+ List<uint8_t> m_computeShader;
+ };
+ class BufferImpl: public Buffer
+ {
+ public:
+ BufferImpl(const BufferDesc& desc):
+ m_desc(desc),
+ m_mapFlavor(MapFlavor::HostRead)
+ {
+ }
+
+ D3D12Resource m_resource;
+ D3D12Resource m_uploadResource;
+
+ BufferDesc m_desc;
+ List<uint8_t> m_memory;
+ MapFlavor m_mapFlavor;
+ };
+ class InputLayoutImpl: public InputLayout
+ {
+ public:
+ List<D3D12_INPUT_ELEMENT_DESC> m_elements;
+ List<char> m_text; ///< Holds all strings to keep in scope
+ };
+
+ struct Binding
+ {
+ ShaderInputType m_type;
+ InputBufferType m_bufferType; // Only valid if `type` is `Buffer`
+ int m_srvIndex = -1;
+ int m_uavIndex = -1;
+ int m_samplerIndex = -1;
+
+ D3D12Resource m_resource;
+
+ int m_binding = 0;
+ bool m_isOutput = false;
+ int m_bufferLength = 0;
+ };
+
+ class BindingStateImpl: public BindingState
+ {
+ public:
+
+ Result init(ID3D12Device* device)
+ {
+ // Set up descriptor heaps
+ SLANG_RETURN_ON_FAIL(m_viewHeap.init(device, 256, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE));
+ SLANG_RETURN_ON_FAIL(m_samplerHeap.init(device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE));
+ return SLANG_OK;
+ }
+
+ List<Binding> m_bindings;
+ int m_numRenderTargets = 0;
+
+ D3D12DescriptorHeap m_viewHeap; ///< Cbv, Srv, Uav
+ D3D12DescriptorHeap m_samplerHeap; ///< Heap for samplers
+ };
+
+ class RenderState: public RefObject
+ {
+ public:
+ D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType;
+ RefPtr<BindingStateImpl> m_bindingState;
+ RefPtr<InputLayoutImpl> m_inputLayout;
+ RefPtr<ShaderProgramImpl> m_shaderProgram;
+
+ ComPtr<ID3D12RootSignature> m_rootSignature;
+ ComPtr<ID3D12PipelineState> m_pipelineState;
+ };
+
+ struct BoundVertexBuffer
+ {
+ RefPtr<BufferImpl> m_buffer;
+ int m_stride;
+ int m_offset;
+ };
+
+ struct Submitter
+ {
+ virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0;
+ virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor) = 0;
+ virtual void setRootSigniture(ID3D12RootSignature* rootSignature) = 0;
+ };
+
+ struct BindParameters
+ {
+ enum
+ {
+ kMaxRanges = 16,
+ kMaxParameters = 32
+ };
+
+ D3D12_DESCRIPTOR_RANGE& nextRange() { return m_ranges[m_rangeIndex++]; }
+ D3D12_ROOT_PARAMETER& nextParameter() { return m_parameters[m_paramIndex++]; }
+
+ BindParameters():
+ m_rangeIndex(0),
+ m_paramIndex(0)
+ {}
+
+ D3D12_DESCRIPTOR_RANGE m_ranges[kMaxRanges];
+ int m_rangeIndex;
+ D3D12_ROOT_PARAMETER m_parameters[kMaxParameters];
+ int m_paramIndex;
+ };
+
+ struct GraphicsSubmitter : public Submitter
+ {
+ virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override
+ {
+ m_commandList->SetGraphicsRootConstantBufferView(index, gpuBufferLocation);
+ }
+ virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override
+ {
+ m_commandList->SetGraphicsRootDescriptorTable(index, baseDescriptor);
+ }
+ void setRootSigniture(ID3D12RootSignature* rootSignature)
+ {
+ m_commandList->SetGraphicsRootSignature(rootSignature);
+ }
+
+ GraphicsSubmitter(ID3D12GraphicsCommandList* commandList):
+ m_commandList(commandList)
+ {
+ }
+
+ ID3D12GraphicsCommandList* m_commandList;
+ };
+
+ struct ComputeSubmitter : public Submitter
+ {
+ virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override
+ {
+ m_commandList->SetComputeRootConstantBufferView(index, gpuBufferLocation);
+ }
+ virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override
+ {
+ m_commandList->SetComputeRootDescriptorTable(index, baseDescriptor);
+ }
+ void setRootSigniture(ID3D12RootSignature* rootSignature)
+ {
+ m_commandList->SetComputeRootSignature(rootSignature);
+ }
+
+ ComputeSubmitter(ID3D12GraphicsCommandList* commandList) :
+ m_commandList(commandList)
+ {
+ }
+
+ ID3D12GraphicsCommandList* m_commandList;
+ };
+
+ static PROC loadProc(HMODULE module, char const* name);
+ Result createFrameResources();
+ /// Blocks until gpu has completed all work
+ void releaseFrameResources();
+
+ Result createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut);
+ Result createTexture(const InputTextureDesc& inputDesc, const TextureData& texData, D3D12Resource& resourceOut);
+
+ Result createInputSampler(const InputSamplerDesc& inputDesc, D3D12DescriptorHeap& samplerHeap, int samplerIndex);
+ Result createInputTexture(const InputTextureDesc& inputDesc, D3D12DescriptorHeap& viewHeap, int srvIndex, D3D12Resource& resourceOut);
+ Result createInputBuffer(InputBufferDesc& bufferDesc, const List<unsigned int>& bufferData, D3D12DescriptorHeap& viewHeap, int uavIndex, int srvIndex,
+ D3D12Resource& resourceOut);
+
+ void beginGpuWork();
+
+ void beginRender();
+
+ void endRender();
+
+ void submitGpuWorkAndWait();
+
+ Result captureTextureToFile(D3D12Resource& resource, const char* outputPath);
+
+ FrameInfo& getFrame() { return m_frameInfos[m_frameIndex]; }
+ const FrameInfo& getFrame() const { return m_frameInfos[m_frameIndex]; }
+
+ ID3D12GraphicsCommandList* getCommandList() const { return m_commandList; }
+
+ RenderState* calcRenderState();
+ /// From current bindings calculate the root signature and pipeline state
+ Result calcGraphicsPipelineState(ComPtr<ID3D12RootSignature>& sigOut, ComPtr<ID3D12PipelineState>& pipelineStateOut);
+ Result calcComputePipelineState(ComPtr<ID3D12RootSignature>& signatureOut, ComPtr<ID3D12PipelineState>& pipelineStateOut);
+
+ Result _bindRenderState(RenderState* renderState, ID3D12GraphicsCommandList* commandList, Submitter* submitter);
+
+ Result _calcBindParameters(BindParameters& params);
+ RenderState* findRenderState(ProgramType programType);
+
+ PFN_D3D12_SERIALIZE_ROOT_SIGNATURE m_D3D12SerializeRootSignature = nullptr;
+
+ D3D12CircularResourceHeap m_circularResourceHeap;
+
+ int m_commandListOpenCount = 0; ///< If >0 the command list should be open
+
+ List<BoundVertexBuffer> m_boundVertexBuffers;
+ List<RefPtr<BufferImpl> > m_boundConstantBuffers;
+
+ RefPtr<ShaderProgramImpl> m_boundShaderProgram;
+ RefPtr<InputLayoutImpl> m_boundInputLayout;
+ RefPtr<BindingStateImpl> m_boundBindingState;
+
+ DXGI_FORMAT m_targetFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
+ DXGI_FORMAT m_depthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
+ bool m_hasVsync = true;
+ bool m_isFullSpeed = false;
+ bool m_allowFullScreen = false;
+ bool m_isMultiSampled = false;
+ int m_numTargetSamples = 1; ///< The number of multi sample samples
+ int m_targetSampleQuality = 0; ///< The multi sample quality
+
+ int m_windowWidth = 0;
+ int m_windowHeight = 0;
+
+ bool m_isInitialized = false;
+
+ D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+ D3D12_PRIMITIVE_TOPOLOGY m_primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
float m_clearColor[4] = { 0, 0, 0, 0 };
- IDXGISwapChain* m_swapChain = nullptr;
- ID3D12Device* m_device = nullptr;
+
+ D3D12_VIEWPORT m_viewport = {};
+
+ ComPtr<ID3D12Debug> m_dxDebug;
+
+ ComPtr<ID3D12Device> m_device;
+ ComPtr<IDXGISwapChain3> m_swapChain;
+ ComPtr<ID3D12CommandQueue> m_commandQueue;
+ ComPtr<ID3D12DescriptorHeap> m_rtvHeap;
+ ComPtr<ID3D12GraphicsCommandList> m_commandList;
+
+ D3D12_RECT m_scissorRect = {};
+
+ List<RefPtr<RenderState> > m_renderStates; ///< Holds list of all render state combinations
+ RenderState* m_currentRenderState = nullptr; ///< The current combination
+
+ UINT m_rtvDescriptorSize = 0;
+
+ ComPtr<ID3D12DescriptorHeap> m_dsvHeap;
+ UINT m_dsvDescriptorSize = 0;
+
+ // Synchronization objects.
+ D3D12CounterFence m_fence;
+
+ HANDLE m_swapChainWaitableObject;
+
+ // Frame specific data
+ int m_numRenderFrames = 0;
+ UINT m_frameIndex = 0;
+ FrameInfo m_frameInfos[kMaxNumRenderFrames];
+
+ int m_numRenderTargets = 2;
+ int m_renderTargetIndex = 0;
+
+ D3D12Resource* m_backBuffers[kMaxNumRenderTargets];
+ D3D12Resource* m_renderTargets[kMaxNumRenderTargets];
+
+ D3D12Resource m_backBufferResources[kMaxNumRenderTargets];
+ D3D12Resource m_renderTargetResources[kMaxNumRenderTargets];
+
+ D3D12Resource m_depthStencil;
+ D3D12_CPU_DESCRIPTOR_HANDLE m_depthStencilView = {};
+
+ int32_t m_depthStencilUsageFlags = 0; ///< D3DUtil::UsageFlag combination for depth stencil
+ int32_t m_targetUsageFlags = 0; ///< D3DUtil::UsageFlag combination for target
+
+ HWND m_hwnd = nullptr;
};
Renderer* createD3D12Renderer()
@@ -92,7 +387,7 @@ Renderer* createD3D12Renderer()
return new D3D12Renderer;
}
-PROC D3D12Renderer::loadProc(HMODULE module, char const* name)
+/* static */PROC D3D12Renderer::loadProc(HMODULE module, char const* name)
{
PROC proc = ::GetProcAddress(module, name);
if (!proc)
@@ -103,24 +398,1106 @@ PROC D3D12Renderer::loadProc(HMODULE module, char const* name)
return proc;
}
-/* static */DXGI_FORMAT D3D12Renderer::mapFormat(Format format)
+void D3D12Renderer::releaseFrameResources()
{
- switch (format)
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/bb174577%28v=vs.85%29.aspx
+
+ // Release the resources holding references to the swap chain (requirement of
+ // IDXGISwapChain::ResizeBuffers) and reset the frame fence values to the
+ // current fence value.
+ for (int i = 0; i < m_numRenderFrames; i++)
{
- case Format::RGB_Float32:
- return DXGI_FORMAT_R32G32B32_FLOAT;
- case Format::RG_Float32:
- return DXGI_FORMAT_R32G32_FLOAT;
- default:
- return DXGI_FORMAT_UNKNOWN;
+ FrameInfo& info = m_frameInfos[i];
+ info.reset();
+ info.m_fenceValue = m_fence.getCurrentValue();
+ }
+ for (int i = 0; i < m_numRenderTargets; i++)
+ {
+ m_backBuffers[i]->setResourceNull();
+ m_renderTargets[i]->setResourceNull();
+ }
+}
+
+void D3D12Renderer::waitForGpu()
+{
+ m_fence.nextSignalAndWait(m_commandQueue);
+}
+
+D3D12Renderer::~D3D12Renderer()
+{
+ if (m_isInitialized)
+ {
+ // Ensure that the GPU is no longer referencing resources that are about to be
+ // cleaned up by the destructor.
+ waitForGpu();
+ }
+}
+
+Result D3D12Renderer::createInputSampler(const InputSamplerDesc& inputDesc, D3D12DescriptorHeap& samplerHeap, int samplerIndex)
+{
+ D3D12_SAMPLER_DESC desc = {};
+ desc.AddressU = desc.AddressV = desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
+ desc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
+
+ if (inputDesc.isCompareSampler)
+ {
+ desc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
+ desc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT;
+ }
+ else
+ {
+ desc.Filter = D3D12_FILTER_ANISOTROPIC;
+ desc.MaxAnisotropy = 8;
+ desc.MinLOD = 0.0f;
+ desc.MaxLOD = 100.0f;
+ }
+
+ m_device->CreateSampler(&desc, samplerHeap.getCpuHandle(samplerIndex));
+ return SLANG_OK;
+}
+
+static void _initSrvDesc(const D3D12_RESOURCE_DESC& desc, DXGI_FORMAT pixelFormat, D3D12_SHADER_RESOURCE_VIEW_DESC& descOut)
+{
+ // create SRV
+ descOut = D3D12_SHADER_RESOURCE_VIEW_DESC();
+
+ descOut.Format = (pixelFormat == DXGI_FORMAT_UNKNOWN) ? D3DUtil::calcFormat(D3DUtil::USAGE_SRV, desc.Format) : pixelFormat;
+ descOut.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ if (desc.DepthOrArraySize == 1)
+ {
+ switch (desc.Dimension)
+ {
+ case D3D12_RESOURCE_DIMENSION_TEXTURE1D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; break;
+ case D3D12_RESOURCE_DIMENSION_TEXTURE2D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; break;
+ case D3D12_RESOURCE_DIMENSION_TEXTURE3D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; break;
+ default: assert(!"Unknown dimension");
+ }
+
+ descOut.Texture2D.MipLevels = desc.MipLevels;
+ descOut.Texture2D.MostDetailedMip = 0;
+ descOut.Texture2D.PlaneSlice = 0;
+ descOut.Texture2D.ResourceMinLODClamp = 0.0f;
+ }
+ else if (desc.DepthOrArraySize == 6)
+ {
+ descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
+
+ descOut.TextureCube.MipLevels = desc.MipLevels;
+ descOut.TextureCube.MostDetailedMip = 0;
+ descOut.TextureCube.ResourceMinLODClamp = 0;
+ }
+ else
+ {
+ descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
+
+ descOut.Texture2DArray.ArraySize = desc.DepthOrArraySize;
+ descOut.Texture2DArray.MostDetailedMip = 0;
+ descOut.Texture2DArray.MipLevels = desc.MipLevels;
+ descOut.Texture2DArray.FirstArraySlice = 0;
+ descOut.Texture2DArray.PlaneSlice = 0;
+ descOut.Texture2DArray.ResourceMinLODClamp = 0;
+ }
+}
+
+void _initBufferResourceDesc(size_t bufferSize, D3D12_RESOURCE_DESC& out)
+{
+ out = {};
+
+ out.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ out.Alignment = 0;
+ out.Width = bufferSize;
+ out.Height = 1;
+ out.DepthOrArraySize = 1;
+ out.MipLevels = 1;
+ out.Format = DXGI_FORMAT_UNKNOWN;
+ out.SampleDesc.Count = 1;
+ out.SampleDesc.Quality = 0;
+ out.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+ out.Flags = D3D12_RESOURCE_FLAG_NONE;
+}
+
+Result D3D12Renderer::createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut)
+{
+ const size_t bufferSize = size_t(resourceDesc.Width);
+
+ {
+ D3D12_HEAP_PROPERTIES heapProps;
+ heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
+ heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heapProps.CreationNodeMask = 1;
+ heapProps.VisibleNodeMask = 1;
+
+ const D3D12_RESOURCE_STATES initialState = srcData ? D3D12_RESOURCE_STATE_COPY_DEST : finalState;
+
+ SLANG_RETURN_ON_FAIL(resourceOut.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, initialState, nullptr));
+ }
+
+ {
+ D3D12_HEAP_PROPERTIES heapProps;
+ heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
+ heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heapProps.CreationNodeMask = 1;
+ heapProps.VisibleNodeMask = 1;
+
+ SLANG_RETURN_ON_FAIL(uploadResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr));
+ }
+
+ if (srcData)
+ {
+ // Copy data to the intermediate upload heap and then schedule a copy
+ // from the upload heap to the vertex buffer.
+ UINT8* dstData;
+ D3D12_RANGE readRange = {}; // We do not intend to read from this resource on the CPU.
+
+ ID3D12Resource* dxUploadResource = uploadResource.getResource();
+
+ SLANG_RETURN_ON_FAIL(dxUploadResource->Map(0, &readRange, reinterpret_cast<void**>(&dstData)));
+ ::memcpy(dstData, srcData, bufferSize);
+ dxUploadResource->Unmap(0, nullptr);
+
+ m_commandList->CopyBufferRegion(resourceOut, 0, uploadResource, 0, bufferSize);
+
+ // Make sure it's in the right state
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ resourceOut.transition(finalState, submitter);
+ }
+
+ submitGpuWorkAndWait();
+ }
+
+ return SLANG_OK;
+}
+
+Result D3D12Renderer::createTexture(const InputTextureDesc& inputDesc, const TextureData& texData, D3D12Resource& resourceOut)
+{
+ // generateTextureData(texData, inputDesc);
+
+ const DXGI_FORMAT pixelFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
+ const int numMipMaps = texData.mipLevels;
+
+ const int width = inputDesc.size;
+ const int height = width;
+
+ // Setup desc
+ D3D12_RESOURCE_DESC resourceDesc;
+
+ resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ resourceDesc.Format = pixelFormat;
+ resourceDesc.Width = width;
+ resourceDesc.Height = height;
+ resourceDesc.DepthOrArraySize = texData.arraySize;
+ resourceDesc.MipLevels = numMipMaps;
+ resourceDesc.SampleDesc.Count = 1;
+ resourceDesc.SampleDesc.Quality = 0;
+ resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
+ resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ resourceDesc.Alignment = 0;
+
+ switch (inputDesc.dimension)
+ {
+ case 1: resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE1D; break;
+ case 2: resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; break;
+ case 3: resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D; break;
+ default: return SLANG_FAIL;
+ }
+
+ // Calculate the layout
+ List<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> layouts;
+ layouts.SetSize(numMipMaps);
+ List<UInt64> mipRowSizeInBytes;
+ mipRowSizeInBytes.SetSize(numMipMaps);
+ List<UInt32> mipNumRows;
+ mipNumRows.SetSize(numMipMaps);
+
+ UInt64 requiredSize = 0;
+ m_device->GetCopyableFootprints(&resourceDesc, 0, texData.mipLevels, 0, layouts.begin(), mipNumRows.begin(), mipRowSizeInBytes.begin(), &requiredSize);
+
+ List<D3D12_SUBRESOURCE_DATA> subData;
+ subData.SetSize(numMipMaps);
+ // Zero it all initially
+ ::memset(subData.Buffer(), 0, numMipMaps * sizeof(D3D12_SUBRESOURCE_DATA));
+
+ // 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");
+ }
+
+ // Map it all
+ {
+ ID3D12Resource* uploadResource = uploadTexture;
+
+ uint8_t* p;
+ uploadResource->Map(0, nullptr, reinterpret_cast<void**>(&p));
+
+ // Strictly speaking it should be bigger
+ assert(texData.dataBuffer.Count() == numMipMaps);
+
+ for (int i = 0; i < texData.arraySize; i++)
+ {
+ for (int j = 0; j < numMipMaps; ++j)
+ {
+ int size = texData.textureSize >> j;
+
+ const size_t mipSizeInBytes = layouts[j].Footprint.RowPitch * mipNumRows[j];
+
+ // NOTE! Like the D3D11 implementation -> this repeats the same mip pixels to every target
+
+ const uint8_t* srcPixels = (const uint8_t*)texData.dataBuffer[j].Buffer();
+ assert(mipSizeInBytes == texData.dataBuffer[j].Count() * sizeof(uint32_t));
+
+ ::memcpy(p + layouts[j].Offset, srcPixels, mipSizeInBytes);
+ }
+ }
+ uploadResource->Unmap(0, nullptr);
+ }
+
+ {
+ D3D12_HEAP_PROPERTIES heapProps;
+
+ heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
+ heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heapProps.CreationNodeMask = 1;
+ heapProps.VisibleNodeMask = 1;
+
+ SLANG_RETURN_ON_FAIL(resourceOut.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr));
+
+ resourceOut.setDebugName(L"Texture");
+ }
+
+ {
+ for (int i = 0; i < numMipMaps; ++i)
+ {
+ D3D12_TEXTURE_COPY_LOCATION src;
+ src.pResource = uploadTexture;
+ src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ src.PlacedFootprint = layouts[i];
+
+ D3D12_TEXTURE_COPY_LOCATION dst;
+ dst.pResource = resourceOut;
+ dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dst.SubresourceIndex = UINT(i);
+ m_commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
+ }
+ }
+
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ resourceOut.transition(D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, submitter);
+ }
+
+ // Block - waiting for copy to complete (so can drop upload texture)
+ submitGpuWorkAndWait();
+
+ return SLANG_OK;
+}
+
+Result D3D12Renderer::createInputTexture(const InputTextureDesc& inputDesc, D3D12DescriptorHeap& viewHeap, int srvIndex, D3D12Resource& resourceOut)
+{
+ TextureData texData;
+ generateTextureData(texData, inputDesc);
+
+ SLANG_RETURN_ON_FAIL(createTexture(inputDesc, texData, resourceOut));
+
+ if (srvIndex >= 0)
+ {
+ const D3D12_RESOURCE_DESC resourceDesc = resourceOut.getResource()->GetDesc();
+
+ DXGI_FORMAT pixelFormat = resourceDesc.Format;
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ _initSrvDesc(resourceDesc, pixelFormat, srvDesc);
+
+ // Copy to the descriptor
+ m_device->CreateShaderResourceView(resourceOut, &srvDesc, viewHeap.getCpuHandle(srvIndex));
+ }
+
+ return SLANG_OK;
+}
+
+Result D3D12Renderer::createInputBuffer(InputBufferDesc& bufferDesc, const List<unsigned int>& bufferData, D3D12DescriptorHeap& viewHeap, int uavIndex, int srvIndex,
+ D3D12Resource& bufferOut)
+{
+ const size_t bufferSize = bufferData.Count() * sizeof(unsigned int);
+
+ D3D12_RESOURCE_DESC resourceDesc;
+ _initBufferResourceDesc(bufferSize, resourceDesc);
+
+ D3D12_RESOURCE_STATES finalState = D3D12_RESOURCE_STATE_GENERIC_READ;
+
+ if (bufferDesc.type == InputBufferType::ConstantBuffer)
+ {
+ finalState = D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
+ }
+ else
+ {
+ finalState = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
+ resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+
+ /* if (bufferDesc.stride != 0)
+ {
+ desc.StructureByteStride = bufferDesc.stride;
+ desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
+ }
+ else
+ {
+ desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
+ } */
+ }
+
+ D3D12Resource uploadBuffer;
+ SLANG_RETURN_ON_FAIL(createBuffer(resourceDesc, bufferData.Buffer(), uploadBuffer, finalState, bufferOut));
+
+ const int elemSize = bufferDesc.stride <= 0 ? 1 : bufferDesc.stride;
+
+ if (bufferDesc.type == InputBufferType::StorageBuffer)
+ {
+ D3D12_UNORDERED_ACCESS_VIEW_DESC viewDesc = {};
+
+ viewDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
+ viewDesc.Format = DXGI_FORMAT_UNKNOWN;
+
+ viewDesc.Buffer.FirstElement = 0;
+ viewDesc.Buffer.NumElements = (UINT)(bufferData.Count() * sizeof(unsigned int) / elemSize);
+ viewDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
+
+ if (bufferDesc.stride == 0)
+ {
+ // TODO: are there UAV cases we need to handle that are neither
+ // raw nor structured? RWBuffer<T> would be one...
+ viewDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW;
+ viewDesc.Format = DXGI_FORMAT_R32_TYPELESS;
+ }
+
+ m_device->CreateUnorderedAccessView(bufferOut.getResource(), nullptr, &viewDesc, viewHeap.getCpuHandle(uavIndex));
+ }
+
+ if (bufferDesc.type != InputBufferType::ConstantBuffer)
+ {
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
+
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
+ srvDesc.Format = DXGI_FORMAT_UNKNOWN;
+
+ srvDesc.Buffer.FirstElement = 0;
+ srvDesc.Buffer.NumElements = (UINT)(bufferData.Count() * sizeof(unsigned int) / elemSize);
+ srvDesc.Buffer.StructureByteStride = elemSize;
+ srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
+
+ if (bufferDesc.stride == 0)
+ {
+ srvDesc.Format = DXGI_FORMAT_R32_FLOAT;
+ }
+
+ m_device->CreateShaderResourceView(bufferOut.getResource(), &srvDesc, viewHeap.getCpuHandle(srvIndex));
+ }
+
+ return SLANG_OK;
+}
+
+void D3D12Renderer::beginGpuWork()
+{
+ if (m_commandListOpenCount == 0)
+ {
+ // It's not open so open it
+ const FrameInfo& frame = getFrame();
+ ID3D12GraphicsCommandList* commandList = getCommandList();
+ commandList->Reset(frame.m_commandAllocator, nullptr);
+ }
+ m_commandListOpenCount++;
+}
+
+void D3D12Renderer::beginRender()
+{
+ // Should currently not be open!
+ assert(m_commandListOpenCount == 0);
+
+ m_circularResourceHeap.updateCompleted();
+
+ getFrame().m_commandAllocator->Reset();
+ beginGpuWork();
+
+ // Indicate that the render target needs to be writable
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ m_renderTargets[m_renderTargetIndex]->transition(D3D12_RESOURCE_STATE_RENDER_TARGET, submitter);
+ }
+
+ {
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = {m_rtvHeap->GetCPUDescriptorHandleForHeapStart().ptr + m_renderTargetIndex * m_rtvDescriptorSize };
+ if (m_depthStencil)
+ {
+ m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &m_depthStencilView);
+ }
+ else
+ {
+ m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
+ }
+
+ // Set necessary state.
+ m_commandList->RSSetViewports(1, &m_viewport);
+ m_commandList->RSSetScissorRects(1, &m_scissorRect);
+ }
+}
+
+void D3D12Renderer::endRender()
+{
+ assert(m_commandListOpenCount == 1);
+
+ {
+ const UInt64 signalValue = m_fence.nextSignal(m_commandQueue);
+ m_circularResourceHeap.addSync(signalValue);
+ }
+
+ D3D12Resource& backBuffer = *m_backBuffers[m_renderTargetIndex];
+ if (m_isMultiSampled)
+ {
+ // MSAA resolve
+ D3D12Resource& renderTarget = *m_renderTargets[m_renderTargetIndex];
+ assert(&renderTarget != &backBuffer);
+ // Barriers to wait for the render target, and the backbuffer to be in correct state
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ renderTarget.transition(D3D12_RESOURCE_STATE_RESOLVE_SOURCE, submitter);
+ backBuffer.transition(D3D12_RESOURCE_STATE_RESOLVE_DEST, submitter);
+ }
+
+ // Do the resolve...
+ m_commandList->ResolveSubresource(backBuffer, 0, renderTarget, 0, m_targetFormat);
+ }
+
+ // Make the back buffer presentable
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ backBuffer.transition(D3D12_RESOURCE_STATE_PRESENT, submitter);
+ }
+
+ SLANG_ASSERT_VOID_ON_FAIL(m_commandList->Close());
+
+ {
+ // Execute the command list.
+ ID3D12CommandList* commandLists[] = { m_commandList };
+ m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists);
+ }
+
+ /*
+ if (m_listener)
+ {
+ m_listener->onGpuWorkSubmitted(Dx12Type::wrap(m_commandQueue));
+ } */
+
+ assert(m_commandListOpenCount == 1);
+ // Must be 0
+ m_commandListOpenCount = 0;
+}
+
+void D3D12Renderer::submitGpuWork()
+{
+ assert(m_commandListOpenCount);
+ ID3D12GraphicsCommandList* commandList = getCommandList();
+
+ SLANG_ASSERT_VOID_ON_FAIL(commandList->Close());
+ {
+ // Execute the command list.
+ ID3D12CommandList* commandLists[] = { commandList };
+ m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists);
+ }
+/*
+ if (m_listener)
+ {
+ m_listener->onGpuWorkSubmitted(Dx12Type::wrap(m_commandQueue));
+ }
+ */
+ // Reopen
+ commandList->Reset(getFrame().m_commandAllocator, nullptr);
+}
+
+void D3D12Renderer::submitGpuWorkAndWait()
+{
+ submitGpuWork();
+ waitForGpu();
+}
+
+Result D3D12Renderer::captureTextureToFile(D3D12Resource& resource, const char* outputPath)
+{
+ const D3D12_RESOURCE_STATES initialState = resource.getState();
+
+ const D3D12_RESOURCE_DESC desc = resource.getResource()->GetDesc();
+
+ // Don't bother supporting MSAA for right now
+ if (desc.SampleDesc.Count > 1)
+ {
+ fprintf(stderr, "ERROR: cannot capture multi-sample texture\n");
+ return SLANG_FAIL;
+ }
+
+ size_t bytesPerPixel = sizeof(uint32_t);
+ size_t rowPitch = int(desc.Width) * bytesPerPixel;
+ size_t bufferSize = rowPitch * int(desc.Height);
+
+ D3D12Resource stagingResource;
+ {
+ D3D12_RESOURCE_DESC stagingDesc;
+ _initBufferResourceDesc(bufferSize, stagingDesc);
+
+ D3D12_HEAP_PROPERTIES heapProps;
+ heapProps.Type = D3D12_HEAP_TYPE_READBACK;
+ heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heapProps.CreationNodeMask = 1;
+ heapProps.VisibleNodeMask = 1;
+
+ SLANG_RETURN_ON_FAIL(stagingResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr));
+ }
+
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter);
+ }
+
+ // Do the copy
+ {
+ D3D12_TEXTURE_COPY_LOCATION srcLoc;
+ srcLoc.pResource = resource;
+ srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ srcLoc.SubresourceIndex = 0;
+
+ D3D12_TEXTURE_COPY_LOCATION dstLoc;
+ dstLoc.pResource = stagingResource;
+ dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ dstLoc.PlacedFootprint.Offset = 0;
+ dstLoc.PlacedFootprint.Footprint.Format = desc.Format;
+ dstLoc.PlacedFootprint.Footprint.Width = UINT(desc.Width);
+ dstLoc.PlacedFootprint.Footprint.Height = UINT(desc.Height);
+ dstLoc.PlacedFootprint.Footprint.Depth = 1;
+ dstLoc.PlacedFootprint.Footprint.RowPitch = UINT(rowPitch);
+
+ m_commandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr);
+ }
+
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ resource.transition(initialState, submitter);
+ }
+
+ // Submit the copy, and wait for copy to complete
+ submitGpuWorkAndWait();
+
+ int stbResult = 0;
+ {
+ ID3D12Resource* dxResource = stagingResource;
+
+ UINT8* data;
+ D3D12_RANGE readRange = {0, bufferSize};
+
+ SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast<void**>(&data)));
+
+ stbResult = stbi_write_png(outputPath, int(desc.Width), int(desc.Height), 4, data, int(rowPitch));
+
+ dxResource->Unmap(0, nullptr);
}
+
+ if (!stbResult)
+ {
+ fprintf(stderr, "ERROR: failed to write texture to file\n");
+ return SLANG_FAIL;
+ }
+ return SLANG_OK;
+}
+
+Result D3D12Renderer::calcComputePipelineState(ComPtr<ID3D12RootSignature>& signatureOut, ComPtr<ID3D12PipelineState>& pipelineStateOut)
+{
+ BindParameters bindParameters;
+ _calcBindParameters(bindParameters);
+
+ ComPtr<ID3D12RootSignature> rootSignature;
+ ComPtr<ID3D12PipelineState> pipelineState;
+
+ {
+ D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
+ rootSignatureDesc.NumParameters = bindParameters.m_paramIndex;
+ rootSignatureDesc.pParameters = bindParameters.m_parameters;
+ rootSignatureDesc.NumStaticSamplers = 0;
+ rootSignatureDesc.pStaticSamplers = nullptr;
+ rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
+
+ ComPtr<ID3DBlob> signature;
+ ComPtr<ID3DBlob> error;
+ SLANG_RETURN_ON_FAIL(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef()));
+ SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef())));
+ }
+
+ {
+ // Describe and create the compute pipeline state object
+ D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {};
+ computeDesc.pRootSignature = rootSignature;
+ computeDesc.CS = { m_boundShaderProgram->m_computeShader.Buffer(), m_boundShaderProgram->m_computeShader.Count() };
+ SLANG_RETURN_ON_FAIL(m_device->CreateComputePipelineState(&computeDesc, IID_PPV_ARGS(pipelineState.writeRef())));
+ }
+
+ signatureOut.swap(rootSignature);
+ pipelineStateOut.swap(pipelineState);
+
+ return SLANG_OK;
+}
+
+Result D3D12Renderer::calcGraphicsPipelineState(ComPtr<ID3D12RootSignature>& signatureOut, ComPtr<ID3D12PipelineState>& pipelineStateOut)
+{
+ BindParameters bindParameters;
+ _calcBindParameters(bindParameters);
+
+ ComPtr<ID3D12RootSignature> rootSignature;
+ ComPtr<ID3D12PipelineState> pipelineState;
+
+ {
+ // Deny unnecessary access to certain pipeline stages
+ D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
+ rootSignatureDesc.NumParameters = bindParameters.m_paramIndex;
+ rootSignatureDesc.pParameters = bindParameters.m_parameters;
+ rootSignatureDesc.NumStaticSamplers = 0;
+ rootSignatureDesc.pStaticSamplers = nullptr;
+ rootSignatureDesc.Flags = m_boundInputLayout ? D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT : D3D12_ROOT_SIGNATURE_FLAG_NONE;
+
+ ComPtr<ID3DBlob> signature;
+ ComPtr<ID3DBlob> error;
+ SLANG_RETURN_ON_FAIL(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef()));
+ SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef())));
+ }
+
+ {
+ // Describe and create the graphics pipeline state object (PSO)
+ D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
+
+ psoDesc.pRootSignature = rootSignature;
+
+ psoDesc.VS = { m_boundShaderProgram->m_vertexShader.Buffer(), m_boundShaderProgram->m_vertexShader.Count() };
+ psoDesc.PS = { m_boundShaderProgram->m_pixelShader.Buffer(), m_boundShaderProgram->m_pixelShader.Count() };
+
+ {
+ psoDesc.InputLayout = { m_boundInputLayout->m_elements.Buffer(), UINT(m_boundInputLayout->m_elements.Count()) };
+ psoDesc.PrimitiveTopologyType = m_primitiveTopologyType;
+
+ {
+ psoDesc.DSVFormat = m_depthStencilFormat;
+ psoDesc.NumRenderTargets = m_boundBindingState->m_numRenderTargets;
+ for (Int i = 0; i < m_boundBindingState->m_numRenderTargets; i++)
+ {
+ psoDesc.RTVFormats[i] = m_targetFormat;
+ }
+
+ psoDesc.SampleDesc.Count = 1;
+ psoDesc.SampleDesc.Quality = 0;
+
+ psoDesc.SampleMask = UINT_MAX;
+ }
+
+ {
+ auto& rs = psoDesc.RasterizerState;
+ rs.FillMode = D3D12_FILL_MODE_SOLID;
+ rs.CullMode = D3D12_CULL_MODE_NONE;
+ rs.FrontCounterClockwise = FALSE;
+ rs.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
+ rs.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
+ rs.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
+ rs.DepthClipEnable = TRUE;
+ rs.MultisampleEnable = FALSE;
+ rs.AntialiasedLineEnable = FALSE;
+ rs.ForcedSampleCount = 0;
+ rs.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
+ }
+
+ {
+ D3D12_BLEND_DESC& blend = psoDesc.BlendState;
+
+ blend.AlphaToCoverageEnable = FALSE;
+ blend.IndependentBlendEnable = FALSE;
+ const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc =
+ {
+ FALSE,FALSE,
+ D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
+ D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
+ D3D12_LOGIC_OP_NOOP,
+ D3D12_COLOR_WRITE_ENABLE_ALL,
+ };
+ for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
+ {
+ blend.RenderTarget[i] = defaultRenderTargetBlendDesc;
+ }
+ }
+
+ {
+ auto& ds = psoDesc.DepthStencilState;
+
+ ds.DepthEnable = FALSE;
+ ds.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
+ ds.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
+ //ds.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
+ ds.StencilEnable = FALSE;
+ ds.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
+ ds.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
+ const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp =
+ {
+ D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS
+ };
+ ds.FrontFace = defaultStencilOp;
+ ds.BackFace = defaultStencilOp;
+ }
+ }
+
+ psoDesc.PrimitiveTopologyType = m_primitiveTopologyType;
+
+ SLANG_RETURN_ON_FAIL(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(pipelineState.writeRef())));
+ }
+
+ signatureOut.swap(rootSignature);
+ pipelineStateOut.swap(pipelineState);
+
+ return SLANG_OK;
+}
+
+D3D12Renderer::RenderState* D3D12Renderer::findRenderState(ProgramType programType)
+{
+ switch (programType)
+ {
+ case ProgramType::kCompute:
+ {
+ // Check if current state is a match
+ if (m_currentRenderState)
+ {
+ if (m_currentRenderState->m_bindingState == m_boundBindingState &&
+ m_currentRenderState->m_shaderProgram == m_boundShaderProgram)
+ {
+ return m_currentRenderState;
+ }
+ }
+
+ const int num = int(m_renderStates.Count());
+ for (int i = 0; i < num; i++)
+ {
+ RenderState* renderState = m_renderStates[i];
+ if (renderState->m_bindingState == m_boundBindingState &&
+ renderState->m_shaderProgram == m_boundShaderProgram)
+ {
+ return renderState;
+ }
+ }
+ break;
+ }
+ case ProgramType::kGraphics:
+ {
+ if (m_currentRenderState)
+ {
+ if (m_currentRenderState->m_bindingState == m_boundBindingState &&
+ m_currentRenderState->m_inputLayout == m_boundInputLayout &&
+ m_currentRenderState->m_shaderProgram == m_boundShaderProgram &&
+ m_currentRenderState->m_primitiveTopologyType == m_primitiveTopologyType)
+ {
+ return m_currentRenderState;
+ }
+ }
+ // See if matches one in the list
+ {
+ const int num = int(m_renderStates.Count());
+ for (int i = 0; i < num; i++)
+ {
+ RenderState* renderState = m_renderStates[i];
+ if (renderState->m_bindingState == m_boundBindingState &&
+ renderState->m_inputLayout == m_boundInputLayout &&
+ renderState->m_shaderProgram == m_boundShaderProgram &&
+ renderState->m_primitiveTopologyType == m_primitiveTopologyType)
+ {
+ // Okay we have a match
+ return renderState;
+ }
+ }
+ }
+ break;
+ }
+ default: break;
+ }
+ return nullptr;
+}
+
+D3D12Renderer::RenderState* D3D12Renderer::calcRenderState()
+{
+ if (!m_boundShaderProgram)
+ {
+ return nullptr;
+ }
+ m_currentRenderState = findRenderState(m_boundShaderProgram->m_programType);
+ if (m_currentRenderState)
+ {
+ return m_currentRenderState;
+ }
+
+ ComPtr<ID3D12RootSignature> rootSignature;
+ ComPtr<ID3D12PipelineState> pipelineState;
+
+ switch (m_boundShaderProgram->m_programType)
+ {
+ case ProgramType::kCompute:
+ {
+ if (SLANG_FAILED(calcComputePipelineState(rootSignature, pipelineState)))
+ {
+ return nullptr;
+ }
+ break;
+ }
+ case ProgramType::kGraphics:
+ {
+ if (SLANG_FAILED(calcGraphicsPipelineState(rootSignature, pipelineState)))
+ {
+ return nullptr;
+ }
+ break;
+ }
+ default: return nullptr;
+ }
+
+ RenderState* renderState = new RenderState;
+
+ renderState->m_primitiveTopologyType = m_primitiveTopologyType;
+ renderState->m_bindingState = m_boundBindingState;
+ renderState->m_inputLayout = m_boundInputLayout;
+ renderState->m_shaderProgram = m_boundShaderProgram;
+
+ renderState->m_rootSignature.swap(rootSignature);
+ renderState->m_pipelineState.swap(pipelineState);
+
+ m_renderStates.Add(renderState);
+
+ m_currentRenderState = renderState;
+
+ return renderState;
+}
+
+Result D3D12Renderer::_calcBindParameters(BindParameters& params)
+{
+ int numConstantBuffers = 0;
+ {
+ // Okay we need to try and create a render state
+ for (int i = 0; i < int(m_boundConstantBuffers.Count()); i++)
+ {
+ const BufferImpl* buffer = m_boundConstantBuffers[i];
+ if (buffer)
+ {
+ D3D12_ROOT_PARAMETER& param = params.nextParameter();
+ param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+
+ D3D12_ROOT_DESCRIPTOR& descriptor = param.Descriptor;
+ descriptor.ShaderRegister = numConstantBuffers;
+ descriptor.RegisterSpace = 0;
+
+ numConstantBuffers++;
+ }
+ }
+
+ if (m_boundBindingState)
+ {
+ const int numBoundConstantBuffers = numConstantBuffers;
+ for (int i = 0; i < int(m_boundBindingState->m_bindings.Count()); i++)
+ {
+ const Binding& binding = m_boundBindingState->m_bindings[i];
+ if (binding.m_type == ShaderInputType::Buffer)
+ {
+ if (binding.m_bufferType == InputBufferType::ConstantBuffer)
+ {
+ // Make sure it's not overlapping the ones we just statically defined
+ assert(binding.m_binding < numBoundConstantBuffers);
+
+ D3D12_ROOT_PARAMETER& param = params.nextParameter();
+ param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+
+ D3D12_ROOT_DESCRIPTOR& descriptor = param.Descriptor;
+ descriptor.ShaderRegister = binding.m_binding;
+ descriptor.RegisterSpace = 0;
+
+ numConstantBuffers++;
+ }
+
+ if (binding.m_bufferType == InputBufferType::StorageBuffer)
+ {
+ D3D12_DESCRIPTOR_RANGE& range = params.nextRange();
+
+ range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+ range.NumDescriptors = 1;
+ range.BaseShaderRegister = binding.m_binding;
+ range.RegisterSpace = 0;
+ range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+ D3D12_ROOT_PARAMETER& param = params.nextParameter();
+
+ param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+
+ D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable;
+ table.NumDescriptorRanges = 1;
+ table.pDescriptorRanges = &range;
+ }
+
+ if (binding.m_uavIndex >= 0)
+ {
+ D3D12_DESCRIPTOR_RANGE& range = params.nextRange();
+
+ range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
+ range.NumDescriptors = 1;
+ range.BaseShaderRegister = binding.m_binding;
+ range.RegisterSpace = 0;
+ range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+ D3D12_ROOT_PARAMETER& param = params.nextParameter();
+
+ param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+
+ D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable;
+ table.NumDescriptorRanges = 1;
+ table.pDescriptorRanges = &range;
+ }
+ }
+ }
+ }
+ }
+
+ if (m_boundBindingState && m_boundBindingState->m_samplerHeap.getUsedSize() > 0)
+ {
+ D3D12_DESCRIPTOR_RANGE& range = params.nextRange();
+
+ range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
+ range.NumDescriptors = m_boundBindingState->m_samplerHeap.getUsedSize();
+ range.BaseShaderRegister = 0;
+ range.RegisterSpace = 0;
+ range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+ D3D12_ROOT_PARAMETER& param = params.nextParameter();
+
+ param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+
+ D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable;
+ table.NumDescriptorRanges = 1;
+ table.pDescriptorRanges = &range;
+ }
+ return SLANG_OK;
+}
+
+Result D3D12Renderer::_bindRenderState(RenderState* renderState, ID3D12GraphicsCommandList* commandList, Submitter* submitter)
+{
+ BindingStateImpl* bindingState = m_boundBindingState;
+
+ submitter->setRootSigniture(renderState->m_rootSignature);
+ commandList->SetPipelineState(renderState->m_pipelineState);
+
+ {
+ int index = 0;
+
+ int numConstantBuffers = 0;
+ {
+ // Okay we need to try and create a render state
+ for (int i = 0; i < int(m_boundConstantBuffers.Count()); i++)
+ {
+ const BufferImpl* buffer = m_boundConstantBuffers[i];
+ if (buffer)
+ {
+ size_t bufferSize = buffer->m_memory.Count();
+
+ D3D12CircularResourceHeap::Cursor cursor = m_circularResourceHeap.allocateConstantBuffer(bufferSize);
+ ::memcpy(cursor.m_position, buffer->m_memory.Buffer(), bufferSize);
+ // Set the constant buffer
+ submitter->setRootConstantBufferView(index++, m_circularResourceHeap.getGpuHandle(cursor));
+
+ numConstantBuffers++;
+ }
+ }
+
+
+ if (bindingState)
+ {
+ D3D12DescriptorHeap& heap = bindingState->m_viewHeap;
+
+ for (int i = 0; i < int(bindingState->m_bindings.Count()); i++)
+ {
+ const Binding& binding = bindingState->m_bindings[i];
+ if (binding.m_type == ShaderInputType::Buffer)
+ {
+ if (binding.m_bufferType == InputBufferType::ConstantBuffer)
+ {
+ submitter->setRootConstantBufferView(index++, binding.m_resource.getResource()->GetGPUVirtualAddress());
+ numConstantBuffers++;
+ }
+
+ if (binding.m_bufferType == InputBufferType::StorageBuffer)
+ {
+ submitter->setRootDescriptorTable(index++, heap.getGpuHandle(binding.m_srvIndex));
+ }
+
+ if (binding.m_uavIndex >= 0)
+ {
+ submitter->setRootDescriptorTable(index++, heap.getGpuHandle(binding.m_uavIndex));
+ }
+ }
+ }
+ }
+ }
+
+ if (bindingState && bindingState->m_samplerHeap.getUsedSize() > 0)
+ {
+ submitter->setRootDescriptorTable(index, bindingState->m_samplerHeap.getGpuStart());
+ }
+ }
+
+ if (bindingState)
+ {
+ ID3D12DescriptorHeap* heaps[] =
+ {
+ bindingState->m_viewHeap.getHeap(),
+ bindingState->m_samplerHeap.getHeap(),
+ };
+ commandList->SetDescriptorHeaps(SLANG_COUNT_OF(heaps), heaps);
+ }
+ else
+ {
+ commandList->SetDescriptorHeaps(0, nullptr);
+ }
+
+ return SLANG_OK;
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!!
-SlangResult D3D12Renderer::initialize(void* inWindowHandle)
+Result D3D12Renderer::initialize(void* inWindowHandle)
{
- auto windowHandle = (HWND)inWindowHandle;
+ m_hwnd = (HWND)inWindowHandle;
// Rather than statically link against D3D, we load it dynamically.
HMODULE d3dModule = LoadLibraryA("d3d12.dll");
@@ -130,43 +1507,71 @@ SlangResult D3D12Renderer::initialize(void* inWindowHandle)
return SLANG_FAIL;
}
-#define LOAD_PROC(TYPE, NAME) \
- TYPE NAME##_ = (TYPE) loadProc(d3dModule, #NAME); \
- if (NAME##_ == nullptr) return SLANG_FAIL;
+ HMODULE dxgiModule = LoadLibraryA("Dxgi.dll");
+ if (!dxgiModule)
+ {
+ fprintf(stderr, "error: failed load 'dxgi.dll'\n");
+ return SLANG_FAIL;
+ }
+
+
+#define LOAD_D3D_PROC(TYPE, NAME) \
+ TYPE NAME##_ = (TYPE) loadProc(d3dModule, #NAME);
+#define LOAD_DXGI_PROC(TYPE, NAME) \
+ TYPE NAME##_ = (TYPE) loadProc(dxgiModule, #NAME);
UINT dxgiFactoryFlags = 0;
#if ENABLE_DEBUG_LAYER
- LOAD_PROC(PFN_D3D12_GET_DEBUG_INTERFACE, D3D12GetDebugInterface);
-
- ID3D12Debug* debugController;
- if (SUCCEEDED(D3D12GetDebugInterface_(IID_PPV_ARGS(&debugController))))
{
- debugController->EnableDebugLayer();
- dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
+ LOAD_D3D_PROC(PFN_D3D12_GET_DEBUG_INTERFACE, D3D12GetDebugInterface);
+ if (D3D12GetDebugInterface_)
+ {
+ if (SUCCEEDED(D3D12GetDebugInterface_(IID_PPV_ARGS(m_dxDebug.writeRef()))))
+ {
+ m_dxDebug->EnableDebugLayer();
+ dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
+ }
+ }
}
#endif
- typedef HRESULT(WINAPI *PFN_DXGI_CREATE_FACTORY_2)(UINT Flags, REFIID riid, _COM_Outptr_ void **ppFactory);
-
- LOAD_PROC(PFN_DXGI_CREATE_FACTORY_2, CreateDXGIFactory2);
-
- IDXGIFactory4* dxgiFactory;
- SLANG_RETURN_ON_FAIL(CreateDXGIFactory2_(dxgiFactoryFlags, IID_PPV_ARGS(&dxgiFactory)));
+ m_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)loadProc(d3dModule, "D3D12SerializeRootSignature");
+ if (!m_D3D12SerializeRootSignature)
+ {
+ return SLANG_FAIL;
+ }
+ // Try and create DXGIFactory
+ ComPtr<IDXGIFactory4> dxgiFactory;
+ {
+ typedef HRESULT(WINAPI *PFN_DXGI_CREATE_FACTORY_2)(UINT Flags, REFIID riid, _COM_Outptr_ void **ppFactory);
+ LOAD_DXGI_PROC(PFN_DXGI_CREATE_FACTORY_2, CreateDXGIFactory2);
+ if (!CreateDXGIFactory2_)
+ {
+ return SLANG_FAIL;
+ }
+ SLANG_RETURN_ON_FAIL(CreateDXGIFactory2_(dxgiFactoryFlags, IID_PPV_ARGS(dxgiFactory.writeRef())));
+ }
+
D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
// Search for an adapter that meets our requirements
- IDXGIAdapter* adapter = nullptr;
-
- LOAD_PROC(PFN_D3D12_CREATE_DEVICE, D3D12CreateDevice);
+ ComPtr<IDXGIAdapter> adapter;
+
+ LOAD_D3D_PROC(PFN_D3D12_CREATE_DEVICE, D3D12CreateDevice);
+ if (!D3D12CreateDevice_)
+ {
+ return SLANG_FAIL;
+ }
UINT adapterCounter = 0;
for (;;)
{
UINT adapterIndex = adapterCounter++;
- IDXGIAdapter1* candidateAdapter = nullptr;
- if (dxgiFactory->EnumAdapters1(adapterIndex, &candidateAdapter) == DXGI_ERROR_NOT_FOUND)
+
+ ComPtr<IDXGIAdapter1> candidateAdapter;
+ if (dxgiFactory->EnumAdapters1(adapterIndex, candidateAdapter.writeRef()) == DXGI_ERROR_NOT_FOUND)
break;
DXGI_ADAPTER_DESC1 desc;
@@ -176,14 +1581,12 @@ SlangResult D3D12Renderer::initialize(void* inWindowHandle)
{
// TODO: may want to allow software driver as fallback
}
- else if (SUCCEEDED(D3D12CreateDevice_(candidateAdapter, featureLevel, IID_PPV_ARGS(&m_device))))
+ else if (SUCCEEDED(D3D12CreateDevice_(candidateAdapter, featureLevel, IID_PPV_ARGS(m_device.writeRef()))))
{
// We found one!
adapter = candidateAdapter;
break;
}
-
- candidateAdapter->Release();
}
if (!adapter)
@@ -191,61 +1594,266 @@ SlangResult D3D12Renderer::initialize(void* inWindowHandle)
// Couldn't find an adapter
return SLANG_FAIL;
}
+
+ m_numRenderFrames = 3;
+ m_numRenderTargets = 2;
+
+ m_windowWidth = gWindowWidth;
+ m_windowHeight = gWindowHeight;
+
+ // set viewport
+ {
+ m_viewport.Width = float(m_windowWidth);
+ m_viewport.Height = float(m_windowHeight);
+ m_viewport.MinDepth = 0;
+ m_viewport.MaxDepth = 1;
+ m_viewport.TopLeftX = 0;
+ m_viewport.TopLeftY = 0;
+ }
+
+ {
+ m_scissorRect.left = 0;
+ m_scissorRect.top = 0;
+ m_scissorRect.right = m_windowWidth;
+ m_scissorRect.bottom = m_windowHeight;
+ }
- // Command Queue
+ // Describe and create the command queue.
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
+ queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
- ID3D12CommandQueue* commandQueue;
- SLANG_RETURN_ON_FAIL(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)));
-
- // Swap Chain
- UINT frameCount = 2; // TODO: configure
+ SLANG_RETURN_ON_FAIL(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(m_commandQueue.writeRef())));
- DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
- swapChainDesc.BufferCount = frameCount;
- swapChainDesc.Width = gWindowWidth;
- swapChainDesc.Height = gWindowHeight;
- swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ // Describe the swap chain.
+ DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
+ swapChainDesc.BufferCount = m_numRenderTargets;
+ swapChainDesc.BufferDesc.Width = m_windowWidth;
+ swapChainDesc.BufferDesc.Height = m_windowHeight;
+ swapChainDesc.BufferDesc.Format = m_targetFormat;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
- swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ swapChainDesc.OutputWindow = m_hwnd;
swapChainDesc.SampleDesc.Count = 1;
+ swapChainDesc.Windowed = TRUE;
+
+ if (m_isFullSpeed)
+ {
+ m_hasVsync = false;
+ m_allowFullScreen = false;
+ }
+
+ if (!m_hasVsync)
+ {
+ swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
+ }
+
+ // Swap chain needs the queue so that it can force a flush on it.
+ ComPtr<IDXGISwapChain> swapChain;
+ SLANG_RETURN_ON_FAIL(dxgiFactory->CreateSwapChain(m_commandQueue, &swapChainDesc, swapChain.writeRef()));
+ SLANG_RETURN_ON_FAIL(swapChain->QueryInterface(m_swapChain.writeRef()));
+
+ if (!m_hasVsync)
+ {
+ m_swapChainWaitableObject = m_swapChain->GetFrameLatencyWaitableObject();
+
+ int maxLatency = m_numRenderTargets - 2;
+
+ // Make sure the maximum latency is in the range required by dx12 runtime
+ maxLatency = (maxLatency < 1) ? 1 : maxLatency;
+ maxLatency = (maxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS) ? DXGI_MAX_SWAP_CHAIN_BUFFERS : maxLatency;
+
+ m_swapChain->SetMaximumFrameLatency(maxLatency);
+ }
- IDXGISwapChain1* swapChain;
- SLANG_RETURN_ON_FAIL(dxgiFactory->CreateSwapChainForHwnd(commandQueue, windowHandle, &swapChainDesc, nullptr, nullptr, &swapChain));
+ // This sample does not support fullscreen transitions.
+ SLANG_RETURN_ON_FAIL(dxgiFactory->MakeWindowAssociation(m_hwnd, DXGI_MWA_NO_ALT_ENTER));
- // Is this needed?
- dxgiFactory->MakeWindowAssociation(windowHandle, DXGI_MWA_NO_ALT_ENTER);
+ m_renderTargetIndex = m_swapChain->GetCurrentBackBufferIndex();
- IDXGISwapChain3* swapChainEx;
- SLANG_RETURN_ON_FAIL(swapChain->QueryInterface(IID_PPV_ARGS(&swapChainEx)));
+ // Create descriptor heaps.
+ {
+ // Describe and create a render target view (RTV) descriptor heap.
+ D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
+
+ rtvHeapDesc.NumDescriptors = m_numRenderTargets;
+ rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+ rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+ SLANG_RETURN_ON_FAIL(m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(m_rtvHeap.writeRef())));
+ m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ }
- UINT frameIndex = swapChainEx->GetCurrentBackBufferIndex();
+ {
+ // Describe and create a depth stencil view (DSV) descriptor heap.
+ D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
+ dsvHeapDesc.NumDescriptors = 1;
+ dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
+ dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+ SLANG_RETURN_ON_FAIL(m_device->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(m_dsvHeap.writeRef())));
+
+ m_dsvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
+ }
- // Descriptor heaps
+ // Setup frame resources
+ {
+ SLANG_RETURN_ON_FAIL(createFrameResources());
+ }
- D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
- rtvHeapDesc.NumDescriptors = frameCount;
- rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+ // Setup fence, and close the command list (as default state without begin/endRender is closed)
+ {
+ SLANG_RETURN_ON_FAIL(m_fence.init(m_device));
+ // Create the command list. When command lists are created they are open, so close it.
+ FrameInfo& frame = m_frameInfos[m_frameIndex];
+ SLANG_RETURN_ON_FAIL(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frame.m_commandAllocator, nullptr, IID_PPV_ARGS(m_commandList.writeRef())));
+ m_commandList->Close();
+ }
- ID3D12DescriptorHeap* rtvHeap;
- SLANG_RETURN_ON_FAIL(m_device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvHeap)));
+ {
+ D3D12CircularResourceHeap::Desc desc;
+ desc.init();
+ // Define size
+ desc.m_blockSize = 65536;
+ // Set up the heap
+ m_circularResourceHeap.init(m_device, desc, &m_fence);
+ }
- UINT rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ // Setup for rendering
+ beginRender();
- D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap->GetCPUDescriptorHandleForHeapStart();
+ m_isInitialized = true;
+ return SLANG_OK;
+}
- // Create per-frame RTVs
- ID3D12Resource* backBufferResources[2];
- for (UINT ff = 0; ff < frameCount; ++ff)
+Result D3D12Renderer::createFrameResources()
+{
+ // Create back buffers
{
- SLANG_RETURN_ON_FAIL(swapChainEx->GetBuffer(ff, IID_PPV_ARGS(&backBufferResources[ff])));
- m_device->CreateRenderTargetView(backBufferResources[ff], nullptr, rtvHandle);
- rtvHandle.ptr += rtvDescriptorSize;
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvStart(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());
+
+ // Work out target format
+ D3D12_RESOURCE_DESC resourceDesc;
+ {
+ ComPtr<ID3D12Resource> backBuffer;
+ SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, IID_PPV_ARGS(backBuffer.writeRef())));
+ resourceDesc = backBuffer->GetDesc();
+ }
+ const DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_TARGET, m_targetUsageFlags, resourceDesc.Format);
+ const DXGI_FORMAT targetFormat = D3DUtil::calcFormat(D3DUtil::USAGE_TARGET, resourceFormat);
+
+ // Set the target format
+ m_targetFormat = targetFormat;
+
+ // Create a RTV, and a command allocator for each frame.
+ for (int i = 0; i < m_numRenderTargets; i++)
+ {
+ // Get the back buffer
+ ComPtr<ID3D12Resource> backBuffer;
+ SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(UINT(i), IID_PPV_ARGS(backBuffer.writeRef())));
+
+ // Set up resource for back buffer
+ m_backBufferResources[i].setResource(backBuffer, D3D12_RESOURCE_STATE_COMMON);
+ m_backBuffers[i] = &m_backBufferResources[i];
+ // Assume they are the same thing for now...
+ m_renderTargets[i] = &m_backBufferResources[i];
+
+ // If we are multi-sampling - create a render target separate from the back buffer
+ if (m_isMultiSampled)
+ {
+ D3D12_HEAP_PROPERTIES heapProps;
+ heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
+ heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heapProps.CreationNodeMask = 1;
+ heapProps.VisibleNodeMask = 1;
+ D3D12_CLEAR_VALUE clearValue = {};
+ clearValue.Format = m_targetFormat;
+
+ // Don't know targets alignment, so just memory copy
+ ::memcpy(clearValue.Color, m_clearColor, sizeof(m_clearColor));
+
+ D3D12_RESOURCE_DESC desc(resourceDesc);
+
+ desc.Format = resourceFormat;
+ desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ desc.SampleDesc.Count = m_numTargetSamples;
+ desc.SampleDesc.Quality = m_targetSampleQuality;
+ desc.Alignment = 0;
+
+ SLANG_RETURN_ON_FAIL(m_renderTargetResources[i].initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, desc, D3D12_RESOURCE_STATE_RENDER_TARGET, &clearValue));
+ m_renderTargets[i] = &m_renderTargetResources[i];
+ }
+
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvStart.ptr + i * m_rtvDescriptorSize };
+ m_device->CreateRenderTargetView(*m_renderTargets[i], nullptr, rtvHandle);
+ }
}
- ID3D12CommandAllocator* commandAllocator;
- SLANG_RETURN_ON_FAIL(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)));
+ // Set up frames
+ for (int i = 0; i < m_numRenderFrames; i++)
+ {
+ FrameInfo& frame = m_frameInfos[i];
+ SLANG_RETURN_ON_FAIL(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(frame.m_commandAllocator.writeRef())));
+ }
+
+ {
+ D3D12_RESOURCE_DESC desc = m_backBuffers[0]->getResource()->GetDesc();
+ assert(desc.Width == UINT64(m_windowWidth) && desc.Height == UINT64(m_windowHeight));
+ }
+
+ // Create the depth stencil view.
+ {
+ D3D12_HEAP_PROPERTIES heapProps;
+ heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
+ heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heapProps.CreationNodeMask = 1;
+ heapProps.VisibleNodeMask = 1;
+
+ DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_DEPTH_STENCIL, m_depthStencilUsageFlags, m_depthStencilFormat);
+ DXGI_FORMAT depthStencilFormat = D3DUtil::calcFormat(D3DUtil::USAGE_DEPTH_STENCIL, resourceFormat);
+
+ // Set the depth stencil format
+ m_depthStencilFormat = depthStencilFormat;
+
+ // Setup default clear
+ D3D12_CLEAR_VALUE clearValue = {};
+ clearValue.Format = depthStencilFormat;
+ clearValue.DepthStencil.Depth = 1.0f;
+ clearValue.DepthStencil.Stencil = 0;
+
+ D3D12_RESOURCE_DESC resourceDesc = {};
+ resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ resourceDesc.Format = resourceFormat;
+ resourceDesc.Width = m_windowWidth;
+ resourceDesc.Height = m_windowHeight;
+ resourceDesc.DepthOrArraySize = 1;
+ resourceDesc.MipLevels = 1;
+ resourceDesc.SampleDesc.Count = m_numTargetSamples;
+ resourceDesc.SampleDesc.Quality = m_targetSampleQuality;
+ resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
+ resourceDesc.Alignment = 0;
+
+ SLANG_RETURN_ON_FAIL(m_depthStencil.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue));
+
+ // Set the depth stencil
+ D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {};
+ depthStencilDesc.Format = depthStencilFormat;
+ depthStencilDesc.ViewDimension = m_isMultiSampled ? D3D12_DSV_DIMENSION_TEXTURE2DMS : D3D12_DSV_DIMENSION_TEXTURE2D;
+ depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE;
+
+ // Set up as the depth stencil view
+ m_device->CreateDepthStencilView(m_depthStencil, &depthStencilDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
+ m_depthStencilView = m_dsvHeap->GetCPUDescriptorHandleForHeapStart();
+ }
+
+ m_viewport.Width = static_cast<float>(m_windowWidth);
+ m_viewport.Height = static_cast<float>(m_windowHeight);
+ m_viewport.MaxDepth = 1.0f;
+
+ m_scissorRect.right = static_cast<LONG>(m_windowWidth);
+ m_scissorRect.bottom = static_cast<LONG>(m_windowHeight);
+
return SLANG_OK;
}
@@ -256,15 +1864,62 @@ void D3D12Renderer::setClearColor(const float color[4])
void D3D12Renderer::clearFrame()
{
+ // Record commands
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { m_rtvHeap->GetCPUDescriptorHandleForHeapStart().ptr + m_renderTargetIndex * m_rtvDescriptorSize };
+ m_commandList->ClearRenderTargetView(rtvHandle, m_clearColor, 0, nullptr);
+ if (m_depthStencil)
+ {
+ m_commandList->ClearDepthStencilView(m_depthStencilView, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
+ }
}
void D3D12Renderer::presentFrame()
{
+ endRender();
+
+ if (m_swapChainWaitableObject)
+ {
+ // check if now is good time to present
+ // This doesn't wait - because the wait time is 0. If it returns WAIT_TIMEOUT it means that no frame is waiting to be be displayed
+ // so there is no point doing a present.
+ const bool shouldPresent = (WaitForSingleObjectEx(m_swapChainWaitableObject, 0, TRUE) != WAIT_TIMEOUT);
+ if (shouldPresent)
+ {
+ m_swapChain->Present(0, 0);
+ }
+ }
+ else
+ {
+ if (SLANG_FAILED(m_swapChain->Present(1, 0)))
+ {
+ assert(!"Problem presenting");
+ beginRender();
+ return;
+ }
+ }
+
+ // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >=
+ m_frameInfos[m_frameIndex].m_fenceValue = m_fence.nextSignal(m_commandQueue);
+
+ // increment frame index after signal
+ m_frameIndex = (m_frameIndex + 1) % m_numRenderFrames;
+ // Update the render target index.
+ m_renderTargetIndex = m_swapChain->GetCurrentBackBufferIndex();
+
+ // On the current frame wait until it is completed
+ {
+ FrameInfo& frame = m_frameInfos[m_frameIndex];
+ // If the next frame is not ready to be rendered yet, wait until it is ready.
+ m_fence.waitUntilCompleted(frame.m_fenceValue);
+ }
+
+ // Setup such that rendering can restart
+ beginRender();
}
SlangResult D3D12Renderer::captureScreenShot(const char* outputPath)
{
- return SLANG_FAIL;
+ return captureTextureToFile(*m_renderTargets[m_renderTargetIndex], outputPath);
}
ShaderCompiler* D3D12Renderer::getShaderCompiler()
@@ -274,71 +1929,466 @@ ShaderCompiler* D3D12Renderer::getShaderCompiler()
Buffer* D3D12Renderer::createBuffer(const BufferDesc& desc)
{
- return nullptr;
-}
+ RefPtr<BufferImpl> buffer(new BufferImpl(desc));
+ const size_t bufferSize = desc.size;
+ switch (desc.flavor)
+ {
+ case BufferFlavor::Constant:
+ {
+ // Assume the constant buffer will change every frame. We'll just keep a copy of the contents
+ // in regular memory until it needed
+ buffer->m_memory.SetSize(UInt(bufferSize));
+ break;
+ }
+ case BufferFlavor::Vertex:
+ {
+ D3D12_RESOURCE_DESC bufferDesc;
+ _initBufferResourceDesc(bufferSize, bufferDesc);
+
+ SLANG_RETURN_NULL_ON_FAIL(createBuffer(bufferDesc, desc.initData, buffer->m_uploadResource, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, buffer->m_resource));
+ break;
+ }
+ default:
+ return nullptr;
+ }
+
+ return buffer.detach();
+}
InputLayout* D3D12Renderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount)
{
- return nullptr;
+ RefPtr<InputLayoutImpl> layout(new InputLayoutImpl);
+
+ // Work out a buffer size to hold all text
+ size_t textSize = 0;
+ for (int i = 0; i < Int(inputElementCount); ++i)
+ {
+ const char* text = inputElements[i].semanticName;
+ textSize += text ? (::strlen(text) + 1) : 0;
+ }
+ layout->m_text.SetSize(textSize);
+ char* textPos = layout->m_text.Buffer();
+
+ //
+ List<D3D12_INPUT_ELEMENT_DESC>& elements = layout->m_elements;
+ elements.SetSize(inputElementCount);
+
+
+ for (UInt i = 0; i < inputElementCount; ++i)
+ {
+ const InputElementDesc& srcEle = inputElements[i];
+ D3D12_INPUT_ELEMENT_DESC& dstEle = elements[i];
+
+ // Add text to the buffer
+ const char* semanticName = srcEle.semanticName;
+ if (semanticName)
+ {
+ const int len = int(::strlen(semanticName));
+ ::memcpy(textPos, semanticName, len + 1);
+ semanticName = textPos;
+ textPos += len + 1;
+ }
+
+ dstEle.SemanticName = semanticName;
+ dstEle.SemanticIndex = (UINT)srcEle.semanticIndex;
+ dstEle.Format = D3DUtil::getMapFormat(srcEle.format);
+ dstEle.InputSlot = 0;
+ dstEle.AlignedByteOffset = (UINT)srcEle.offset;
+ dstEle.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
+ dstEle.InstanceDataStepRate = 0;
+ }
+
+ return layout.detach();
}
-void* D3D12Renderer::map(Buffer* buffer, MapFlavor flavor)
+void* D3D12Renderer::map(Buffer* bufferIn, MapFlavor flavor)
{
+ BufferImpl* buffer = static_cast<BufferImpl*>(bufferIn);
+ buffer->m_mapFlavor = flavor;
+
+ switch (buffer->m_desc.flavor)
+ {
+ case BufferFlavor::Vertex:
+ {
+ D3D12_RANGE readRange = {}; // We do not intend to read from this resource on the CPU.
+
+ // We need this in a state so we can upload
+ switch (flavor)
+ {
+ case MapFlavor::HostWrite:
+ case MapFlavor::WriteDiscard:
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ buffer->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter);
+ buffer->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter);
+ break;
+ }
+ case MapFlavor::HostRead:
+ {
+ // Lock whole of the buffer
+ readRange.End = buffer->m_desc.size;
+ break;
+ }
+ }
+
+ // Lock it
+ void* uploadData;
+ SLANG_RETURN_NULL_ON_FAIL(buffer->m_uploadResource.getResource()->Map(0, &readRange, reinterpret_cast<void**>(&uploadData)));
+ return uploadData;
+ }
+ case BufferFlavor::Constant:
+ {
+ return buffer->m_memory.Buffer();
+ }
+ }
+
return nullptr;
}
void D3D12Renderer::unmap(Buffer* buffer)
{
+ BufferImpl* impl = static_cast<BufferImpl*>(buffer);
+
+ switch (impl->m_desc.flavor)
+ {
+ case BufferFlavor::Vertex:
+ {
+ // Unmap
+ ID3D12Resource* uploadResource = impl->m_uploadResource;
+ ID3D12Resource* resource = impl->m_resource;
+
+ uploadResource->Unmap(0, nullptr);
+
+ // We need this in a state so we can upload
+ switch (impl->m_mapFlavor)
+ {
+ case MapFlavor::HostWrite:
+ case MapFlavor::WriteDiscard:
+ {
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ impl->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter);
+ impl->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter);
+ }
+
+ m_commandList->CopyBufferRegion(resource, 0, uploadResource, 0, impl->m_desc.size);
+
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ impl->m_resource.transition(D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, submitter);
+ }
+
+ break;
+ }
+ case MapFlavor::HostRead: break;
+ }
+ break;
+ }
+ case BufferFlavor::Constant:
+ {
+ break;
+ }
+ }
}
void D3D12Renderer::setInputLayout(InputLayout* inputLayout)
{
+ m_boundInputLayout = static_cast<InputLayoutImpl*>(inputLayout);
}
void D3D12Renderer::setPrimitiveTopology(PrimitiveTopology topology)
{
+ switch (topology)
+ {
+ case PrimitiveTopology::TriangleList:
+ {
+ m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+ m_primitiveTopology = D3DUtil::getPrimitiveTopology(topology);
+ break;
+ }
+ default:
+ {
+ assert(!"Unhandled type");
+ }
+ }
}
-void D3D12Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, Buffer*const* buffers, const UInt * strides, const UInt* offsets)
+void D3D12Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, Buffer*const* buffers, const UInt* strides, const UInt* offsets)
{
+ {
+ const UInt num = startSlot + slotCount;
+ if (num > m_boundVertexBuffers.Count())
+ {
+ m_boundVertexBuffers.SetSize(num);
+ }
+ }
+
+ for (UInt i = 0; i < slotCount; i++)
+ {
+ BufferImpl* buffer = static_cast<BufferImpl*>(buffers[i]);
+ if (buffer)
+ {
+ assert(buffer->m_desc.flavor == BufferFlavor::Vertex);
+ }
+
+ BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i];
+ boundBuffer.m_buffer = buffer;
+ boundBuffer.m_stride = int(strides[i]);
+ boundBuffer.m_offset = int(offsets[i]);
+ }
}
void D3D12Renderer::setShaderProgram(ShaderProgram* inProgram)
{
+ m_boundShaderProgram = static_cast<ShaderProgramImpl*>(inProgram);
}
void D3D12Renderer::setConstantBuffers(UInt startSlot, UInt slotCount, Buffer*const* buffers, const UInt* offsets)
{
+ {
+ const UInt num = startSlot + slotCount;
+ if (num > m_boundConstantBuffers.Count())
+ {
+ m_boundConstantBuffers.SetSize(num);
+ }
+ }
+
+ for (UInt i = 0; i < slotCount; i++)
+ {
+ BufferImpl* buffer = static_cast<BufferImpl*>(buffers[i]);
+ if (buffer)
+ {
+ assert(buffer->m_desc.flavor == BufferFlavor::Constant);
+ }
+ m_boundConstantBuffers[startSlot + i] = buffer;
+ }
}
void D3D12Renderer::draw(UInt vertexCount, UInt startVertex)
{
-}
+ ID3D12GraphicsCommandList* commandList = m_commandList;
+
+ RenderState* renderState = calcRenderState();
+ BindingStateImpl* bindingState = m_boundBindingState;
+
+ // Submit - setting for graphics
+ {
+ GraphicsSubmitter submitter(commandList);
+ _bindRenderState(renderState, commandList, &submitter);
+ }
+
+ commandList->IASetPrimitiveTopology(m_primitiveTopology);
+
+ // Set up vertex buffer views
+ {
+ int numVertexViews = 0;
+ D3D12_VERTEX_BUFFER_VIEW vertexViews[16];
+ for (int i = 0; i < int(m_boundVertexBuffers.Count()); i++)
+ {
+ const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[i];
+ BufferImpl* buffer = boundVertexBuffer.m_buffer;
+ if (buffer)
+ {
+ D3D12_VERTEX_BUFFER_VIEW& vertexView = vertexViews[numVertexViews++];
+ vertexView.BufferLocation = buffer->m_resource.getResource()->GetGPUVirtualAddress();
+ vertexView.SizeInBytes = int(buffer->m_desc.size);
+ vertexView.StrideInBytes = boundVertexBuffer.m_stride;
+ }
+ }
+ commandList->IASetVertexBuffers(0, numVertexViews, vertexViews);
+ }
+ commandList->DrawInstanced(UINT(vertexCount), 1, UINT(startVertex), 0);
+}
void D3D12Renderer::dispatchCompute(int x, int y, int z)
{
+ ID3D12GraphicsCommandList* commandList = m_commandList;
+ RenderState* renderState = calcRenderState();
+
+ // Submit binding for compute
+ {
+ ComputeSubmitter submitter(commandList);
+ _bindRenderState(renderState, commandList, &submitter);
+ }
+
+ commandList->Dispatch(x, y, z);
}
BindingState* D3D12Renderer::createBindingState(const ShaderInputLayout& layout)
{
- return nullptr;
+ RefPtr<BindingStateImpl> bindingState(new BindingStateImpl);
+
+ SLANG_RETURN_NULL_ON_FAIL(bindingState->init(m_device));
+ bindingState->m_numRenderTargets = layout.numRenderTargets;
+
+ const List<ShaderInputLayoutEntry>& srcBindings = layout.entries;
+ const int numBindings = int(srcBindings.Count());
+
+ List<Binding>& dstBindings = bindingState->m_bindings;
+ dstBindings.SetSize(numBindings);
+
+ for (int i = 0; i < numBindings; ++i)
+ {
+ ShaderInputLayoutEntry& srcEntry = srcBindings[i];
+ Binding& dstEntry = dstBindings[i];
+
+ dstEntry.m_type = srcEntry.type;
+ dstEntry.m_binding = srcEntry.hlslBinding;
+ dstEntry.m_isOutput = srcEntry.isOutput;
+
+ switch (srcEntry.type)
+ {
+ case ShaderInputType::Buffer:
+ {
+ dstEntry.m_uavIndex = bindingState->m_viewHeap.allocate();
+ dstEntry.m_srvIndex = bindingState->m_viewHeap.allocate();
+
+ if (dstEntry.m_uavIndex < 0 || dstEntry.m_srvIndex < 0)
+ {
+ return nullptr;
+ }
+
+ SLANG_RETURN_NULL_ON_FAIL(createInputBuffer(srcEntry.bufferDesc, srcEntry.bufferData, bindingState->m_viewHeap, dstEntry.m_uavIndex, dstEntry.m_srvIndex, dstEntry.m_resource));
+
+ dstEntry.m_bufferLength = (int)(srcEntry.bufferData.Count() * sizeof(unsigned int));
+ dstEntry.m_bufferType = srcEntry.bufferDesc.type;
+ break;
+ }
+ case ShaderInputType::Texture:
+ {
+ dstEntry.m_srvIndex = bindingState->m_viewHeap.allocate();
+
+ if (dstEntry.m_srvIndex < 0)
+ {
+ return nullptr;
+ }
+
+ SLANG_RETURN_NULL_ON_FAIL(createInputTexture(srcEntry.textureDesc, bindingState->m_viewHeap, dstEntry.m_srvIndex, dstEntry.m_resource));
+ break;
+ }
+ case ShaderInputType::Sampler:
+ {
+ dstEntry.m_samplerIndex = srcEntry.hlslBinding;
+ bindingState->m_samplerHeap.placeAt(srcEntry.hlslBinding);
+
+ SLANG_RETURN_NULL_ON_FAIL(createInputSampler(srcEntry.samplerDesc, bindingState->m_samplerHeap, dstEntry.m_samplerIndex));
+ break;
+ }
+ case ShaderInputType::CombinedTextureSampler:
+ {
+ assert(!"Not implemented");
+ //throw "not implemented";
+ return nullptr;
+ break;
+ }
+ }
+ }
+
+ return bindingState.detach();
}
void D3D12Renderer::setBindingState(BindingState* state)
{
+ m_boundBindingState = static_cast<BindingStateImpl*>(state);
}
-void D3D12Renderer::serializeOutput(BindingState* state, const char* fileName)
+void D3D12Renderer::serializeOutput(BindingState* stateIn, const char* fileName)
{
+ auto bindingState = static_cast<BindingStateImpl*>(stateIn);
+ FILE * f = fopen(fileName, "wb");
+
+ 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;
+
+ int id = 0;
+ for (auto & binding : bindingState->m_bindings)
+ {
+ if (binding.m_isOutput)
+ {
+ if (binding.m_resource.getResource())
+ {
+ // create staging buffer
+
+ size_t bufferSize = D3DUtil::calcAligned(binding.m_bufferLength, 256);
+
+ D3D12_RESOURCE_DESC stagingDesc;
+ _initBufferResourceDesc(bufferSize, stagingDesc);
+
+ D3D12Resource stageBuf;
+ SLANG_RETURN_VOID_ON_FAIL(stageBuf.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr));
+
+ const D3D12_RESOURCE_STATES initialState = binding.m_resource.getState();
+
+ // Make it a source
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ binding.m_resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter);
+ }
+ // Do the copy
+ m_commandList->CopyBufferRegion(stageBuf, 0, binding.m_resource, 0, bufferSize);
+ // Switch it back
+ {
+ D3D12BarrierSubmitter submitter(m_commandList);
+ binding.m_resource.transition(initialState, submitter);
+ }
+
+ // Wait until complete
+ submitGpuWorkAndWait();
+
+ UINT8* data;
+ D3D12_RANGE readRange = {0, bufferSize};
+
+ SLANG_RETURN_VOID_ON_FAIL(stageBuf.getResource()->Map(0, &readRange, reinterpret_cast<void**>(&data)));
+ {
+ auto ptr = (unsigned int *)data;
+ for (auto i = 0u; i < binding.m_bufferLength / sizeof(unsigned int); i++)
+ fprintf(f, "%X\n", ptr[i]);
+ }
+ stageBuf.getResource()->Unmap(0, nullptr);
+ }
+ else
+ {
+ printf("invalid output type at %d.\n", id);
+ }
+ }
+ id++;
+ }
+ fclose(f);
}
// ShaderCompiler interface
ShaderProgram* D3D12Renderer::compileProgram(const ShaderCompileRequest& request)
{
- return nullptr;
+ RefPtr<ShaderProgramImpl> program(new ShaderProgramImpl);
+
+ if (request.computeShader.name)
+ {
+ program->m_programType = ProgramType::kCompute;
+ ComPtr<ID3DBlob> computeShaderBlob;
+ SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader(request.computeShader.source.path, request.computeShader.source.dataBegin, request.computeShader.name, request.computeShader.profile, computeShaderBlob));
+
+ program->m_computeShader.InsertRange(0, (const uint8_t*)computeShaderBlob->GetBufferPointer(), UInt(computeShaderBlob->GetBufferSize()));
+ }
+ else
+ {
+ program->m_programType = ProgramType::kGraphics;
+ ComPtr<ID3DBlob> vertexShaderBlob, fragmentShaderBlob;
+ SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader(request.vertexShader.source.path, request.vertexShader.source.dataBegin, request.vertexShader.name, request.vertexShader.profile, vertexShaderBlob));
+ SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader(request.fragmentShader.source.path, request.fragmentShader.source.dataBegin, request.fragmentShader.name, request.fragmentShader.profile, fragmentShaderBlob));
+
+ program->m_vertexShader.InsertRange(0, (const uint8_t*)vertexShaderBlob->GetBufferPointer(), UInt(vertexShaderBlob->GetBufferSize()));
+ program->m_pixelShader.InsertRange(0, (const uint8_t*)fragmentShaderBlob->GetBufferPointer(), UInt(fragmentShaderBlob->GetBufferSize()));
+ }
+
+ return program.detach();
}
+
} // renderer_test
diff --git a/tools/render-test/render-gl.cpp b/tools/render-test/render-gl.cpp
index 37745d93b..f888e72a2 100644
--- a/tools/render-test/render-gl.cpp
+++ b/tools/render-test/render-gl.cpp
@@ -98,6 +98,8 @@ public:
virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer*const* buffers, const UInt* offsets) override;
virtual void draw(UInt vertexCount, UInt startVertex) override;
virtual void dispatchCompute(int x, int y, int z) override;
+ virtual void submitGpuWork() override {}
+ virtual void waitForGpu() override {}
// ShaderCompiler implementation
virtual ShaderProgram* compileProgram(const ShaderCompileRequest& request) override;
diff --git a/tools/render-test/render-test.vcxproj b/tools/render-test/render-test.vcxproj
index 1b022f913..59ae16a14 100644
--- a/tools/render-test/render-test.vcxproj
+++ b/tools/render-test/render-test.vcxproj
@@ -166,22 +166,30 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClCompile Include="circular-resource-heap-d3d12.cpp" />
+ <ClCompile Include="d3d-util.cpp" />
+ <ClCompile Include="descriptor-heap-d3d12.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="options.cpp" />
<ClCompile Include="render-d3d11.cpp" />
<ClCompile Include="render-d3d12.cpp" />
<ClCompile Include="render-gl.cpp" />
<ClCompile Include="render-vk.cpp" />
+ <ClCompile Include="resource-d3d12.cpp" />
<ClCompile Include="shader-input-layout.cpp" />
<ClCompile Include="slang-support.cpp" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="circular-resource-heap-d3d12.h" />
+ <ClInclude Include="d3d-util.h" />
+ <ClInclude Include="descriptor-heap-d3d12.h" />
<ClInclude Include="options.h" />
<ClInclude Include="render-d3d11.h" />
<ClInclude Include="render-d3d12.h" />
<ClInclude Include="render-gl.h" />
<ClInclude Include="render-vk.h" />
<ClInclude Include="render.h" />
+ <ClInclude Include="resource-d3d12.h" />
<ClInclude Include="shader-input-layout.h" />
<ClInclude Include="slang-support.h" />
<ClInclude Include="window.h" />
diff --git a/tools/render-test/render-test.vcxproj.filters b/tools/render-test/render-test.vcxproj.filters
index e1077e4e8..c0a24b015 100644
--- a/tools/render-test/render-test.vcxproj.filters
+++ b/tools/render-test/render-test.vcxproj.filters
@@ -39,6 +39,18 @@
<ClCompile Include="render-d3d12.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="d3d-util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="resource-d3d12.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="circular-resource-heap-d3d12.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="descriptor-heap-d3d12.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="options.h">
@@ -68,5 +80,17 @@
<ClInclude Include="render-d3d12.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="d3d-util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource-d3d12.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="circular-resource-heap-d3d12.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="descriptor-heap-d3d12.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/tools/render-test/render-vk.cpp b/tools/render-test/render-vk.cpp
index bb177a88a..97da0bc40 100644
--- a/tools/render-test/render-vk.cpp
+++ b/tools/render-test/render-vk.cpp
@@ -105,6 +105,8 @@ public:
virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer*const* buffers, const UInt* offsets) override;
virtual void draw(UInt vertexCount, UInt startVertex) override;
virtual void dispatchCompute(int x, int y, int z) override;
+ virtual void submitGpuWork() override {}
+ virtual void waitForGpu() override {}
// ShaderCompiler implementation
virtual ShaderProgram* compileProgram(const ShaderCompileRequest& request) override;
diff --git a/tools/render-test/render.h b/tools/render-test/render.h
index 7484f0f3d..761f9e759 100644
--- a/tools/render-test/render.h
+++ b/tools/render-test/render.h
@@ -141,6 +141,12 @@ public:
virtual void draw(UInt vertexCount, UInt startVertex = 0) = 0;
virtual void dispatchCompute(int x, int y, int z) = 0;
+
+ /// Commit any buffered state changes or draw calls.
+ /// presentFrame will commitAll implicitly before doing a present
+ virtual void submitGpuWork() = 0;
+ /// Blocks until Gpu work is complete
+ virtual void waitForGpu() = 0;
};
diff --git a/tools/render-test/resource-d3d12.cpp b/tools/render-test/resource-d3d12.cpp
new file mode 100644
index 000000000..160c7f898
--- /dev/null
+++ b/tools/render-test/resource-d3d12.cpp
@@ -0,0 +1,214 @@
+// resource-d3d12.cpp
+#include "resource-d3d12.h"
+
+namespace renderer_test {
+using namespace Slang;
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12BarrierSubmitter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void D3D12BarrierSubmitter::_flush()
+{
+ assert(m_numBarriers > 0);
+
+ if (m_commandList)
+ {
+ m_commandList->ResourceBarrier(UINT(m_numBarriers), m_barriers);
+ }
+ m_numBarriers = 0;
+}
+
+D3D12_RESOURCE_BARRIER& D3D12BarrierSubmitter::_expandOne()
+{
+ _flush();
+ return m_barriers[m_numBarriers++];
+}
+
+void D3D12BarrierSubmitter::transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState)
+{
+ if (nextState != prevState)
+ {
+ D3D12_RESOURCE_BARRIER& barrier = expandOne();
+
+ const UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ const D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+
+ ::memset(&barrier, 0, sizeof(barrier));
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = flags;
+ barrier.Transition.pResource = resource;
+ barrier.Transition.StateBefore = prevState;
+ barrier.Transition.StateAfter = nextState;
+ barrier.Transition.Subresource = subresource;
+ }
+ else
+ {
+ if (nextState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS)
+ {
+ D3D12_RESOURCE_BARRIER& barrier = expandOne();
+
+ ::memset(&barrier, 0, sizeof(barrier));
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
+ barrier.UAV.pResource = resource;
+ }
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12ResourceBase !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+/* static */DXGI_FORMAT D3D12ResourceBase::calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource)
+{
+ return resource ? D3DUtil::calcFormat(usage, resource->GetDesc().Format) : DXGI_FORMAT_UNKNOWN;
+}
+
+void D3D12ResourceBase::transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter)
+{
+ // Transition only if there is a resource
+ if (m_resource)
+ {
+ submitter.transition(m_resource, m_state, nextState);
+ m_state = nextState;
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12CounterFence !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+D3D12CounterFence::~D3D12CounterFence()
+{
+ if (m_event)
+ {
+ CloseHandle(m_event);
+ }
+}
+
+Result D3D12CounterFence::init(ID3D12Device* device, uint64_t initialValue)
+{
+ m_currentValue = initialValue;
+
+ SLANG_RETURN_ON_FAIL(device->CreateFence(m_currentValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef())));
+ // Create an event handle to use for frame synchronization.
+ m_event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ if (m_event == nullptr)
+ {
+ Result res = HRESULT_FROM_WIN32(GetLastError());
+ return SLANG_FAILED(res) ? res : SLANG_FAIL;
+ }
+ return SLANG_OK;
+}
+
+UInt64 D3D12CounterFence::nextSignal(ID3D12CommandQueue* commandQueue)
+{
+ // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >=
+ m_currentValue++;
+ // Schedule a Signal command in the queue.
+ Result res = commandQueue->Signal(m_fence, m_currentValue);
+ if (SLANG_FAILED(res))
+ {
+ assert(!"Signal failed");
+ }
+ return m_currentValue;
+}
+
+void D3D12CounterFence::waitUntilCompleted(uint64_t completedValue)
+{
+ // You can only wait for a value that is less than or equal to the current value
+ assert(completedValue <= m_currentValue);
+
+ // Wait until the previous frame is finished.
+ while (m_fence->GetCompletedValue() < completedValue)
+ {
+ // Make it signal with the current value
+ SLANG_ASSERT_VOID_ON_FAIL(m_fence->SetEventOnCompletion(completedValue, m_event));
+ WaitForSingleObject(m_event, INFINITE);
+ }
+}
+
+void D3D12CounterFence::nextSignalAndWait(ID3D12CommandQueue* commandQueue)
+{
+ waitUntilCompleted(nextSignal(commandQueue));
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!! D3D12Resource !!!!!!!!!!!!!!!!!!!!!!!! */
+
+/* static */void D3D12Resource::setDebugName(ID3D12Resource* resource, const char* name)
+{
+ if (resource)
+ {
+ size_t len = ::strlen(name);
+ List<wchar_t> buf;
+ buf.SetSize(len + 1);
+
+ D3DUtil::appendWideChars(name, buf);
+ resource->SetName(buf.begin());
+ }
+}
+
+void D3D12Resource::setDebugName(const char* name)
+{
+ setDebugName(m_resource, name);
+}
+
+void D3D12Resource::setDebugName(const wchar_t* name)
+{
+ if (m_resource)
+ {
+ m_resource->SetName(name);
+ }
+}
+
+void D3D12Resource::setResource(ID3D12Resource* resource, D3D12_RESOURCE_STATES initialState)
+{
+ if (resource != m_resource)
+ {
+ if (resource)
+ {
+ resource->AddRef();
+ }
+ if (m_resource)
+ {
+ m_resource->Release();
+ }
+ m_resource = resource;
+ }
+ m_prevState = initialState;
+ m_state = initialState;
+}
+
+void D3D12Resource::setResourceNull()
+{
+ if (m_resource)
+ {
+ m_resource->Release();
+ m_resource = nullptr;
+ }
+}
+
+Result D3D12Resource::initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue)
+{
+ setResourceNull();
+ ComPtr<ID3D12Resource> resource;
+ SLANG_RETURN_ON_FAIL(device->CreateCommittedResource(&heapProps, heapFlags, &resourceDesc, initState, clearValue, IID_PPV_ARGS(resource.writeRef())));
+ setResource(resource, initState);
+ return SLANG_OK;
+}
+
+ID3D12Resource* D3D12Resource::detach()
+{
+ ID3D12Resource* resource = m_resource;
+ m_resource = nullptr;
+ return resource;
+}
+
+void D3D12Resource::swap(ComPtr<ID3D12Resource>& resourceInOut)
+{
+ ID3D12Resource* tmp = m_resource;
+ m_resource = resourceInOut.detach();
+ resourceInOut.attach(tmp);
+}
+
+void D3D12Resource::setState(D3D12_RESOURCE_STATES state)
+{
+ m_prevState = state;
+ m_state = state;
+}
+
+} // renderer_test
diff --git a/tools/render-test/resource-d3d12.h b/tools/render-test/resource-d3d12.h
new file mode 100644
index 000000000..a6537a7d7
--- /dev/null
+++ b/tools/render-test/resource-d3d12.h
@@ -0,0 +1,178 @@
+// resource-d3d12.h
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <Windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#undef NOMINMAX
+
+#include <dxgi1_4.h>
+#include <d3d12.h>
+
+#include "../../source/core/slang-com-ptr.h"
+#include "d3d-util.h"
+
+namespace renderer_test {
+
+// Enables more conservative barriers - restoring the state of resources after they are used.
+// Should not need to be enabled in normal builds, as the barriers should correctly sync resources
+// If enabling fixes an issue it implies regular barriers are not correctly used.
+#define SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS 0
+
+struct D3D12BarrierSubmitter
+{
+ enum { MAX_BARRIERS = 8 };
+
+ /// Expand one space to hold a barrier
+ SLANG_FORCE_INLINE D3D12_RESOURCE_BARRIER& expandOne() { return (m_numBarriers < MAX_BARRIERS) ? m_barriers[m_numBarriers++] : _expandOne(); }
+ /// Flush barriers to command list
+ SLANG_FORCE_INLINE void flush() { if (m_numBarriers > 0) _flush(); }
+
+ /// Transition resource from prevState to nextState
+ void transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState);
+
+ /// Ctor
+ SLANG_FORCE_INLINE D3D12BarrierSubmitter(ID3D12GraphicsCommandList* commandList) : m_numBarriers(0), m_commandList(commandList) { }
+ /// Dtor
+ SLANG_FORCE_INLINE ~D3D12BarrierSubmitter() { flush(); }
+
+protected:
+ D3D12_RESOURCE_BARRIER& _expandOne();
+ void _flush();
+
+ ID3D12GraphicsCommandList* m_commandList;
+ int m_numBarriers;
+ D3D12_RESOURCE_BARRIER m_barriers[MAX_BARRIERS];
+};
+
+/*! \brief A class to simplify using Dx12 fences.
+
+A fence is a mechanism to track GPU work. This is achieved by having a counter that the CPU holds
+called the current value. Calling nextSignal will increase the CPU counter, and add a fence
+with that value to the commandQueue. When the GPU has completed all the work before the fence it will
+update the completed value. This is typically used when
+the CPU needs to know the GPU has finished some piece of work has completed. To do this the CPU
+can check the completed value, and when it is greater or equal to the value returned by nextSignal the
+CPU will know that all the work prior to when the nextSignal was added to the queue will have completed.
+
+NOTE! This cannot be used across threads, as for amongst other reasons SetEventOnCompletion
+only works with a single value.
+
+Signal on the CommandQueue updates the fence on the GPU side. Signal on the fence object changes
+the value on the CPU side (not used here).
+
+Useful article describing how Dx12 synchronization works:
+https://msdn.microsoft.com/en-us/library/windows/desktop/dn899217%28v=vs.85%29.aspx
+*/
+class D3D12CounterFence
+{
+public:
+ /// Must be called before used
+ SlangResult init(ID3D12Device* device, uint64_t initialValue = 0);
+ /// Increases the counter, signals the queue and waits for the signal to be hit
+ void nextSignalAndWait(ID3D12CommandQueue* queue);
+ /// Signals with next counter value. Returns the value the signal was called on
+ uint64_t nextSignal(ID3D12CommandQueue* commandQueue);
+ /// Get the current value
+ SLANG_FORCE_INLINE uint64_t getCurrentValue() const { return m_currentValue; }
+ /// Get the completed value
+ SLANG_FORCE_INLINE uint64_t getCompletedValue() const { return m_fence->GetCompletedValue(); }
+
+ /// Waits for the the specified value
+ void waitUntilCompleted(uint64_t completedValue);
+
+ /// Ctor
+ D3D12CounterFence() :m_event(nullptr), m_currentValue(0) {}
+ /// Dtor
+ ~D3D12CounterFence();
+
+protected:
+ HANDLE m_event;
+ Slang::ComPtr<ID3D12Fence> m_fence;
+ UINT64 m_currentValue;
+};
+
+/** The base class for resource types allows for tracking of state. It does not allow for setting of the resource though, such that
+an interface can return a D3D12ResourceBase, and a client cant manipulate it's state, but it cannot replace/change the actual resource */
+struct D3D12ResourceBase
+{
+ /// Add a transition if necessary to the list
+ void transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter);
+ /// Get the current state
+ SLANG_FORCE_INLINE D3D12_RESOURCE_STATES getState() const { return m_state; }
+
+ /// Get the associated resource
+ SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_resource; }
+
+ /// True if a resource is set
+ SLANG_FORCE_INLINE bool isSet() const { return m_resource != nullptr; }
+
+ /// Coercable into ID3D12Resource
+ SLANG_FORCE_INLINE operator ID3D12Resource*() const { return m_resource; }
+
+ /// restore previous state
+#if SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS
+ NV_FORCE_INLINE Void restore(Dx12BarrierSubmitter& submitter) { transition(m_prevState, submitter); }
+#else
+ SLANG_FORCE_INLINE void restore(D3D12BarrierSubmitter& submitter) { SLANG_UNUSED(submitter) }
+#endif
+
+ /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible
+ static DXGI_FORMAT calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource);
+
+ /// Ctor
+ SLANG_FORCE_INLINE D3D12ResourceBase() :
+ m_state(D3D12_RESOURCE_STATE_COMMON),
+ m_prevState(D3D12_RESOURCE_STATE_COMMON),
+ m_resource(nullptr)
+ {}
+
+protected:
+ /// This is protected so as clients cannot slice the class, and so state tracking is lost
+ ~D3D12ResourceBase() {}
+
+ ID3D12Resource* m_resource;
+ D3D12_RESOURCE_STATES m_state;
+ D3D12_RESOURCE_STATES m_prevState;
+};
+
+struct D3D12Resource : public D3D12ResourceBase
+{
+
+ /// Dtor
+ ~D3D12Resource()
+ {
+ if (m_resource)
+ {
+ m_resource->Release();
+ }
+ }
+
+ /// Initialize as committed resource
+ Slang::Result initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue);
+
+ /// Set a resource with an initial state
+ void setResource(ID3D12Resource* resource, D3D12_RESOURCE_STATES initialState);
+ /// Make the resource null
+ void setResourceNull();
+ /// Returns the attached resource (with any ref counts) and sets to nullptr on this.
+ ID3D12Resource* detach();
+
+ /// Swaps the resource contents with the contents of the smart pointer
+ void swap(Slang::ComPtr<ID3D12Resource>& resourceInOut);
+
+ /// Sets the current state of the resource (the current state is taken to be the future state once the command list has executed)
+ /// NOTE! This must be used with care, otherwise state tracking can be made incorrect.
+ void setState(D3D12_RESOURCE_STATES state);
+
+ /// Set the debug name on a resource
+ static void setDebugName(ID3D12Resource* resource, const char* name);
+
+ /// Set the the debug name on the resource
+ void setDebugName(const wchar_t* name);
+ /// Set the debug name
+ void setDebugName(const char* name);
+};
+
+} // renderer_test
diff --git a/tools/slang-test/README.md b/tools/slang-test/README.md
index 95ed497ba..f4418311d 100644
--- a/tools/slang-test/README.md
+++ b/tools/slang-test/README.md
@@ -9,13 +9,15 @@ Most command line options are prefixed by - for both switches and parameter opti
An example command line:
```
-slang-test -bindir "E:\slang\bin\windows-x64\Debug\\" -category full tests/compute/array-param
+slang-test -bindir E:\slang\bin\windows-x64\Debug\\ -category full tests/compute/array-param
```
* The -bindir value means that the tools slang-test will use the binaries found in this directory.
* The -category full means that all tests can be run.
* The final 'free parameter' is 'tests/compute/array-param' and means only tests starting with this string will run.
+It may also be necessary to have the working directory the root directory of the slang distribution - in the example about this would be "E:\slang\".
+
## Test Categories
There are the following test categories