diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2018-04-02 19:59:33 -0400 |
|---|---|---|
| committer | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-04-02 16:59:33 -0700 |
| commit | 38d5ef4764e9271ce2360f42b0759a236cddd9fe (patch) | |
| tree | 6c8eafb72eb997167dd77e3d1d5abc3582c5789a /tools/render-test | |
| parent | 5a0273848aa4b854bb3c99a81701b044a8929bf8 (diff) | |
Feature/dx12 (#469)
* Fix signed/unsigned comparison warning.
* Split out d3d functions that will work across dx11 and 12.
* Improve slang-test/README.md around command line options.
* Make Guid comparison honor alignment for comparisons, such that mechanism work on architectures that can only do aligned accesses.
* Initial setup of D3D12 Renderer, with presentFrame and clearFrame.
* More support for D3D12
* Added FreeList
* Added D3D12CircularResourceHeap
* First attempt at createBuffer
* First pass at map/unmap.
* First pass binding vertex/constant buffers, and setting up InputLayout. Note that memory is not kept in scope on binding yet.
* First pass of D3DDescriptorHeap
* Small tidy up in render-d3d11. Added D3DDescriptorHeap to project.
* First pass at D3D12 bind state.
* Fix typos in D3D12Resource
* Tidy up Dx11 render binding a little to match more with Dx12 style.
* First pass at Dx12 BindingState
* Handling of the command list d3d12. Support for submitGpuWork and waitForGpu.
* First attempt at Dx12 capture of backbuffer to file.
* First attempt at D3D12 binding for graphics.
* D3D12 setup viewport etc - does now render triangle in render0.hlsl.
* First pass at support for compute on D3D12Renderer
* Use spaces over tabs in D3DUtil
* Tabs to spaces in D3D12DescriptorHeap
* Convert tab->spaces on render-d3d12.cpp
Diffstat (limited to 'tools/render-test')
| -rw-r--r-- | tools/render-test/circular-resource-heap-d3d12.cpp | 221 | ||||
| -rw-r--r-- | tools/render-test/circular-resource-heap-d3d12.h | 206 | ||||
| -rw-r--r-- | tools/render-test/d3d-util.cpp | 280 | ||||
| -rw-r--r-- | tools/render-test/d3d-util.h | 61 | ||||
| -rw-r--r-- | tools/render-test/descriptor-heap-d3d12.cpp | 47 | ||||
| -rw-r--r-- | tools/render-test/descriptor-heap-d3d12.h | 115 | ||||
| -rw-r--r-- | tools/render-test/main.cpp | 12 | ||||
| -rw-r--r-- | tools/render-test/render-d3d11.cpp | 168 | ||||
| -rw-r--r-- | tools/render-test/render-d3d12.cpp | 2248 | ||||
| -rw-r--r-- | tools/render-test/render-gl.cpp | 2 | ||||
| -rw-r--r-- | tools/render-test/render-test.vcxproj | 8 | ||||
| -rw-r--r-- | tools/render-test/render-test.vcxproj.filters | 24 | ||||
| -rw-r--r-- | tools/render-test/render-vk.cpp | 2 | ||||
| -rw-r--r-- | tools/render-test/render.h | 6 | ||||
| -rw-r--r-- | tools/render-test/resource-d3d12.cpp | 214 | ||||
| -rw-r--r-- | tools/render-test/resource-d3d12.h | 178 |
16 files changed, 3563 insertions, 229 deletions
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 = ⦥ + } + + 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 = ⦥ + } + } + } + } + } + + if (m_boundBindingState && m_boundBindingState->m_samplerHeap.getUsedSize() > 0) + { + D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); + + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + range.NumDescriptors = m_boundBindingState->m_samplerHeap.getUsedSize(); + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_PARAMETER& param = params.nextParameter(); + + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; + table.NumDescriptorRanges = 1; + table.pDescriptorRanges = ⦥ + } + return SLANG_OK; +} + +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 |
