From 68d705f6c805c9b4d31b386e065762e6db13ad18 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Fri, 3 Aug 2018 08:39:28 -0700 Subject: Major overhaul of Renderer abstraction, to support a new example (#624) The original goal here was to bring up a second example program: `model-viewer`. While the existing `hello-world` example is enough to get somebody up to speed with the basics of the Slang API (as a drop-in replacement for `D3DCompile` or similar), it doesn't really show any of the big-picture stuff that Slang is meant to enable. There wasn't any use of D3D12/Vulkan descriptor tables/sets, and there wasn't any use of interfaces, generics, or `ParameterBlock`s in the shader code. The `model-viewer` example addresses these issues. Its shader code involves generics, interfaces, and multiple `ParameterBlock`s, and the host-side code demonstrates a few key things for working with Slang: * There is an application-level abstraction for parameter blocks, that combines the graphics-API descriptor set object with Slang type information * There is a shader cache layer used to look up an appropriate variant of a rendering effect by using parameter block types to "plug in" global type variables * There is a clear separation between the phases of compilation: a first phase that does semantic checking and enables reflection-based allocation of graphics API objects, followed by one or more code generation passes for specialized kernels. This example is certainly not perfect, and it will need to be revamped more going forward. In particular: * The output picture is ugly as sin. We need a plan for how to get this to load better content, perhaps even popping up an error message to note that the required input data isn't present in the basic repository. * The shader code is too simplistic. There isn't any real material variety, and the `IMaterial` abstraction is completely wrong. * The use of parameter blocks is facile because there are no resource parameters right now. Fixing that will likely expose issues around interfacing with Slang's reflection API. * The whole example exposes the issue that Slang's current APIs aren't really designed for the benefit of two-phase compilation (since our many client application has been stuck on one-phase compilation). * Global type parameters are actually a Bad Idea that we only did for compatibility with existing codebases. We should not be showing them off in an example of the Right Way to use Slang, but the language support for type parameters on entry points is still not complete. Of course, the majority of the changes here are *not* inside the example applications, and instead involve a major overhaul of the `Renderer` abstraction that is used for both tests and examples. The main thrust of the change is to make the abstraction layer be closer to the D3D12/Vulkan model than to a D3D11-style model. This is important for the `model-viewer` example, since it aspires to show how Slang can be incorporated into a renderer that targets a modern API. The most important bit is actually the use of descriptor sets and "pipeline layouts" a la Vulkan, since without these Slang's `ParameterBlock` abstraction won't make a lot of sense. Implementation of the abstraction for the various APIs has very much been on an as-needed basis. The current implementation is just enough for the two examples to work, plus enough to get all the tests to pass in both debug and release builds on Windows. A big missing feature in the API abstraction right now is memory lifetime management. The code had been trending toward something D3D11-like where a constant buffer could be mapped per-frame with the implementation doing behind-the-scenes allocation for targets like D3D12/Vulkan. I'd like to shift more toward a model of just exposing "transient" allocations that are only valid for one frame, because these are more representation of how an efficient renderer for next-generation APIs will work. That transition isn't actually complete, though, so there are problems with the existing examples where `hello-world` is actually scribbling into memory that the GPU might still be using, while `model-viewer` is doing full-on heavy-weight allocations on a per-frame basis with no real concern for the performance implications. All together, there are a lot of things here that need more work, but this branch has been way too long-lived already, and so I'd like to get this checked in as long as all the tests pass. --- tools/gfx/circular-resource-heap-d3d12.cpp | 222 ++ tools/gfx/circular-resource-heap-d3d12.h | 206 ++ tools/gfx/d3d-util.cpp | 306 ++ tools/gfx/d3d-util.h | 61 + tools/gfx/descriptor-heap-d3d12.cpp | 47 + tools/gfx/descriptor-heap-d3d12.h | 198 ++ tools/gfx/gfx.vcxproj | 215 ++ tools/gfx/gfx.vcxproj.filters | 120 + tools/gfx/model.cpp | 530 +++ tools/gfx/model.h | 73 + tools/gfx/render-d3d11.cpp | 2112 ++++++++++++ tools/gfx/render-d3d11.h | 10 + tools/gfx/render-d3d12.cpp | 3557 ++++++++++++++++++++ tools/gfx/render-d3d12.h | 10 + tools/gfx/render-gl.cpp | 1426 ++++++++ tools/gfx/render-gl.h | 10 + tools/gfx/render-vk.cpp | 2569 ++++++++++++++ tools/gfx/render-vk.h | 10 + tools/gfx/render.cpp | 391 +++ tools/gfx/render.h | 869 +++++ tools/gfx/resource-d3d12.cpp | 214 ++ tools/gfx/resource-d3d12.h | 178 + tools/gfx/surface.cpp | 222 ++ tools/gfx/surface.h | 86 + tools/gfx/vector-math.h | 14 + tools/gfx/vk-api.cpp | 138 + tools/gfx/vk-api.h | 196 ++ tools/gfx/vk-device-queue.cpp | 199 ++ tools/gfx/vk-device-queue.h | 94 + tools/gfx/vk-module.cpp | 76 + tools/gfx/vk-module.h | 39 + tools/gfx/vk-swap-chain.cpp | 421 +++ tools/gfx/vk-swap-chain.h | 141 + tools/gfx/vk-util.cpp | 59 + tools/gfx/vk-util.h | 41 + tools/gfx/window.cpp | 289 ++ tools/gfx/window.h | 78 + tools/render-test/main.cpp | 117 +- tools/render-test/options.h | 2 +- tools/render-test/png-serialize-util.h | 2 +- tools/render-test/render-test.vcxproj | 10 +- tools/render-test/shader-input-layout.h | 2 +- tools/render-test/shader-renderer-util.cpp | 251 +- tools/render-test/shader-renderer-util.h | 56 +- tools/render-test/slang-support.cpp | 4 +- tools/render-test/slang-support.h | 4 +- .../circular-resource-heap-d3d12.cpp | 222 -- .../slang-graphics/circular-resource-heap-d3d12.h | 206 -- tools/slang-graphics/d3d-util.cpp | 306 -- tools/slang-graphics/d3d-util.h | 61 - tools/slang-graphics/descriptor-heap-d3d12.cpp | 47 - tools/slang-graphics/descriptor-heap-d3d12.h | 115 - tools/slang-graphics/render-d3d11.cpp | 1101 ------ tools/slang-graphics/render-d3d11.h | 10 - tools/slang-graphics/render-d3d12.cpp | 2467 -------------- tools/slang-graphics/render-d3d12.h | 10 - tools/slang-graphics/render-gl.cpp | 1049 ------ tools/slang-graphics/render-gl.h | 10 - tools/slang-graphics/render-vk.cpp | 2019 ----------- tools/slang-graphics/render-vk.h | 10 - tools/slang-graphics/render.cpp | 390 --- tools/slang-graphics/render.h | 583 ---- tools/slang-graphics/resource-d3d12.cpp | 214 -- tools/slang-graphics/resource-d3d12.h | 178 - tools/slang-graphics/slang-graphics.vcxproj | 212 -- .../slang-graphics/slang-graphics.vcxproj.filters | 111 - tools/slang-graphics/surface.cpp | 222 -- tools/slang-graphics/surface.h | 86 - tools/slang-graphics/vk-api.cpp | 138 - tools/slang-graphics/vk-api.h | 196 -- tools/slang-graphics/vk-device-queue.cpp | 199 -- tools/slang-graphics/vk-device-queue.h | 94 - tools/slang-graphics/vk-module.cpp | 76 - tools/slang-graphics/vk-module.h | 39 - tools/slang-graphics/vk-swap-chain.cpp | 421 --- tools/slang-graphics/vk-swap-chain.h | 141 - tools/slang-graphics/vk-util.cpp | 59 - tools/slang-graphics/vk-util.h | 41 - tools/slang-graphics/window.cpp | 245 -- tools/slang-graphics/window.h | 69 - 80 files changed, 15780 insertions(+), 11442 deletions(-) create mode 100644 tools/gfx/circular-resource-heap-d3d12.cpp create mode 100644 tools/gfx/circular-resource-heap-d3d12.h create mode 100644 tools/gfx/d3d-util.cpp create mode 100644 tools/gfx/d3d-util.h create mode 100644 tools/gfx/descriptor-heap-d3d12.cpp create mode 100644 tools/gfx/descriptor-heap-d3d12.h create mode 100644 tools/gfx/gfx.vcxproj create mode 100644 tools/gfx/gfx.vcxproj.filters create mode 100644 tools/gfx/model.cpp create mode 100644 tools/gfx/model.h create mode 100644 tools/gfx/render-d3d11.cpp create mode 100644 tools/gfx/render-d3d11.h create mode 100644 tools/gfx/render-d3d12.cpp create mode 100644 tools/gfx/render-d3d12.h create mode 100644 tools/gfx/render-gl.cpp create mode 100644 tools/gfx/render-gl.h create mode 100644 tools/gfx/render-vk.cpp create mode 100644 tools/gfx/render-vk.h create mode 100644 tools/gfx/render.cpp create mode 100644 tools/gfx/render.h create mode 100644 tools/gfx/resource-d3d12.cpp create mode 100644 tools/gfx/resource-d3d12.h create mode 100644 tools/gfx/surface.cpp create mode 100644 tools/gfx/surface.h create mode 100644 tools/gfx/vector-math.h create mode 100644 tools/gfx/vk-api.cpp create mode 100644 tools/gfx/vk-api.h create mode 100644 tools/gfx/vk-device-queue.cpp create mode 100644 tools/gfx/vk-device-queue.h create mode 100644 tools/gfx/vk-module.cpp create mode 100644 tools/gfx/vk-module.h create mode 100644 tools/gfx/vk-swap-chain.cpp create mode 100644 tools/gfx/vk-swap-chain.h create mode 100644 tools/gfx/vk-util.cpp create mode 100644 tools/gfx/vk-util.h create mode 100644 tools/gfx/window.cpp create mode 100644 tools/gfx/window.h delete mode 100644 tools/slang-graphics/circular-resource-heap-d3d12.cpp delete mode 100644 tools/slang-graphics/circular-resource-heap-d3d12.h delete mode 100644 tools/slang-graphics/d3d-util.cpp delete mode 100644 tools/slang-graphics/d3d-util.h delete mode 100644 tools/slang-graphics/descriptor-heap-d3d12.cpp delete mode 100644 tools/slang-graphics/descriptor-heap-d3d12.h delete mode 100644 tools/slang-graphics/render-d3d11.cpp delete mode 100644 tools/slang-graphics/render-d3d11.h delete mode 100644 tools/slang-graphics/render-d3d12.cpp delete mode 100644 tools/slang-graphics/render-d3d12.h delete mode 100644 tools/slang-graphics/render-gl.cpp delete mode 100644 tools/slang-graphics/render-gl.h delete mode 100644 tools/slang-graphics/render-vk.cpp delete mode 100644 tools/slang-graphics/render-vk.h delete mode 100644 tools/slang-graphics/render.cpp delete mode 100644 tools/slang-graphics/render.h delete mode 100644 tools/slang-graphics/resource-d3d12.cpp delete mode 100644 tools/slang-graphics/resource-d3d12.h delete mode 100644 tools/slang-graphics/slang-graphics.vcxproj delete mode 100644 tools/slang-graphics/slang-graphics.vcxproj.filters delete mode 100644 tools/slang-graphics/surface.cpp delete mode 100644 tools/slang-graphics/surface.h delete mode 100644 tools/slang-graphics/vk-api.cpp delete mode 100644 tools/slang-graphics/vk-api.h delete mode 100644 tools/slang-graphics/vk-device-queue.cpp delete mode 100644 tools/slang-graphics/vk-device-queue.h delete mode 100644 tools/slang-graphics/vk-module.cpp delete mode 100644 tools/slang-graphics/vk-module.h delete mode 100644 tools/slang-graphics/vk-swap-chain.cpp delete mode 100644 tools/slang-graphics/vk-swap-chain.h delete mode 100644 tools/slang-graphics/vk-util.cpp delete mode 100644 tools/slang-graphics/vk-util.h delete mode 100644 tools/slang-graphics/window.cpp delete mode 100644 tools/slang-graphics/window.h (limited to 'tools') diff --git a/tools/gfx/circular-resource-heap-d3d12.cpp b/tools/gfx/circular-resource-heap-d3d12.cpp new file mode 100644 index 000000000..20e47c4dd --- /dev/null +++ b/tools/gfx/circular-resource-heap-d3d12.cpp @@ -0,0 +1,222 @@ +#include "circular-resource-heap-d3d12.h" + +namespace gfx { +using namespace Slang; + +D3D12CircularResourceHeap::D3D12CircularResourceHeap(): + m_fence(nullptr), + m_device(nullptr), + m_blockFreeList(sizeof(Block), SLANG_ALIGN_OF(Block), 16), + m_blocks(nullptr) +{ + m_back.m_block = nullptr; + m_back.m_position = nullptr; + m_front.m_block = nullptr; + m_front.m_position = nullptr; +} + +D3D12CircularResourceHeap::~D3D12CircularResourceHeap() +{ + _freeBlockListResources(m_blocks); +} + +void D3D12CircularResourceHeap::_freeBlockListResources(const Block* start) +{ + if (start) + { + const Block* block = start; + do + { + ID3D12Resource* resource = block->m_resource; + + resource->Unmap(0, nullptr); + resource->Release(); + + // Next in list + block = block->m_next; + + } while (block != start); + } +} + +Result D3D12CircularResourceHeap::init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence) +{ + assert(m_blocks == nullptr); + assert(desc.m_blockSize > 0); + + m_fence = fence; + m_desc = desc; + m_device = device; + + return SLANG_OK; +} + +void D3D12CircularResourceHeap::addSync(uint64_t signalValue) +{ + assert(signalValue == m_fence->getCurrentValue()); + PendingEntry entry; + entry.m_completedValue = signalValue; + entry.m_cursor = m_front; + m_pendingQueue.Add(entry); +} + +void D3D12CircularResourceHeap::updateCompleted() +{ + const uint64_t completedValue = m_fence->getCompletedValue(); + +#if 0 + while (m_pendingQueue.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 resource; + Result res = m_device->CreateCommittedResource(&m_desc.m_heapProperties, m_desc.m_heapFlags, &desc, m_desc.m_initialState, nullptr, IID_PPV_ARGS(resource.writeRef())); + if (SLANG_FAILED(res)) + { + assert(!"Resource allocation failed"); + return nullptr; + } + + uint8_t* data = nullptr; + if (m_desc.m_heapProperties.Type == D3D12_HEAP_TYPE_READBACK) + { + } + else + { + // Map it, and keep it mapped + resource->Map(0, nullptr, (void**)&data); + } + + // We have no blocks -> so lets allocate the first + Block* block = (Block*)m_blockFreeList.allocate(); + block->m_next = nullptr; + + block->m_resource = resource.detach(); + block->m_start = data; + return block; +} + +D3D12CircularResourceHeap::Cursor D3D12CircularResourceHeap::allocate(size_t size, size_t alignment) +{ + const size_t blockSize = getBlockSize(); + + assert(size <= blockSize); + + // If nothing is allocated add the first block + if (m_blocks == nullptr) + { + Block* block = _newBlock(); + if (!block) + { + Cursor cursor = {}; + return cursor; + } + m_blocks = block; + // Make circular + block->m_next = block; + + // Point front and back to same position, as currently it is all free + m_back = { block, block->m_start }; + m_front = m_back; + } + + // If front and back are in the same block then front MUST be ahead of back (as that defined as + // an invariant and is required for block insertion to be possible + Block* block = m_front.m_block; + + // Check the invariant + assert(block != m_back.m_block || m_front.m_position >= m_back.m_position); + + { + uint8_t* cur = (uint8_t*)((size_t(m_front.m_position) + alignment - 1) & ~(alignment - 1)); + // Does the the allocation fit? + if (cur + size <= block->m_start + blockSize) + { + // It fits + // Move the front forward + m_front.m_position = cur + size; + Cursor cursor = { block, cur }; + return cursor; + } + } + + // Okay I can't fit into current block... + + // If the next block contains front, we need to add a block, else we can use that block + if (block->m_next == m_back.m_block) + { + Block* newBlock = _newBlock(); + // Insert into the list + newBlock->m_next = block->m_next; + block->m_next = newBlock; + } + + // Use the block we are going to add to + block = block->m_next; + uint8_t* cur = (uint8_t*)((size_t(block->m_start) + alignment - 1) & ~(alignment - 1)); + // Does the the allocation fit? + if (cur + size > block->m_start + blockSize) + { + assert(!"Couldn't fit into a free block(!) Alignment breaks it?"); + Cursor cursor = {}; + return cursor; + } + // It fits + // Move the front forward + m_front.m_block = block; + m_front.m_position = cur + size; + Cursor cursor = { block, cur }; + return cursor; +} + +} // namespace gfx diff --git a/tools/gfx/circular-resource-heap-d3d12.h b/tools/gfx/circular-resource-heap-d3d12.h new file mode 100644 index 000000000..cca981601 --- /dev/null +++ b/tools/gfx/circular-resource-heap-d3d12.h @@ -0,0 +1,206 @@ +#pragma once + +#include "../../slang-com-ptr.h" +#include "../../source/core/list.h" +#include "../../source/core/slang-free-list.h" + +#include "resource-d3d12.h" + +namespace gfx { + +/*! \brief The D3D12CircularResourceHeap is a heap that is suited for size constrained real-time resources allocation that +is transitory in nature. It is designed to allocate resources which are used and discarded, often used where in +previous versions of DirectX the 'DISCARD' flag was used. + +The idea is to have a heap which chunks of resource can be allocated, and used for GPU execution, +and that the heap is able through the addSync/updateCompleted idiom is able to track when the usage of the resources is +completed allowing them to be reused. The heap is arranged as circularly, with new allocations made from the front, and the back +being updated as the GPU updating the back when it is informed anything using prior parts of the heap have completed. In this +arrangement all the heap between the back and the front can be thought of as in use or potentially in use by the GPU. All the heap +from the front back around to the back, is free and can be allocated from. It is the responsibility of the user of the Heap to make +sure the invariant holds, but in most normal usage it does so simply. + +Another feature of the heap is that it does not require upfront knowledge of how big a heap is needed. The backing resources will be expanded +dynamically with requests as needed. The only requirement is that know single request can be larger than m_blockSize specified in the Desc +used to initialize the heap. This is because all the backing resources are allocated to a single size. This limitation means the D3D12CircularResourceHeap +may not be the best use for example for uploading a texture - because it's design is really around transitory uploads or write backs, and so more suited +to constant buffers, vertex buffer, index buffers and the like. + +To upload a texture at program startup it is most likely better to use a D3D12ResourceScopeManager. + +\code{.cpp} + +typedef D3D12CircularResourceHeap Heap; + +Heap::Cursor cursor = heap.allocateVertexBuffer(sizeof(Vertex) * numVerts); +Memory:copy(cursor.m_position, verts, sizeof(Vertex) * numVerts); + +// Do a command using the GPU handle +m_commandList->... +// Do another command using the GPU handle + +m_commandList->... + +// Execute the command list on the command queue +{ + ID3D12CommandList* lists[] = { m_commandList }; + m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(lists), lists); +} + +// Add a sync point +const uint64_t signalValue = m_fence.nextSignal(m_commandQueue); +heap.addSync(signalValue) + +// The cursors cannot be used anymore + +// At some later point call updateCompleted. This will see where the GPU is at, and make resources available that the GPU no longer accesses. +heap.updateCompleted(); + +\endcode + +### Implementation + +Front and back can be in the same block, but ONLY if back is behind front, because we have to always be able to insert +new blocks in front of front. So it must be possible to do an block insertion between the two of them. + +|--B---F-----| |----------| + +When B and F are on top of one another it means there is nothing in the list. NOTE this also means that a move of front can never place it +top of the back. + +https://msdn.microsoft.com/en-us/library/windows/desktop/dn899125%28v=vs.85%29.aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx +*/ + +class D3D12CircularResourceHeap +{ + protected: + struct Block; + public: + typedef D3D12CircularResourceHeap ThisType; + + /// The alignment used for VERTEX_BUFFER allocations + /// Strictly speaking it seems the hardware can handle 4 byte alignment, but since often in use + /// data will be copied from CPU memory to the allocation, using 16 byte alignment is superior as allows + /// significantly faster memcpy. + /// The sample that shows sizeof(float) - 4 bytes is appropriate is at the link below. + /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx + enum + { + VERTEX_BUFFER_ALIGNMENT = 16, + }; + + struct Desc + { + void init() + { + { + D3D12_HEAP_PROPERTIES& props = m_heapProperties; + + props.Type = D3D12_HEAP_TYPE_UPLOAD; + props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + props.CreationNodeMask = 1; + props.VisibleNodeMask = 1; + } + m_heapFlags = D3D12_HEAP_FLAG_NONE; + m_initialState = D3D12_RESOURCE_STATE_GENERIC_READ; + m_blockSize = 0; + } + + D3D12_HEAP_PROPERTIES m_heapProperties; + D3D12_HEAP_FLAGS m_heapFlags; + D3D12_RESOURCE_STATES m_initialState; + size_t m_blockSize; + }; + + /// Cursor position + struct Cursor + { + /// Get GpuHandle + SLANG_FORCE_INLINE D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle() const { return m_block->m_resource->GetGPUVirtualAddress() + size_t(m_position - m_block->m_start); } + /// Must have a block and position + SLANG_FORCE_INLINE bool isValid() const { return m_block != nullptr; } + /// Calculate the offset into the underlying resource + SLANG_FORCE_INLINE size_t getOffset() const { return size_t(m_position - m_block->m_start); } + /// Get the underlying resource + SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_block->m_resource; } + + Block* m_block; ///< The block index + uint8_t* m_position; ///< The current position + }; + + /// Get the desc used to initialize the heap + SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } + + /// Must be called before used + /// Block size must be at least as large as the _largest_ thing allocated + /// Also note depending on alignment of a resource allocation, the block size might also need to take into account the + /// maximum alignment use. It is a REQUIREMENT that a newly allocated resource block is large enough to hold any + /// allocation taking into account the alignment used. + Slang::Result init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence); + + /// Get the block size + SLANG_FORCE_INLINE size_t getBlockSize() const { return m_desc.m_blockSize; } + + /// Allocate constant buffer of specified size + Cursor allocate(size_t size, size_t alignment); + + /// Allocate a constant buffer + SLANG_FORCE_INLINE Cursor allocateConstantBuffer(size_t size) { return allocate(size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); } + /// Allocate a vertex buffer + SLANG_FORCE_INLINE Cursor allocateVertexBuffer(size_t size) { return allocate(size, VERTEX_BUFFER_ALIGNMENT); } + + /// Create filled in constant buffer + SLANG_FORCE_INLINE Cursor newConstantBuffer(const void* data, size_t size) { Cursor cursor = allocateConstantBuffer(size); ::memcpy(cursor.m_position, data, size); return cursor; } + /// Create in filled in constant buffer + template + SLANG_FORCE_INLINE Cursor newConstantBuffer(const T& in) { return newConstantBuffer(&in, sizeof(T)); } + + /// Look where the GPU has got to and release anything not currently used + void updateCompleted(); + /// Add a sync point - meaning that when this point is hit in the queue + /// all of the resources up to this point will no longer be used. + void addSync(uint64_t signalValue); + + /// Get the gpu address of this cursor + D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle(const Cursor& cursor) const { return cursor.m_block->m_resource->GetGPUVirtualAddress() + size_t(cursor.m_position - cursor.m_block->m_start); } + + /// Ctor + D3D12CircularResourceHeap(); + /// Dtor + ~D3D12CircularResourceHeap(); + + protected: + + struct Block + { + ID3D12Resource* m_resource; ///< The mapped resource + uint8_t* m_start; ///< Once created the resource is mapped to here + Block* m_next; ///< Points to next block in the list + }; + struct PendingEntry + { + uint64_t m_completedValue; ///< The value when this is completed + Cursor m_cursor; ///< the cursor at that point + }; + void _freeBlockListResources(const Block* block); + /// Create a new block (with associated resource), do not add the block list + Block* _newBlock(); + + Block* m_blocks; ///< Circular singly linked list of block. nullptr initially + Slang::FreeList m_blockFreeList; ///< Free list of actual allocations of blocks + Slang::List m_pendingQueue; ///< Holds the list of pending positions. When the fence value is greater than the value on the queue entry, the entry is done. + + // Allocation is made from the front, and freed from the back. + Cursor m_back; ///< Current back position. + Cursor m_front; ///< Current front position. + + Desc m_desc; ///< Describes the heap + + D3D12CounterFence* m_fence; ///< The fence to use + ID3D12Device* m_device; ///< The device that resources will be constructed on +}; + +} // namespace gfx + diff --git a/tools/gfx/d3d-util.cpp b/tools/gfx/d3d-util.cpp new file mode 100644 index 000000000..19135707b --- /dev/null +++ b/tools/gfx/d3d-util.cpp @@ -0,0 +1,306 @@ +// d3d-util.cpp +#include "d3d-util.h" + +#include + +// We will use the C standard library just for printing error messages. +#include + +namespace gfx { +using namespace Slang; + +/* static */D3D_PRIMITIVE_TOPOLOGY D3DUtil::getPrimitiveTopology(PrimitiveTopology topology) +{ + switch (topology) + { + case PrimitiveTopology::TriangleList: + { + return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + } + default: break; + } + return D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; +} + +/* static */DXGI_FORMAT D3DUtil::getMapFormat(Format format) +{ + switch (format) + { + case Format::RGBA_Float32: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case Format::RGB_Float32: return DXGI_FORMAT_R32G32B32_FLOAT; + case Format::RG_Float32: return DXGI_FORMAT_R32G32_FLOAT; + case Format::R_Float32: return DXGI_FORMAT_R32_FLOAT; + case Format::RGBA_Unorm_UInt8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case Format::R_UInt32: return DXGI_FORMAT_R32_UINT; + + case Format::D_Float32: return DXGI_FORMAT_D32_FLOAT; + case Format::D_Unorm24_S8: return DXGI_FORMAT_D24_UNORM_S8_UINT; + + default: return DXGI_FORMAT_UNKNOWN; + } +} + +/* static */DXGI_FORMAT D3DUtil::calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format) +{ + SLANG_UNUSED(usage); + if (usageFlags) + { + switch (format) + { + case DXGI_FORMAT_R32_FLOAT: /* fallthru */ + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_D32_FLOAT: + { + return DXGI_FORMAT_R32_TYPELESS; + } + case DXGI_FORMAT_D24_UNORM_S8_UINT: return DXGI_FORMAT_R24G8_TYPELESS; + default: break; + } + return format; + } + return format; +} + +/* static */DXGI_FORMAT D3DUtil::calcFormat(UsageType usage, DXGI_FORMAT format) +{ + switch (usage) + { + case USAGE_COUNT_OF: + case USAGE_UNKNOWN: + { + return DXGI_FORMAT_UNKNOWN; + } + case USAGE_DEPTH_STENCIL: + { + switch (format) + { + case DXGI_FORMAT_D32_FLOAT: /* fallthru */ + case DXGI_FORMAT_R32_TYPELESS: + { + return DXGI_FORMAT_D32_FLOAT; + } + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; + case DXGI_FORMAT_R24G8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; + default: break; + } + return format; + } + case USAGE_TARGET: + { + switch (format) + { + case DXGI_FORMAT_D32_FLOAT: /* fallthru */ + case DXGI_FORMAT_D24_UNORM_S8_UINT: + { + return DXGI_FORMAT_UNKNOWN; + } + case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT; + default: break; + } + return format; + } + case USAGE_SRV: + { + switch (format) + { + case DXGI_FORMAT_D32_FLOAT: /* fallthru */ + case DXGI_FORMAT_R32_TYPELESS: + { + return DXGI_FORMAT_R32_FLOAT; + } + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + default: break; + } + + return format; + } + } + + assert(!"Not reachable"); + return DXGI_FORMAT_UNKNOWN; +} + +bool D3DUtil::isTypeless(DXGI_FORMAT format) +{ + switch (format) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC7_TYPELESS: + { + return true; + } + default: break; + } + return false; +} + +/* static */Int D3DUtil::getNumColorChannelBits(DXGI_FORMAT fmt) +{ + switch (fmt) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + { + return 32; + } + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + { + return 16; + } + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + { + return 10; + } + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + { + return 8; + } + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + { + return 5; + } + case DXGI_FORMAT_B4G4R4A4_UNORM: + return 4; + + default: + return 0; + } +} + +// Note: this subroutine is now only used by D3D11 for generating bytecode to go into input layouts. +// +// TODO: we can probably remove that code completely by switching to a PSO-like model across all APIs. +// +/* static */Result D3DUtil::compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, ComPtr& shaderBlobOut) +{ + // Rather than statically link against the `d3dcompile` library, we + // dynamically load it. + // + // Note: A more realistic application would compile from HLSL text to D3D + // shader bytecode as part of an offline process, rather than doing it + // on-the-fly like this + // + static pD3DCompile compileFunc = nullptr; + if (!compileFunc) + { + // TODO(tfoley): maybe want to search for one of a few versions of the DLL + HMODULE compilerModule = LoadLibraryA("d3dcompiler_47.dll"); + if (!compilerModule) + { + fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n"); + return SLANG_FAIL; + } + + compileFunc = (pD3DCompile)GetProcAddress(compilerModule, "D3DCompile"); + if (!compileFunc) + { + fprintf(stderr, "error: failed load symbol 'D3DCompile'\n"); + return SLANG_FAIL; + } + } + + // For this example, we turn on debug output, and turn off all + // optimization. A real application would only use these flags + // when shader debugging is needed. + UINT flags = 0; + flags |= D3DCOMPILE_DEBUG; + flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION; + + // We will always define `__HLSL__` when compiling here, so that + // input code can react differently to being compiled as pure HLSL. + D3D_SHADER_MACRO defines[] = { + { "__HLSL__", "1" }, + { nullptr, nullptr }, + }; + + // The `D3DCompile` entry point takes a bunch of parameters, but we + // don't really need most of them for Slang-generated code. + ComPtr shaderBlob; + ComPtr errorBlob; + + HRESULT hr = compileFunc(source, strlen(source), sourcePath, &defines[0], nullptr, entryPointName, dxProfileName, flags, 0, + shaderBlob.writeRef(), errorBlob.writeRef()); + + // If the HLSL-to-bytecode compilation produced any diagnostic messages + // then we will print them out (whether or not the compilation failed). + if (errorBlob) + { + ::fputs((const char*)errorBlob->GetBufferPointer(), stderr); + ::fflush(stderr); + ::OutputDebugStringA((const char*)errorBlob->GetBufferPointer()); + } + + SLANG_RETURN_ON_FAIL(hr); + shaderBlobOut.swap(shaderBlob); + return SLANG_OK; +} + +/* static */void D3DUtil::appendWideChars(const char* in, List& out) +{ + size_t len = ::strlen(in); + + const DWORD dwFlags = 0; + int outSize = ::MultiByteToWideChar(CP_UTF8, dwFlags, in, int(len), nullptr, 0); + + if (outSize > 0) + { + const 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/gfx/d3d-util.h b/tools/gfx/d3d-util.h new file mode 100644 index 000000000..04bfae63d --- /dev/null +++ b/tools/gfx/d3d-util.h @@ -0,0 +1,61 @@ +// d3d-util.h +#pragma once + +#include + +#include "../../slang-com-helper.h" + +#include "../../slang-com-ptr.h" +#include "../../source/core/list.h" + +#include "render.h" + +#include +#include + +namespace gfx { + +class D3DUtil +{ + public: + enum UsageType + { + USAGE_UNKNOWN, ///< Generally used to mark an error + USAGE_TARGET, ///< Format should be used when written as target + USAGE_DEPTH_STENCIL, ///< Format should be used when written as depth stencil + USAGE_SRV, ///< Format if being read as srv + USAGE_COUNT_OF, + }; + enum UsageFlag + { + USAGE_FLAG_MULTI_SAMPLE = 0x1, ///< If set will be used form multi sampling (such as MSAA) + USAGE_FLAG_SRV = 0x2, ///< If set means will be used as a shader resource view (SRV) + }; + + /// Get primitive topology as D3D primitive topology + static D3D_PRIMITIVE_TOPOLOGY getPrimitiveTopology(PrimitiveTopology prim); + + /// Calculate size taking into account alignment. Alignment must be a power of 2 + static UInt calcAligned(UInt size, UInt alignment) { return (size + alignment - 1) & ~(alignment - 1); } + + /// Compile HLSL code to DXBC + static Slang::Result compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, Slang::ComPtr& shaderBlobOut); + + /// Given a slang pixel format returns the equivalent DXGI_ pixel format. If the format is not known, will return DXGI_FORMAT_UNKNOWN + static DXGI_FORMAT getMapFormat(Format format); + + /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible + static DXGI_FORMAT calcFormat(UsageType usage, DXGI_FORMAT format); + /// Calculate appropriate format for creating a buffer for usage and flags + static DXGI_FORMAT calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format); + /// True if the type is 'typeless' + static bool isTypeless(DXGI_FORMAT format); + + /// Returns number of bits used for color channel for format (for channels with multiple sizes, returns smallest ie RGB565 -> 5) + static Int getNumColorChannelBits(DXGI_FORMAT fmt); + + /// Append text in in, into wide char array + static void appendWideChars(const char* in, Slang::List& out); +}; + +} // renderer_test diff --git a/tools/gfx/descriptor-heap-d3d12.cpp b/tools/gfx/descriptor-heap-d3d12.cpp new file mode 100644 index 000000000..382fc3219 --- /dev/null +++ b/tools/gfx/descriptor-heap-d3d12.cpp @@ -0,0 +1,47 @@ + +#include "descriptor-heap-d3d12.h" + +namespace gfx { +using namespace Slang; + +D3D12DescriptorHeap::D3D12DescriptorHeap(): + m_totalSize(0), + m_currentIndex(0), + m_descriptorSize(0) +{ +} + +Result D3D12DescriptorHeap::init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags) +{ + D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {}; + srvHeapDesc.NumDescriptors = size; + srvHeapDesc.Flags = flags; + srvHeapDesc.Type = type; + SLANG_RETURN_ON_FAIL(device->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(m_heap.writeRef()))); + + m_descriptorSize = device->GetDescriptorHandleIncrementSize(type); + m_totalSize = size; + + return SLANG_OK; +} + +Result D3D12DescriptorHeap::init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags) +{ + SLANG_RETURN_ON_FAIL(init(device, numHandles, type, flags)); + D3D12_CPU_DESCRIPTOR_HANDLE dst = m_heap->GetCPUDescriptorHandleForHeapStart(); + + // Copy them all + for (int i = 0; i < numHandles; i++, dst.ptr += m_descriptorSize) + { + D3D12_CPU_DESCRIPTOR_HANDLE src = handles[i]; + if (src.ptr != 0) + { + device->CopyDescriptorsSimple(1, dst, src, type); + } + } + + return SLANG_OK; +} + +} // namespace gfx + diff --git a/tools/gfx/descriptor-heap-d3d12.h b/tools/gfx/descriptor-heap-d3d12.h new file mode 100644 index 000000000..2a814583b --- /dev/null +++ b/tools/gfx/descriptor-heap-d3d12.h @@ -0,0 +1,198 @@ +#pragma once + + +#include +#include + +#include "../../slang-com-ptr.h" +#include "../../source/core/list.h" + +namespace gfx { + +/*! \brief A simple class to manage an underlying Dx12 Descriptor Heap. Allocations are made linearly in order. It is not possible to free +individual allocations, but all allocations can be deallocated with 'deallocateAll'. */ +class D3D12DescriptorHeap +{ + public: + typedef D3D12DescriptorHeap ThisType; + + /// Initialize + Slang::Result init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags); + /// Initialize with an array of handles copying over the representation + Slang::Result init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags); + + /// Returns the number of slots that have been used + SLANG_FORCE_INLINE int getUsedSize() const { return m_currentIndex; } + + /// Get the total amount of descriptors possible on the heap + SLANG_FORCE_INLINE int getTotalSize() const { return m_totalSize; } + /// Allocate a descriptor. Returns the index, or -1 if none left. + SLANG_FORCE_INLINE int allocate(); + /// Allocate a number of descriptors. Returns the start index (or -1 if not possible) + SLANG_FORCE_INLINE int allocate(int numDescriptors); + + /// + SLANG_FORCE_INLINE int placeAt(int index); + + /// Deallocates all allocations, and starts allocation from the start of the underlying heap again + SLANG_FORCE_INLINE void deallocateAll() { m_currentIndex = 0; } + + /// Get the size of each + SLANG_FORCE_INLINE int getDescriptorSize() const { return m_descriptorSize; } + + /// Get the GPU heap start + SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuStart() const { return m_heap->GetGPUDescriptorHandleForHeapStart(); } + /// Get the CPU heap start + SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuStart() const { return m_heap->GetCPUDescriptorHandleForHeapStart(); } + + /// Get the GPU handle at the specified index + SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(int index) const; + /// Get the CPU handle at the specified index + SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(int index) const; + + /// Get the underlying heap + SLANG_FORCE_INLINE ID3D12DescriptorHeap* getHeap() const { return m_heap; } + + /// Ctor + D3D12DescriptorHeap(); + +protected: + Slang::ComPtr m_heap; ///< The underlying heap being allocated from + int m_totalSize; ///< Total amount of allocations available on the heap + int m_currentIndex; ///< The current descriptor + int m_descriptorSize; ///< The size of each descriptor +}; + +/// A host-visible descriptor, used as "backing storage" for a view. +/// +/// This type is intended to be used to represent descriptors that +/// are allocated and freed through a `HostVisibleDescriptorAllocator`. +struct D3D12HostVisibleDescriptor +{ + D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle; +}; + +/// An allocator for host-visible descriptors. +/// +/// Unlike the `D3D12DescriptorHeap` type, this class allows for both +/// allocation and freeing of descriptors, by maintaining a free list. +/// In order to keep the implementation simple, this class only supports +/// allocation of single descriptors and not ranges. +/// +class D3D12HostVisibleDescriptorAllocator +{ + ID3D12Device* m_device; + int m_chunkSize; + D3D12_DESCRIPTOR_HEAP_TYPE m_type; + + D3D12DescriptorHeap m_heap; + Slang::List m_freeList; + Slang::List m_heaps; + +public: + D3D12HostVisibleDescriptorAllocator() + {} + + Slang::Result init(ID3D12Device* device, int chunkSize, D3D12_DESCRIPTOR_HEAP_TYPE type) + { + m_device = device; + m_chunkSize = chunkSize; + m_type = type; + + SLANG_RETURN_ON_FAIL(m_heap.init(m_device, m_chunkSize, m_type, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); + + return SLANG_OK; + } + + Slang::Result allocate(D3D12HostVisibleDescriptor* outDescriptor) + { + // TODO: this allocator would take some work to make thread-safe + + if(m_freeList.Count() > 0) + { + auto descriptor = m_freeList[0]; + m_freeList.FastRemoveAt(0); + + *outDescriptor = descriptor; + return SLANG_OK; + } + + int index = m_heap.allocate(); + if(index < 0) + { + // Allocate a new heap and try again. + m_heaps.Add(m_heap); + SLANG_RETURN_ON_FAIL(m_heap.init(m_device, m_chunkSize, m_type, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); + + int index = m_heap.allocate(); + if(index < 0) + { + assert(!"descriptor allocation failed on fresh heap"); + return SLANG_FAIL; + } + } + + D3D12HostVisibleDescriptor descriptor; + descriptor.cpuHandle = m_heap.getCpuHandle(index); + + *outDescriptor = descriptor; + return SLANG_OK; + } + + void free(D3D12HostVisibleDescriptor descriptor) + { + m_freeList.Add(descriptor); + } +}; + +// --------------------------------------------------------------------------- +int D3D12DescriptorHeap::allocate() +{ + assert(m_currentIndex < m_totalSize); + if (m_currentIndex < m_totalSize) + { + return m_currentIndex++; + } + return -1; +} +// --------------------------------------------------------------------------- +int D3D12DescriptorHeap::allocate(int numDescriptors) +{ + assert(m_currentIndex + numDescriptors <= m_totalSize); + if (m_currentIndex + numDescriptors <= m_totalSize) + { + const int index = m_currentIndex; + m_currentIndex += numDescriptors; + return index; + } + return -1; +} +// --------------------------------------------------------------------------- +SLANG_FORCE_INLINE int D3D12DescriptorHeap::placeAt(int index) +{ + assert(index >= 0 && index < m_totalSize); + m_currentIndex = index + 1; + return index; +} + +// --------------------------------------------------------------------------- +SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getCpuHandle(int index) const +{ + assert(index >= 0 && index < m_totalSize); + D3D12_CPU_DESCRIPTOR_HANDLE start = m_heap->GetCPUDescriptorHandleForHeapStart(); + D3D12_CPU_DESCRIPTOR_HANDLE dst; + dst.ptr = start.ptr + m_descriptorSize * index; + return dst; +} +// --------------------------------------------------------------------------- +SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE D3D12DescriptorHeap::getGpuHandle(int index) const +{ + assert(index >= 0 && index < m_totalSize); + D3D12_GPU_DESCRIPTOR_HANDLE start = m_heap->GetGPUDescriptorHandleForHeapStart(); + D3D12_GPU_DESCRIPTOR_HANDLE dst; + dst.ptr = start.ptr + m_descriptorSize * index; + return dst; +} + +} // namespace gfx + diff --git a/tools/gfx/gfx.vcxproj b/tools/gfx/gfx.vcxproj new file mode 100644 index 000000000..cbafe84b1 --- /dev/null +++ b/tools/gfx/gfx.vcxproj @@ -0,0 +1,215 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {222F7498-B40C-4F3F-A704-DDEB91A4484A} + true + Win32Proj + gfx + 10.0.14393.0 + + + + StaticLibrary + true + Unicode + v140 + + + StaticLibrary + true + Unicode + v140 + + + StaticLibrary + false + Unicode + v140 + + + StaticLibrary + false + Unicode + v140 + + + + + + + + + + + + + + + + + + + ..\..\bin\windows-x86\debug\ + ..\..\intermediate\windows-x86\debug\gfx\ + gfx + .lib + + + ..\..\bin\windows-x64\debug\ + ..\..\intermediate\windows-x64\debug\gfx\ + gfx + .lib + + + ..\..\bin\windows-x86\release\ + ..\..\intermediate\windows-x86\release\gfx\ + gfx + .lib + + + ..\..\bin\windows-x64\release\ + ..\..\intermediate\windows-x64\release\gfx\ + gfx + .lib + + + + NotUsing + Level3 + _DEBUG;%(PreprocessorDefinitions) + ..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + + + "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../bin/windows-x86/debug/" + + + + + NotUsing + Level3 + _DEBUG;%(PreprocessorDefinitions) + ..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories) + EditAndContinue + Disabled + MultiThreadedDebug + + + Windows + true + + + "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../bin/windows-x64/debug/" + + + + + NotUsing + Level3 + NDEBUG;%(PreprocessorDefinitions) + ..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + + + "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../bin/windows-x86/release/" + + + + + NotUsing + Level3 + NDEBUG;%(PreprocessorDefinitions) + ..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + + + Windows + true + true + + + "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../bin/windows-x64/release/" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/gfx/gfx.vcxproj.filters b/tools/gfx/gfx.vcxproj.filters new file mode 100644 index 000000000..f1c7f7f5e --- /dev/null +++ b/tools/gfx/gfx.vcxproj.filters @@ -0,0 +1,120 @@ + + + + + {21EB8090-0D4E-1035-B6D3-48EBA215DCB7} + + + {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/tools/gfx/model.cpp b/tools/gfx/model.cpp new file mode 100644 index 000000000..c8218102e --- /dev/null +++ b/tools/gfx/model.cpp @@ -0,0 +1,530 @@ +// model.cpp +#include "model.h" + +#define TINYOBJLOADER_IMPLEMENTATION +#include "../../external/tinyobjloader/tiny_obj_loader.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "../../external/stb/stb_image.h" + +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "../../external/stb/stb_image_resize.h" + +#include "../../external/glm/glm/glm.hpp" +#include "../../external/glm/glm/gtc/matrix_transform.hpp" +#include "../../external/glm/glm/gtc/constants.hpp" + +#include +#include +#include + +namespace gfx { + +// TinyObj provides a tuple type that bundles up indices, but doesn't +// provide equality comparison or hashing for that type. We'd like +// to have a hash function so that we can unique indices. +// +// In the simplest case, we could define hashing and operator== operations +// directly on `tinobj::index_t`, but that would create problems if they +// revise their API. +// +// We will instead define our own wrapper type that supports equality +// comparisons. +// +struct ObjIndexKey +{ + tinyobj::index_t index; +}; + +bool operator==(ObjIndexKey const& left, ObjIndexKey const& right) +{ + return left.index.vertex_index == right.index.vertex_index + && left.index.normal_index == right.index.normal_index + && left.index.texcoord_index == right.index.texcoord_index; +} + +struct Hasher +{ + template + void add(T const& v) + { + state ^= std::hash()(v) + 0x9e3779b9 + (state << 6) + (state >> 2); + } + size_t state = 0; +}; + +struct SmoothingGroupVertexID +{ + size_t smoothingGroup; + size_t positionID; +}; +bool operator==(SmoothingGroupVertexID const& left, SmoothingGroupVertexID const& right) +{ + return left.smoothingGroup == right.smoothingGroup + && left.positionID == right.positionID; +} + +} + +namespace std +{ + template<> struct hash + { + size_t operator()(gfx::ObjIndexKey const& key) const + { + gfx::Hasher hasher; + hasher.add(key.index.vertex_index); + hasher.add(key.index.normal_index); + hasher.add(key.index.texcoord_index); + return hasher.state; + } + }; + + template<> struct hash + { + size_t operator()(gfx::SmoothingGroupVertexID const& id) const + { + gfx::Hasher hasher; + hasher.add(id.smoothingGroup); + hasher.add(id.positionID); + return hasher.state; + } + }; +} + +namespace gfx +{ + +RefPtr loadTextureImage( + Renderer* renderer, + char const* path) +{ + int extentX = 0; + int extentY = 0; + int originalChannelCount = 0; + int requestedChannelCount = 4; // force to 4-component result + stbi_uc* data = stbi_load( + path, + &extentX, + &extentY, + &originalChannelCount, + requestedChannelCount); + if(!data) + return nullptr; + + int channelCount = requestedChannelCount ? requestedChannelCount : originalChannelCount; + + Format format; + switch(channelCount) + { + default: + return nullptr; + + case 4: format = Format::RGBA_Unorm_UInt8; + + // TODO: handle other cases here if/when we stop forcing 4-component + // results when loading the image with stb_image. + } + + std::vector subresourceInitData; + std::vector mipRowStrides; + + ptrdiff_t stride = extentX * channelCount * sizeof(stbi_uc); + + subresourceInitData.push_back(data); + mipRowStrides.push_back(stride); + + // create down-sampled images for the different mip levels + bool generateMips = true; + if(generateMips) + { + int prevExtentX = extentX; + int prevExtentY = extentY; + stbi_uc* prevData = data; + ptrdiff_t prevStride = stride; + + for(;;) + { + if(prevExtentX == 1 && prevExtentY == 1) + break; + + int newExtentX = prevExtentX / 2; + int newExtentY = prevExtentY / 2; + + if(!newExtentX) newExtentX = 1; + if(!newExtentY) newExtentY = 1; + + stbi_uc* newData = (stbi_uc*) malloc(newExtentX * newExtentY * channelCount * sizeof(stbi_uc)); + ptrdiff_t newStride = newExtentX * channelCount * sizeof(stbi_uc); + + stbir_resize_uint8_srgb( + prevData, prevExtentX, prevExtentY, prevStride, + newData, newExtentX, newExtentY, newStride, + channelCount, + STBIR_ALPHA_CHANNEL_NONE, + STBIR_FLAG_ALPHA_PREMULTIPLIED); + + subresourceInitData.push_back(newData); + mipRowStrides.push_back(newStride); + + prevExtentX = newExtentX; + prevExtentY = newExtentY; + prevData = newData; + prevStride = newStride; + } + } + + int mipCount = (int) mipRowStrides.size(); + + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, format, extentX, extentY, mipCount); + + TextureResource::Data initData; + initData.numSubResources = mipCount; + initData.numMips = mipCount; + initData.subResources = &subresourceInitData[0]; + initData.mipRowStrides = &mipRowStrides[0]; + + auto texture = renderer->createTextureResource( + Resource::Usage::PixelShaderResource, + desc, + &initData); + + free(data); + + return texture; +} + +Result ModelLoader::load( + char const* inputPath, + void** outModel) +{ + // TODO: need to actually allocate/load the data + + tinyobj::attrib_t objVertexAttributes; + std::vector objShapes; + std::vector objMaterials; + + std::string diagnostics; + bool shouldTriangulate = true; + bool success = tinyobj::LoadObj( + &objVertexAttributes, + &objShapes, + &objMaterials, + &diagnostics, + inputPath, + nullptr, + shouldTriangulate); + + if(!diagnostics.empty()) + { + log("%s", diagnostics.c_str()); + } + if(!success) + { + return SLANG_FAIL; + } + + // Translate each material imported by TinyObj into a format that + // we can actually use for rendering. + // + std::vector materials; + for(auto& objMaterial : objMaterials) + { + MaterialData materialData; + + materialData.diffuseColor = glm::vec3( + objMaterial.diffuse[0], + objMaterial.diffuse[1], + objMaterial.diffuse[2]); + + // load any referenced textures here + if(objMaterial.diffuse_texname.length()) + { + materialData.diffuseMap = loadTextureImage( + renderer, + objMaterial.diffuse_texname.c_str()); + } + + auto material = callbacks->createMaterial(materialData); + materials.push_back(material); + } + + // Flip the winding order on all faces if we are asked to... + // + if(loadFlags & LoadFlag::FlipWinding) + { + for(auto& objShape : objShapes) + { + size_t objIndexCounter = 0; + size_t objFaceCounter = 0; + for(auto objFaceVertexCount : objShape.mesh.num_face_vertices) + { + size_t beginIndex = objIndexCounter; + size_t endIndex = beginIndex + objFaceVertexCount; + objIndexCounter = endIndex; + + size_t halfCount = objFaceVertexCount / 2; + for(size_t ii = 0; ii < halfCount; ++ii) + { + std::swap( + objShape.mesh.indices[beginIndex + ii], + objShape.mesh.indices[endIndex - (ii + 1)]); + } + } + } + + } + + // Identify cases where a face has a vertex without a normal, and in that + // case remember that the given vertex needs to be "smoothed" as part of + // the smoothing group for that face. Note that it is possible for the + // same vertex (position) to be part of faces in distinct smoothing groups. + // + std::unordered_map smoothedVertexNormals; + size_t firstSmoothedNormalID = objVertexAttributes.normals.size() / 3; + size_t flatFaceCounter = 0; + for(auto& objShape : objShapes) + { + size_t objIndexCounter = 0; + size_t objFaceCounter = 0; + for(auto objFaceVertexCount : objShape.mesh.num_face_vertices) + { + size_t flatFaceIndex = flatFaceCounter++; + size_t objFaceIndex = objFaceCounter++; + size_t smoothingGroup = objShape.mesh.smoothing_group_ids[objFaceIndex]; + if(!smoothingGroup) + { + smoothingGroup = ~flatFaceIndex; + } + + for(size_t objFaceVertex = 0; objFaceVertex < objFaceVertexCount; ++objFaceVertex) + { + tinyobj::index_t& objIndex = objShape.mesh.indices[objIndexCounter++]; + + if(objIndex.normal_index < 0) + { + SmoothingGroupVertexID smoothVertexID; + smoothVertexID.positionID = objIndex.vertex_index; + smoothVertexID.smoothingGroup = smoothingGroup; + + if(smoothedVertexNormals.find(smoothVertexID) == smoothedVertexNormals.end()) + { + size_t normalID = objVertexAttributes.normals.size() / 3; + objVertexAttributes.normals.push_back(0); + objVertexAttributes.normals.push_back(0); + objVertexAttributes.normals.push_back(0); + + smoothedVertexNormals.insert(std::make_pair(smoothVertexID, normalID)); + + objIndex.normal_index = normalID; + } + } + } + } + } + // + // Having identified which vertices we need to smooth, we will make another + // pass to compute face normals and apply them to the vertices that belong + // to the same smoothing group. + // + flatFaceCounter = 0; + for(auto& objShape : objShapes) + { + size_t objIndexCounter = 0; + size_t objFaceCounter = 0; + for(auto objFaceVertexCount : objShape.mesh.num_face_vertices) + { + size_t flatFaceIndex = flatFaceCounter++; + size_t objFaceIndex = objFaceCounter++; + unsigned int smoothingGroup = objShape.mesh.smoothing_group_ids[objFaceIndex]; + if(!smoothingGroup) + { + smoothingGroup = ~flatFaceIndex; + } + + glm::vec3 faceNormal; + if(objFaceVertexCount >= 3) + { + glm::vec3 v[3]; + for(size_t objFaceVertex = 0; objFaceVertex < 3; ++objFaceVertex) + { + tinyobj::index_t objIndex = objShape.mesh.indices[objIndexCounter + objFaceVertex]; + if(objIndex.vertex_index >= 0) + { + v[objFaceVertex] = glm::vec3( + objVertexAttributes.vertices[3 * objIndex.vertex_index + 0], + objVertexAttributes.vertices[3 * objIndex.vertex_index + 1], + objVertexAttributes.vertices[3 * objIndex.vertex_index + 2]); + } + } + faceNormal = cross(v[1] - v[0], v[2] - v[0]); + } + + // Add this face normal to any to-be-smoothed vertex on the face. + for(size_t objFaceVertex = 0; objFaceVertex < objFaceVertexCount; ++objFaceVertex) + { + tinyobj::index_t objIndex = objShape.mesh.indices[objIndexCounter++]; + + SmoothingGroupVertexID smoothVertexID; + smoothVertexID.positionID = objIndex.vertex_index; + smoothVertexID.smoothingGroup = smoothingGroup; + + auto ii = smoothedVertexNormals.find(smoothVertexID); + if(ii != smoothedVertexNormals.end()) + { + size_t normalID = ii->second; + objVertexAttributes.normals[normalID * 3 + 0] += faceNormal.x; + objVertexAttributes.normals[normalID * 3 + 1] += faceNormal.y; + objVertexAttributes.normals[normalID * 3 + 2] += faceNormal.z; + } + } + } + } + // + // Once we've added all contributions from each smoothing group, + // we can normalize the normals to compute the area-weighted average. + // + size_t normalCount = objVertexAttributes.normals.size() / 3; + for(size_t ii = firstSmoothedNormalID; ii < normalCount; ++ii) + { + glm::vec3 normal = glm::vec3( + objVertexAttributes.normals[3 * ii + 0], + objVertexAttributes.normals[3 * ii + 1], + objVertexAttributes.normals[3 * ii + 2]); + + normal = normalize(normal); + + objVertexAttributes.normals[3 * ii + 0] = normal.x; + objVertexAttributes.normals[3 * ii + 1] = normal.y; + objVertexAttributes.normals[3 * ii + 2] = normal.z; + } + + // TODO: we should sort the faces to group faces with + // the same material ID together, in case they weren't + // grouped in the original file. + + // We need to undo the .obj indexing stuff so that we have + // standard position/normal/etc. data in a single flat array + + std::unordered_map mapObjIndexToFlatIndex; + std::vector flatVertices; + std::vector flatIndices; + + MeshData* currentMesh = nullptr; + MeshData currentMeshStorage; + + std::vector meshes; + + for(auto& objShape : objShapes) + { + size_t objIndexCounter = 0; + size_t objFaceCounter = 0; + for(auto objFaceVertexCount : objShape.mesh.num_face_vertices) + { + size_t objFaceIndex = objFaceCounter++; + int faceMaterialID = objShape.mesh.material_ids[objFaceIndex]; + void* faceMaterial = materials[faceMaterialID]; + + if(!currentMesh || (faceMaterial != currentMesh->material)) + { + // finish old mesh. + if(currentMesh) + { + meshes.push_back(callbacks->createMesh(*currentMesh)); + } + + // Need to start a new mesh. + currentMesh = ¤tMeshStorage; + currentMesh->material = faceMaterial; + currentMesh->firstIndex = (int)flatIndices.size(); + currentMesh->indexCount = 0; + } + + for(size_t objFaceVertex = 0; objFaceVertex < objFaceVertexCount; ++objFaceVertex) + { + tinyobj::index_t objIndex = objShape.mesh.indices[objIndexCounter++]; + ObjIndexKey objIndexKey; objIndexKey.index = objIndex; + + + Index flatIndex = Index(-1); + auto iter = mapObjIndexToFlatIndex.find(objIndexKey); + if(iter != mapObjIndexToFlatIndex.end()) + { + flatIndex = iter->second; + } + else + { + Vertex flatVertex; + if(objIndex.vertex_index >= 0) + { + flatVertex.position = scale * glm::vec3( + objVertexAttributes.vertices[3 * objIndex.vertex_index + 0], + objVertexAttributes.vertices[3 * objIndex.vertex_index + 1], + objVertexAttributes.vertices[3 * objIndex.vertex_index + 2]); + } + if(objIndex.normal_index >= 0) + { + flatVertex.normal = glm::vec3( + objVertexAttributes.normals[3 * objIndex.normal_index + 0], + objVertexAttributes.normals[3 * objIndex.normal_index + 1], + objVertexAttributes.normals[3 * objIndex.normal_index + 2]); + } + if(objIndex.texcoord_index >= 0) + { + flatVertex.uv = glm::vec2( + objVertexAttributes.texcoords[2 * objIndex.texcoord_index + 0], + objVertexAttributes.texcoords[2 * objIndex.texcoord_index + 1]); + } + + flatIndex = flatVertices.size(); + mapObjIndexToFlatIndex.insert(std::make_pair(objIndexKey, flatIndex)); + flatVertices.push_back(flatVertex); + } + + flatIndices.push_back(flatIndex); + currentMesh->indexCount++; + } + } + } + + // finish last mesh. + if(currentMesh) + { + meshes.push_back(callbacks->createMesh(*currentMesh)); + } + + ModelData modelData; + + modelData.vertexCount = (int)flatVertices.size(); + modelData.indexCount = (int)flatIndices.size(); + + modelData.meshCount = meshes.size(); + modelData.meshes = meshes.data(); + + BufferResource::Desc vertexBufferDesc; + vertexBufferDesc.init(modelData.vertexCount * sizeof(Vertex)); + vertexBufferDesc.setDefaults(Resource::Usage::VertexBuffer); + + modelData.vertexBuffer = renderer->createBufferResource( + Resource::Usage::VertexBuffer, + vertexBufferDesc, + flatVertices.data()); + if(!modelData.vertexBuffer) return SLANG_FAIL; + + BufferResource::Desc indexBufferDesc; + indexBufferDesc.init(modelData.indexCount * sizeof(Index)); + vertexBufferDesc.setDefaults(Resource::Usage::IndexBuffer); + + modelData.indexBuffer = renderer->createBufferResource( + Resource::Usage::IndexBuffer, + indexBufferDesc, + flatIndices.data()); + if(!modelData.indexBuffer) return SLANG_FAIL; + + *outModel = callbacks->createModel(modelData); + + return SLANG_OK; +} + +} // gfx diff --git a/tools/gfx/model.h b/tools/gfx/model.h new file mode 100644 index 000000000..046b9764b --- /dev/null +++ b/tools/gfx/model.h @@ -0,0 +1,73 @@ +// model.h +#pragma once + +#include "render.h" +#include "vector-math.h" + +#include + +namespace gfx { + +struct ModelLoader +{ + struct MaterialData + { + glm::vec3 diffuseColor; + RefPtr diffuseMap; + }; + + struct Vertex + { + glm::vec3 position; + glm::vec3 normal; + glm::vec2 uv; + }; + + typedef uint32_t Index; + + struct MeshData + { + int firstIndex; + int indexCount; + + void* material; + }; + + struct ModelData + { + RefPtr vertexBuffer; + RefPtr indexBuffer; + PrimitiveTopology primitiveTopology; + int vertexCount; + int indexCount; + int meshCount; + void* const* meshes; + }; + + struct ICallbacks + { + typedef ModelLoader::MaterialData MaterialData; + typedef ModelLoader::MeshData MeshData; + typedef ModelLoader::ModelData ModelData; + + virtual void* createMaterial(MaterialData const& data) = 0; + virtual void* createMesh(MeshData const& data) = 0; + virtual void* createModel(ModelData const& data) = 0; + }; + + typedef uint32_t LoadFlags; + enum LoadFlag : LoadFlags + { + FlipWinding = 1 << 0, + }; + + ICallbacks* callbacks = nullptr; + RefPtr renderer; + LoadFlags loadFlags = 0; + float scale = 1.0f; + + Result load(char const* inputPath, void** outModel); +}; + + +} // gfx diff --git a/tools/gfx/render-d3d11.cpp b/tools/gfx/render-d3d11.cpp new file mode 100644 index 000000000..57c0672bd --- /dev/null +++ b/tools/gfx/render-d3d11.cpp @@ -0,0 +1,2112 @@ +// render-d3d11.cpp + +#define _CRT_SECURE_NO_WARNINGS + +#include "render-d3d11.h" + +//WORKING: #include "options.h" +#include "render.h" +#include "d3d-util.h" + +#include "surface.h" + +// In order to use the Slang API, we need to include its header + +//#include + +#include "../../slang-com-ptr.h" + +// We will be rendering with Direct3D 11, so we need to include +// the Windows and D3D11 headers + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#include +#include + +// We will use the C standard library just for printing error messages. +#include + +#ifdef _MSC_VER +#include +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif +// +using namespace Slang; + +namespace gfx { + +class D3D11Renderer : public Renderer +{ +public: + enum + { + kMaxUAVs = 64, + kMaxRTVs = 8, + }; + + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + TextureResource::Desc getSwapChainTextureDesc() override; + + Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; + Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; + Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; + + Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; + Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; + + Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; + + Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; + Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; + Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; + + Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; + Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; + Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; + + virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; + virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override {} + virtual void waitForGpu() override {} + virtual RendererType getRendererType() const override { return RendererType::DirectX11; } + + protected: + +#if 0 + struct BindingDetail + { + ComPtr m_srv; + ComPtr m_uav; + ComPtr m_samplerState; + }; + + class BindingStateImpl: public BindingState + { + public: + typedef BindingState Parent; + + /// Ctor + BindingStateImpl(const Desc& desc): + Parent(desc) + {} + + List m_bindingDetails; + }; +#endif + + enum class D3D11DescriptorSlotType + { + ConstantBuffer, + ShaderResourceView, + UnorderedAccessView, + Sampler, + + CombinedTextureSampler, + + CountOf, + }; + + class DescriptorSetLayoutImpl : public DescriptorSetLayout + { + public: + struct RangeInfo + { + D3D11DescriptorSlotType type; + UInt arrayIndex; + UInt pairedSamplerArrayIndex; + }; + List m_ranges; + + UInt m_counts[int(D3D11DescriptorSlotType::CountOf)]; + }; + + class PipelineLayoutImpl : public PipelineLayout + { + public: + struct DescriptorSetInfo + { + RefPtr layout; + UInt baseIndices[int(D3D11DescriptorSlotType::CountOf)]; + }; + + List m_descriptorSets; + UINT m_uavCount; + }; + + class DescriptorSetImpl : public DescriptorSet + { + public: + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; + virtual void setResource(UInt range, UInt index, ResourceView* view) override; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) override; + + RefPtr m_layout; + + List> m_cbs; + List> m_srvs; + List> m_uavs; + List> m_samplers; + }; + + class ShaderProgramImpl: public ShaderProgram + { + public: + ComPtr m_vertexShader; + ComPtr m_pixelShader; + ComPtr m_computeShader; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + BufferResourceImpl(const Desc& desc, Usage initialUsage): + Parent(desc), + m_initialUsage(initialUsage) + { + } + + MapFlavor m_mapFlavor; + Usage m_initialUsage; + ComPtr m_buffer; + ComPtr m_staging; + }; + class TextureResourceImpl : public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(const Desc& desc, Usage initialUsage) : + Parent(desc), + m_initialUsage(initialUsage) + { + } + Usage m_initialUsage; + ComPtr m_resource; + + }; + + class SamplerStateImpl : public SamplerState + { + public: + ComPtr m_sampler; + }; + + + class ResourceViewImpl : public ResourceView + { + public: + enum class Type + { + SRV, + UAV, + DSV, + RTV, + }; + Type m_type; + }; + + class ShaderResourceViewImpl : public ResourceViewImpl + { + public: + ComPtr m_srv; + }; + + class UnorderedAccessViewImpl : public ResourceViewImpl + { + public: + ComPtr m_uav; + }; + + class DepthStencilViewImpl : public ResourceViewImpl + { + public: + ComPtr m_dsv; + }; + + class RenderTargetViewImpl : public ResourceViewImpl + { + public: + ComPtr m_rtv; + }; + + class InputLayoutImpl: public InputLayout + { + public: + ComPtr m_layout; + }; + + class PipelineStateImpl : public PipelineState + { + public: + RefPtr m_program; + RefPtr m_pipelineLayout; + }; + + + class GraphicsPipelineStateImpl : public PipelineStateImpl + { + public: + UINT m_rtvCount; + + RefPtr m_inputLayout; + ComPtr m_depthStencilState; + ComPtr m_rasterizerState; + + UINT m_stencilRef; + }; + + class ComputePipelineStateImpl : public PipelineStateImpl + { + public: + }; + + /// Capture a texture to a file + static HRESULT captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut); + + void _flushGraphicsState(); + void _flushComputeState(); + + ComPtr m_swapChain; + ComPtr m_device; + ComPtr m_immediateContext; + ComPtr m_backBufferTexture; + + RefPtr m_primaryRenderTargetTexture; + RefPtr m_primaryRenderTargetView; + +// List > m_renderTargetViews; +// List > m_renderTargetTextures; + + bool m_renderTargetBindingsDirty = false; + + RefPtr m_currentGraphicsState; + RefPtr m_currentComputeState; + + ComPtr m_rtvBindings[kMaxRTVs]; + ComPtr m_dsvBinding; + ComPtr m_uavBindings[int(PipelineType::CountOf)][kMaxUAVs]; + bool m_targetBindingsDirty[int(PipelineType::CountOf)]; + + Desc m_desc; + + float m_clearColor[4] = { 0, 0, 0, 0 }; +}; + +Renderer* createD3D11Renderer() +{ + return new D3D11Renderer(); +} + +/* static */HRESULT D3D11Renderer::captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut) +{ + if (!context) return E_INVALIDARG; + if (!texture) return E_INVALIDARG; + + D3D11_TEXTURE2D_DESC textureDesc; + texture->GetDesc(&textureDesc); + + // Don't bother supporting MSAA for right now + if (textureDesc.SampleDesc.Count > 1) + { + fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + ComPtr stagingTexture; + + if (textureDesc.Usage == D3D11_USAGE_STAGING && (textureDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) + { + stagingTexture = texture; + } + else + { + // Modify the descriptor to give us a staging texture + textureDesc.BindFlags = 0; + textureDesc.MiscFlags &= ~D3D11_RESOURCE_MISC_TEXTURECUBE; + textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + textureDesc.Usage = D3D11_USAGE_STAGING; + + hr = device->CreateTexture2D(&textureDesc, 0, stagingTexture.writeRef()); + if (FAILED(hr)) + { + fprintf(stderr, "ERROR: failed to create staging texture\n"); + return hr; + } + + context->CopyResource(stagingTexture, texture); + } + + // Now just read back texels from the staging textures + { + D3D11_MAPPED_SUBRESOURCE mappedResource; + SLANG_RETURN_ON_FAIL(context->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource)); + + Result res = surfaceOut.set(textureDesc.Width, textureDesc.Height, Format::RGBA_Unorm_UInt8, mappedResource.RowPitch, mappedResource.pData, SurfaceAllocator::getMallocAllocator()); + + // Make sure to unmap + context->Unmap(stagingTexture, 0); + return res; + } +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult D3D11Renderer::initialize(const Desc& desc, void* inWindowHandle) +{ + auto windowHandle = (HWND)inWindowHandle; + m_desc = desc; + + // Rather than statically link against D3D, we load it dynamically. + HMODULE d3dModule = LoadLibraryA("d3d11.dll"); + if (!d3dModule) + { + fprintf(stderr, "error: failed load 'd3d11.dll'\n"); + return SLANG_FAIL; + } + + PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN D3D11CreateDeviceAndSwapChain_ = + (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress(d3dModule, "D3D11CreateDeviceAndSwapChain"); + if (!D3D11CreateDeviceAndSwapChain_) + { + fprintf(stderr, + "error: failed load symbol 'D3D11CreateDeviceAndSwapChain'\n"); + return SLANG_FAIL; + } + + UINT deviceFlags = 0; + +#ifdef _DEBUG + // We will enable the D3D debug more for debug builds. + // + // TODO: we should probably provide a command-line option + // to override this kind of default rather than leave it + // up to each back-end to specify. + deviceFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + // Our swap chain uses RGBA8 with sRGB, with double buffering. + DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + + // Note(tfoley): Disabling sRGB for DX back buffer for now, so that we + // can get consistent output with OpenGL, where setting up sRGB will + // probably be more involved. + // swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferCount = 2; + swapChainDesc.OutputWindow = windowHandle; + swapChainDesc.Windowed = TRUE; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapChainDesc.Flags = 0; + + // We will ask for the highest feature level that can be supported. + const D3D_FEATURE_LEVEL featureLevels[] = { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1, + }; + D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_9_1; + const int totalNumFeatureLevels = SLANG_COUNT_OF(featureLevels); + + // On a machine that does not have an up-to-date version of D3D installed, + // the `D3D11CreateDeviceAndSwapChain` call will fail with `E_INVALIDARG` + // if you ask for featuer level 11_1. The workaround is to call + // `D3D11CreateDeviceAndSwapChain` up to twice: the first time with 11_1 + // at the start of the list of requested feature levels, and the second + // time without it. + + for (int ii = 0; ii < 2; ++ii) + { + const HRESULT hr = D3D11CreateDeviceAndSwapChain_( + nullptr, // adapter (use default) + D3D_DRIVER_TYPE_REFERENCE, +// D3D_DRIVER_TYPE_HARDWARE, + nullptr, // software + deviceFlags, + &featureLevels[ii], + totalNumFeatureLevels - ii, + D3D11_SDK_VERSION, + &swapChainDesc, + m_swapChain.writeRef(), + m_device.writeRef(), + &featureLevel, + m_immediateContext.writeRef()); + + // Failures with `E_INVALIDARG` might be due to feature level 11_1 + // not being supported. + if (hr == E_INVALIDARG) + { + continue; + } + + // Other failures are real, though. + SLANG_RETURN_ON_FAIL(hr); + // We must have a swap chain + break; + } + + // TODO: Add support for debugging to help detect leaks: + // + // ComPtr gDebug; + // m_device->QueryInterface(IID_PPV_ARGS(gDebug.writeRef())); + // + + // After we've created the swap chain, we can request a pointer to the + // back buffer as a D3D11 texture, and create a render-target view from it. + + static const IID kIID_ID3D11Texture2D = { + 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, + 0x95, 0x35, 0xd3, 0x4f, 0x9c }; + + SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, kIID_ID3D11Texture2D, (void**)m_backBufferTexture.writeRef())); + +// for (int i = 0; i < 8; i++) + { + ComPtr texture; + D3D11_TEXTURE2D_DESC textureDesc; + m_backBufferTexture->GetDesc(&textureDesc); + SLANG_RETURN_ON_FAIL(m_device->CreateTexture2D(&textureDesc, nullptr, texture.writeRef())); + + ComPtr rtv; + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + rtvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + rtvDesc.Texture2D.MipSlice = 0; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + SLANG_RETURN_ON_FAIL(m_device->CreateRenderTargetView(texture, &rtvDesc, rtv.writeRef())); + + TextureResource::Desc resourceDesc; + resourceDesc.init2D(Resource::Type::Texture2D, Format::RGBA_Unorm_UInt8, textureDesc.Width, textureDesc.Height, 1); + + RefPtr primaryRenderTargetTexture; + SLANG_RETURN_ON_FAIL(createTextureResource(Resource::Usage::RenderTarget, resourceDesc, nullptr, primaryRenderTargetTexture.writeRef())); + + ResourceView::Desc viewDesc; + viewDesc.format = resourceDesc.format; + viewDesc.type = ResourceView::Type::RenderTarget; + RefPtr primaryRenderTargetView; + SLANG_RETURN_ON_FAIL(createTextureView(primaryRenderTargetTexture, viewDesc, primaryRenderTargetView.writeRef())); + + m_primaryRenderTargetTexture = (TextureResourceImpl*) primaryRenderTargetTexture.Ptr(); + m_primaryRenderTargetView = (RenderTargetViewImpl*) primaryRenderTargetView.Ptr(); + } + +// m_immediateContext->OMSetRenderTargets(1, m_primaryRenderTargetView->m_rtv.readRef(), nullptr); + m_rtvBindings[0] = m_primaryRenderTargetView->m_rtv; + m_targetBindingsDirty[int(PipelineType::Graphics)] = true; + + // Similarly, we are going to set up a viewport once, and then never + // switch, since this is a simple test app. + D3D11_VIEWPORT viewport; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + viewport.Width = (float)desc.width; + viewport.Height = (float)desc.height; + viewport.MaxDepth = 1; // TODO(tfoley): use reversed depth + viewport.MinDepth = 0; + m_immediateContext->RSSetViewports(1, &viewport); + + return SLANG_OK; +} + +void D3D11Renderer::setClearColor(const float color[4]) +{ + memcpy(m_clearColor, color, sizeof(m_clearColor)); +} + +void D3D11Renderer::clearFrame() +{ + m_immediateContext->ClearRenderTargetView(m_primaryRenderTargetView->m_rtv, m_clearColor); + + if(m_dsvBinding) + { + m_immediateContext->ClearDepthStencilView(m_dsvBinding, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); + } +} + +void D3D11Renderer::presentFrame() +{ + m_immediateContext->CopyResource(m_backBufferTexture, m_primaryRenderTargetTexture->m_resource); + m_swapChain->Present(0, 0); +} + +TextureResource::Desc D3D11Renderer::getSwapChainTextureDesc() +{ + D3D11_TEXTURE2D_DESC dxDesc; + ((ID3D11Texture2D*)m_primaryRenderTargetTexture->m_resource.get())->GetDesc(&dxDesc); + + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::Unknown, dxDesc.Width, dxDesc.Height, 1); + + return desc; +} + +SlangResult D3D11Renderer::captureScreenSurface(Surface& surfaceOut) +{ + return captureTextureToSurface(m_device, m_immediateContext, (ID3D11Texture2D*) m_primaryRenderTargetTexture->m_resource.get(), surfaceOut); +} + +static D3D11_BIND_FLAG _calcResourceFlag(Resource::BindFlag::Enum bindFlag) +{ + typedef Resource::BindFlag BindFlag; + switch (bindFlag) + { + case BindFlag::VertexBuffer: return D3D11_BIND_VERTEX_BUFFER; + case BindFlag::IndexBuffer: return D3D11_BIND_INDEX_BUFFER; + case BindFlag::ConstantBuffer: return D3D11_BIND_CONSTANT_BUFFER; + case BindFlag::StreamOutput: return D3D11_BIND_STREAM_OUTPUT; + case BindFlag::RenderTarget: return D3D11_BIND_RENDER_TARGET; + case BindFlag::DepthStencil: return D3D11_BIND_DEPTH_STENCIL; + case BindFlag::UnorderedAccess: return D3D11_BIND_UNORDERED_ACCESS; + case BindFlag::PixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; + case BindFlag::NonPixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; + default: return D3D11_BIND_FLAG(0); + } +} + +static int _calcResourceBindFlags(int bindFlags) +{ + int dstFlags = 0; + while (bindFlags) + { + int lsb = bindFlags & -bindFlags; + + dstFlags |= _calcResourceFlag(Resource::BindFlag::Enum(lsb)); + bindFlags &= ~lsb; + } + return dstFlags; +} + +static int _calcResourceAccessFlags(int accessFlags) +{ + switch (accessFlags) + { + case 0: return 0; + case Resource::AccessFlag::Read: return D3D11_CPU_ACCESS_READ; + case Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_WRITE; + case Resource::AccessFlag::Read | + Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + default: assert(!"Invalid flags"); return 0; + } +} + +Result D3D11Renderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) +{ + TextureResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); + + if(initData) + { + assert(initData->numSubResources == srcDesc.numMipLevels * effectiveArraySize * srcDesc.size.depth); + } + + const DXGI_FORMAT format = D3DUtil::getMapFormat(srcDesc.format); + if (format == DXGI_FORMAT_UNKNOWN) + { + return SLANG_FAIL; + } + + const int bindFlags = _calcResourceBindFlags(srcDesc.bindFlags); + + // Set up the initialize data + List subRes; + D3D11_SUBRESOURCE_DATA* subResourcesPtr = nullptr; + if(initData) + { + subRes.SetSize(srcDesc.numMipLevels * effectiveArraySize); + { + int subResourceIndex = 0; + for (int i = 0; i < effectiveArraySize; i++) + { + for (int j = 0; j < srcDesc.numMipLevels; j++) + { + const int mipHeight = TextureResource::calcMipSize(srcDesc.size.height, j); + + D3D11_SUBRESOURCE_DATA& data = subRes[subResourceIndex]; + + data.pSysMem = initData->subResources[subResourceIndex]; + + data.SysMemPitch = UINT(initData->mipRowStrides[j]); + data.SysMemSlicePitch = UINT(initData->mipRowStrides[j] * mipHeight); + + subResourceIndex++; + } + } + } + subResourcesPtr = subRes.Buffer(); + } + + const int accessFlags = _calcResourceAccessFlags(srcDesc.cpuAccessFlags); + + RefPtr texture(new TextureResourceImpl(srcDesc, initialUsage)); + + switch (srcDesc.type) + { + case Resource::Type::Texture1D: + { + D3D11_TEXTURE1D_DESC desc = { 0 }; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = accessFlags; + desc.Format = format; + desc.MiscFlags = 0; + desc.MipLevels = srcDesc.numMipLevels; + desc.ArraySize = effectiveArraySize; + desc.Width = srcDesc.size.width; + desc.Usage = D3D11_USAGE_DEFAULT; + + ComPtr texture1D; + SLANG_RETURN_ON_FAIL(m_device->CreateTexture1D(&desc, subResourcesPtr, texture1D.writeRef())); + + texture->m_resource = texture1D; + break; + } + case Resource::Type::TextureCube: + case Resource::Type::Texture2D: + { + D3D11_TEXTURE2D_DESC desc = { 0 }; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = accessFlags; + desc.Format = format; + desc.MiscFlags = 0; + desc.MipLevels = srcDesc.numMipLevels; + desc.ArraySize = effectiveArraySize; + + desc.Width = srcDesc.size.width; + desc.Height = srcDesc.size.height; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.SampleDesc.Count = srcDesc.sampleDesc.numSamples; + desc.SampleDesc.Quality = srcDesc.sampleDesc.quality; + + if (srcDesc.type == Resource::Type::TextureCube) + { + desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE; + } + + ComPtr texture2D; + SLANG_RETURN_ON_FAIL(m_device->CreateTexture2D(&desc, subResourcesPtr, texture2D.writeRef())); + + texture->m_resource = texture2D; + break; + } + case Resource::Type::Texture3D: + { + D3D11_TEXTURE3D_DESC desc = { 0 }; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = accessFlags; + desc.Format = format; + desc.MiscFlags = 0; + desc.MipLevels = srcDesc.numMipLevels; + desc.Width = srcDesc.size.width; + desc.Height = srcDesc.size.height; + desc.Depth = srcDesc.size.depth; + desc.Usage = D3D11_USAGE_DEFAULT; + + ComPtr texture3D; + SLANG_RETURN_ON_FAIL(m_device->CreateTexture3D(&desc, subResourcesPtr, texture3D.writeRef())); + + texture->m_resource = texture3D; + break; + } + default: + return SLANG_FAIL; + } + + *outResource = texture.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) +{ + BufferResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + // Make aligned to 256 bytes... not sure why, but if you remove this the tests do fail. + const size_t alignedSizeInBytes = D3DUtil::calcAligned(srcDesc.sizeInBytes, 256); + + // Hack to make the initialization never read from out of bounds memory, by copying into a buffer + List initDataBuffer; + if (initData && alignedSizeInBytes > srcDesc.sizeInBytes) + { + initDataBuffer.SetSize(alignedSizeInBytes); + ::memcpy(initDataBuffer.Buffer(), initData, srcDesc.sizeInBytes); + initData = initDataBuffer.Buffer(); + } + + D3D11_BUFFER_DESC bufferDesc = { 0 }; + bufferDesc.ByteWidth = UINT(alignedSizeInBytes); + bufferDesc.BindFlags = _calcResourceBindFlags(srcDesc.bindFlags); + // For read we'll need to do some staging + bufferDesc.CPUAccessFlags = _calcResourceAccessFlags(descIn.cpuAccessFlags & Resource::AccessFlag::Write); + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + + // If written by CPU, make it dynamic + if (descIn.cpuAccessFlags & Resource::AccessFlag::Write) + { + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + } + + switch (initialUsage) + { + case Resource::Usage::ConstantBuffer: + { + // We'll just assume ConstantBuffers are dynamic for now + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + break; + } + default: break; + } + + if (bufferDesc.BindFlags & (D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE)) + { + //desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; + if (srcDesc.elementSize != 0) + { + bufferDesc.StructureByteStride = srcDesc.elementSize; + bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + } + else + { + bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + } + } + + D3D11_SUBRESOURCE_DATA subResourceData = { 0 }; + subResourceData.pSysMem = initData; + + RefPtr buffer(new BufferResourceImpl(srcDesc, initialUsage)); + + SLANG_RETURN_ON_FAIL(m_device->CreateBuffer(&bufferDesc, initData ? &subResourceData : nullptr, buffer->m_buffer.writeRef())); + + if (srcDesc.cpuAccessFlags & Resource::AccessFlag::Read) + { + D3D11_BUFFER_DESC bufDesc = {}; + bufDesc.BindFlags = 0; + bufDesc.ByteWidth = (UINT)alignedSizeInBytes; + bufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + bufDesc.Usage = D3D11_USAGE_STAGING; + + SLANG_RETURN_ON_FAIL(m_device->CreateBuffer(&bufDesc, nullptr, buffer->m_staging.writeRef())); + } + + *outResource = buffer.detach(); + return SLANG_OK; +} + +D3D11_FILTER_TYPE translateFilterMode(TextureFilteringMode mode) +{ + switch (mode) + { + default: + return D3D11_FILTER_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureFilteringMode::SRC: return D3D11_FILTER_TYPE_##DST + + CASE(Point, POINT); + CASE(Linear, LINEAR); + +#undef CASE + } +} + +D3D11_FILTER_REDUCTION_TYPE translateFilterReduction(TextureReductionOp op) +{ + switch (op) + { + default: + return D3D11_FILTER_REDUCTION_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureReductionOp::SRC: return D3D11_FILTER_REDUCTION_TYPE_##DST + + CASE(Average, STANDARD); + CASE(Comparison, COMPARISON); + CASE(Minimum, MINIMUM); + CASE(Maximum, MAXIMUM); + +#undef CASE + } +} + +D3D11_TEXTURE_ADDRESS_MODE translateAddressingMode(TextureAddressingMode mode) +{ + switch (mode) + { + default: + return D3D11_TEXTURE_ADDRESS_MODE(0); + +#define CASE(SRC, DST) \ + case TextureAddressingMode::SRC: return D3D11_TEXTURE_ADDRESS_##DST + + CASE(Wrap, WRAP); + CASE(ClampToEdge, CLAMP); + CASE(ClampToBorder, BORDER); + CASE(MirrorRepeat, MIRROR); + CASE(MirrorOnce, MIRROR_ONCE); + +#undef CASE + } +} + +static D3D11_COMPARISON_FUNC translateComparisonFunc(ComparisonFunc func) +{ + switch (func) + { + default: + // TODO: need to report failures + return D3D11_COMPARISON_ALWAYS; + +#define CASE(FROM, TO) \ + case ComparisonFunc::FROM: return D3D11_COMPARISON_##TO + + CASE(Never, NEVER); + CASE(Less, LESS); + CASE(Equal, EQUAL); + CASE(LessEqual, LESS_EQUAL); + CASE(Greater, GREATER); + CASE(NotEqual, NOT_EQUAL); + CASE(GreaterEqual, GREATER_EQUAL); + CASE(Always, ALWAYS); +#undef CASE + } +} + +Result D3D11Renderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) +{ + D3D11_FILTER_REDUCTION_TYPE dxReduction = translateFilterReduction(desc.reductionOp); + D3D11_FILTER dxFilter; + if (desc.maxAnisotropy > 1) + { + dxFilter = D3D11_ENCODE_ANISOTROPIC_FILTER(dxReduction); + } + else + { + D3D11_FILTER_TYPE dxMin = translateFilterMode(desc.minFilter); + D3D11_FILTER_TYPE dxMag = translateFilterMode(desc.magFilter); + D3D11_FILTER_TYPE dxMip = translateFilterMode(desc.mipFilter); + + dxFilter = D3D11_ENCODE_BASIC_FILTER(dxMin, dxMag, dxMip, dxReduction); + } + + D3D11_SAMPLER_DESC dxDesc = {}; + dxDesc.Filter = dxFilter; + dxDesc.AddressU = translateAddressingMode(desc.addressU); + dxDesc.AddressV = translateAddressingMode(desc.addressV); + dxDesc.AddressW = translateAddressingMode(desc.addressW); + dxDesc.MipLODBias = desc.mipLODBias; + dxDesc.MaxAnisotropy = desc.maxAnisotropy; + dxDesc.ComparisonFunc = translateComparisonFunc(desc.comparisonFunc); + for (int ii = 0; ii < 4; ++ii) + dxDesc.BorderColor[ii] = desc.borderColor[ii]; + dxDesc.MinLOD = desc.minLOD; + dxDesc.MaxLOD = desc.maxLOD; + + ComPtr sampler; + SLANG_RETURN_ON_FAIL(m_device->CreateSamplerState( + &dxDesc, + sampler.writeRef())); + + RefPtr samplerImpl = new SamplerStateImpl(); + samplerImpl->m_sampler = sampler; + *outSampler = samplerImpl.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (TextureResourceImpl*) texture; + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::RenderTarget: + { + ComPtr rtv; + SLANG_RETURN_ON_FAIL(m_device->CreateRenderTargetView(resourceImpl->m_resource, nullptr, rtv.writeRef())); + + RefPtr viewImpl = new RenderTargetViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::RTV; + viewImpl->m_rtv = rtv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::DepthStencil: + { + ComPtr dsv; + SLANG_RETURN_ON_FAIL(m_device->CreateDepthStencilView(resourceImpl->m_resource, nullptr, dsv.writeRef())); + + RefPtr viewImpl = new DepthStencilViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::DSV; + viewImpl->m_dsv = dsv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::UnorderedAccess: + { + ComPtr uav; + SLANG_RETURN_ON_FAIL(m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, uav.writeRef())); + + RefPtr viewImpl = new UnorderedAccessViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::UAV; + viewImpl->m_uav = uav; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::ShaderResource: + { + ComPtr srv; + SLANG_RETURN_ON_FAIL(m_device->CreateShaderResourceView(resourceImpl->m_resource, nullptr, srv.writeRef())); + + RefPtr viewImpl = new ShaderResourceViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::SRV; + viewImpl->m_srv = srv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + } +} + +Result D3D11Renderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + auto resourceDesc = resourceImpl->getDesc(); + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::UnorderedAccess: + { + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + uavDesc.Format = D3DUtil::getMapFormat(desc.format); + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = resourceDesc.sizeInBytes; + + if(resourceDesc.elementSize) + { + uavDesc.Buffer.NumElements = resourceDesc.sizeInBytes / resourceDesc.elementSize; + } + else if(desc.format == Format::Unknown) + { + uavDesc.Buffer.Flags |= D3D11_BUFFER_UAV_FLAG_RAW; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + } + + ComPtr uav; + SLANG_RETURN_ON_FAIL(m_device->CreateUnorderedAccessView(resourceImpl->m_buffer, &uavDesc, uav.writeRef())); + + RefPtr viewImpl = new UnorderedAccessViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::UAV; + viewImpl->m_uav = uav; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + + case ResourceView::Type::ShaderResource: + { + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; + srvDesc.Format = D3DUtil::getMapFormat(desc.format); + srvDesc.Buffer.ElementOffset = 0; + srvDesc.Buffer.ElementWidth = 1; + srvDesc.Buffer.FirstElement = 0; + srvDesc.Buffer.NumElements = resourceDesc.sizeInBytes; + + if(resourceDesc.elementSize) + { + srvDesc.Buffer.ElementWidth = resourceDesc.elementSize; + srvDesc.Buffer.NumElements = resourceDesc.sizeInBytes / resourceDesc.elementSize; + } + + ComPtr srv; + SLANG_RETURN_ON_FAIL(m_device->CreateShaderResourceView(resourceImpl->m_buffer, &srvDesc, srv.writeRef())); + + RefPtr viewImpl = new ShaderResourceViewImpl(); + viewImpl->m_type = ResourceViewImpl::Type::SRV; + viewImpl->m_srv = srv; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + } +} + +Result D3D11Renderer::createInputLayout(const InputElementDesc* inputElementsIn, UInt inputElementCount, InputLayout** outLayout) +{ + D3D11_INPUT_ELEMENT_DESC inputElements[16] = {}; + + char hlslBuffer[1024]; + char* hlslCursor = &hlslBuffer[0]; + + hlslCursor += sprintf(hlslCursor, "float4 main(\n"); + + for (UInt ii = 0; ii < inputElementCount; ++ii) + { + inputElements[ii].SemanticName = inputElementsIn[ii].semanticName; + inputElements[ii].SemanticIndex = (UINT)inputElementsIn[ii].semanticIndex; + inputElements[ii].Format = D3DUtil::getMapFormat(inputElementsIn[ii].format); + inputElements[ii].InputSlot = 0; + inputElements[ii].AlignedByteOffset = (UINT)inputElementsIn[ii].offset; + inputElements[ii].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + inputElements[ii].InstanceDataStepRate = 0; + + if (ii != 0) + { + hlslCursor += sprintf(hlslCursor, ",\n"); + } + + char const* typeName = "Unknown"; + switch (inputElementsIn[ii].format) + { + case Format::RGBA_Float32: + typeName = "float4"; + break; + case Format::RGB_Float32: + typeName = "float3"; + break; + case Format::RG_Float32: + typeName = "float2"; + break; + case Format::R_Float32: + typeName = "float"; + break; + default: + return SLANG_FAIL; + } + + hlslCursor += sprintf(hlslCursor, "%s a%d : %s%d", + typeName, + (int)ii, + inputElementsIn[ii].semanticName, + (int)inputElementsIn[ii].semanticIndex); + } + + hlslCursor += sprintf(hlslCursor, "\n) : SV_Position { return 0; }"); + + ComPtr vertexShaderBlob; + SLANG_RETURN_ON_FAIL(D3DUtil::compileHLSLShader("inputLayout", hlslBuffer, "main", "vs_5_0", vertexShaderBlob)); + + ComPtr inputLayout; + SLANG_RETURN_ON_FAIL(m_device->CreateInputLayout(&inputElements[0], (UINT)inputElementCount, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), + inputLayout.writeRef())); + + RefPtr impl = new InputLayoutImpl; + impl->m_layout.swap(inputLayout); + + *outLayout = impl.detach(); + return SLANG_OK; +} + +void* D3D11Renderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + BufferResourceImpl* bufferResource = static_cast(bufferIn); + + D3D11_MAP mapType; + ID3D11Buffer* buffer = bufferResource->m_buffer; + + switch (flavor) + { + case MapFlavor::WriteDiscard: + mapType = D3D11_MAP_WRITE_DISCARD; + break; + case MapFlavor::HostWrite: + mapType = D3D11_MAP_WRITE; + break; + case MapFlavor::HostRead: + mapType = D3D11_MAP_READ; + + buffer = bufferResource->m_staging; + if (!buffer) + { + return nullptr; + } + + // Okay copy the data over + m_immediateContext->CopyResource(buffer, bufferResource->m_buffer); + + break; + default: + return nullptr; + } + + // We update our constant buffer per-frame, just for the purposes + // of the example, but we don't actually load different data + // per-frame (we always use an identity projection). + D3D11_MAPPED_SUBRESOURCE mappedSub; + SLANG_RETURN_NULL_ON_FAIL(m_immediateContext->Map(buffer, 0, mapType, 0, &mappedSub)); + + bufferResource->m_mapFlavor = flavor; + + return mappedSub.pData; +} + +void D3D11Renderer::unmap(BufferResource* bufferIn) +{ + BufferResourceImpl* bufferResource = static_cast(bufferIn); + ID3D11Buffer* buffer = (bufferResource->m_mapFlavor == MapFlavor::HostRead) ? bufferResource->m_staging : bufferResource->m_buffer; + m_immediateContext->Unmap(buffer, 0); +} + +#if 0 +void D3D11Renderer::setInputLayout(InputLayout* inputLayoutIn) +{ + auto inputLayout = static_cast(inputLayoutIn); + m_immediateContext->IASetInputLayout(inputLayout->m_layout); +} +#endif + +void D3D11Renderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + m_immediateContext->IASetPrimitiveTopology(D3DUtil::getPrimitiveTopology(topology)); +} + +void D3D11Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffersIn, const UInt* stridesIn, const UInt* offsetsIn) +{ + static const int kMaxVertexBuffers = 16; + assert(slotCount <= kMaxVertexBuffers); + + UINT vertexStrides[kMaxVertexBuffers]; + UINT vertexOffsets[kMaxVertexBuffers]; + ID3D11Buffer* dxBuffers[kMaxVertexBuffers]; + + auto buffers = (BufferResourceImpl*const*)buffersIn; + + for (UInt ii = 0; ii < slotCount; ++ii) + { + vertexStrides[ii] = (UINT)stridesIn[ii]; + vertexOffsets[ii] = (UINT)offsetsIn[ii]; + dxBuffers[ii] = buffers[ii]->m_buffer; + } + + m_immediateContext->IASetVertexBuffers((UINT)startSlot, (UINT)slotCount, dxBuffers, &vertexStrides[0], &vertexOffsets[0]); +} + +void D3D11Renderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) +{ + DXGI_FORMAT dxFormat = D3DUtil::getMapFormat(indexFormat); + m_immediateContext->IASetIndexBuffer(((BufferResourceImpl*)buffer)->m_buffer, dxFormat, offset); +} + +void D3D11Renderer::setDepthStencilTarget(ResourceView* depthStencilView) +{ + m_dsvBinding = ((DepthStencilViewImpl*) depthStencilView)->m_dsv; + m_targetBindingsDirty[int(PipelineType::Graphics)] = true; +} + +void D3D11Renderer::setPipelineState(PipelineType pipelineType, PipelineState* state) +{ + switch(pipelineType) + { + default: + break; + + case PipelineType::Graphics: + { + auto stateImpl = (GraphicsPipelineStateImpl*) state; + auto programImpl = stateImpl->m_program; + + // TODO: We could conceivably do some lightweight state + // differencing here (e.g., check if `programImpl` is the + // same as the program that is currently bound). + // + // It isn't clear how much that would pay off given that + // the D3D11 runtime seems to do its own state diffing. + + // IA + + m_immediateContext->IASetInputLayout(stateImpl->m_inputLayout->m_layout); + + // VS + + m_immediateContext->VSSetShader(programImpl->m_vertexShader, nullptr, 0); + + // HS + + // DS + + // GS + + // RS + + m_immediateContext->RSSetState(stateImpl->m_rasterizerState); + + // PS + + m_immediateContext->PSSetShader(programImpl->m_pixelShader, nullptr, 0); + + // OM + + m_immediateContext->OMSetDepthStencilState(stateImpl->m_depthStencilState, stateImpl->m_stencilRef); + + m_currentGraphicsState = stateImpl; + } + break; + + case PipelineType::Compute: + { + auto stateImpl = (ComputePipelineStateImpl*) state; + auto programImpl = stateImpl->m_program; + + // CS + + m_immediateContext->CSSetShader(programImpl->m_computeShader, nullptr, 0); + + m_currentComputeState = stateImpl; + } + break; + } + + /// ... +} + +void D3D11Renderer::draw(UInt vertexCount, UInt startVertex) +{ + _flushGraphicsState(); + m_immediateContext->Draw((UINT)vertexCount, (UINT)startVertex); +} + +void D3D11Renderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) +{ + _flushGraphicsState(); + m_immediateContext->DrawIndexed((UINT)indexCount, (UINT)startIndex, (UInt)baseVertex); +} + +Result D3D11Renderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) +{ + if (desc.pipelineType == PipelineType::Compute) + { + auto computeKernel = desc.findKernel(StageType::Compute); + + ComPtr computeShader; + SLANG_RETURN_ON_FAIL(m_device->CreateComputeShader(computeKernel->codeBegin, computeKernel->getCodeSize(), nullptr, computeShader.writeRef())); + + RefPtr shaderProgram = new ShaderProgramImpl(); + shaderProgram->m_computeShader.swap(computeShader); + + *outProgram = shaderProgram.detach(); + return SLANG_OK; + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + ComPtr vertexShader; + ComPtr pixelShader; + + SLANG_RETURN_ON_FAIL(m_device->CreateVertexShader(vertexKernel->codeBegin, vertexKernel->getCodeSize(), nullptr, vertexShader.writeRef())); + SLANG_RETURN_ON_FAIL(m_device->CreatePixelShader(fragmentKernel->codeBegin, fragmentKernel->getCodeSize(), nullptr, pixelShader.writeRef())); + + RefPtr shaderProgram = new ShaderProgramImpl(); + shaderProgram->m_vertexShader.swap(vertexShader); + shaderProgram->m_pixelShader.swap(pixelShader); + + *outProgram = shaderProgram.detach(); + return SLANG_OK; + } +} + +static D3D11_STENCIL_OP translateStencilOp(StencilOp op) +{ + switch(op) + { + default: + // TODO: need to report failures + return D3D11_STENCIL_OP_KEEP; + +#define CASE(FROM, TO) \ + case StencilOp::FROM: return D3D11_STENCIL_OP_##TO + + CASE(Keep, KEEP); + CASE(Zero, ZERO); + CASE(Replace, REPLACE); + CASE(IncrementSaturate, INCR_SAT); + CASE(DecrementSaturate, DECR_SAT); + CASE(Invert, INVERT); + CASE(IncrementWrap, INCR); + CASE(DecrementWrap, DECR); +#undef CASE + + } +} + +static D3D11_FILL_MODE translateFillMode(FillMode mode) +{ + switch(mode) + { + default: + // TODO: need to report failures + return D3D11_FILL_SOLID; + + case FillMode::Solid: return D3D11_FILL_SOLID; + case FillMode::Wireframe: return D3D11_FILL_WIREFRAME; + } +} + +static D3D11_CULL_MODE translateCullMode(CullMode mode) +{ + switch(mode) + { + default: + // TODO: need to report failures + return D3D11_CULL_NONE; + + case CullMode::None: return D3D11_CULL_NONE; + case CullMode::Back: return D3D11_CULL_BACK; + case CullMode::Front: return D3D11_CULL_FRONT; + } +} + +Result D3D11Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + + ComPtr depthStencilState; + { + D3D11_DEPTH_STENCIL_DESC dsDesc; + dsDesc.DepthEnable = desc.depthStencil.depthTestEnable; + dsDesc.DepthWriteMask = desc.depthStencil.depthWriteEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dsDesc.DepthFunc = translateComparisonFunc(desc.depthStencil.depthFunc); + dsDesc.StencilEnable = desc.depthStencil.stencilEnable; + dsDesc.StencilReadMask = desc.depthStencil.stencilReadMask; + dsDesc.StencilWriteMask = desc.depthStencil.stencilWriteMask; + + #define FACE(DST, SRC) \ + dsDesc.DST.StencilFailOp = translateStencilOp( desc.depthStencil.SRC.stencilFailOp); \ + dsDesc.DST.StencilDepthFailOp = translateStencilOp( desc.depthStencil.SRC.stencilDepthFailOp); \ + dsDesc.DST.StencilPassOp = translateStencilOp( desc.depthStencil.SRC.stencilPassOp); \ + dsDesc.DST.StencilFunc = translateComparisonFunc(desc.depthStencil.SRC.stencilFunc); \ + /* end */ + + FACE(FrontFace, frontFace); + FACE(BackFace, backFace); + + SLANG_RETURN_ON_FAIL(m_device->CreateDepthStencilState( + &dsDesc, + depthStencilState.writeRef())); + } + + ComPtr rasterizerState; + { + D3D11_RASTERIZER_DESC rsDesc; + rsDesc.FillMode = translateFillMode(desc.rasterizer.fillMode); + rsDesc.CullMode = translateCullMode(desc.rasterizer.cullMode); + rsDesc.FrontCounterClockwise = desc.rasterizer.frontFace == FrontFaceMode::Clockwise; + rsDesc.DepthBias = desc.rasterizer.depthBias; + rsDesc.DepthBiasClamp = desc.rasterizer.depthBiasClamp; + rsDesc.SlopeScaledDepthBias = desc.rasterizer.slopeScaledDepthBias; + rsDesc.DepthClipEnable = desc.rasterizer.depthClipEnable; + rsDesc.ScissorEnable = desc.rasterizer.scissorEnable; + rsDesc.MultisampleEnable = desc.rasterizer.multisampleEnable; + rsDesc.AntialiasedLineEnable = desc.rasterizer.antialiasedLineEnable; + + SLANG_RETURN_ON_FAIL(m_device->CreateRasterizerState( + &rsDesc, + rasterizerState.writeRef())); + + } + + RefPtr state = new GraphicsPipelineStateImpl(); + state->m_program = programImpl; + state->m_stencilRef = desc.depthStencil.stencilRef; + state->m_depthStencilState = depthStencilState; + state->m_rasterizerState = rasterizerState; + state->m_pipelineLayout = (PipelineLayoutImpl*) desc.pipelineLayout; + state->m_inputLayout = (InputLayoutImpl*) desc.inputLayout; + state->m_rtvCount = desc.renderTargetCount; + + *outState = state.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + + RefPtr state = new ComputePipelineStateImpl(); + state->m_program = programImpl; + state->m_pipelineLayout = pipelineLayoutImpl; + + *outState = state.detach(); + return SLANG_OK; +} + +void D3D11Renderer::dispatchCompute(int x, int y, int z) +{ + _flushComputeState(); + m_immediateContext->Dispatch(x, y, z); +} + +Result D3D11Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) +{ + RefPtr descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(); + + UInt counts[int(D3D11DescriptorSlotType::CountOf)] = { 0, }; + + UInt rangeCount = desc.slotRangeCount; + for(UInt rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + + DescriptorSetLayoutImpl::RangeInfo rangeInfo; + + switch(rangeDesc.type) + { + default: + assert(!"invalid slot type"); + return SLANG_FAIL; + + case DescriptorSlotType::Sampler: + rangeInfo.type = D3D11DescriptorSlotType::Sampler; + break; + + case DescriptorSlotType::CombinedImageSampler: + rangeInfo.type = D3D11DescriptorSlotType::CombinedTextureSampler; + break; + + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::DynamicUniformBuffer: + rangeInfo.type = D3D11DescriptorSlotType::ConstantBuffer; + break; + + case DescriptorSlotType::SampledImage: + case DescriptorSlotType::UniformTexelBuffer: + case DescriptorSlotType::InputAttachment: + rangeInfo.type = D3D11DescriptorSlotType::ShaderResourceView; + break; + + case DescriptorSlotType::StorageImage: + case DescriptorSlotType::StorageTexelBuffer: + case DescriptorSlotType::StorageBuffer: + case DescriptorSlotType::DynamicStorageBuffer: + rangeInfo.type = D3D11DescriptorSlotType::UnorderedAccessView; + break; + } + + if(rangeInfo.type == D3D11DescriptorSlotType::CombinedTextureSampler) + { + auto srvTypeIndex = int(D3D11DescriptorSlotType::ShaderResourceView); + auto samplerTypeIndex = int(D3D11DescriptorSlotType::Sampler); + + rangeInfo.arrayIndex = counts[srvTypeIndex]; + rangeInfo.pairedSamplerArrayIndex = counts[samplerTypeIndex]; + + counts[srvTypeIndex] += rangeDesc.count; + counts[samplerTypeIndex] += rangeDesc.count; + } + else + { + auto typeIndex = int(rangeInfo.type); + + rangeInfo.arrayIndex = counts[typeIndex]; + counts[typeIndex] += rangeDesc.count; + } + descriptorSetLayoutImpl->m_ranges.Add(rangeInfo); + } + + for(int ii = 0; ii < int(D3D11DescriptorSlotType::CountOf); ++ii) + { + descriptorSetLayoutImpl->m_counts[ii] = counts[ii]; + } + + *outLayout = descriptorSetLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) +{ + RefPtr pipelineLayoutImpl = new PipelineLayoutImpl(); + + UInt counts[int(D3D11DescriptorSlotType::CountOf)] = { 0, }; + + UInt setCount = desc.descriptorSetCount; + for(UInt ii = 0; ii < setCount; ++ii) + { + auto setDesc = desc.descriptorSets[ii]; + PipelineLayoutImpl::DescriptorSetInfo setInfo; + + setInfo.layout = (DescriptorSetLayoutImpl*) setDesc.layout; + + for(int jj = 0; jj < int(D3D11DescriptorSlotType::CountOf); ++jj) + { + setInfo.baseIndices[jj] = counts[jj]; + counts[jj] += setInfo.layout->m_counts[jj]; + } + + pipelineLayoutImpl->m_descriptorSets.Add(setInfo); + } + + pipelineLayoutImpl->m_uavCount = counts[int(D3D11DescriptorSlotType::UnorderedAccessView)]; + + *outLayout = pipelineLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D11Renderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) +{ + auto layoutImpl = (DescriptorSetLayoutImpl*)layout; + + RefPtr descriptorSetImpl = new DescriptorSetImpl(); + + descriptorSetImpl->m_layout = layoutImpl; + descriptorSetImpl->m_cbs .SetSize(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ConstantBuffer)]); + descriptorSetImpl->m_srvs .SetSize(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ShaderResourceView)]); + descriptorSetImpl->m_uavs .SetSize(layoutImpl->m_counts[int(D3D11DescriptorSlotType::UnorderedAccessView)]); + descriptorSetImpl->m_samplers.SetSize(layoutImpl->m_counts[int(D3D11DescriptorSlotType::Sampler)]); + + *outDescriptorSet = descriptorSetImpl.detach(); + return SLANG_OK; +} + + +#if 0 +BindingState* D3D11Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); + + const auto& srcBindings = bindingStateDesc.m_bindings; + const int numBindings = int(srcBindings.Count()); + + auto& dstDetails = bindingState->m_bindingDetails; + dstDetails.SetSize(numBindings); + + for (int i = 0; i < numBindings; ++i) + { + auto& dstDetail = dstDetails[i]; + const auto& srcBinding = srcBindings[i]; + + assert(srcBinding.registerRange.isSingle()); + + switch (srcBinding.bindingType) + { + case BindingType::Buffer: + { + assert(srcBinding.resource && srcBinding.resource->isBuffer()); + + BufferResourceImpl* buffer = static_cast(srcBinding.resource.Ptr()); + const BufferResource::Desc& desc = buffer->getDesc(); + + const int elemSize = bufferDesc.elementSize <= 0 ? 1 : bufferDesc.elementSize; + + if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) + { + D3D11_UNORDERED_ACCESS_VIEW_DESC viewDesc; + memset(&viewDesc, 0, sizeof(viewDesc)); + viewDesc.Buffer.FirstElement = 0; + viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); + viewDesc.Buffer.Flags = 0; + viewDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + viewDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); + + if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) + { + viewDesc.Buffer.Flags |= D3D11_BUFFER_UAV_FLAG_RAW; + viewDesc.Format = DXGI_FORMAT_R32_TYPELESS; + } + + SLANG_RETURN_NULL_ON_FAIL(m_device->CreateUnorderedAccessView(buffer->m_buffer, &viewDesc, dstDetail.m_uav.writeRef())); + } + if (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource)) + { + D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; + memset(&viewDesc, 0, sizeof(viewDesc)); + viewDesc.Buffer.FirstElement = 0; + viewDesc.Buffer.ElementWidth = elemSize; + viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); + viewDesc.Buffer.ElementOffset = 0; + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; + viewDesc.Format = DXGI_FORMAT_UNKNOWN; + + if (bufferDesc.elementSize == 0) + { + viewDesc.Format = DXGI_FORMAT_R32_FLOAT; + } + + SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(buffer->m_buffer, &viewDesc, dstDetail.m_srv.writeRef())); + } + break; + } + case BindingType::Texture: + case BindingType::CombinedTextureSampler: + { + assert(srcBinding.resource && srcBinding.resource->isTexture()); + + TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); + + const TextureResource::Desc& textureDesc = texture->getDesc(); + + D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; + viewDesc.Format = D3DUtil::getMapFormat(textureDesc.format); + + switch (texture->getType()) + { + case Resource::Type::Texture1D: + { + if (textureDesc.arraySize <= 0) + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; + viewDesc.Texture1D.MipLevels = textureDesc.numMipLevels; + viewDesc.Texture1D.MostDetailedMip = 0; + } + else + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; + viewDesc.Texture1DArray.ArraySize = textureDesc.arraySize; + viewDesc.Texture1DArray.FirstArraySlice = 0; + viewDesc.Texture1DArray.MipLevels = textureDesc.numMipLevels; + viewDesc.Texture1DArray.MostDetailedMip = 0; + } + break; + } + case Resource::Type::Texture2D: + { + if (textureDesc.arraySize <= 0) + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + viewDesc.Texture2D.MipLevels = textureDesc.numMipLevels; + viewDesc.Texture2D.MostDetailedMip = 0; + } + else + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + viewDesc.Texture2DArray.ArraySize = textureDesc.arraySize; + viewDesc.Texture2DArray.FirstArraySlice = 0; + viewDesc.Texture2DArray.MipLevels = textureDesc.numMipLevels; + viewDesc.Texture2DArray.MostDetailedMip = 0; + } + break; + } + case Resource::Type::TextureCube: + { + if (textureDesc.arraySize <= 0) + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + viewDesc.TextureCube.MipLevels = textureDesc.numMipLevels; + viewDesc.TextureCube.MostDetailedMip = 0; + } + else + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; + viewDesc.TextureCubeArray.MipLevels = textureDesc.numMipLevels; + viewDesc.TextureCubeArray.MostDetailedMip = 0; + viewDesc.TextureCubeArray.First2DArrayFace = 0; + viewDesc.TextureCubeArray.NumCubes = textureDesc.arraySize; + } + break; + } + case Resource::Type::Texture3D: + { + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + viewDesc.Texture3D.MipLevels = textureDesc.numMipLevels; // Old code fixed as one + viewDesc.Texture3D.MostDetailedMip = 0; + break; + } + default: + { + assert(!"Unhandled type"); + return nullptr; + } + } + + SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(texture->m_resource, &viewDesc, dstDetail.m_srv.writeRef())); + break; + } + case BindingType::Sampler: + { + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; + + D3D11_SAMPLER_DESC desc = {}; + desc.AddressU = desc.AddressV = desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + + if (samplerDesc.isCompareSampler) + { + desc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; + desc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + desc.MinLOD = desc.MaxLOD = 0.0f; + } + else + { + desc.Filter = D3D11_FILTER_ANISOTROPIC; + desc.MaxAnisotropy = 8; + desc.MinLOD = 0.0f; + desc.MaxLOD = 100.0f; + } + SLANG_RETURN_NULL_ON_FAIL(m_device->CreateSamplerState(&desc, dstDetail.m_samplerState.writeRef())); + break; + } + default: + { + assert(!"Unhandled type"); + return nullptr; + } + } + } + + // Done + return bindingState.detach(); +} + +void D3D11Renderer::_applyBindingState(bool isCompute) +{ + auto context = m_immediateContext.get(); + + const auto& details = m_currentBindings->m_bindingDetails; + const auto& bindings = m_currentBindings->getDesc().m_bindings; + + const int numBindings = int(bindings.Count()); + + for (int i = 0; i < numBindings; ++i) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + const int bindingIndex = binding.registerRange.getSingleIndex(); + + switch (binding.bindingType) + { + case BindingType::Buffer: + { + assert(binding.resource && binding.resource->isBuffer()); + if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) + { + ID3D11Buffer* buffer = static_cast(binding.resource.Ptr())->m_buffer; + if (isCompute) + context->CSSetConstantBuffers(bindingIndex, 1, &buffer); + else + { + context->VSSetConstantBuffers(bindingIndex, 1, &buffer); + context->PSSetConstantBuffers(bindingIndex, 1, &buffer); + } + } + else if (detail.m_uav) + { + if (isCompute) + context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); + else + context->OMSetRenderTargetsAndUnorderedAccessViews( + m_currentBindings->getDesc().m_numRenderTargets, + m_renderTargetViews.Buffer()->readRef(), + m_depthStencilView, + bindingIndex, + 1, + detail.m_uav.readRef(), + nullptr); + } + else + { + if (isCompute) + context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + else + { + context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + } + } + break; + } + case BindingType::Texture: + { + if (detail.m_uav) + { + if (isCompute) + context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); + else + context->OMSetRenderTargetsAndUnorderedAccessViews(D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, + nullptr, nullptr, bindingIndex, 1, detail.m_uav.readRef(), nullptr); + } + else + { + if (isCompute) + context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + else + { + context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); + } + } + break; + } + case BindingType::Sampler: + { + if (isCompute) + context->CSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); + else + { + context->PSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); + context->VSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); + } + break; + } + default: + { + assert(!"Not implemented"); + return; + } + } + } +} + +void D3D11Renderer::setBindingState(BindingState* state) +{ + m_currentBindings = static_cast(state); +} +#endif + +void D3D11Renderer::_flushGraphicsState() +{ + auto pipelineType = int(PipelineType::Graphics); + if(m_targetBindingsDirty[pipelineType]) + { + m_targetBindingsDirty[pipelineType] = false; + + auto pipelineState = m_currentGraphicsState.Ptr(); + + auto rtvCount = pipelineState->m_rtvCount; + auto uavCount = pipelineState->m_pipelineLayout->m_uavCount; + + m_immediateContext->OMSetRenderTargetsAndUnorderedAccessViews( + rtvCount, + m_rtvBindings[0].readRef(), + m_dsvBinding, + rtvCount, + uavCount, + m_uavBindings[pipelineType][0].readRef(), + nullptr); + } +} + +void D3D11Renderer::_flushComputeState() +{ + auto pipelineType = int(PipelineType::Compute); + if(m_targetBindingsDirty[pipelineType]) + { + m_targetBindingsDirty[pipelineType] = false; + + auto pipelineState = m_currentComputeState.Ptr(); + + auto uavCount = pipelineState->m_pipelineLayout->m_uavCount; + + m_immediateContext->CSSetUnorderedAccessViews( + 0, + uavCount, + m_uavBindings[pipelineType][0].readRef(), + nullptr); + } +} + +void D3D11Renderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) +{ + auto bufferImpl = (BufferResourceImpl*) buffer; + auto& rangeInfo = m_layout->m_ranges[range]; + + assert(rangeInfo.type == D3D11DescriptorSlotType::ConstantBuffer); + + m_cbs[rangeInfo.arrayIndex + index] = bufferImpl->m_buffer; +} + +void D3D11Renderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) +{ + auto viewImpl = (ResourceViewImpl*)view; + auto& rangeInfo = m_layout->m_ranges[range]; + + switch (rangeInfo.type) + { + case D3D11DescriptorSlotType::ShaderResourceView: + { + assert(viewImpl->m_type == ResourceViewImpl::Type::SRV); + auto srvImpl = (ShaderResourceViewImpl*)viewImpl; + m_srvs[rangeInfo.arrayIndex + index] = srvImpl->m_srv; + } + break; + + case D3D11DescriptorSlotType::UnorderedAccessView: + { + assert(viewImpl->m_type == ResourceViewImpl::Type::UAV); + auto uavImpl = (UnorderedAccessViewImpl*)viewImpl; + m_uavs[rangeInfo.arrayIndex + index] = uavImpl->m_uav; + } + break; + + default: + assert(!"invalid to bind a resource view to this descriptor range"); + break; + } +} + +void D3D11Renderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) +{ + auto samplerImpl = (SamplerStateImpl*) sampler; + auto& rangeInfo = m_layout->m_ranges[range]; + + assert(rangeInfo.type == D3D11DescriptorSlotType::Sampler); + + m_samplers[rangeInfo.arrayIndex + index] = samplerImpl->m_sampler; +} + +void D3D11Renderer::DescriptorSetImpl::setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) +{ + auto viewImpl = (ResourceViewImpl*) textureView; + auto samplerImpl = (SamplerStateImpl*)sampler; + + auto& rangeInfo = m_layout->m_ranges[range]; + assert(rangeInfo.type == D3D11DescriptorSlotType::CombinedTextureSampler); + + assert(viewImpl->m_type == ResourceViewImpl::Type::SRV); + auto srvImpl = (ShaderResourceViewImpl*)viewImpl; + m_srvs[rangeInfo.arrayIndex + index] = srvImpl->m_srv; + + m_samplers[rangeInfo.arrayIndex + index] = samplerImpl->m_sampler; + + // TODO: need a place to bind the matching sampler... + m_srvs[rangeInfo.pairedSamplerArrayIndex + index] = srvImpl->m_srv; +} + +void D3D11Renderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) +{ + auto pipelineLayoutImpl = (PipelineLayoutImpl*)layout; + auto descriptorSetImpl = (DescriptorSetImpl*) descriptorSet; + + auto descriptorSetLayoutImpl = descriptorSetImpl->m_layout; + auto& setInfo = pipelineLayoutImpl->m_descriptorSets[index]; + + // Note: `setInfo->layout` and `descriptorSetLayoutImpl` need to be compatible + + // TODO: If/when we add per-stage visibility masks, it would be best to organize + // this as a loop over stages, so that we only do the binding that is required + // for each stage. + + { + int slotType = int(D3D11DescriptorSlotType::ConstantBuffer); + UInt slotCount = setInfo.layout->m_counts[slotType]; + if(slotCount) + { + UInt startSlot = setInfo.baseIndices[slotType]; + + auto cbs = descriptorSetImpl->m_cbs[0].readRef(); + + m_immediateContext->VSSetConstantBuffers(startSlot, slotCount, cbs); + // ... + m_immediateContext->PSSetConstantBuffers(startSlot, slotCount, cbs); + + m_immediateContext->CSSetConstantBuffers(startSlot, slotCount, cbs); + } + } + + { + int slotType = int(D3D11DescriptorSlotType::ShaderResourceView); + UInt slotCount = setInfo.layout->m_counts[slotType]; + if(slotCount) + { + UInt startSlot = setInfo.baseIndices[slotType]; + + auto srvs = descriptorSetImpl->m_srvs[0].readRef(); + + m_immediateContext->VSSetShaderResources(startSlot, slotCount, srvs); + // ... + m_immediateContext->PSSetShaderResources(startSlot, slotCount, srvs); + + m_immediateContext->CSSetShaderResources(startSlot, slotCount, srvs); + } + } + + { + int slotType = int(D3D11DescriptorSlotType::Sampler); + UInt slotCount = setInfo.layout->m_counts[slotType]; + if(slotCount) + { + UInt startSlot = setInfo.baseIndices[slotType]; + + auto samplers = descriptorSetImpl->m_samplers[0].readRef(); + + m_immediateContext->VSSetSamplers(startSlot, slotCount, samplers); + // ... + m_immediateContext->PSSetSamplers(startSlot, slotCount, samplers); + + m_immediateContext->CSSetSamplers(startSlot, slotCount, samplers); + } + } + + { + // Note: UAVs are handled differently from other bindings, because + // D3D11 requires all UAVs to be set with a single call, rather + // than allowing incremental updates. We will therefore shadow + // the UAV bindings with `m_uavBindings` and then flush them + // as needed right before a draw/dispatch. + // + int slotType = int(D3D11DescriptorSlotType::UnorderedAccessView); + UInt slotCount = setInfo.layout->m_counts[slotType]; + if(slotCount) + { + UInt startSlot = setInfo.baseIndices[slotType]; + + auto uavs = descriptorSetImpl->m_uavs[0].readRef(); + + for(UINT ii = 0; ii < slotCount; ++ii) + { + m_uavBindings[int(pipelineType)][startSlot + ii] = uavs[ii]; + } + m_targetBindingsDirty[int(pipelineType)] = true; + } + } + + +} + +} // renderer_test diff --git a/tools/gfx/render-d3d11.h b/tools/gfx/render-d3d11.h new file mode 100644 index 000000000..9e671d541 --- /dev/null +++ b/tools/gfx/render-d3d11.h @@ -0,0 +1,10 @@ +// render-d3d11.h +#pragma once + +namespace gfx { + +class Renderer; + +Renderer* createD3D11Renderer(); + +} // gfx diff --git a/tools/gfx/render-d3d12.cpp b/tools/gfx/render-d3d12.cpp new file mode 100644 index 000000000..2d3b8f521 --- /dev/null +++ b/tools/gfx/render-d3d12.cpp @@ -0,0 +1,3557 @@ +// render-d3d12.cpp +#define _CRT_SECURE_NO_WARNINGS + +#include "render-d3d12.h" + +//WORKING:#include "options.h" +#include "render.h" + +#include "surface.h" + +// In order to use the Slang API, we need to include its header + +//WORKING:#include + +// We will be rendering with Direct3D 12, so we need to include +// the Windows and D3D12 headers + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#include +#include +#include + +#include "../../slang-com-ptr.h" + +#include "resource-d3d12.h" +#include "descriptor-heap-d3d12.h" +#include "circular-resource-heap-d3d12.h" + +#include "d3d-util.h" + +// We will use the C standard library just for printing error messages. +#include + +#ifdef _MSC_VER +#include +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif +// + +#define ENABLE_DEBUG_LAYER 1 + +namespace gfx { +using namespace Slang; + +class D3D12Renderer : public Renderer +{ +public: + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + TextureResource::Desc getSwapChainTextureDesc() override; + + Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; + Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; + Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; + + Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; + Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; + + Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; + + Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; + Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; + Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; + + Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; + Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; + Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; + + virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; +// virtual void setInputLayout(InputLayout* inputLayout) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; + virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override; + virtual void waitForGpu() override; + virtual RendererType getRendererType() const override { return RendererType::DirectX12; } + + ~D3D12Renderer(); + +protected: + static const Int kMaxNumRenderFrames = 4; + static const Int kMaxNumRenderTargets = 3; + + static const Int kMaxRTVCount = 8; + static const Int kMaxDescriptorSetCount = 16; + + struct Submitter + { + virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0; + virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor) = 0; + virtual void setRootSignature(ID3D12RootSignature* rootSignature) = 0; + }; + + struct FrameInfo + { + FrameInfo() :m_fenceValue(0) {} + void reset() + { + m_commandAllocator.setNull(); + } + ComPtr m_commandAllocator; ///< The command allocator for this frame + UINT64 m_fenceValue; ///< The fence value when rendering this Frame is complete + }; + + class ShaderProgramImpl: public ShaderProgram + { + public: + PipelineType m_pipelineType; + List m_vertexShader; + List m_pixelShader; + List m_computeShader; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + enum class BackingStyle + { + Unknown, + ResourceBacked, ///< The contents is only held within the resource + MemoryBacked, ///< The current contents is held in m_memory and copied to GPU every time it's used (typically used for constant buffers) + }; + + void bindConstantBufferView(D3D12CircularResourceHeap& circularHeap, int index, Submitter* submitter) const + { + switch (m_backingStyle) + { + case BackingStyle::MemoryBacked: + { + const size_t bufferSize = m_memory.Count(); + D3D12CircularResourceHeap::Cursor cursor = circularHeap.allocateConstantBuffer(bufferSize); + ::memcpy(cursor.m_position, m_memory.Buffer(), bufferSize); + // Set the constant buffer + submitter->setRootConstantBufferView(index, circularHeap.getGpuHandle(cursor)); + break; + } + case BackingStyle::ResourceBacked: + { + // Set the constant buffer + submitter->setRootConstantBufferView(index, m_resource.getResource()->GetGPUVirtualAddress()); + break; + } + default: break; + } + } + + BufferResourceImpl(Resource::Usage initialUsage, const Desc& desc): + Parent(desc), + m_mapFlavor(MapFlavor::HostRead), + m_initialUsage(initialUsage) + { + } + + static BackingStyle _calcResourceBackingStyle(Usage usage) + { + // Note: the D3D12 back-end has support for "versioning" of constant buffers, + // where the same logical `BufferResource` can actually point to different + // backing storage over its lifetime, to emulate the ability to modify the + // buffer contents as in D3D11, etc. + // + // The VK back-end doesn't have the same behavior, and it is difficult + // to both support this degree of flexibility *and* efficeintly exploit + // descriptor tables (since any table referencing the buffer would need + // to be updated when a new buffer "version" gets allocated). + // + // I'm choosing to disable this for now, and make all buffers be memory-backed, + // although this creates synchronization issues that we'll have to address + // next. + + return BackingStyle::ResourceBacked; +#if 0 + switch (usage) + { + case Usage::ConstantBuffer: return BackingStyle::MemoryBacked; + default: return BackingStyle::ResourceBacked; + } +#endif + } + + BackingStyle m_backingStyle; ///< How the resource is 'backed' - either as a resource or cpu memory. Cpu memory is typically used for constant buffers. + D3D12Resource m_resource; ///< The resource typically in gpu memory + D3D12Resource m_uploadResource; ///< If the resource can be written to, and is in gpu memory (ie not Memory backed), will have upload resource + + Usage m_initialUsage; + + List m_memory; ///< Cpu memory buffer, used if the m_backingStyle is MemoryBacked + MapFlavor m_mapFlavor; ///< If the resource is mapped holds the current mapping flavor + }; + + class TextureResourceImpl: public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(const Desc& desc): + Parent(desc) + { + } + + D3D12Resource m_resource; + }; + + class SamplerStateImpl : public SamplerState + { + public: + D3D12_CPU_DESCRIPTOR_HANDLE m_cpuHandle; + }; + + class ResourceViewImpl : public ResourceView + { + public: + RefPtr m_resource; + D3D12HostVisibleDescriptor m_descriptor; + }; + + class InputLayoutImpl: public InputLayout + { + public: + List m_elements; + List m_text; ///< Holds all strings to keep in scope + }; + +#if 0 + struct BindingDetail + { + int m_srvIndex = -1; + int m_uavIndex = -1; + int m_samplerIndex = -1; + }; + + class BindingStateImpl: public BindingState + { + public: + typedef BindingState Parent; + + Result init(ID3D12Device* device) + { + // Set up descriptor heaps + SLANG_RETURN_ON_FAIL(m_viewHeap.init(device, 256, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); + SLANG_RETURN_ON_FAIL(m_samplerHeap.init(device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); + return SLANG_OK; + } + + /// Ctor + BindingStateImpl(const Desc& desc) : + Parent(desc) + {} + + List m_bindingDetails; ///< These match 1-1 to the bindings in the m_desc + }; +#endif + + class DescriptorSetLayoutImpl : public DescriptorSetLayout + { + public: + struct RangeInfo + { + DescriptorSlotType type; + Int count; + Int arrayIndex; + }; + + List m_ranges; + + List m_dxRanges; + List m_dxRootParameters; + + Int m_resourceCount; + Int m_samplerCount; + }; + + class PipelineLayoutImpl : public PipelineLayout + { + public: + ComPtr m_rootSignature; + UInt m_descriptorSetCount; + }; + + class DescriptorSetImpl : public DescriptorSet + { + public: + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; + virtual void setResource(UInt range, UInt index, ResourceView* view) override; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) override; + + RefPtr m_renderer; + RefPtr m_layout; + + D3D12DescriptorHeap* m_resourceHeap = nullptr; + D3D12DescriptorHeap* m_samplerHeap = nullptr; + + Int m_resourceTable = 0; + Int m_samplerTable = 0; + + // The following arrays are used to retain the relevant + // objects so that they will not be released while this + // descriptor-set is still alive. + // + // For the `m_resourceObjects` array, the values are either + // the relevant `ResourceViewImpl` for SRV/UAV slots, or + // a `BufferResourceImpl` for a CBV slot. + // + List> m_resourceObjects; + List> m_samplerObjects; + }; + + + // During command submission, we need all the descriptor tables that get + // used to come from a single heap (for each descritpor heap type). + // + // We will thus keep a single heap of each type that we hope will hold + // all the descriptors that actually get needed in a frame. + // + // TODO: we need an allocation policy to reallocate and resize these + // if/when we run out of space during a frame. + // + D3D12DescriptorHeap m_viewHeap; ///< Cbv, Srv, Uav + D3D12DescriptorHeap m_samplerHeap; ///< Heap for samplers + + D3D12HostVisibleDescriptorAllocator m_rtvAllocator; + D3D12HostVisibleDescriptorAllocator m_dsvAllocator; + + D3D12HostVisibleDescriptorAllocator m_viewAllocator; + D3D12HostVisibleDescriptorAllocator m_samplerAllocator; + + // Space in the GPU-visible heaps is precious, so we will also keep + // around CPU-visible heaps for storing descriptors in a format + // that is ready for copying into the GPU-visible heaps as needed. + // + D3D12DescriptorHeap m_cpuViewHeap; ///< Cbv, Srv, Uav + D3D12DescriptorHeap m_cpuSamplerHeap; ///< Heap for samplers + + class PipelineStateImpl : public PipelineState + { + public: + PipelineType m_pipelineType; + RefPtr m_pipelineLayout; + ComPtr m_pipelineState; + }; + + struct BoundVertexBuffer + { + RefPtr m_buffer; + int m_stride; + int m_offset; + }; + +#if 0 + struct BindParameters + { + enum + { + kMaxRanges = 16, + kMaxParameters = 32 + }; + + D3D12_DESCRIPTOR_RANGE& nextRange() { return m_ranges[m_rangeIndex++]; } + D3D12_ROOT_PARAMETER& nextParameter() { return m_parameters[m_paramIndex++]; } + + BindParameters(): + m_rangeIndex(0), + m_paramIndex(0) + {} + + D3D12_DESCRIPTOR_RANGE m_ranges[kMaxRanges]; + int m_rangeIndex; + D3D12_ROOT_PARAMETER m_parameters[kMaxParameters]; + int m_paramIndex; + }; +#endif + + struct GraphicsSubmitter : public Submitter + { + virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override + { + m_commandList->SetGraphicsRootConstantBufferView(index, gpuBufferLocation); + } + virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override + { + m_commandList->SetGraphicsRootDescriptorTable(index, baseDescriptor); + } + void setRootSignature(ID3D12RootSignature* rootSignature) + { + m_commandList->SetGraphicsRootSignature(rootSignature); + } + + GraphicsSubmitter(ID3D12GraphicsCommandList* commandList): + m_commandList(commandList) + { + } + + ID3D12GraphicsCommandList* m_commandList; + }; + + struct ComputeSubmitter : public Submitter + { + virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override + { + m_commandList->SetComputeRootConstantBufferView(index, gpuBufferLocation); + } + virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override + { + m_commandList->SetComputeRootDescriptorTable(index, baseDescriptor); + } + void setRootSignature(ID3D12RootSignature* rootSignature) + { + m_commandList->SetComputeRootSignature(rootSignature); + } + + ComputeSubmitter(ID3D12GraphicsCommandList* commandList) : + m_commandList(commandList) + { + } + + ID3D12GraphicsCommandList* m_commandList; + }; + + static PROC loadProc(HMODULE module, char const* name); + Result createFrameResources(); + /// Blocks until gpu has completed all work + void releaseFrameResources(); + + Result createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut); + + void beginRender(); + + void endRender(); + + void submitGpuWorkAndWait(); + void _resetCommandList(); + + Result captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut); + + FrameInfo& getFrame() { return m_frameInfos[m_frameIndex]; } + const FrameInfo& getFrame() const { return m_frameInfos[m_frameIndex]; } + + ID3D12GraphicsCommandList* getCommandList() const { return m_commandList; } + +// RenderState* calcRenderState(); + + /// From current bindings calculate the root signature and pipeline state +// Result calcGraphicsPipelineState(ComPtr& sigOut, ComPtr& pipelineStateOut); +// Result calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut); + + Result _bindRenderState(PipelineStateImpl* pipelineStateImpl, ID3D12GraphicsCommandList* commandList, Submitter* submitter); + +// Result _calcBindParameters(BindParameters& params); +// RenderState* findRenderState(PipelineType pipelineType); + + PFN_D3D12_SERIALIZE_ROOT_SIGNATURE m_D3D12SerializeRootSignature = nullptr; + + D3D12CircularResourceHeap m_circularResourceHeap; + + int m_commandListOpenCount = 0; ///< If >0 the command list should be open + + List m_boundVertexBuffers; + + RefPtr m_boundIndexBuffer; + DXGI_FORMAT m_boundIndexFormat; + UINT m_boundIndexOffset; + + RefPtr m_currentPipelineState; + +// RefPtr m_boundShaderProgram; +// RefPtr m_boundInputLayout; + +// RefPtr m_boundBindingState; + RefPtr m_boundDescriptorSets[int(PipelineType::CountOf)][kMaxDescriptorSetCount]; + + DXGI_FORMAT m_targetFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + DXGI_FORMAT m_depthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; + bool m_hasVsync = true; + bool m_isFullSpeed = false; + bool m_allowFullScreen = false; + bool m_isMultiSampled = false; + int m_numTargetSamples = 1; ///< The number of multi sample samples + int m_targetSampleQuality = 0; ///< The multi sample quality + + Desc m_desc; + + bool m_isInitialized = false; + + D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + D3D12_PRIMITIVE_TOPOLOGY m_primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + + float m_clearColor[4] = { 0, 0, 0, 0 }; + + D3D12_VIEWPORT m_viewport = {}; + + ComPtr m_dxDebug; + + ComPtr m_device; + ComPtr m_swapChain; + ComPtr m_commandQueue; +// ComPtr m_rtvHeap; + ComPtr m_commandList; + + D3D12_RECT m_scissorRect = {}; + +// List > m_renderStates; ///< Holds list of all render state combinations +// RenderState* m_currentRenderState = nullptr; ///< The current combination + + UINT m_rtvDescriptorSize = 0; + +// ComPtr m_dsvHeap; + UINT m_dsvDescriptorSize = 0; + + // Synchronization objects. + D3D12CounterFence m_fence; + + HANDLE m_swapChainWaitableObject; + + // Frame specific data + int m_numRenderFrames = 0; + UINT m_frameIndex = 0; + FrameInfo m_frameInfos[kMaxNumRenderFrames]; + + int m_numRenderTargets = 2; + int m_renderTargetIndex = 0; + + D3D12Resource* m_backBuffers[kMaxNumRenderTargets]; + D3D12Resource* m_renderTargets[kMaxNumRenderTargets]; + + D3D12Resource m_backBufferResources[kMaxNumRenderTargets]; + D3D12Resource m_renderTargetResources[kMaxNumRenderTargets]; + + RefPtr m_rtvs[kMaxRTVCount]; + RefPtr m_dsv; + + int32_t m_depthStencilUsageFlags = 0; ///< D3DUtil::UsageFlag combination for depth stencil + int32_t m_targetUsageFlags = 0; ///< D3DUtil::UsageFlag combination for target + + HWND m_hwnd = nullptr; +}; + +Renderer* createD3D12Renderer() +{ + return new D3D12Renderer; +} + +/* static */PROC D3D12Renderer::loadProc(HMODULE module, char const* name) +{ + PROC proc = ::GetProcAddress(module, name); + if (!proc) + { + fprintf(stderr, "error: failed load symbol '%s'\n", name); + return nullptr; + } + return proc; +} + +void D3D12Renderer::releaseFrameResources() +{ + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb174577%28v=vs.85%29.aspx + + // Release the resources holding references to the swap chain (requirement of + // IDXGISwapChain::ResizeBuffers) and reset the frame fence values to the + // current fence value. + for (int i = 0; i < m_numRenderFrames; i++) + { + FrameInfo& info = m_frameInfos[i]; + info.reset(); + info.m_fenceValue = m_fence.getCurrentValue(); + } + for (int i = 0; i < m_numRenderTargets; i++) + { + m_backBuffers[i]->setResourceNull(); + m_renderTargets[i]->setResourceNull(); + } +} + +void D3D12Renderer::waitForGpu() +{ + m_fence.nextSignalAndWait(m_commandQueue); +} + +D3D12Renderer::~D3D12Renderer() +{ + if (m_isInitialized) + { + // Ensure that the GPU is no longer referencing resources that are about to be + // cleaned up by the destructor. + waitForGpu(); + } +} + +static void _initSrvDesc(Resource::Type resourceType, const TextureResource::Desc& textureDesc, const D3D12_RESOURCE_DESC& desc, DXGI_FORMAT pixelFormat, D3D12_SHADER_RESOURCE_VIEW_DESC& descOut) +{ + // create SRV + descOut = D3D12_SHADER_RESOURCE_VIEW_DESC(); + + descOut.Format = (pixelFormat == DXGI_FORMAT_UNKNOWN) ? D3DUtil::calcFormat(D3DUtil::USAGE_SRV, desc.Format) : pixelFormat; + descOut.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + if (desc.DepthOrArraySize == 1) + { + switch (desc.Dimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; break; + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; break; + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; break; + default: assert(!"Unknown dimension"); + } + + descOut.Texture2D.MipLevels = desc.MipLevels; + descOut.Texture2D.MostDetailedMip = 0; + descOut.Texture2D.PlaneSlice = 0; + descOut.Texture2D.ResourceMinLODClamp = 0.0f; + } + else if (resourceType == Resource::Type::TextureCube) + { + if (textureDesc.arraySize > 1) + { + descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; + + descOut.TextureCubeArray.NumCubes = textureDesc.arraySize; + descOut.TextureCubeArray.First2DArrayFace = 0; + descOut.TextureCubeArray.MipLevels = desc.MipLevels; + descOut.TextureCubeArray.MostDetailedMip = 0; + descOut.TextureCubeArray.ResourceMinLODClamp = 0; + } + else + { + descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; + + descOut.TextureCube.MipLevels = desc.MipLevels; + descOut.TextureCube.MostDetailedMip = 0; + descOut.TextureCube.ResourceMinLODClamp = 0; + } + } + else + { + assert(desc.DepthOrArraySize > 1); + + switch (desc.Dimension) + { + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY; break; + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; break; + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; break; + + default: assert(!"Unknown dimension"); + } + + descOut.Texture2DArray.ArraySize = desc.DepthOrArraySize; + descOut.Texture2DArray.MostDetailedMip = 0; + descOut.Texture2DArray.MipLevels = desc.MipLevels; + descOut.Texture2DArray.FirstArraySlice = 0; + descOut.Texture2DArray.PlaneSlice = 0; + descOut.Texture2DArray.ResourceMinLODClamp = 0; + } +} + +static void _initBufferResourceDesc(size_t bufferSize, D3D12_RESOURCE_DESC& out) +{ + out = {}; + + out.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + out.Alignment = 0; + out.Width = bufferSize; + out.Height = 1; + out.DepthOrArraySize = 1; + out.MipLevels = 1; + out.Format = DXGI_FORMAT_UNKNOWN; + out.SampleDesc.Count = 1; + out.SampleDesc.Quality = 0; + out.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + out.Flags = D3D12_RESOURCE_FLAG_NONE; +} + +Result D3D12Renderer::createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut) +{ + const size_t bufferSize = size_t(resourceDesc.Width); + + { + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + const D3D12_RESOURCE_STATES initialState = srcData ? D3D12_RESOURCE_STATE_COPY_DEST : finalState; + + SLANG_RETURN_ON_FAIL(resourceOut.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, initialState, nullptr)); + } + + { + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC uploadResourceDesc(resourceDesc); + uploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + SLANG_RETURN_ON_FAIL(uploadResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, uploadResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr)); + } + + if (srcData) + { + // Copy data to the intermediate upload heap and then schedule a copy + // from the upload heap to the vertex buffer. + UINT8* dstData; + D3D12_RANGE readRange = {}; // We do not intend to read from this resource on the CPU. + + ID3D12Resource* dxUploadResource = uploadResource.getResource(); + + SLANG_RETURN_ON_FAIL(dxUploadResource->Map(0, &readRange, reinterpret_cast(&dstData))); + ::memcpy(dstData, srcData, 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; +} + +void D3D12Renderer::_resetCommandList() +{ + const FrameInfo& frame = getFrame(); + + ID3D12GraphicsCommandList* commandList = getCommandList(); + commandList->Reset(frame.m_commandAllocator, nullptr); + + // TIM: when should this get set? +// commandList->OMSetRenderTargets( +// 1, +// &m_rtvs[0]->m_descriptor.cpuHandle, +// FALSE, +// m_dsv ? &m_dsv->m_descriptor.cpuHandle : nullptr); + + // Set necessary state. + commandList->RSSetViewports(1, &m_viewport); + commandList->RSSetScissorRects(1, &m_scissorRect); +} + +void D3D12Renderer::beginRender() +{ + // Should currently not be open! + assert(m_commandListOpenCount == 0); + + m_circularResourceHeap.updateCompleted(); + + getFrame().m_commandAllocator->Reset(); + + _resetCommandList(); + + // Indicate that the render target needs to be writable + { + D3D12BarrierSubmitter submitter(m_commandList); + m_renderTargets[m_renderTargetIndex]->transition(D3D12_RESOURCE_STATE_RENDER_TARGET, submitter); + } + + m_commandListOpenCount = 1; +} + +void D3D12Renderer::endRender() +{ + assert(m_commandListOpenCount == 1); + + { + const UInt64 signalValue = m_fence.nextSignal(m_commandQueue); + m_circularResourceHeap.addSync(signalValue); + } + + D3D12Resource& backBuffer = *m_backBuffers[m_renderTargetIndex]; + if (m_isMultiSampled) + { + // MSAA resolve + D3D12Resource& renderTarget = *m_renderTargets[m_renderTargetIndex]; + assert(&renderTarget != &backBuffer); + // Barriers to wait for the render target, and the backbuffer to be in correct state + { + D3D12BarrierSubmitter submitter(m_commandList); + renderTarget.transition(D3D12_RESOURCE_STATE_RESOLVE_SOURCE, submitter); + backBuffer.transition(D3D12_RESOURCE_STATE_RESOLVE_DEST, submitter); + } + + // Do the resolve... + m_commandList->ResolveSubresource(backBuffer, 0, renderTarget, 0, m_targetFormat); + } + + // Make the back buffer presentable + { + D3D12BarrierSubmitter submitter(m_commandList); + backBuffer.transition(D3D12_RESOURCE_STATE_PRESENT, submitter); + } + + SLANG_ASSERT_VOID_ON_FAIL(m_commandList->Close()); + + { + // Execute the command list. + ID3D12CommandList* commandLists[] = { m_commandList }; + m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists); + } + + assert(m_commandListOpenCount == 1); + // Must be 0 + m_commandListOpenCount = 0; +} + +void D3D12Renderer::submitGpuWork() +{ + assert(m_commandListOpenCount); + ID3D12GraphicsCommandList* commandList = getCommandList(); + + SLANG_ASSERT_VOID_ON_FAIL(commandList->Close()); + { + // Execute the command list. + ID3D12CommandList* commandLists[] = { commandList }; + m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists); + } + + // Reset the render target + _resetCommandList(); +} + +void D3D12Renderer::submitGpuWorkAndWait() +{ + submitGpuWork(); + waitForGpu(); +} + +Result D3D12Renderer::captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut) +{ + const D3D12_RESOURCE_STATES initialState = resource.getState(); + + const D3D12_RESOURCE_DESC desc = resource.getResource()->GetDesc(); + + // Don't bother supporting MSAA for right now + if (desc.SampleDesc.Count > 1) + { + fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); + return SLANG_FAIL; + } + + size_t bytesPerPixel = sizeof(uint32_t); + size_t rowPitch = int(desc.Width) * bytesPerPixel; + size_t bufferSize = rowPitch * int(desc.Height); + + D3D12Resource stagingResource; + { + D3D12_RESOURCE_DESC stagingDesc; + _initBufferResourceDesc(bufferSize, stagingDesc); + + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_READBACK; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + SLANG_RETURN_ON_FAIL(stagingResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); + } + + { + D3D12BarrierSubmitter submitter(m_commandList); + resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter); + } + + // Do the copy + { + D3D12_TEXTURE_COPY_LOCATION srcLoc; + srcLoc.pResource = resource; + srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + srcLoc.SubresourceIndex = 0; + + D3D12_TEXTURE_COPY_LOCATION dstLoc; + dstLoc.pResource = stagingResource; + dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + dstLoc.PlacedFootprint.Offset = 0; + dstLoc.PlacedFootprint.Footprint.Format = desc.Format; + dstLoc.PlacedFootprint.Footprint.Width = UINT(desc.Width); + dstLoc.PlacedFootprint.Footprint.Height = UINT(desc.Height); + dstLoc.PlacedFootprint.Footprint.Depth = 1; + dstLoc.PlacedFootprint.Footprint.RowPitch = UINT(rowPitch); + + m_commandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr); + } + + { + D3D12BarrierSubmitter submitter(m_commandList); + resource.transition(initialState, submitter); + } + + // Submit the copy, and wait for copy to complete + submitGpuWorkAndWait(); + + { + ID3D12Resource* dxResource = stagingResource; + + UINT8* data; + D3D12_RANGE readRange = {0, bufferSize}; + + SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast(&data))); + + Result res = surfaceOut.set(int(desc.Width), int(desc.Height), Format::RGBA_Unorm_UInt8, int(rowPitch), data, SurfaceAllocator::getMallocAllocator()); + + dxResource->Unmap(0, nullptr); + return res; + } +} + +#if 0 +Result D3D12Renderer::calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut) +{ + BindParameters bindParameters; + _calcBindParameters(bindParameters); + + ComPtr rootSignature; + ComPtr pipelineState; + + { + D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc; + rootSignatureDesc.NumParameters = bindParameters.m_paramIndex; + rootSignatureDesc.pParameters = bindParameters.m_parameters; + rootSignatureDesc.NumStaticSamplers = 0; + rootSignatureDesc.pStaticSamplers = nullptr; + rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; + + ComPtr signature; + ComPtr error; + SLANG_RETURN_ON_FAIL(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef())); + SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef()))); + } + + { + // Describe and create the compute pipeline state object + D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {}; + computeDesc.pRootSignature = rootSignature; + computeDesc.CS = { m_boundShaderProgram->m_computeShader.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; +} +#endif + +#if 0 +D3D12Renderer::RenderState* D3D12Renderer::findRenderState(PipelineType pipelineType) +{ + switch (pipelineType) + { + case PipelineType::Compute: + { + // Check if current state is a match + if (m_currentRenderState) + { + if (m_currentRenderState->m_bindingState == m_boundBindingState && + m_currentRenderState->m_shaderProgram == m_boundShaderProgram) + { + return m_currentRenderState; + } + } + + const int num = int(m_renderStates.Count()); + for (int i = 0; i < num; i++) + { + RenderState* renderState = m_renderStates[i]; + if (renderState->m_bindingState == m_boundBindingState && + renderState->m_shaderProgram == m_boundShaderProgram) + { + return renderState; + } + } + break; + } + case PipelineType::Graphics: + { + if (m_currentRenderState) + { + if (m_currentRenderState->m_bindingState == m_boundBindingState && + m_currentRenderState->m_inputLayout == m_boundInputLayout && + m_currentRenderState->m_shaderProgram == m_boundShaderProgram && + m_currentRenderState->m_primitiveTopologyType == m_primitiveTopologyType) + { + return m_currentRenderState; + } + } + // See if matches one in the list + { + const int num = int(m_renderStates.Count()); + for (int i = 0; i < num; i++) + { + RenderState* renderState = m_renderStates[i]; + if (renderState->m_bindingState == m_boundBindingState && + renderState->m_inputLayout == m_boundInputLayout && + renderState->m_shaderProgram == m_boundShaderProgram && + renderState->m_primitiveTopologyType == m_primitiveTopologyType) + { + // Okay we have a match + return renderState; + } + } + } + break; + } + default: break; + } + return nullptr; +} + +D3D12Renderer::RenderState* D3D12Renderer::calcRenderState() +{ + if (!m_boundShaderProgram) + { + return nullptr; + } + m_currentRenderState = findRenderState(m_boundShaderProgram->m_pipelineType); + if (m_currentRenderState) + { + return m_currentRenderState; + } + + ComPtr rootSignature; + ComPtr pipelineState; + + switch (m_boundShaderProgram->m_pipelineType) + { + case PipelineType::Compute: + { + if (SLANG_FAILED(calcComputePipelineState(rootSignature, pipelineState))) + { + return nullptr; + } + break; + } + case PipelineType::Graphics: + { + if (SLANG_FAILED(calcGraphicsPipelineState(rootSignature, pipelineState))) + { + return nullptr; + } + break; + } + default: return nullptr; + } + + RenderState* renderState = new RenderState; + + renderState->m_primitiveTopologyType = m_primitiveTopologyType; + renderState->m_bindingState = m_boundBindingState; + renderState->m_inputLayout = m_boundInputLayout; + renderState->m_shaderProgram = m_boundShaderProgram; + + renderState->m_rootSignature.swap(rootSignature); + renderState->m_pipelineState.swap(pipelineState); + + m_renderStates.Add(renderState); + + m_currentRenderState = renderState; + + return renderState; +} + +Result D3D12Renderer::_calcBindParameters(BindParameters& params) +{ + int numConstantBuffers = 0; + { + if (m_boundBindingState) + { + const int numBoundConstantBuffers = numConstantBuffers; + + const BindingState::Desc& bindingStateDesc = m_boundBindingState->getDesc(); + + const auto& bindings = bindingStateDesc.m_bindings; + const auto& details = m_boundBindingState->m_bindingDetails; + + const int numBindings = int(bindings.Count()); + + for (int i = 0; i < numBindings; i++) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + const int bindingIndex = binding.registerRange.getSingleIndex(); + + if (binding.bindingType == BindingType::Buffer) + { + assert(binding.resource && binding.resource->isBuffer()); + if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) + { + // Make sure it's not overlapping the ones we just statically defined + //assert(binding.m_binding < numBoundConstantBuffers); + + D3D12_ROOT_PARAMETER& param = params.nextParameter(); + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + D3D12_ROOT_DESCRIPTOR& descriptor = param.Descriptor; + descriptor.ShaderRegister = bindingIndex; + descriptor.RegisterSpace = 0; + + numConstantBuffers++; + } + } + + if (detail.m_srvIndex >= 0) + { + D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); + + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + range.NumDescriptors = 1; + range.BaseShaderRegister = bindingIndex; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_PARAMETER& param = params.nextParameter(); + + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; + table.NumDescriptorRanges = 1; + table.pDescriptorRanges = ⦥ + } + + if (detail.m_uavIndex >= 0) + { + D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); + + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + range.NumDescriptors = 1; + range.BaseShaderRegister = bindingIndex; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_PARAMETER& param = params.nextParameter(); + + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; + table.NumDescriptorRanges = 1; + table.pDescriptorRanges = ⦥ + } + } + } + } + + // All the samplers are in one continuous section of the sampler heap + if (m_boundBindingState && m_boundBindingState->m_samplerHeap.getUsedSize() > 0) + { + D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); + + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + range.NumDescriptors = m_boundBindingState->m_samplerHeap.getUsedSize(); + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_PARAMETER& param = params.nextParameter(); + + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; + table.NumDescriptorRanges = 1; + table.pDescriptorRanges = ⦥ + } + return SLANG_OK; +} +#endif + +Result D3D12Renderer::_bindRenderState(PipelineStateImpl* pipelineStateImpl, ID3D12GraphicsCommandList* commandList, Submitter* submitter) +{ + // TODO: we should only set some of this state as needed... + + auto pipelineTypeIndex = (int) pipelineStateImpl->m_pipelineType; + auto pipelineLayout = pipelineStateImpl->m_pipelineLayout; + + submitter->setRootSignature(pipelineLayout->m_rootSignature); + commandList->SetPipelineState(pipelineStateImpl->m_pipelineState); + + ID3D12DescriptorHeap* heaps[] = + { + m_viewHeap.getHeap(), + m_samplerHeap.getHeap(), + }; + commandList->SetDescriptorHeaps(SLANG_COUNT_OF(heaps), heaps); + + // We need to copy descriptors over from the descriptor sets + // (where they are stored in CPU-visible heaps) to the GPU-visible + // heaps so that they can be accessed by shader code. + + Int descriptorSetCount = pipelineLayout->m_descriptorSetCount; + Int rootParameterIndex = 0; + for(Int dd = 0; dd < descriptorSetCount; ++dd) + { + auto descriptorSet = m_boundDescriptorSets[pipelineTypeIndex][dd]; + auto descriptorSetLayout = descriptorSet->m_layout; + + // TODO: require that `descriptorSetLayout` is compatible with + // `pipelineLayout->descriptorSetlayouts[dd]`. + + { + if(auto descriptorCount = descriptorSetLayout->m_resourceCount) + { + auto& gpuHeap = m_viewHeap; + auto gpuDescriptorTable = gpuHeap.allocate(descriptorCount); + + auto& cpuHeap = *descriptorSet->m_resourceHeap; + auto cpuDescriptorTable = descriptorSet->m_resourceTable; + + m_device->CopyDescriptorsSimple( + descriptorCount, + gpuHeap.getCpuHandle(gpuDescriptorTable), + cpuHeap.getCpuHandle(cpuDescriptorTable), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + submitter->setRootDescriptorTable(rootParameterIndex++, gpuHeap.getGpuHandle(gpuDescriptorTable)); + } + } + { + if(auto descriptorCount = descriptorSetLayout->m_samplerCount) + { + auto& gpuHeap = m_samplerHeap; + auto gpuDescriptorTable = gpuHeap.allocate(descriptorCount); + + auto& cpuHeap = *descriptorSet->m_samplerHeap; + auto cpuDescriptorTable = descriptorSet->m_samplerTable; + + m_device->CopyDescriptorsSimple( + descriptorCount, + gpuHeap.getCpuHandle(gpuDescriptorTable), + cpuHeap.getCpuHandle(cpuDescriptorTable), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + + submitter->setRootDescriptorTable(rootParameterIndex++, gpuHeap.getGpuHandle(gpuDescriptorTable)); + } + } + } + + return SLANG_OK; +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +Result D3D12Renderer::initialize(const Desc& desc, void* inWindowHandle) +{ + m_hwnd = (HWND)inWindowHandle; + // Rather than statically link against D3D, we load it dynamically. + + HMODULE d3dModule = LoadLibraryA("d3d12.dll"); + if (!d3dModule) + { + fprintf(stderr, "error: failed load 'd3d12.dll'\n"); + return SLANG_FAIL; + } + + 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_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 + + m_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)loadProc(d3dModule, "D3D12SerializeRootSignature"); + if (!m_D3D12SerializeRootSignature) + { + return SLANG_FAIL; + } + + // Try and create DXGIFactory + ComPtr 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 + ComPtr adapter; + + LOAD_D3D_PROC(PFN_D3D12_CREATE_DEVICE, D3D12CreateDevice); + if (!D3D12CreateDevice_) + { + return SLANG_FAIL; + } + + const bool useWarp = false; + + if (useWarp) + { + SLANG_RETURN_ON_FAIL(dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(adapter.writeRef()))); + SLANG_RETURN_ON_FAIL(D3D12CreateDevice_(adapter, featureLevel, IID_PPV_ARGS(m_device.writeRef()))); + } + else + { + UINT adapterCounter = 0; + for (;;) + { + UINT adapterIndex = adapterCounter++; + + ComPtr candidateAdapter; + if (dxgiFactory->EnumAdapters1(adapterIndex, candidateAdapter.writeRef()) == DXGI_ERROR_NOT_FOUND) + break; + + DXGI_ADAPTER_DESC1 desc; + candidateAdapter->GetDesc1(&desc); + + if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) + { + + // TODO: may want to allow software driver as fallback + } + else + { + continue; + } + + if (SUCCEEDED(D3D12CreateDevice_(candidateAdapter, featureLevel, IID_PPV_ARGS(m_device.writeRef())))) + { + // We found one! + adapter = candidateAdapter; + break; + } + } + } + + if (!adapter) + { + // Couldn't find an adapter + return SLANG_FAIL; + } + + // set up debug layer +#ifndef NDEBUG + { + + LOAD_D3D_PROC(PFN_D3D12_GET_DEBUG_INTERFACE, D3D12GetDebugInterface); + if (!D3D12GetDebugInterface_) + { + return SLANG_FAIL; + } + + ComPtr debug; + + if (!SUCCEEDED(D3D12GetDebugInterface_(IID_PPV_ARGS(debug.writeRef())))) + { + return SLANG_FAIL; + } + + debug->EnableDebugLayer(); + } +#endif + + m_numRenderFrames = 3; + m_numRenderTargets = 2; + + m_desc = desc; + + // set viewport + { + m_viewport.Width = float(m_desc.width); + m_viewport.Height = float(m_desc.height); + m_viewport.MinDepth = 0; + m_viewport.MaxDepth = 1; + m_viewport.TopLeftX = 0; + m_viewport.TopLeftY = 0; + } + + { + m_scissorRect.left = 0; + m_scissorRect.top = 0; + m_scissorRect.right = m_desc.width; + m_scissorRect.bottom = m_desc.height; + } + + // Describe and create the command queue. + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + SLANG_RETURN_ON_FAIL(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(m_commandQueue.writeRef()))); + + // Describe the swap chain. + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferCount = m_numRenderTargets; + swapChainDesc.BufferDesc.Width = m_desc.width; + swapChainDesc.BufferDesc.Height = m_desc.height; + swapChainDesc.BufferDesc.Format = m_targetFormat; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.OutputWindow = m_hwnd; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.Windowed = TRUE; + + if (m_isFullSpeed) + { + m_hasVsync = false; + m_allowFullScreen = false; + } + + if (!m_hasVsync) + { + swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; + } + + // Swap chain needs the queue so that it can force a flush on it. + ComPtr swapChain; + SLANG_RETURN_ON_FAIL(dxgiFactory->CreateSwapChain(m_commandQueue, &swapChainDesc, swapChain.writeRef())); + SLANG_RETURN_ON_FAIL(swapChain->QueryInterface(m_swapChain.writeRef())); + + if (!m_hasVsync) + { + m_swapChainWaitableObject = m_swapChain->GetFrameLatencyWaitableObject(); + + int maxLatency = m_numRenderTargets - 2; + + // Make sure the maximum latency is in the range required by dx12 runtime + maxLatency = (maxLatency < 1) ? 1 : maxLatency; + maxLatency = (maxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS) ? DXGI_MAX_SWAP_CHAIN_BUFFERS : maxLatency; + + m_swapChain->SetMaximumFrameLatency(maxLatency); + } + + // This sample does not support fullscreen transitions. + SLANG_RETURN_ON_FAIL(dxgiFactory->MakeWindowAssociation(m_hwnd, DXGI_MWA_NO_ALT_ENTER)); + + m_renderTargetIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // Create descriptor heaps. + + SLANG_RETURN_ON_FAIL(m_viewHeap.init (m_device, 256, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); + SLANG_RETURN_ON_FAIL(m_samplerHeap.init(m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); + + SLANG_RETURN_ON_FAIL(m_cpuViewHeap.init (m_device, 1024, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); + SLANG_RETURN_ON_FAIL(m_cpuSamplerHeap.init(m_device, 64, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_NONE)); + + SLANG_RETURN_ON_FAIL(m_rtvAllocator.init (m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_RTV)); + SLANG_RETURN_ON_FAIL(m_dsvAllocator.init (m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_DSV)); + SLANG_RETURN_ON_FAIL(m_viewAllocator.init (m_device, 64, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); + SLANG_RETURN_ON_FAIL(m_samplerAllocator.init(m_device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)); + + // Setup frame resources + { + SLANG_RETURN_ON_FAIL(createFrameResources()); + } + + // Setup fence, and close the command list (as default state without begin/endRender is closed) + { + SLANG_RETURN_ON_FAIL(m_fence.init(m_device)); + // Create the command list. When command lists are created they are open, so close it. + FrameInfo& frame = m_frameInfos[m_frameIndex]; + SLANG_RETURN_ON_FAIL(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frame.m_commandAllocator, nullptr, IID_PPV_ARGS(m_commandList.writeRef()))); + m_commandList->Close(); + } + + { + D3D12CircularResourceHeap::Desc desc; + desc.init(); + // Define size + desc.m_blockSize = 65536; + // Set up the heap + m_circularResourceHeap.init(m_device, desc, &m_fence); + } + + // Setup for rendering + beginRender(); + + m_isInitialized = true; + return SLANG_OK; +} + +Result D3D12Renderer::createFrameResources() +{ + // Create back buffers + { +// D3D12_CPU_DESCRIPTOR_HANDLE rtvStart(m_rtvHeap->GetCPUDescriptorHandleForHeapStart()); + + // Work out target format + D3D12_RESOURCE_DESC resourceDesc; + { + ComPtr backBuffer; + SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, IID_PPV_ARGS(backBuffer.writeRef()))); + resourceDesc = backBuffer->GetDesc(); + } + const DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_TARGET, m_targetUsageFlags, resourceDesc.Format); + const DXGI_FORMAT targetFormat = D3DUtil::calcFormat(D3DUtil::USAGE_TARGET, resourceFormat); + + // Set the target format + m_targetFormat = targetFormat; + + // Create a RTV, and a command allocator for each frame. + for (int i = 0; i < m_numRenderTargets; i++) + { + // Get the back buffer + ComPtr backBuffer; + SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(UINT(i), IID_PPV_ARGS(backBuffer.writeRef()))); + + // Set up resource for back buffer + m_backBufferResources[i].setResource(backBuffer, D3D12_RESOURCE_STATE_COMMON); + m_backBuffers[i] = &m_backBufferResources[i]; + // Assume they are the same thing for now... + m_renderTargets[i] = &m_backBufferResources[i]; + + // If we are multi-sampling - create a render target separate from the back buffer + if (m_isMultiSampled) + { + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + D3D12_CLEAR_VALUE clearValue = {}; + clearValue.Format = m_targetFormat; + + // Don't know targets alignment, so just memory copy + ::memcpy(clearValue.Color, m_clearColor, sizeof(m_clearColor)); + + D3D12_RESOURCE_DESC desc(resourceDesc); + + desc.Format = resourceFormat; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc.SampleDesc.Count = m_numTargetSamples; + desc.SampleDesc.Quality = m_targetSampleQuality; + desc.Alignment = 0; + + SLANG_RETURN_ON_FAIL(m_renderTargetResources[i].initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, desc, D3D12_RESOURCE_STATE_RENDER_TARGET, &clearValue)); + m_renderTargets[i] = &m_renderTargetResources[i]; + } + + D3D12HostVisibleDescriptor rtvDescriptor; + SLANG_RETURN_ON_FAIL(m_rtvAllocator.allocate(&rtvDescriptor)); + + m_device->CreateRenderTargetView(*m_renderTargets[i], nullptr, rtvDescriptor.cpuHandle); + } + } + + // Set up frames + for (int i = 0; i < m_numRenderFrames; i++) + { + FrameInfo& frame = m_frameInfos[i]; + SLANG_RETURN_ON_FAIL(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(frame.m_commandAllocator.writeRef()))); + } + + { + D3D12_RESOURCE_DESC desc = m_backBuffers[0]->getResource()->GetDesc(); + assert(desc.Width == UINT64(m_desc.width) && desc.Height == UINT64(m_desc.height)); + } + + // Create the depth stencil view. + { + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_DEPTH_STENCIL, m_depthStencilUsageFlags, m_depthStencilFormat); + DXGI_FORMAT depthStencilFormat = D3DUtil::calcFormat(D3DUtil::USAGE_DEPTH_STENCIL, resourceFormat); + + // Set the depth stencil format + m_depthStencilFormat = depthStencilFormat; + + // Setup default clear + D3D12_CLEAR_VALUE clearValue = {}; + clearValue.Format = depthStencilFormat; + clearValue.DepthStencil.Depth = 1.0f; + clearValue.DepthStencil.Stencil = 0; + + D3D12_RESOURCE_DESC resourceDesc = {}; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + resourceDesc.Format = resourceFormat; + resourceDesc.Width = m_desc.width; + resourceDesc.Height = m_desc.height; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.SampleDesc.Count = m_numTargetSamples; + resourceDesc.SampleDesc.Quality = m_targetSampleQuality; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + resourceDesc.Alignment = 0; + +#if 0 + SLANG_RETURN_ON_FAIL(m_depthStencil.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue)); + + // Set the depth stencil + D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {}; + depthStencilDesc.Format = depthStencilFormat; + depthStencilDesc.ViewDimension = m_isMultiSampled ? D3D12_DSV_DIMENSION_TEXTURE2DMS : D3D12_DSV_DIMENSION_TEXTURE2D; + depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE; + + // Set up as the depth stencil view + m_device->CreateDepthStencilView(m_depthStencil, &depthStencilDesc, m_dsvHeap->GetCPUDescriptorHandleForHeapStart()); + m_depthStencilView = m_dsvHeap->GetCPUDescriptorHandleForHeapStart(); +#endif + } + + m_viewport.Width = static_cast(m_desc.width); + m_viewport.Height = static_cast(m_desc.height); + m_viewport.MaxDepth = 1.0f; + + m_scissorRect.right = static_cast(m_desc.width); + m_scissorRect.bottom = static_cast(m_desc.height); + + return SLANG_OK; +} + +void D3D12Renderer::setClearColor(const float color[4]) +{ + memcpy(m_clearColor, color, sizeof(m_clearColor)); +} + +void D3D12Renderer::clearFrame() +{ + // Record commands + if(auto rtv = m_rtvs[0]) + { + m_commandList->ClearRenderTargetView(rtv->m_descriptor.cpuHandle, m_clearColor, 0, nullptr); + } + if (m_dsv) + { + m_commandList->ClearDepthStencilView(m_dsv->m_descriptor.cpuHandle, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); + } +} + +void D3D12Renderer::presentFrame() +{ + endRender(); + + if (m_swapChainWaitableObject) + { + // check if now is good time to present + // This doesn't wait - because the wait time is 0. If it returns WAIT_TIMEOUT it means that no frame is waiting to be be displayed + // so there is no point doing a present. + const bool shouldPresent = (WaitForSingleObjectEx(m_swapChainWaitableObject, 0, TRUE) != WAIT_TIMEOUT); + if (shouldPresent) + { + m_swapChain->Present(0, 0); + } + } + else + { + if (SLANG_FAILED(m_swapChain->Present(1, 0))) + { + assert(!"Problem presenting"); + beginRender(); + return; + } + } + + // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >= + m_frameInfos[m_frameIndex].m_fenceValue = m_fence.nextSignal(m_commandQueue); + + // increment frame index after signal + m_frameIndex = (m_frameIndex + 1) % m_numRenderFrames; + // Update the render target index. + m_renderTargetIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // On the current frame wait until it is completed + { + FrameInfo& frame = m_frameInfos[m_frameIndex]; + // If the next frame is not ready to be rendered yet, wait until it is ready. + m_fence.waitUntilCompleted(frame.m_fenceValue); + } + + // Setup such that rendering can restart + beginRender(); +} + +TextureResource::Desc D3D12Renderer::getSwapChainTextureDesc() +{ + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1); + + return desc; +} + +SlangResult D3D12Renderer::captureScreenSurface(Surface& surfaceOut) +{ + return captureTextureToSurface(*m_renderTargets[m_renderTargetIndex], surfaceOut); +} + +static D3D12_RESOURCE_STATES _calcResourceState(Resource::Usage usage) +{ + typedef Resource::Usage Usage; + switch (usage) + { + case Usage::VertexBuffer: return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; + case Usage::IndexBuffer: return D3D12_RESOURCE_STATE_INDEX_BUFFER; + case Usage::ConstantBuffer: return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; + case Usage::StreamOutput: return D3D12_RESOURCE_STATE_STREAM_OUT; + case Usage::RenderTarget: return D3D12_RESOURCE_STATE_RENDER_TARGET; + case Usage::DepthWrite: return D3D12_RESOURCE_STATE_DEPTH_WRITE; + case Usage::DepthRead: return D3D12_RESOURCE_STATE_DEPTH_READ; + case Usage::UnorderedAccess: return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + case Usage::PixelShaderResource: return D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + case Usage::NonPixelShaderResource: return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + case Usage::GenericRead: return D3D12_RESOURCE_STATE_GENERIC_READ; + default: return D3D12_RESOURCE_STATES(0); + } +} + +static D3D12_RESOURCE_FLAGS _calcResourceFlag(Resource::BindFlag::Enum bindFlag) +{ + typedef Resource::BindFlag BindFlag; + switch (bindFlag) + { + case BindFlag::RenderTarget: return D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + case BindFlag::DepthStencil: return D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + case BindFlag::UnorderedAccess: return D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + default: return D3D12_RESOURCE_FLAG_NONE; + } +} + +static D3D12_RESOURCE_FLAGS _calcResourceBindFlags(Resource::Usage initialUsage, int bindFlags) +{ + int dstFlags = 0; + while (bindFlags) + { + int lsb = bindFlags & -bindFlags; + + dstFlags |= _calcResourceFlag(Resource::BindFlag::Enum(lsb)); + bindFlags &= ~lsb; + } + return D3D12_RESOURCE_FLAGS(dstFlags); +} + +static D3D12_RESOURCE_DIMENSION _calcResourceDimension(Resource::Type type) +{ + switch (type) + { + case Resource::Type::Buffer: return D3D12_RESOURCE_DIMENSION_BUFFER; + case Resource::Type::Texture1D: return D3D12_RESOURCE_DIMENSION_TEXTURE1D; + case Resource::Type::TextureCube: + case Resource::Type::Texture2D: + { + return D3D12_RESOURCE_DIMENSION_TEXTURE2D; + } + case Resource::Type::Texture3D: return D3D12_RESOURCE_DIMENSION_TEXTURE3D; + default: return D3D12_RESOURCE_DIMENSION_UNKNOWN; + } +} + +Result D3D12Renderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) +{ + // Description of uploading on Dx12 + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn899215%28v=vs.85%29.aspx + + TextureResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + const DXGI_FORMAT pixelFormat = D3DUtil::getMapFormat(srcDesc.format); + if (pixelFormat == DXGI_FORMAT_UNKNOWN) + { + return SLANG_FAIL; + } + + const int arraySize = srcDesc.calcEffectiveArraySize(); + + const D3D12_RESOURCE_DIMENSION dimension = _calcResourceDimension(srcDesc.type); + if (dimension == D3D12_RESOURCE_DIMENSION_UNKNOWN) + { + return SLANG_FAIL; + } + + const int numMipMaps = srcDesc.numMipLevels; + + // Setup desc + D3D12_RESOURCE_DESC resourceDesc; + + resourceDesc.Dimension = dimension; + resourceDesc.Format = pixelFormat; + resourceDesc.Width = srcDesc.size.width; + resourceDesc.Height = srcDesc.size.height; + resourceDesc.DepthOrArraySize = (srcDesc.size.depth > 1) ? srcDesc.size.depth : arraySize; + + resourceDesc.MipLevels = numMipMaps; + resourceDesc.SampleDesc.Count = srcDesc.sampleDesc.numSamples; + resourceDesc.SampleDesc.Quality = srcDesc.sampleDesc.quality; + + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resourceDesc.Alignment = 0; + + RefPtr texture(new TextureResourceImpl(srcDesc)); + + // Create the target resource + { + D3D12_HEAP_PROPERTIES heapProps; + + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + SLANG_RETURN_ON_FAIL(texture->m_resource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); + + texture->m_resource.setDebugName(L"Texture"); + } + + // Calculate the layout + List layouts; + layouts.SetSize(numMipMaps); + List mipRowSizeInBytes; + mipRowSizeInBytes.SetSize(numMipMaps); + List mipNumRows; + mipNumRows.SetSize(numMipMaps); + + // Since textures are effectively immutable currently initData must be set + assert(initData); + // We should have this many sub resources + assert(initData->numSubResources == numMipMaps * srcDesc.size.depth * arraySize); + + // This is just the size for one array upload -> not for the whole texure + UInt64 requiredSize = 0; + m_device->GetCopyableFootprints(&resourceDesc, 0, numMipMaps, 0, layouts.begin(), mipNumRows.begin(), mipRowSizeInBytes.begin(), &requiredSize); + + // Sub resource indexing + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn705766(v=vs.85).aspx#subresource_indexing + + int subResourceIndex = 0; + for (int i = 0; i < arraySize; i++) + { + // 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"); + } + + ID3D12Resource* uploadResource = uploadTexture; + + uint8_t* p; + uploadResource->Map(0, nullptr, reinterpret_cast(&p)); + + for (int j = 0; j < numMipMaps; ++j) + { + const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& layout = layouts[j]; + const D3D12_SUBRESOURCE_FOOTPRINT& footprint = layout.Footprint; + + const TextureResource::Size mipSize = srcDesc.size.calcMipSize(j); + + assert(footprint.Width == mipSize.width && footprint.Height == mipSize.height && footprint.Depth == mipSize.depth); + + const ptrdiff_t dstMipRowPitch = ptrdiff_t(layouts[j].Footprint.RowPitch); + const ptrdiff_t srcMipRowPitch = ptrdiff_t(initData->mipRowStrides[j]); + + assert(dstMipRowPitch >= srcMipRowPitch); + + const uint8_t* srcRow = (const uint8_t*)initData->subResources[subResourceIndex]; + uint8_t* dstRow = p + layouts[j].Offset; + + // Copy the depth each mip + for (int l = 0; l < mipSize.depth; l++) + { + // Copy rows + for (int k = 0; k < mipSize.height; ++k) + { + ::memcpy(dstRow, srcRow, srcMipRowPitch); + + srcRow += srcMipRowPitch; + dstRow += dstMipRowPitch; + } + } + + //assert(srcRow == (const uint8_t*)(srcMip.Buffer() + srcMip.Count())); + } + uploadResource->Unmap(0, nullptr); + + for (int mipIndex = 0; mipIndex < numMipMaps; ++mipIndex) + { + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn903862(v=vs.85).aspx + + D3D12_TEXTURE_COPY_LOCATION src; + src.pResource = uploadTexture; + src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + src.PlacedFootprint = layouts[mipIndex]; + + D3D12_TEXTURE_COPY_LOCATION dst; + dst.pResource = texture->m_resource; + dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dst.SubresourceIndex = subResourceIndex; + m_commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); + + subResourceIndex++; + } + + // Block - waiting for copy to complete (so can drop upload texture) + submitGpuWorkAndWait(); + } + + { + const D3D12_RESOURCE_STATES finalState = _calcResourceState(initialUsage); + D3D12BarrierSubmitter submitter(m_commandList); + texture->m_resource.transition(finalState, submitter); + + submitGpuWorkAndWait(); + } + + *outResource = texture.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) +{ + typedef BufferResourceImpl::BackingStyle Style; + + BufferResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + // Always align up to 256 bytes, since that is required for constant buffers. + // + // TODO: only do this for buffers that could potentially be bound as constant buffers... + // + const size_t alignedSizeInBytes = D3DUtil::calcAligned(srcDesc.sizeInBytes, 256); + + RefPtr buffer(new BufferResourceImpl(initialUsage, srcDesc)); + + // Save the style + buffer->m_backingStyle = BufferResourceImpl::_calcResourceBackingStyle(initialUsage); + + D3D12_RESOURCE_DESC bufferDesc; + _initBufferResourceDesc(alignedSizeInBytes, bufferDesc); + + bufferDesc.Flags = _calcResourceBindFlags(initialUsage, srcDesc.bindFlags); + + switch (buffer->m_backingStyle) + { + case Style::MemoryBacked: + { + // Assume the constant buffer will change every frame. We'll just keep a copy of the contents + // in regular memory until it needed + buffer->m_memory.SetSize(UInt(alignedSizeInBytes)); + // Initialize + if (initData) + { + ::memcpy(buffer->m_memory.Buffer(), initData, srcDesc.sizeInBytes); + } + break; + } + case Style::ResourceBacked: + { + const D3D12_RESOURCE_STATES initialState = _calcResourceState(initialUsage); + SLANG_RETURN_ON_FAIL(createBuffer(bufferDesc, initData, buffer->m_uploadResource, initialState, buffer->m_resource)); + break; + } + default: + return SLANG_FAIL; + } + + *outResource = buffer.detach(); + return SLANG_OK; +} + +D3D12_FILTER_TYPE translateFilterMode(TextureFilteringMode mode) +{ + switch (mode) + { + default: + return D3D12_FILTER_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureFilteringMode::SRC: return D3D12_FILTER_TYPE_##DST + + CASE(Point, POINT); + CASE(Linear, LINEAR); + +#undef CASE + } +} + +D3D12_FILTER_REDUCTION_TYPE translateFilterReduction(TextureReductionOp op) +{ + switch (op) + { + default: + return D3D12_FILTER_REDUCTION_TYPE(0); + +#define CASE(SRC, DST) \ + case TextureReductionOp::SRC: return D3D12_FILTER_REDUCTION_TYPE_##DST + + CASE(Average, STANDARD); + CASE(Comparison, COMPARISON); + CASE(Minimum, MINIMUM); + CASE(Maximum, MAXIMUM); + +#undef CASE + } +} + +D3D12_TEXTURE_ADDRESS_MODE translateAddressingMode(TextureAddressingMode mode) +{ + switch (mode) + { + default: + return D3D12_TEXTURE_ADDRESS_MODE(0); + +#define CASE(SRC, DST) \ + case TextureAddressingMode::SRC: return D3D12_TEXTURE_ADDRESS_MODE_##DST + + CASE(Wrap, WRAP); + CASE(ClampToEdge, CLAMP); + CASE(ClampToBorder, BORDER); + CASE(MirrorRepeat, MIRROR); + CASE(MirrorOnce, MIRROR_ONCE); + +#undef CASE + } +} + +static D3D12_COMPARISON_FUNC translateComparisonFunc(ComparisonFunc func) +{ + switch (func) + { + default: + // TODO: need to report failures + return D3D12_COMPARISON_FUNC_ALWAYS; + +#define CASE(FROM, TO) \ + case ComparisonFunc::FROM: return D3D12_COMPARISON_FUNC_##TO + + CASE(Never, NEVER); + CASE(Less, LESS); + CASE(Equal, EQUAL); + CASE(LessEqual, LESS_EQUAL); + CASE(Greater, GREATER); + CASE(NotEqual, NOT_EQUAL); + CASE(GreaterEqual, GREATER_EQUAL); + CASE(Always, ALWAYS); +#undef CASE + } +} + +Result D3D12Renderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) +{ + D3D12_FILTER_REDUCTION_TYPE dxReduction = translateFilterReduction(desc.reductionOp); + D3D12_FILTER dxFilter; + if (desc.maxAnisotropy > 1) + { + dxFilter = D3D12_ENCODE_ANISOTROPIC_FILTER(dxReduction); + } + else + { + D3D12_FILTER_TYPE dxMin = translateFilterMode(desc.minFilter); + D3D12_FILTER_TYPE dxMag = translateFilterMode(desc.magFilter); + D3D12_FILTER_TYPE dxMip = translateFilterMode(desc.mipFilter); + + dxFilter = D3D12_ENCODE_BASIC_FILTER(dxMin, dxMag, dxMip, dxReduction); + } + + D3D12_SAMPLER_DESC dxDesc = {}; + dxDesc.Filter = dxFilter; + dxDesc.AddressU = translateAddressingMode(desc.addressU); + dxDesc.AddressV = translateAddressingMode(desc.addressV); + dxDesc.AddressW = translateAddressingMode(desc.addressW); + dxDesc.MipLODBias = desc.mipLODBias; + dxDesc.MaxAnisotropy = desc.maxAnisotropy; + dxDesc.ComparisonFunc = translateComparisonFunc(desc.comparisonFunc); + for (int ii = 0; ii < 4; ++ii) + dxDesc.BorderColor[ii] = desc.borderColor[ii]; + dxDesc.MinLOD = desc.minLOD; + dxDesc.MaxLOD = desc.maxLOD; + + auto samplerHeap = &m_cpuSamplerHeap; + + int indexInSamplerHeap = samplerHeap->allocate(); + if(indexInSamplerHeap < 0) + { + // We ran out of room in our CPU sampler heap. + // + // TODO: this should not be a catastrophic failure, because + // we should just allocate another CPU sampler heap that + // can service subsequent allocation. + // + return SLANG_FAIL; + } + auto cpuDescriptorHandle = samplerHeap->getCpuHandle(indexInSamplerHeap); + + m_device->CreateSampler(&dxDesc, cpuDescriptorHandle); + + // TODO: We really ought to have a free-list of sampler-heap + // entries that we check before we go to the heap, and then + // when we are done with a sampler we simply add it to the free list. + // + RefPtr samplerImpl = new SamplerStateImpl(); + samplerImpl->m_cpuHandle = cpuDescriptorHandle; + *outSampler = samplerImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (TextureResourceImpl*) texture; + + RefPtr viewImpl = new ResourceViewImpl(); + viewImpl->m_resource = resourceImpl; + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::RenderTarget: + { + SLANG_RETURN_ON_FAIL(m_rtvAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateRenderTargetView(resourceImpl->m_resource, nullptr, viewImpl->m_descriptor.cpuHandle); + } + break; + + case ResourceView::Type::DepthStencil: + { + SLANG_RETURN_ON_FAIL(m_dsvAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateDepthStencilView(resourceImpl->m_resource, nullptr, viewImpl->m_descriptor.cpuHandle); + } + break; + + case ResourceView::Type::UnorderedAccess: + { + // TODO: need to support the separate "counter resource" for the case + // of append/consume buffers with attached counters. + + SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, nullptr, viewImpl->m_descriptor.cpuHandle); + } + break; + + case ResourceView::Type::ShaderResource: + { + SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateShaderResourceView(resourceImpl->m_resource, nullptr, viewImpl->m_descriptor.cpuHandle); + } + break; + } + + *outView = viewImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + auto resourceDesc = resourceImpl->getDesc(); + + RefPtr viewImpl = new ResourceViewImpl(); + viewImpl->m_resource = resourceImpl; + + switch (desc.type) + { + default: + return SLANG_FAIL; + + case ResourceView::Type::UnorderedAccess: + { + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uavDesc.Format = D3DUtil::getMapFormat(desc.format); + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = resourceDesc.sizeInBytes; + + if(resourceDesc.elementSize) + { + uavDesc.Buffer.StructureByteStride = resourceDesc.elementSize; + uavDesc.Buffer.NumElements = resourceDesc.sizeInBytes / resourceDesc.elementSize; + } + else if(desc.format == Format::Unknown) + { + uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + } + + + // TODO: need to support the separate "counter resource" for the case + // of append/consume buffers with attached counters. + + SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateUnorderedAccessView(resourceImpl->m_resource, nullptr, &uavDesc, viewImpl->m_descriptor.cpuHandle); + } + break; + + case ResourceView::Type::ShaderResource: + { + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Format = D3DUtil::getMapFormat(desc.format); + srvDesc.Buffer.StructureByteStride = 0; + srvDesc.Buffer.FirstElement = 0; + srvDesc.Buffer.NumElements = resourceDesc.sizeInBytes; + + if(resourceDesc.elementSize) + { + srvDesc.Buffer.StructureByteStride = resourceDesc.elementSize; + srvDesc.Buffer.NumElements = resourceDesc.sizeInBytes / resourceDesc.elementSize; + } + + SLANG_RETURN_ON_FAIL(m_viewAllocator.allocate(&viewImpl->m_descriptor)); + m_device->CreateShaderResourceView(resourceImpl->m_resource, &srvDesc, viewImpl->m_descriptor.cpuHandle); + } + break; + } + + *outView = viewImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) +{ + RefPtr layout(new InputLayoutImpl); + + // Work out a buffer size to hold all text + size_t textSize = 0; + for (int i = 0; i < Int(inputElementCount); ++i) + { + const char* text = inputElements[i].semanticName; + textSize += text ? (::strlen(text) + 1) : 0; + } + layout->m_text.SetSize(textSize); + char* textPos = layout->m_text.Buffer(); + + // + List& 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; + } + + *outLayout = layout.detach(); + return SLANG_OK; +} + +void* D3D12Renderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + typedef BufferResourceImpl::BackingStyle Style; + + BufferResourceImpl* buffer = static_cast(bufferIn); + buffer->m_mapFlavor = flavor; + + const size_t bufferSize = buffer->getDesc().sizeInBytes; + + switch (buffer->m_backingStyle) + { + case Style::ResourceBacked: + { + // We need this in a state so we can upload + switch (flavor) + { + case MapFlavor::HostWrite: + case MapFlavor::WriteDiscard: + { + D3D12BarrierSubmitter submitter(m_commandList); + buffer->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter); + buffer->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter); + + const D3D12_RANGE readRange = {}; + + void* uploadData; + SLANG_RETURN_NULL_ON_FAIL(buffer->m_uploadResource.getResource()->Map(0, &readRange, reinterpret_cast(&uploadData))); + return uploadData; + + break; + } + case MapFlavor::HostRead: + { + // This will be slow!!! - it blocks CPU on GPU completion + D3D12Resource& resource = buffer->m_resource; + + // Readback heap + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_READBACK; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + // Resource to readback to + D3D12_RESOURCE_DESC stagingDesc; + _initBufferResourceDesc(bufferSize, stagingDesc); + + D3D12Resource stageBuf; + SLANG_RETURN_NULL_ON_FAIL(stageBuf.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); + + const D3D12_RESOURCE_STATES initialState = resource.getState(); + + // Make it a source + { + D3D12BarrierSubmitter submitter(m_commandList); + resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter); + } + // Do the copy + m_commandList->CopyBufferRegion(stageBuf, 0, resource, 0, bufferSize); + // Switch it back + { + D3D12BarrierSubmitter submitter(m_commandList); + resource.transition(initialState, submitter); + } + + // Wait until complete + submitGpuWorkAndWait(); + + // Map and copy + { + UINT8* data; + D3D12_RANGE readRange = { 0, bufferSize }; + + SLANG_RETURN_NULL_ON_FAIL(stageBuf.getResource()->Map(0, &readRange, reinterpret_cast(&data))); + + // Copy to memory buffer + buffer->m_memory.SetSize(bufferSize); + ::memcpy(buffer->m_memory.Buffer(), data, bufferSize); + + stageBuf.getResource()->Unmap(0, nullptr); + } + + return buffer->m_memory.Buffer(); + } + } + break; + } + case Style::MemoryBacked: + { + return buffer->m_memory.Buffer(); + } + default: return nullptr; + } + + return nullptr; +} + +void D3D12Renderer::unmap(BufferResource* bufferIn) +{ + typedef BufferResourceImpl::BackingStyle Style; + BufferResourceImpl* buffer = static_cast(bufferIn); + + switch (buffer->m_backingStyle) + { + case Style::MemoryBacked: + { + // Don't need to do anything, as will be uploaded automatically when used + break; + } + case Style::ResourceBacked: + { + // We need this in a state so we can upload + switch (buffer->m_mapFlavor) + { + case MapFlavor::HostWrite: + case MapFlavor::WriteDiscard: + { + // Unmap + ID3D12Resource* uploadResource = buffer->m_uploadResource; + ID3D12Resource* resource = buffer->m_resource; + + uploadResource->Unmap(0, nullptr); + + const D3D12_RESOURCE_STATES initialState = buffer->m_resource.getState(); + + { + D3D12BarrierSubmitter submitter(m_commandList); + buffer->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter); + buffer->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter); + } + + m_commandList->CopyBufferRegion(resource, 0, uploadResource, 0, buffer->getDesc().sizeInBytes); + + { + D3D12BarrierSubmitter submitter(m_commandList); + buffer->m_resource.transition(initialState, submitter); + } + break; + } + case MapFlavor::HostRead: + { + break; + } + } + } + } +} + +#if 0 +void D3D12Renderer::setInputLayout(InputLayout* inputLayout) +{ + m_boundInputLayout = static_cast(inputLayout); +} +#endif + +void D3D12Renderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + switch (topology) + { + case PrimitiveTopology::TriangleList: + { + m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + m_primitiveTopology = D3DUtil::getPrimitiveTopology(topology); + break; + } + default: + { + assert(!"Unhandled type"); + } + } +} + +void D3D12Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) +{ + { + const UInt num = startSlot + slotCount; + if (num > m_boundVertexBuffers.Count()) + { + m_boundVertexBuffers.SetSize(num); + } + } + + for (UInt i = 0; i < slotCount; i++) + { + BufferResourceImpl* buffer = static_cast(buffers[i]); + if (buffer) + { + assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer); + } + + BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i]; + boundBuffer.m_buffer = buffer; + boundBuffer.m_stride = int(strides[i]); + boundBuffer.m_offset = int(offsets[i]); + } +} + +void D3D12Renderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) +{ + m_boundIndexBuffer = (BufferResourceImpl*) buffer; + m_boundIndexFormat = D3DUtil::getMapFormat(indexFormat); + m_boundIndexOffset = offset; +} + +void D3D12Renderer::setDepthStencilTarget(ResourceView* depthStencilView) +{ +} + +void D3D12Renderer::setPipelineState(PipelineType pipelineType, PipelineState* state) +{ + m_currentPipelineState = (PipelineStateImpl*)state; +} + +void D3D12Renderer::draw(UInt vertexCount, UInt startVertex) +{ + ID3D12GraphicsCommandList* commandList = m_commandList; + + auto pipelineState = m_currentPipelineState.Ptr(); + if (!pipelineState || (pipelineState->m_pipelineType != PipelineType::Graphics)) + { + assert(!"No graphics pipeline state set"); + return; + } + + // Submit - setting for graphics + { + GraphicsSubmitter submitter(commandList); + _bindRenderState(pipelineState, commandList, &submitter); + } + + commandList->IASetPrimitiveTopology(m_primitiveTopology); + + // Set up vertex buffer views + { + int numVertexViews = 0; + D3D12_VERTEX_BUFFER_VIEW vertexViews[16]; + for (int i = 0; i < int(m_boundVertexBuffers.Count()); i++) + { + const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[i]; + BufferResourceImpl* buffer = boundVertexBuffer.m_buffer; + if (buffer) + { + D3D12_VERTEX_BUFFER_VIEW& vertexView = vertexViews[numVertexViews++]; + vertexView.BufferLocation = buffer->m_resource.getResource()->GetGPUVirtualAddress() + + boundVertexBuffer.m_offset; + vertexView.SizeInBytes = buffer->getDesc().sizeInBytes - boundVertexBuffer.m_offset; + vertexView.StrideInBytes = boundVertexBuffer.m_stride; + } + } + commandList->IASetVertexBuffers(0, numVertexViews, vertexViews); + } + + // Set up index buffer + if(m_boundIndexBuffer) + { + D3D12_INDEX_BUFFER_VIEW indexBufferView; + indexBufferView.BufferLocation = m_boundIndexBuffer->m_resource.getResource()->GetGPUVirtualAddress() + + m_boundIndexOffset; + indexBufferView.SizeInBytes = m_boundIndexBuffer->getDesc().sizeInBytes - m_boundIndexOffset; + indexBufferView.Format = m_boundIndexFormat; + + commandList->IASetIndexBuffer(&indexBufferView); + } + + commandList->DrawInstanced(UINT(vertexCount), 1, UINT(startVertex), 0); +} + +void D3D12Renderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) +{ +} + +void D3D12Renderer::dispatchCompute(int x, int y, int z) +{ + ID3D12GraphicsCommandList* commandList = m_commandList; + auto pipelineStateImpl = m_currentPipelineState; + + // Submit binding for compute + { + ComputeSubmitter submitter(commandList); + _bindRenderState(pipelineStateImpl, commandList, &submitter); + } + + commandList->Dispatch(x, y, z); +} + +#if 0 +BindingState* D3D12Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); + + SLANG_RETURN_NULL_ON_FAIL(bindingState->init(m_device)); + + const auto& srcBindings = bindingStateDesc.m_bindings; + const int numBindings = int(srcBindings.Count()); + + auto& dstDetails = bindingState->m_bindingDetails; + dstDetails.SetSize(numBindings); + + for (int i = 0; i < numBindings; ++i) + { + const auto& srcEntry = srcBindings[i]; + auto& dstDetail = dstDetails[i]; + + const int bindingIndex = srcEntry.registerRange.getSingleIndex(); + + switch (srcEntry.bindingType) + { + case BindingType::Buffer: + { + assert(srcEntry.resource && srcEntry.resource->isBuffer()); + BufferResourceImpl* bufferResource = static_cast(srcEntry.resource.Ptr()); + const BufferResource::Desc& desc = bufferResource->getDesc(); + + const size_t bufferSize = bufferDesc.sizeInBytes; + const int elemSize = bufferDesc.elementSize <= 0 ? sizeof(uint32_t) : bufferDesc.elementSize; + + const bool createSrv = false; + + // NOTE! In this arrangement the buffer can either be a ConstantBuffer or a 'StorageBuffer'. + // If it's a storage buffer then it has a 'uav'. + // In neither circumstance is there an associated srv + // This departs a little from dx11 code - in that it will create srv and uav for a storage buffer. + if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) + { + dstDetail.m_uavIndex = bindingState->m_viewHeap.allocate(); + if (dstDetail.m_uavIndex < 0) + { + return nullptr; + } + + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uavDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); + + uavDesc.Buffer.StructureByteStride = elemSize; + + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); + uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE; + + if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) + { + uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + + uavDesc.Buffer.StructureByteStride = 0; + } + else if( bufferDesc.format != Format::Unknown ) + { + uavDesc.Buffer.StructureByteStride = 0; + } + + m_device->CreateUnorderedAccessView(bufferResource->m_resource, nullptr, &uavDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_uavIndex)); + } + if (createSrv && (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource))) + { + dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); + if (dstDetail.m_srvIndex < 0) + { + return nullptr; + } + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; + + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + + srvDesc.Buffer.FirstElement = 0; + srvDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); + srvDesc.Buffer.StructureByteStride = elemSize; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + + if (bufferDesc.elementSize == 0) + { + srvDesc.Format = DXGI_FORMAT_R32_FLOAT; + } + + m_device->CreateShaderResourceView(bufferResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); + } + + break; + } + case BindingType::Texture: + { + assert(srcEntry.resource && srcEntry.resource->isTexture()); + + TextureResourceImpl* textureResource = static_cast(srcEntry.resource.Ptr()); + + dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); + if (dstDetail.m_srvIndex < 0) + { + return nullptr; + } + + { + const D3D12_RESOURCE_DESC resourceDesc = textureResource->m_resource.getResource()->GetDesc(); + const DXGI_FORMAT pixelFormat = resourceDesc.Format; + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; + _initSrvDesc(textureResource->getType(), textureResource->getDesc(), resourceDesc, pixelFormat, srvDesc); + + // Create descriptor + m_device->CreateShaderResourceView(textureResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); + } + + break; + } + case BindingType::Sampler: + { + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcEntry.descIndex]; + + const int samplerIndex = bindingIndex; + dstDetail.m_samplerIndex = samplerIndex; + bindingState->m_samplerHeap.placeAt(samplerIndex); + + D3D12_SAMPLER_DESC desc = {}; + desc.AddressU = desc.AddressV = desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + desc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; + + if (samplerDesc.isCompareSampler) + { + desc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + desc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; + } + else + { + desc.Filter = D3D12_FILTER_ANISOTROPIC; + desc.MaxAnisotropy = 8; + desc.MinLOD = 0.0f; + desc.MaxLOD = 100.0f; + } + + m_device->CreateSampler(&desc, bindingState->m_samplerHeap.getCpuHandle(samplerIndex)); + + break; + } + case BindingType::CombinedTextureSampler: + { + assert(!"Not implemented"); + return nullptr; + } + } + } + + return bindingState.detach(); +} + +void D3D12Renderer::setBindingState(BindingState* state) +{ + m_boundBindingState = static_cast(state); +} +#endif + +void D3D12Renderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) +{ + auto dxDevice = m_renderer->m_device; + + + auto resourceImpl = (BufferResourceImpl*) buffer; + auto resourceDesc = resourceImpl->getDesc(); + + // Constant buffer view size must be a multiple of 256 bytes, so we round it up here. + const size_t alignedSizeInBytes = D3DUtil::calcAligned(resourceDesc.sizeInBytes, 256); + + D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {}; + cbvDesc.BufferLocation = resourceImpl->m_resource.getResource()->GetGPUVirtualAddress(); + cbvDesc.SizeInBytes = alignedSizeInBytes; + + auto& rangeInfo = m_layout->m_ranges[range]; + +#ifdef _DEBUG + switch(rangeInfo.type) + { + default: + assert(!"incorrect slot type"); + break; + + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::DynamicUniformBuffer: + break; + } +#endif + + auto arrayIndex = rangeInfo.arrayIndex + index; + auto descriptorIndex = m_resourceTable + arrayIndex; + + m_resourceObjects[arrayIndex] = resourceImpl; + dxDevice->CreateConstantBufferView( + &cbvDesc, + m_resourceHeap->getCpuHandle(descriptorIndex)); +} + +void D3D12Renderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) +{ + auto dxDevice = m_renderer->m_device; + + auto viewImpl = (ResourceViewImpl*) view; + + auto& rangeInfo = m_layout->m_ranges[range]; + + // TODO: validation that slot type matches view + + auto arrayIndex = rangeInfo.arrayIndex + index; + auto descriptorIndex = m_resourceTable + arrayIndex; + + m_resourceObjects[arrayIndex] = viewImpl; + dxDevice->CopyDescriptorsSimple( + 1, + m_resourceHeap->getCpuHandle(descriptorIndex), + viewImpl->m_descriptor.cpuHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); +} + +void D3D12Renderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) +{ + auto dxDevice = m_renderer->m_device; + + auto samplerImpl = (SamplerStateImpl*) sampler; + + auto& rangeInfo = m_layout->m_ranges[range]; + +#ifdef _DEBUG + switch(rangeInfo.type) + { + default: + assert(!"incorrect slot type"); + break; + + case DescriptorSlotType::Sampler: + break; + } +#endif + + auto arrayIndex = rangeInfo.arrayIndex + index; + auto descriptorIndex = m_resourceTable + arrayIndex; + + m_samplerObjects[arrayIndex] = samplerImpl; + dxDevice->CopyDescriptorsSimple( + 1, + m_samplerHeap->getCpuHandle(descriptorIndex), + samplerImpl->m_cpuHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); +} + +void D3D12Renderer::DescriptorSetImpl::setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) +{ + auto dxDevice = m_renderer->m_device; + + auto viewImpl = (ResourceViewImpl*) textureView; + auto samplerImpl = (SamplerStateImpl*) sampler; + + auto& rangeInfo = m_layout->m_ranges[range]; + +#ifdef _DEBUG + switch(rangeInfo.type) + { + default: + assert(!"incorrect slot type"); + break; + + case DescriptorSlotType::CombinedImageSampler: + break; + } +#endif + + auto arrayIndex = rangeInfo.arrayIndex + index; + auto resourceDescriptorIndex = m_resourceTable + arrayIndex; + auto samplerDescriptorIndex = m_samplerTable + arrayIndex; + + m_resourceObjects[arrayIndex] = viewImpl; + dxDevice->CopyDescriptorsSimple( + 1, + m_resourceHeap->getCpuHandle(resourceDescriptorIndex), + viewImpl->m_descriptor.cpuHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + m_samplerObjects[arrayIndex] = samplerImpl; + dxDevice->CopyDescriptorsSimple( + 1, + m_samplerHeap->getCpuHandle(samplerDescriptorIndex), + samplerImpl->m_cpuHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); +} + +void D3D12Renderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) +{ + // In D3D12, unlike Vulkan, binding a root signature invalidates *all* descriptor table + // bindings (rather than preserving those that are part of the longest common prefix + // between the old and new layout). + // + // In order to accomodate having descriptor-set bindings that persist across changes + // in pipeline state (which may also change pipeline layout), we will shadow the + // descriptor-set bindings and only flush them on-demand at draw tiume once the final + // pipline layout is known. + // + + auto descriptorSetImpl = (DescriptorSetImpl*) descriptorSet; + m_boundDescriptorSets[int(pipelineType)][index] = descriptorSetImpl; +} + +Result D3D12Renderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) +{ + RefPtr program(new ShaderProgramImpl()); + program->m_pipelineType = desc.pipelineType; + + if (desc.pipelineType == PipelineType::Compute) + { + auto computeKernel = desc.findKernel(StageType::Compute); + program->m_computeShader.InsertRange(0, (const uint8_t*) computeKernel->codeBegin, computeKernel->getCodeSize()); + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + program->m_vertexShader.InsertRange(0, (const uint8_t*) vertexKernel->codeBegin, vertexKernel->getCodeSize()); + program->m_pixelShader.InsertRange(0, (const uint8_t*) fragmentKernel->codeBegin, fragmentKernel->getCodeSize()); + } + + *outProgram = program.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) +{ + Int rangeCount = desc.slotRangeCount; + + // For our purposes, there are three main cases of descriptor ranges to consider: + // + // 1. Resources: CBV, SRV, UAV + // + // 2. Samplers + // + // 3. Combined texture/sampler pairs + // + // The combined case presents challenges, because we will implement + // them as both a resource slot and a sampler slot, and for conveience + // in the indexing logic, it would be nice it they "lined up." + // + // We will start by counting how many ranges, and how many + // descriptors, of each type we have. + // + + Int dedicatedResourceCount = 0; + Int dedicatedSamplerCount = 0; + Int combinedCount = 0; + + Int dedicatedResourceRangeCount = 0; + Int dedicatedSamplerRangeCount = 0; + Int combinedRangeCount = 0; + + for(Int rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + switch(rangeDesc.type) + { + case DescriptorSlotType::Sampler: + dedicatedSamplerCount += rangeDesc.count; + dedicatedSamplerRangeCount++; + break; + + case DescriptorSlotType::CombinedImageSampler: + combinedCount += rangeDesc.count; + combinedRangeCount++; + break; + + default: + dedicatedResourceCount += rangeDesc.count; + dedicatedResourceRangeCount++; + break; + } + } + + // Now we know how many ranges we have to allocate space for, + // and also how they need to be arranged. + // + // Each "combined" range will map to two ranges in the D3D + // descriptor tables. + + RefPtr descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(); + + // We know the total number of resource and sampler "slots" that an instance + // of this decriptor-set layout would need: + // + descriptorSetLayoutImpl->m_resourceCount = combinedCount + dedicatedResourceCount; + descriptorSetLayoutImpl->m_samplerCount = combinedCount + dedicatedSamplerCount; + + // We can start by allocating the D3D root parameter info needed for the + // descriptor set, based on the total number or ranges we need, which + // we can compute from the combined and dedicated counts: + // + Int totalResourceRangeCount = combinedRangeCount + dedicatedResourceRangeCount; + Int totalSamplerRangeCount = combinedRangeCount + dedicatedSamplerRangeCount; + + if( totalResourceRangeCount ) + { + D3D12_ROOT_PARAMETER dxRootParameter = {}; + dxRootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + dxRootParameter.DescriptorTable.NumDescriptorRanges = totalResourceRangeCount; + descriptorSetLayoutImpl->m_dxRootParameters.Add(dxRootParameter); + } + if( totalSamplerRangeCount ) + { + D3D12_ROOT_PARAMETER dxRootParameter = {}; + dxRootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + dxRootParameter.DescriptorTable.NumDescriptorRanges = totalSamplerRangeCount; + descriptorSetLayoutImpl->m_dxRootParameters.Add(dxRootParameter); + } + + // Next we can allocate space for all the D3D register ranges we need, + // again based on totals that we can compute easily: + // + Int totalRangeCount = totalResourceRangeCount + totalSamplerRangeCount; + descriptorSetLayoutImpl->m_dxRanges.SetSize(totalRangeCount); + + // Now we will walk through the ranges in the order they were + // specified, so that we can fill in the "range info" required for + // binding parameters into descriptor sets allocated with this layout. + // + // This effectively determines the space required in two arrays + // in each descriptor set: one for resources, and one for samplers. + // A "combined" descriptor requires space in both arrays. The entries + // for "dedicated" samplers/resources always come after those for + // "combined" descriptors in the same array, so that a single index + // can be used for both arrays in the combined case. + // + + { + Int samplerCounter = 0; + Int resourceCounter = 0; + Int combinedCounter = 0; + for(Int rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + + DescriptorSetLayoutImpl::RangeInfo rangeInfo; + + rangeInfo.type = rangeDesc.type; + rangeInfo.count = rangeDesc.count; + + switch(rangeDesc.type) + { + default: + // Default case is a dedicated resource, and its index in the + // resource array will come after all the combined entries. + rangeInfo.arrayIndex = combinedCount + resourceCounter; + resourceCounter += rangeInfo.count; + break; + + case DescriptorSlotType::Sampler: + // A dedicated sampler comes after all the entries for + // combined texture/samplers in the sampler array. + rangeInfo.arrayIndex = combinedCount + samplerCounter; + samplerCounter += rangeInfo.count; + break; + + case DescriptorSlotType::CombinedImageSampler: + // Combined descriptors take entries at the front of + // the resource and sampler arrays. + rangeInfo.arrayIndex = combinedCounter; + combinedCounter += rangeInfo.count; + break; + } + + descriptorSetLayoutImpl->m_ranges.Add(rangeInfo); + } + } + + // Finally, we will go through and fill in ready-to-go D3D + // register range information. + { + UInt cbvCounter = 0; + UInt srvCounter = 0; + UInt uavCounter = 0; + UInt samplerCounter = 0; + + Int resourceRangeCounter = 0; + Int samplerRangeCounter = 0; + Int combinedRangeCounter = 0; + + for(Int rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + Int bindingCount = rangeDesc.count; + + // All of these descriptor ranges will be initialized + // with a "space" of zero, with the assumption that + // the actual space number will come from when they are + // used as part of a pipeline layout. + // + Int bindingSpace = 0; + + Int dxRangeIndex = -1; + Int dxPairedSamplerRangeIndex = -1; + + switch(rangeDesc.type) + { + default: + // Default case is a dedicated resource, and its index in the + // resource array will come after all the combined entries. + dxRangeIndex = combinedRangeCount + resourceRangeCounter; + resourceRangeCounter++; + break; + + case DescriptorSlotType::Sampler: + // A dedicated sampler comes after all the entries for + // combined texture/samplers in the sampler array. + dxRangeIndex = totalResourceRangeCount + combinedRangeCount + samplerRangeCounter; + samplerRangeCounter++; + break; + + case DescriptorSlotType::CombinedImageSampler: + // Combined descriptors take entries at the front of + // the resource and sampler arrays. + dxRangeIndex = combinedRangeCounter; + dxPairedSamplerRangeIndex = totalResourceRangeCount + combinedRangeCounter; + combinedRangeCounter++; + break; + } + + D3D12_DESCRIPTOR_RANGE& dxRange = descriptorSetLayoutImpl->m_dxRanges[dxRangeIndex]; + memset(&dxRange, 0, sizeof(dxRange)); + + switch(rangeDesc.type) + { + default: + // ERROR: unsupported slot type. + break; + + case DescriptorSlotType::Sampler: + { + UInt bindingIndex = samplerCounter; samplerCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + dxRange.NumDescriptors = bindingCount; + dxRange.BaseShaderRegister = bindingIndex; + dxRange.RegisterSpace = bindingSpace; + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + break; + + case DescriptorSlotType::SampledImage: + case DescriptorSlotType::UniformTexelBuffer: + { + UInt bindingIndex = srvCounter; srvCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + dxRange.NumDescriptors = bindingCount; + dxRange.BaseShaderRegister = bindingIndex; + dxRange.RegisterSpace = bindingSpace; + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + break; + + case DescriptorSlotType::CombinedImageSampler: + { + // The combined texture/sampler case basically just + // does the work of both the SRV and sampler cases above. + + { + // Here's the SRV logic: + + UInt bindingIndex = srvCounter; srvCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + dxRange.NumDescriptors = bindingCount; + dxRange.BaseShaderRegister = bindingIndex; + dxRange.RegisterSpace = bindingSpace; + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + + { + // And here we do the sampler logic at the "paired" index. + D3D12_DESCRIPTOR_RANGE& dxPairedSamplerRange = descriptorSetLayoutImpl->m_dxRanges[dxPairedSamplerRangeIndex]; + memset(&dxPairedSamplerRange, 0, sizeof(dxPairedSamplerRange)); + + UInt pairedSamplerBindingIndex = srvCounter; srvCounter += bindingCount; + + dxPairedSamplerRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + dxPairedSamplerRange.NumDescriptors = bindingCount; + dxPairedSamplerRange.BaseShaderRegister = pairedSamplerBindingIndex; + dxPairedSamplerRange.RegisterSpace = bindingSpace; + dxPairedSamplerRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + + } + break; + + + case DescriptorSlotType::InputAttachment: + case DescriptorSlotType::StorageImage: + case DescriptorSlotType::StorageTexelBuffer: + case DescriptorSlotType::StorageBuffer: + case DescriptorSlotType::DynamicStorageBuffer: + { + UInt bindingIndex = uavCounter; uavCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + dxRange.NumDescriptors = bindingCount; + dxRange.BaseShaderRegister = bindingIndex; + dxRange.RegisterSpace = bindingSpace; + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + break; + + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::DynamicUniformBuffer: + { + UInt bindingIndex = cbvCounter; cbvCounter += bindingCount; + + dxRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; + dxRange.NumDescriptors = bindingCount; + dxRange.BaseShaderRegister = bindingIndex; + dxRange.RegisterSpace = bindingSpace; + dxRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + break; + } + } + } + + *outLayout = descriptorSetLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) +{ + static const UInt kMaxRanges = 16; + static const UInt kMaxRootParameters = 32; + + D3D12_DESCRIPTOR_RANGE ranges[kMaxRanges]; + D3D12_ROOT_PARAMETER rootParameters[kMaxRootParameters]; + + UInt rangeCount = 0; + UInt rootParameterCount = 0; + + auto descriptorSetCount = desc.descriptorSetCount; + + // We are going to make two passes over the descriptor set layouts + // that are being used to build the pipeline layout. In the first + // pass we will collect all the descriptor ranges that have been + // specified, applying an offset to their register spaces as needed. + // + for(UInt dd = 0; dd < descriptorSetCount; ++dd) + { + auto& descriptorSetInfo = desc.descriptorSets[dd]; + auto descriptorSetLayout = (DescriptorSetLayoutImpl*) descriptorSetInfo.layout; + + // For now we assume that the register space used for + // logical descriptor set #N will be space N. + // + // TODO: This might need to be revisited in the future because + // a single logical descriptor set might need to encompass stuff + // that comes from multiple spaces (e.g., if it contains an unbounded + // array). + // + UInt bindingSpace = dd; + + // Copy descriptor range infromation from the set layout into our + // temporary copy (this is required because the same set layout + // might be applied to different ranges). + // + // API design note: this copy step could be avoided if the D3D + // API allowed for a "space offset" to be applied as part of + // a descriptor-table root parameter. + // + for(auto setDescriptorRange : descriptorSetLayout->m_dxRanges) + { + auto& range = ranges[rangeCount++]; + range = setDescriptorRange; + range.RegisterSpace = bindingSpace; + + // HACK: in order to deal with SM5.0 shaders, `u` registers + // in `space0` need to start with a number *after* the number + // of `SV_Target` outputs that will be used. + // + // TODO: This is clearly a mess, and doing this behavior here + // means it *won't* work for SM5.1 where the restriction is + // lifted. The only real alternative is to rely on explicit + // register numbers (e.g., from shader reflection) but that + // goes against the simplicity that this API layer strives for + // (everything so far has been set up to work correctly with + // automatic assignment of bindings). + // + if( range.RegisterSpace == 0 + && range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV ) + { + range.BaseShaderRegister += desc.renderTargetCount; + } + } + } + + // In our second pass, we will copy over root parameters, which + // may end up pointing into the list of ranges from the first step. + // + auto rangePtr = &ranges[0]; + for(UInt dd = 0; dd < descriptorSetCount; ++dd) + { + auto& descriptorSetInfo = desc.descriptorSets[dd]; + auto descriptorSetLayout = (DescriptorSetLayoutImpl*) descriptorSetInfo.layout; + + // Copy root parameter information from the set layout to our + // overall pipeline layout. + for( auto setRootParameter : descriptorSetLayout->m_dxRootParameters ) + { + auto& rootParameter = rootParameters[rootParameterCount++]; + rootParameter = setRootParameter; + + // In the case where this parameter is a descriptor table, it + // needs to point into our array of ranges (with offsets applied), + // so we will fix up those pointers here. + // + if(rootParameter.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) + { + rootParameter.DescriptorTable.pDescriptorRanges = rangePtr; + rangePtr += rootParameter.DescriptorTable.NumDescriptorRanges; + } + } + } + + D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {}; + rootSignatureDesc.NumParameters = rootParameterCount; + rootSignatureDesc.pParameters = rootParameters; + + // TODO: static samplers should be reasonably easy to support... + rootSignatureDesc.NumStaticSamplers = 0; + rootSignatureDesc.pStaticSamplers = nullptr; + + // TODO: only set this flag if needed (requires creating root + // signature at same time as pipeline state...). + // + rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + + ComPtr signature; + ComPtr error; + if( SLANG_FAILED(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef())) ) + { + fprintf(stderr, "error: D3D12SerializeRootSignature failed"); + if( error ) + { + fprintf(stderr, ": %s\n", (const char*) error->GetBufferPointer()); + } + return SLANG_FAIL; + } + + ComPtr rootSignature; + SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef()))); + + + RefPtr pipelineLayoutImpl = new PipelineLayoutImpl(); + pipelineLayoutImpl->m_rootSignature = rootSignature; + pipelineLayoutImpl->m_descriptorSetCount = descriptorSetCount; + *outLayout = pipelineLayoutImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) +{ + auto layoutImpl = (DescriptorSetLayoutImpl*) layout; + + RefPtr descriptorSetImpl = new DescriptorSetImpl(); + descriptorSetImpl->m_renderer = this; + descriptorSetImpl->m_layout = layoutImpl; + + // We allocate CPU-visible descriptor tables to providing the + // backing storage for each descriptor set. GPU-visible storage + // will only be allocated as needed during per-frame logic in + // order to ensure that a descriptor set it available for use + // in rendering. + // + Int resourceCount = layoutImpl->m_resourceCount; + if( resourceCount ) + { + auto resourceHeap = &m_cpuViewHeap; + descriptorSetImpl->m_resourceHeap = resourceHeap; + descriptorSetImpl->m_resourceTable = resourceHeap->allocate(resourceCount); + descriptorSetImpl->m_resourceObjects.SetSize(resourceCount); + } + + Int samplerCount = layoutImpl->m_samplerCount; + if( samplerCount ) + { + auto samplerHeap = &m_cpuSamplerHeap; + descriptorSetImpl->m_samplerHeap = samplerHeap; + descriptorSetImpl->m_samplerTable = samplerHeap->allocate(samplerCount); + descriptorSetImpl->m_samplerObjects.SetSize(samplerCount); + } + + *outDescriptorSet = descriptorSetImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +{ + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + auto programImpl = (ShaderProgramImpl*) desc.program; + auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; + + // Describe and create the graphics pipeline state object (PSO) + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; + + psoDesc.pRootSignature = pipelineLayoutImpl->m_rootSignature; + + psoDesc.VS = { programImpl->m_vertexShader.Buffer(), programImpl->m_vertexShader.Count() }; + psoDesc.PS = { programImpl->m_pixelShader .Buffer(), programImpl->m_pixelShader .Count() }; + + psoDesc.InputLayout = { inputLayoutImpl->m_elements.Buffer(), UINT(inputLayoutImpl->m_elements.Count()) }; + psoDesc.PrimitiveTopologyType = m_primitiveTopologyType; + + { + const int numRenderTargets = desc.renderTargetCount; + + psoDesc.DSVFormat = m_depthStencilFormat; + psoDesc.NumRenderTargets = numRenderTargets; + for (Int i = 0; i < numRenderTargets; i++) + { + psoDesc.RTVFormats[i] = m_targetFormat; + } + + psoDesc.SampleDesc.Count = 1; + psoDesc.SampleDesc.Quality = 0; + + psoDesc.SampleMask = UINT_MAX; + } + + { + auto& rs = psoDesc.RasterizerState; + rs.FillMode = D3D12_FILL_MODE_SOLID; + rs.CullMode = D3D12_CULL_MODE_NONE; + rs.FrontCounterClockwise = FALSE; + rs.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + rs.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + rs.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + rs.DepthClipEnable = TRUE; + rs.MultisampleEnable = FALSE; + rs.AntialiasedLineEnable = FALSE; + rs.ForcedSampleCount = 0; + rs.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + } + + { + D3D12_BLEND_DESC& blend = psoDesc.BlendState; + + blend.AlphaToCoverageEnable = FALSE; + blend.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = + { + FALSE,FALSE, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + { + blend.RenderTarget[i] = defaultRenderTargetBlendDesc; + } + } + + { + auto& ds = psoDesc.DepthStencilState; + + ds.DepthEnable = FALSE; + ds.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + ds.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; + //ds.DepthFunc = D3D12_COMPARISON_FUNC_LESS; + ds.StencilEnable = FALSE; + ds.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + ds.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = + { + D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS + }; + ds.FrontFace = defaultStencilOp; + ds.BackFace = defaultStencilOp; + } + + psoDesc.PrimitiveTopologyType = m_primitiveTopologyType; + + ComPtr pipelineState; + SLANG_RETURN_ON_FAIL(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(pipelineState.writeRef()))); + + RefPtr pipelineStateImpl = new PipelineStateImpl(); + pipelineStateImpl->m_pipelineType = PipelineType::Graphics; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_pipelineState = pipelineState; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + +Result D3D12Renderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +{ + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + auto programImpl = (ShaderProgramImpl*) desc.program; + + // Describe and create the compute pipeline state object + D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {}; + computeDesc.pRootSignature = pipelineLayoutImpl->m_rootSignature; + computeDesc.CS = { programImpl->m_computeShader.Buffer(), programImpl->m_computeShader.Count() }; + + ComPtr pipelineState; + SLANG_RETURN_ON_FAIL(m_device->CreateComputePipelineState(&computeDesc, IID_PPV_ARGS(pipelineState.writeRef()))); + + RefPtr pipelineStateImpl = new PipelineStateImpl(); + pipelineStateImpl->m_pipelineType = PipelineType::Compute; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_pipelineState = pipelineState; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + +} // renderer_test diff --git a/tools/gfx/render-d3d12.h b/tools/gfx/render-d3d12.h new file mode 100644 index 000000000..b8a3104c0 --- /dev/null +++ b/tools/gfx/render-d3d12.h @@ -0,0 +1,10 @@ +// render-d3d12.h +#pragma once + +namespace gfx { + +class Renderer; + +Renderer* createD3D12Renderer(); + +} // gfx diff --git a/tools/gfx/render-gl.cpp b/tools/gfx/render-gl.cpp new file mode 100644 index 000000000..3ab818fdd --- /dev/null +++ b/tools/gfx/render-gl.cpp @@ -0,0 +1,1426 @@ +// render-gl.cpp +#include "render-gl.h" + +//WORKING:#include "options.h" +#include "render.h" + +#include +#include +#include +#include "core/basic.h" +#include "core/secure-crt.h" +#include "external/stb/stb_image_write.h" + +#include "surface.h" + +// TODO(tfoley): eventually we should be able to run these +// tests on non-Windows targets to confirm that cross-compilation +// at least *works* on those platforms... +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#ifdef _MSC_VER +#include +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif + +#pragma comment(lib, "opengl32") + +#include +#include "external/glext.h" + +// We define an "X-macro" for mapping over loadable OpenGL +// extension entry point that we will use, so that we can +// easily write generic code to iterate over them. +#define MAP_GL_EXTENSION_FUNCS(F) \ + F(glCreateProgram, PFNGLCREATEPROGRAMPROC) \ + F(glCreateShader, PFNGLCREATESHADERPROC) \ + F(glShaderSource, PFNGLSHADERSOURCEPROC) \ + F(glCompileShader, PFNGLCOMPILESHADERPROC) \ + F(glGetShaderiv, PFNGLGETSHADERIVPROC) \ + F(glDeleteShader, PFNGLDELETESHADERPROC) \ + F(glAttachShader, PFNGLATTACHSHADERPROC) \ + F(glLinkProgram, PFNGLLINKPROGRAMPROC) \ + F(glGetProgramiv, PFNGLGETPROGRAMIVPROC) \ + F(glGetProgramInfoLog, PFNGLGETPROGRAMINFOLOGPROC) \ + F(glDeleteProgram, PFNGLDELETEPROGRAMPROC) \ + F(glGetShaderInfoLog, PFNGLGETSHADERINFOLOGPROC) \ + F(glGenBuffers, PFNGLGENBUFFERSPROC) \ + F(glBindBuffer, PFNGLBINDBUFFERPROC) \ + F(glBufferData, PFNGLBUFFERDATAPROC) \ + F(glDeleteBuffers, PFNGLDELETEBUFFERSPROC) \ + F(glMapBuffer, PFNGLMAPBUFFERPROC) \ + F(glUnmapBuffer, PFNGLUNMAPBUFFERPROC) \ + F(glUseProgram, PFNGLUSEPROGRAMPROC) \ + F(glBindBufferBase, PFNGLBINDBUFFERBASEPROC) \ + F(glVertexAttribPointer, PFNGLVERTEXATTRIBPOINTERPROC) \ + F(glEnableVertexAttribArray, PFNGLENABLEVERTEXATTRIBARRAYPROC) \ + F(glDisableVertexAttribArray, PFNGLDISABLEVERTEXATTRIBARRAYPROC) \ + F(glDebugMessageCallback, PFNGLDEBUGMESSAGECALLBACKPROC) \ + F(glDispatchCompute, PFNGLDISPATCHCOMPUTEPROC) \ + F(glActiveTexture, PFNGLACTIVETEXTUREPROC) \ + F(glCreateSamplers, PFNGLCREATESAMPLERSPROC) \ + F(glDeleteSamplers, PFNGLDELETESAMPLERSPROC) \ + F(glBindSampler, PFNGLBINDSAMPLERPROC) \ + F(glTexImage3D, PFNGLTEXIMAGE3DPROC) \ + F(glSamplerParameteri, PFNGLSAMPLERPARAMETERIPROC) \ + /* end */ + +using namespace Slang; + +namespace gfx { + +class GLRenderer : public Renderer +{ +public: + + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + TextureResource::Desc getSwapChainTextureDesc() override; + + Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; + Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; + Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; + + Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; + Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; + + Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; + + Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; + Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; + Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; + + Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; + Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; + Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; + + virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; + virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override {} + virtual void waitForGpu() override {} + virtual RendererType getRendererType() const override { return RendererType::OpenGl; } + + protected: + enum + { + kMaxVertexStreams = 16, + kMaxDescriptorSetCount = 8, + }; + + struct VertexAttributeFormat + { + GLint componentCount; + GLenum componentType; + GLboolean normalized; + }; + + struct VertexAttributeDesc + { + VertexAttributeFormat format; + GLuint streamIndex; + GLsizei offset; + }; + + class InputLayoutImpl: public InputLayout + { + public: + VertexAttributeDesc m_attributes[kMaxVertexStreams]; + UInt m_attributeCount = 0; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + BufferResourceImpl(Usage initialUsage, const Desc& desc, GLRenderer* renderer, GLuint id, GLenum target): + Parent(desc), + m_renderer(renderer), + m_handle(id), + m_initialUsage(initialUsage), + m_target(target) + {} + ~BufferResourceImpl() + { + if (m_renderer) + { + m_renderer->glDeleteBuffers(1, &m_handle); + } + } + + Usage m_initialUsage; + GLRenderer* m_renderer; + GLuint m_handle; + GLenum m_target; + }; + + class TextureResourceImpl: public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(Usage initialUsage, const Desc& desc, GLRenderer* renderer): + Parent(desc), + m_initialUsage(initialUsage), + m_renderer(renderer) + { + m_target = 0; + m_handle = 0; + } + + ~TextureResourceImpl() + { + if (m_handle) + { + glDeleteTextures(1, &m_handle); + } + } + + Usage m_initialUsage; + GLRenderer* m_renderer; + GLenum m_target; + GLuint m_handle; + }; + + class SamplerStateImpl : public SamplerState + { + public: + GLuint m_samplerID; + }; + + class ResourceViewImpl : public ResourceView + { + }; + + class TextureViewImpl : public ResourceViewImpl + { + public: + RefPtr m_resource; + GLuint m_textureID; + }; + + class BufferViewImpl : public ResourceViewImpl + { + public: + RefPtr m_resource; + GLuint m_bufferID; + }; + + enum class GLDescriptorSlotType + { + ConstantBuffer, + CombinedTextureSampler, + + CountOf, + }; + + class DescriptorSetLayoutImpl : public DescriptorSetLayout + { + public: + struct RangeInfo + { + GLDescriptorSlotType type; + UInt arrayIndex; + }; + List m_ranges; + Int m_counts[int(GLDescriptorSlotType::CountOf)]; + }; + + class PipelineLayoutImpl : public PipelineLayout + { + public: + struct DescriptorSetInfo + { + RefPtr layout; + UInt baseArrayIndex[int(GLDescriptorSlotType::CountOf)]; + }; + + List m_sets; + }; + + class DescriptorSetImpl : public DescriptorSet + { + public: + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; + virtual void setResource(UInt range, UInt index, ResourceView* view) override; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) override; + + RefPtr m_layout; + List> m_constantBuffers; + List> m_textures; + List> m_samplers; + }; + + class ShaderProgramImpl : public ShaderProgram + { + public: + ShaderProgramImpl(GLRenderer* renderer, GLuint id): + m_renderer(renderer), + m_id(id) + { + } + ~ShaderProgramImpl() + { + if (m_renderer) + { + m_renderer->glDeleteProgram(m_id); + } + } + + GLuint m_id; + GLRenderer* m_renderer; + }; + + class PipelineStateImpl : public PipelineState + { + public: + RefPtr m_program; + RefPtr m_pipelineLayout; + RefPtr m_inputLayout; + }; + + enum class GlPixelFormat + { + Unknown, + RGBA_Unorm_UInt8, + CountOf, + }; + + struct GlPixelFormatInfo + { + GLint internalFormat; // such as GL_RGBA8 + GLenum format; // such as GL_RGBA + GLenum formatType; // such as GL_UNSIGNED_BYTE + }; + +// void destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details); + + void bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets); + void flushStateForDraw(); + GLuint loadShader(GLenum stage, char const* source); + void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message); + + /// Returns GlPixelFormat::Unknown if not an equivalent + static GlPixelFormat _getGlPixelFormat(Format format); + + static void APIENTRY staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); + static VertexAttributeFormat getVertexAttributeFormat(Format format); + + static void compileTimeAsserts(); + + HDC m_hdc; + HGLRC m_glContext; + float m_clearColor[4] = { 0, 0, 0, 0 }; + + RefPtr m_currentPipelineState; +// RefPtr m_boundShaderProgram; +// RefPtr m_boundInputLayout; + + RefPtr m_boundDescriptorSets[kMaxDescriptorSetCount]; + + GLenum m_boundPrimitiveTopology = GL_TRIANGLES; + GLuint m_boundVertexStreamBuffers[kMaxVertexStreams]; + UInt m_boundVertexStreamStrides[kMaxVertexStreams]; + UInt m_boundVertexStreamOffsets[kMaxVertexStreams]; + + Desc m_desc; + + // Declare a function pointer for each OpenGL + // extension function we need to load +#define DECLARE_GL_EXTENSION_FUNC(NAME, TYPE) TYPE NAME; + MAP_GL_EXTENSION_FUNCS(DECLARE_GL_EXTENSION_FUNC) +#undef DECLARE_GL_EXTENSION_FUNC + + static const GlPixelFormatInfo s_pixelFormatInfos[]; /// Maps GlPixelFormat to a format info +}; + +/* static */GLRenderer::GlPixelFormat GLRenderer::_getGlPixelFormat(Format format) +{ + switch (format) + { + case Format::RGBA_Unorm_UInt8: return GlPixelFormat::RGBA_Unorm_UInt8; + default: return GlPixelFormat::Unknown; + } +} + +/* static */ const GLRenderer::GlPixelFormatInfo GLRenderer::s_pixelFormatInfos[] = +{ + // internalType, format, formatType + { 0, 0, 0}, // GlPixelFormat::Unknown + { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, // GlPixelFormat::RGBA_Unorm_UInt8 +}; + +/* static */void GLRenderer::compileTimeAsserts() +{ + SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_pixelFormatInfos) == int(GlPixelFormat::CountOf)); +} + +Renderer* createGLRenderer() +{ + return new GLRenderer(); +} + +void GLRenderer::debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message) +{ + ::OutputDebugStringA("GL: "); + ::OutputDebugStringA(message); + ::OutputDebugStringA("\n"); + + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + break; + default: + break; + } +} + +/* static */void APIENTRY GLRenderer::staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) +{ + ((GLRenderer*)userParam)->debugCallback(source, type, id, severity, length, message); +} + +/* static */GLRenderer::VertexAttributeFormat GLRenderer::getVertexAttributeFormat(Format format) +{ + switch (format) + { + default: assert(!"unexpected"); return VertexAttributeFormat(); + +#define CASE(NAME, COUNT, TYPE, NORMALIZED) \ + case Format::NAME: do { VertexAttributeFormat result = {COUNT, TYPE, NORMALIZED}; return result; } while (0) + + CASE(RGBA_Float32, 4, GL_FLOAT, GL_FALSE); + CASE(RGB_Float32, 3, GL_FLOAT, GL_FALSE); + CASE(RG_Float32, 2, GL_FLOAT, GL_FALSE); + CASE(R_Float32, 1, GL_FLOAT, GL_FALSE); +#undef CASE + } +} + +void GLRenderer::bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) +{ + for (UInt ii = 0; ii < slotCount; ++ii) + { + UInt slot = startSlot + ii; + + BufferResourceImpl* buffer = static_cast(buffers[ii]); + GLuint bufferID = buffer ? buffer->m_handle : 0; + + assert(!offsets || !offsets[ii]); + + glBindBufferBase(target, (GLuint)slot, bufferID); + } +} + +void GLRenderer::flushStateForDraw() +{ + auto inputLayout = m_currentPipelineState->m_inputLayout.Ptr(); + auto attrCount = inputLayout->m_attributeCount; + for (UInt ii = 0; ii < attrCount; ++ii) + { + auto& attr = inputLayout->m_attributes[ii]; + + auto streamIndex = attr.streamIndex; + + glBindBuffer(GL_ARRAY_BUFFER, m_boundVertexStreamBuffers[streamIndex]); + + glVertexAttribPointer( + (GLuint)ii, + attr.format.componentCount, + attr.format.componentType, + attr.format.normalized, + (GLsizei)m_boundVertexStreamStrides[streamIndex], + (GLvoid*)(attr.offset + m_boundVertexStreamOffsets[streamIndex])); + + glEnableVertexAttribArray((GLuint)ii); + } + for (UInt ii = attrCount; ii < kMaxVertexStreams; ++ii) + { + glDisableVertexAttribArray((GLuint)ii); + } + + // Next bind the descriptor sets as required by the layout + auto pipelineLayout = m_currentPipelineState->m_pipelineLayout; + auto descriptorSetCount = pipelineLayout->m_sets.Count(); + for(UInt ii = 0; ii < descriptorSetCount; ++ii) + { + auto descriptorSet = m_boundDescriptorSets[ii]; + auto descriptorSetInfo = pipelineLayout->m_sets[ii]; + auto descriptorSetLayout = descriptorSetInfo.layout; + + // TODO: need to validate that `descriptorSet->m_layout` matches + // `descriptorSetLayout`. + + { + // First we will bind any uniform buffers that were specified. + + auto slotTypeIndex = int(GLDescriptorSlotType::ConstantBuffer); + auto count = descriptorSetLayout->m_counts[slotTypeIndex]; + auto baseIndex = descriptorSetInfo.baseArrayIndex[slotTypeIndex]; + + for(Int ii = 0; ii < count; ++ii) + { + auto bufferImpl = descriptorSet->m_constantBuffers[ii]; + glBindBufferBase(GL_UNIFORM_BUFFER, ii, bufferImpl->m_handle); + } + } + + + { + // Next we will bind any combined texture/sampler slots. + + auto slotTypeIndex = int(GLDescriptorSlotType::CombinedTextureSampler); + auto count = descriptorSetLayout->m_counts[slotTypeIndex]; + auto baseIndex = descriptorSetInfo.baseArrayIndex[slotTypeIndex]; + + // TODO: We should be able to use a single call to glBindTextures here, + // rather than a loop. This would also eliminate the need to retain + // the appropriate target (e.g., `GL_TEXTURE_2D` for binding). + + for(Int ii = 0; ii < count; ++ii) + { + auto textureViewImpl = descriptorSet->m_textures[ii]; + auto samplerImpl = descriptorSet->m_samplers[ii]; + + glActiveTexture(GL_TEXTURE0 + ii); + glBindTexture(GL_TEXTURE_2D, textureViewImpl->m_textureID); + + glBindSampler(baseIndex + ii, samplerImpl->m_samplerID); + } + } + } +} + +GLuint GLRenderer::loadShader(GLenum stage, const char* source) +{ + // GLSL is monumentally stupid. It officially requires the `#version` directive + // to be the first thing in the file, which wouldn't be so bad but the API + // doesn't provide a way to pass a `#define` into your shader other than by + // prepending it to the whole thing. + // + // We are going to solve this problem by doing some surgery on the source + // that was passed in. + + const char* sourceBegin = source; + const char* sourceEnd = source + strlen(source); + + // Look for a version directive in the user-provided source. + const char* versionBegin = strstr(source, "#version"); + const char* versionEnd = nullptr; + if (versionBegin) + { + // If we found a directive, then scan for the end-of-line + // after it, and use that to specify the slice. + versionEnd = strchr(versionBegin, '\n'); + if (!versionEnd) + { + versionEnd = sourceEnd; + } + else + { + versionEnd = versionEnd + 1; + } + } + else + { + // If we didn't find a directive, then treat it as being + // a zero-byte slice at the start of the string + versionBegin = sourceBegin; + versionEnd = sourceBegin; + } + + enum { kMaxSourceStringCount = 16 }; + const GLchar* sourceStrings[kMaxSourceStringCount]; + GLint sourceStringLengths[kMaxSourceStringCount]; + + int sourceStringCount = 0; + + const char* stagePrelude = "\n"; + switch (stage) + { +#define CASE(NAME) case GL_##NAME##_SHADER: stagePrelude = "#define __GLSL_" #NAME "__ 1\n"; break + + CASE(VERTEX); + CASE(TESS_CONTROL); + CASE(TESS_EVALUATION); + CASE(GEOMETRY); + CASE(FRAGMENT); + CASE(COMPUTE); + +#undef CASE + } + + const char* prelude = + "#define __GLSL__ 1\n" + ; + +#define ADD_SOURCE_STRING_SPAN(BEGIN, END) \ + sourceStrings[sourceStringCount] = BEGIN; \ + sourceStringLengths[sourceStringCount++] = GLint(END - BEGIN) \ + /* end */ + +#define ADD_SOURCE_STRING(BEGIN) \ + sourceStrings[sourceStringCount] = BEGIN; \ + sourceStringLengths[sourceStringCount++] = GLint(strlen(BEGIN)) \ + /* end */ + + ADD_SOURCE_STRING_SPAN(versionBegin, versionEnd); + ADD_SOURCE_STRING(stagePrelude); + ADD_SOURCE_STRING(prelude); + ADD_SOURCE_STRING_SPAN(sourceBegin, versionBegin); + ADD_SOURCE_STRING_SPAN(versionEnd, sourceEnd); + + auto shaderID = glCreateShader(stage); + glShaderSource( + shaderID, + sourceStringCount, + &sourceStrings[0], + &sourceStringLengths[0]); + glCompileShader(shaderID); + + GLint success = GL_FALSE; + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); + if (!success) + { + int maxSize = 0; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxSize); + + auto infoBuffer = (char*)malloc(maxSize); + + int infoSize = 0; + glGetShaderInfoLog(shaderID, maxSize, &infoSize, infoBuffer); + if (infoSize > 0) + { + fprintf(stderr, "%s", infoBuffer); + ::OutputDebugStringA(infoBuffer); + } + + glDeleteShader(shaderID); + return 0; + } + + return shaderID; +} + +#if 0 +void GLRenderer::destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details) +{ + const auto& bindings = desc.m_bindings; + const int numBindings = int(bindings.Count()); + for (int i = 0; i < numBindings; ++i) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + if (binding.bindingType == BindingType::Sampler && detail.m_samplerHandle != 0) + { + glDeleteSamplers(1, &detail.m_samplerHandle); + } + } +} +#endif + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult GLRenderer::initialize(const Desc& desc, void* inWindowHandle) +{ + auto windowHandle = (HWND)inWindowHandle; + m_desc = desc; + + m_hdc = ::GetDC(windowHandle); + + PIXELFORMATDESCRIPTOR pixelFormatDesc = { sizeof(PIXELFORMATDESCRIPTOR) }; + pixelFormatDesc.nVersion = 1; + pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pixelFormatDesc.iPixelType = PFD_TYPE_RGBA; + pixelFormatDesc.cColorBits = 32; + pixelFormatDesc.cDepthBits = 24; + pixelFormatDesc.cStencilBits = 8; + pixelFormatDesc.iLayerType = PFD_MAIN_PLANE; + + int pixelFormatIndex = ChoosePixelFormat(m_hdc, &pixelFormatDesc); + SetPixelFormat(m_hdc, pixelFormatIndex, &pixelFormatDesc); + + m_glContext = wglCreateContext(m_hdc); + wglMakeCurrent(m_hdc, m_glContext); + + auto renderer = glGetString(GL_RENDERER); + auto extensions = glGetString(GL_EXTENSIONS); + + // Load each of our extension functions by name + +#define LOAD_GL_EXTENSION_FUNC(NAME, TYPE) NAME = (TYPE) wglGetProcAddress(#NAME); + MAP_GL_EXTENSION_FUNCS(LOAD_GL_EXTENSION_FUNC) +#undef LOAD_GL_EXTENSION_FUNC + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glViewport(0, 0, desc.width, desc.height); + + if (glDebugMessageCallback) + { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(staticDebugCallback, this); + } + + return SLANG_OK; +} + +void GLRenderer::setClearColor(const float color[4]) +{ + glClearColor(color[0], color[1], color[2], color[3]); +} + +void GLRenderer::clearFrame() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +void GLRenderer::presentFrame() +{ + glFlush(); + ::SwapBuffers(m_hdc); +} + +TextureResource::Desc GLRenderer::getSwapChainTextureDesc() +{ + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1); + return desc; +} + +SlangResult GLRenderer::captureScreenSurface(Surface& surfaceOut) +{ + SLANG_RETURN_ON_FAIL(surfaceOut.allocate(m_desc.width, m_desc.height, Format::RGBA_Unorm_UInt8, 1, SurfaceAllocator::getMallocAllocator())); + glReadPixels(0, 0, m_desc.width, m_desc.height, GL_RGBA, GL_UNSIGNED_BYTE, surfaceOut.m_data); + surfaceOut.flipInplaceVertically(); + return SLANG_OK; +} + +Result GLRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) +{ + TextureResource::Desc srcDesc(descIn); + srcDesc.setDefaults(initialUsage); + + GlPixelFormat pixelFormat = _getGlPixelFormat(srcDesc.format); + if (pixelFormat == GlPixelFormat::Unknown) + { + return SLANG_FAIL; + } + + const GlPixelFormatInfo& info = s_pixelFormatInfos[int(pixelFormat)]; + + const GLint internalFormat = info.internalFormat; + const GLenum format = info.format; + const GLenum formatType = info.formatType; + + RefPtr texture(new TextureResourceImpl(initialUsage, srcDesc, this)); + + GLenum target = 0; + GLuint handle = 0; + glGenTextures(1, &handle); + + const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); + + assert(initData); + assert(initData->numSubResources == srcDesc.numMipLevels * srcDesc.size.depth * effectiveArraySize); + + // Set on texture so will be freed if failure + texture->m_handle = handle; + const void*const*const data = initData->subResources; + + switch (srcDesc.type) + { + case Resource::Type::Texture1D: + { + if (srcDesc.arraySize > 0) + { + target = GL_TEXTURE_1D_ARRAY; + glBindTexture(target, handle); + + int slice = 0; + for (int i = 0; i < effectiveArraySize; i++) + { + for (int j = 0; j < srcDesc.numMipLevels; j++) + { + glTexImage2D(target, j, internalFormat, srcDesc.size.width, i, 0, format, formatType, data[slice++]); + } + } + } + else + { + target = GL_TEXTURE_1D; + glBindTexture(target, handle); + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage1D(target, i, internalFormat, srcDesc.size.width, 0, format, formatType, data[i]); + } + } + break; + } + case Resource::Type::TextureCube: + case Resource::Type::Texture2D: + { + if (srcDesc.arraySize > 0) + { + if (srcDesc.type == Resource::Type::TextureCube) + { + target = GL_TEXTURE_CUBE_MAP_ARRAY; + } + else + { + target = GL_TEXTURE_2D_ARRAY; + } + + glBindTexture(target, handle); + + int slice = 0; + for (int i = 0; i < effectiveArraySize; i++) + { + for (int j = 0; j < srcDesc.numMipLevels; j++) + { + glTexImage3D(target, j, internalFormat, srcDesc.size.width, srcDesc.size.height, slice, 0, format, formatType, data[slice++]); + } + } + } + else + { + if (srcDesc.type == Resource::Type::TextureCube) + { + target = GL_TEXTURE_CUBE_MAP; + glBindTexture(target, handle); + + int slice = 0; + for (int j = 0; j < 6; j++) + { + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[slice++]); + } + } + } + else + { + target = GL_TEXTURE_2D; + glBindTexture(target, handle); + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage2D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[i]); + } + } + } + break; + } + case Resource::Type::Texture3D: + { + target = GL_TEXTURE_3D; + glBindTexture(target, handle); + for (int i = 0; i < srcDesc.numMipLevels; i++) + { + glTexImage3D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, srcDesc.size.depth, 0, format, formatType, data[i]); + } + break; + } + default: + return SLANG_FAIL; + } + + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); + + // Assume regular sampling (might be superseded - if a combined sampler wanted) + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); + + texture->m_target = target; + + *outResource = texture.detach(); + return SLANG_OK; +} + +static GLenum _calcUsage(Resource::Usage usage) +{ + typedef Resource::Usage Usage; + switch (usage) + { + case Usage::ConstantBuffer: return GL_DYNAMIC_DRAW; + default: return GL_STATIC_READ; + } +} + +static GLenum _calcTarget(Resource::Usage usage) +{ + typedef Resource::Usage Usage; + switch (usage) + { + case Usage::ConstantBuffer: return GL_UNIFORM_BUFFER; + default: return GL_SHADER_STORAGE_BUFFER; + } +} + +Result GLRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) +{ + BufferResource::Desc desc(descIn); + desc.setDefaults(initialUsage); + + const GLenum target = _calcTarget(initialUsage); + // TODO: should derive from desc... + const GLenum usage = _calcUsage(initialUsage); + + GLuint bufferID = 0; + glGenBuffers(1, &bufferID); + glBindBuffer(target, bufferID); + + glBufferData(target, descIn.sizeInBytes, initData, usage); + + RefPtr resourceImpl = new BufferResourceImpl(initialUsage, desc, this, bufferID, target); + *outResource = resourceImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) +{ + GLuint samplerID; + glCreateSamplers(1, &samplerID); + + RefPtr samplerImpl = new SamplerStateImpl(); + samplerImpl->m_samplerID = samplerID; + *outSampler = samplerImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (TextureResourceImpl*) texture; + + // TODO: actually do something? + + RefPtr viewImpl = new TextureViewImpl(); + viewImpl->m_resource = resourceImpl; + viewImpl->m_textureID = resourceImpl->m_handle; + *outView = viewImpl; + return SLANG_OK; +} + +Result GLRenderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + + // TODO: actually do something? + + RefPtr viewImpl = new BufferViewImpl(); + viewImpl->m_resource = resourceImpl; + viewImpl->m_bufferID = resourceImpl->m_handle; + *outView = viewImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) +{ + RefPtr inputLayout = new InputLayoutImpl; + + inputLayout->m_attributeCount = inputElementCount; + for (UInt ii = 0; ii < inputElementCount; ++ii) + { + auto& inputAttr = inputElements[ii]; + auto& glAttr = inputLayout->m_attributes[ii]; + + glAttr.streamIndex = 0; + glAttr.format = getVertexAttributeFormat(inputAttr.format); + glAttr.offset = (GLsizei)inputAttr.offset; + } + + *outLayout = inputLayout.detach(); + return SLANG_OK; +} + +void* GLRenderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + BufferResourceImpl* buffer = static_cast(bufferIn); + + //GLenum target = GL_UNIFORM_BUFFER; + + GLuint access = 0; + switch (flavor) + { + case MapFlavor::WriteDiscard: + case MapFlavor::HostWrite: + access = GL_WRITE_ONLY; + break; + case MapFlavor::HostRead: + access = GL_READ_ONLY; + break; + } + + glBindBuffer(buffer->m_target, buffer->m_handle); + + return glMapBuffer(buffer->m_target, access); +} + +void GLRenderer::unmap(BufferResource* bufferIn) +{ + BufferResourceImpl* buffer = static_cast(bufferIn); + glUnmapBuffer(buffer->m_target); +} + +void GLRenderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + GLenum glTopology = 0; + switch (topology) + { +#define CASE(NAME, VALUE) case PrimitiveTopology::NAME: glTopology = VALUE; break + + CASE(TriangleList, GL_TRIANGLES); + +#undef CASE + } + m_boundPrimitiveTopology = glTopology; +} + +void GLRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) +{ + for (UInt ii = 0; ii < slotCount; ++ii) + { + UInt slot = startSlot + ii; + + BufferResourceImpl* buffer = static_cast(buffers[ii]); + GLuint bufferID = buffer ? buffer->m_handle : 0; + + m_boundVertexStreamBuffers[slot] = bufferID; + m_boundVertexStreamStrides[slot] = strides[ii]; + m_boundVertexStreamOffsets[slot] = offsets[ii]; + } +} + +void GLRenderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) +{ +} + +void GLRenderer::setDepthStencilTarget(ResourceView* depthStencilView) +{ +} + +void GLRenderer::setPipelineState(PipelineType pipelineType, PipelineState* state) +{ + auto pipelineStateImpl = (PipelineStateImpl*) state; + + m_currentPipelineState = pipelineStateImpl; + + auto program = pipelineStateImpl->m_program; + GLuint programID = program ? program->m_id : 0; + glUseProgram(programID); +} + +void GLRenderer::draw(UInt vertexCount, UInt startVertex = 0) +{ + flushStateForDraw(); + + glDrawArrays(m_boundPrimitiveTopology, (GLint)startVertex, (GLsizei)vertexCount); +} + +void GLRenderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) +{ + assert(!"unimplemented"); +} + +void GLRenderer::dispatchCompute(int x, int y, int z) +{ + glDispatchCompute(x, y, z); +} + +#if 0 +BindingState* GLRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr bindingState(new BindingStateImpl(bindingStateDesc, this)); + + const auto& srcBindings = bindingStateDesc.m_bindings; + const int numBindings = int(srcBindings.Count()); + + auto& dstDetails = bindingState->m_bindingDetails; + dstDetails.SetSize(numBindings); + + for (int i = 0; i < numBindings; ++i) + { + auto& dstDetail = dstDetails[i]; + const auto& srcBinding = srcBindings[i]; + + + switch (srcBinding.bindingType) + { + case BindingType::Texture: + case BindingType::Buffer: + { + break; + } + case BindingType::CombinedTextureSampler: + { + assert(srcBinding.resource && srcBinding.resource->isTexture()); + TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; + + if (samplerDesc.isCompareSampler) + { + auto target = texture->m_target; + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + break; + } + case BindingType::Sampler: + { + const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; + + GLuint handle; + + glCreateSamplers(1, &handle); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, GL_REPEAT); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, GL_REPEAT); + glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, GL_REPEAT); + + if (samplerDesc.isCompareSampler) + { + glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + else + { + glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(handle, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); + } + + dstDetail.m_samplerHandle = handle; + break; + } + } + } + + return bindingState.detach(); +} + +void GLRenderer::setBindingState(BindingState* stateIn) +{ + BindingStateImpl* state = static_cast(stateIn); + + const auto& bindingDesc = state->getDesc(); + + const auto& details = state->m_bindingDetails; + const auto& bindings = bindingDesc.m_bindings; + const int numBindings = int(bindings.Count()); + + for (int i = 0; i < numBindings; ++i) + { + const auto& binding = bindings[i]; + const auto& detail = details[i]; + + switch (binding.bindingType) + { + case BindingType::Buffer: + { + const int bindingIndex = binding.registerRange.getSingleIndex(); + + BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); + glBindBufferBase(buffer->m_target, bindingIndex, buffer->m_handle); + break; + } + case BindingType::Sampler: + { + for (int index = binding.registerRange.index; index < binding.registerRange.index + binding.registerRange.size; ++index) + { + glBindSampler(index, detail.m_samplerHandle); + } + break; + } + case BindingType::Texture: + case BindingType::CombinedTextureSampler: + { + BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); + + const int bindingIndex = binding.registerRange.getSingleIndex(); + + glActiveTexture(GL_TEXTURE0 + bindingIndex); + glBindTexture(buffer->m_target, buffer->m_handle); + break; + } + } + } +} +#endif + +void GLRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + + auto layout = m_layout; + auto rangeInfo = layout->m_ranges[range]; + auto arrayIndex = rangeInfo.arrayIndex + index; + + m_constantBuffers[arrayIndex] = resourceImpl; +} + +void GLRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) +{ + auto viewImpl = (ResourceViewImpl*) view; + + auto layout = m_layout; + auto rangeInfo = layout->m_ranges[range]; + auto arrayIndex = rangeInfo.arrayIndex + index; + + assert(!"unimplemented"); +} + +void GLRenderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) +{ + assert(!"unsupported"); +} + +void GLRenderer::DescriptorSetImpl::setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) +{ + auto viewImpl = (TextureViewImpl*) textureView; + auto samplerImpl = (SamplerStateImpl*) sampler; + + auto layout = m_layout; + auto rangeInfo = layout->m_ranges[range]; + auto arrayIndex = rangeInfo.arrayIndex + index; + + m_textures[arrayIndex] = viewImpl; + m_samplers[arrayIndex] = samplerImpl; +} + +void GLRenderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) +{ + auto descriptorSetImpl = (DescriptorSetImpl*)descriptorSet; + + // TODO: can we just bind things immediately here, rather than shadowing the state? + + m_boundDescriptorSets[index] = descriptorSetImpl; +} + +Result GLRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) +{ + RefPtr layoutImpl = new DescriptorSetLayoutImpl(); + + Int counts[int(GLDescriptorSlotType::CountOf)] = { 0, }; + + Int rangeCount = desc.slotRangeCount; + for(Int rr = 0; rr < rangeCount; ++rr) + { + auto rangeDesc = desc.slotRanges[rr]; + DescriptorSetLayoutImpl::RangeInfo rangeInfo; + + GLDescriptorSlotType glSlotType; + switch( rangeDesc.type ) + { + default: + assert(!"unsupported"); + break; + + // TODO: There are many other slot types we could support here, + // in particular including storage buffers. + + case DescriptorSlotType::CombinedImageSampler: + glSlotType = GLDescriptorSlotType::CombinedTextureSampler; + break; + + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::DynamicUniformBuffer: + glSlotType = GLDescriptorSlotType::ConstantBuffer; + break; + } + + rangeInfo.type = glSlotType; + rangeInfo.arrayIndex = counts[int(glSlotType)]; + counts[int(glSlotType)] += rangeDesc.count; + + layoutImpl->m_ranges.Add(rangeInfo); + } + + for( Int ii = 0; ii < int(GLDescriptorSlotType::CountOf); ++ii ) + { + layoutImpl->m_counts[ii] = counts[ii]; + } + + *outLayout = layoutImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) +{ + RefPtr layoutImpl = new PipelineLayoutImpl(); + + static const int kSlotTypeCount = int(GLDescriptorSlotType::CountOf); + Int counts[kSlotTypeCount] = { 0, }; + + Int setCount = desc.descriptorSetCount; + for( Int ii = 0; ii < setCount; ++ii ) + { + auto setLayout = (DescriptorSetLayoutImpl*) desc.descriptorSets[ii].layout; + + PipelineLayoutImpl::DescriptorSetInfo setInfo; + setInfo.layout = setLayout; + + for( Int ii = 0; ii < int(GLDescriptorSlotType::CountOf); ++ii ) + { + setInfo.baseArrayIndex[ii] = counts[ii]; + counts[ii] += setLayout->m_counts[ii]; + } + + layoutImpl->m_sets.Add(setInfo); + } + + *outLayout = layoutImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) +{ + auto layoutImpl = (DescriptorSetLayoutImpl*) layout; + + RefPtr descriptorSetImpl = new DescriptorSetImpl(); + + descriptorSetImpl->m_layout = layoutImpl; + + // TODO: storage for the arrays of bound objects could be tail allocated + // as part of the descriptor set, with offsets pre-computed in the + // descriptor set layout. + + { + auto slotTypeIndex = int(GLDescriptorSlotType::ConstantBuffer); + auto slotCount = layoutImpl->m_counts[slotTypeIndex]; + descriptorSetImpl->m_constantBuffers.SetSize(slotCount); + } + + { + auto slotTypeIndex = int(GLDescriptorSlotType::CombinedTextureSampler); + auto slotCount = layoutImpl->m_counts[slotTypeIndex]; + + descriptorSetImpl->m_textures.SetSize(slotCount); + descriptorSetImpl->m_samplers.SetSize(slotCount); + } + + *outDescriptorSet = descriptorSetImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) +{ + auto programID = glCreateProgram(); + if(desc.pipelineType == PipelineType::Compute ) + { + auto computeKernel = desc.findKernel(StageType::Compute); + auto computeShaderID = loadShader(GL_COMPUTE_SHADER, (char const*) computeKernel->codeBegin); + glAttachShader(programID, computeShaderID); + glLinkProgram(programID); + glDeleteShader(computeShaderID); + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + auto vertexShaderID = loadShader(GL_VERTEX_SHADER, (char const*) vertexKernel->codeBegin); + auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, (char const*) fragmentKernel->codeBegin); + + glAttachShader(programID, vertexShaderID); + glAttachShader(programID, fragmentShaderID); + + + glLinkProgram(programID); + + glDeleteShader(vertexShaderID); + glDeleteShader(fragmentShaderID); + } + GLint success = GL_FALSE; + glGetProgramiv(programID, GL_LINK_STATUS, &success); + if (!success) + { + int maxSize = 0; + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxSize); + + auto infoBuffer = (char*)::malloc(maxSize); + + int infoSize = 0; + glGetProgramInfoLog(programID, maxSize, &infoSize, infoBuffer); + if (infoSize > 0) + { + fprintf(stderr, "%s", infoBuffer); + OutputDebugStringA(infoBuffer); + } + + ::free(infoBuffer); + + glDeleteProgram(programID); + return SLANG_FAIL; + } + + *outProgram = new ShaderProgramImpl(this, programID); + return SLANG_OK; +} + +Result GLRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; + + RefPtr pipelineStateImpl = new PipelineStateImpl(); + pipelineStateImpl->m_program = programImpl; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_inputLayout = inputLayoutImpl; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + +Result GLRenderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +{ + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + + RefPtr pipelineStateImpl = new PipelineStateImpl(); + pipelineStateImpl->m_program = programImpl; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + + +} // renderer_test diff --git a/tools/gfx/render-gl.h b/tools/gfx/render-gl.h new file mode 100644 index 000000000..055031d38 --- /dev/null +++ b/tools/gfx/render-gl.h @@ -0,0 +1,10 @@ +// render-d3d11.h +#pragma once + +namespace gfx { + +class Renderer; + +Renderer* createGLRenderer(); + +} // gfx diff --git a/tools/gfx/render-vk.cpp b/tools/gfx/render-vk.cpp new file mode 100644 index 000000000..27926e0e6 --- /dev/null +++ b/tools/gfx/render-vk.cpp @@ -0,0 +1,2569 @@ +// render-vk.cpp +#include "render-vk.h" + +//WORKING:#include "options.h" +#include "render.h" + +#include "../../source/core/smart-pointer.h" + +#include "vk-api.h" +#include "vk-util.h" +#include "vk-device-queue.h" +#include "vk-swap-chain.h" + +#include "surface.h" + +// Vulkan has a different coordinate system to ogl +// http://anki3d.org/vulkan-coordinate-system/ + +#define ENABLE_VALIDATION_LAYER 1 + +#ifdef _MSC_VER +# include +# pragma warning(disable: 4996) +# if (_MSC_VER < 1900) +# define snprintf sprintf_s +# endif +#endif + +namespace gfx { +using namespace Slang; + +class VKRenderer : public Renderer +{ +public: + enum + { + kMaxRenderTargets = 8, + kMaxAttachments = kMaxRenderTargets + 1, + + kMaxDescriptorSets = 4, + }; + + // Renderer implementation + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; + virtual void setClearColor(const float color[4]) override; + virtual void clearFrame() override; + virtual void presentFrame() override; + TextureResource::Desc getSwapChainTextureDesc() override; + + Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) override; + Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) override; + Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) override; + + Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) override; + Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) override; + + Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) override; + + Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) override; + Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) override; + Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) override; + + Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) override; + Result createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) override; + Result createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) override; + + virtual SlangResult captureScreenSurface(Surface& surface) override; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) override; + virtual void unmap(BufferResource* buffer) override; + virtual void setPrimitiveTopology(PrimitiveTopology topology) override; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) override; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) override; + virtual void setDepthStencilTarget(ResourceView* depthStencilView) override; + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) override; + virtual void draw(UInt vertexCount, UInt startVertex) override; + virtual void drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) override; + virtual void dispatchCompute(int x, int y, int z) override; + virtual void submitGpuWork() override; + virtual void waitForGpu() override; + virtual RendererType getRendererType() const override { return RendererType::Vulkan; } + + /// Dtor + ~VKRenderer(); + + protected: + + class Buffer + { + public: + /// Initialize a buffer with specified size, and memory props + Result init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties); + + /// Returns true if has been initialized + bool isInitialized() const { return m_api != nullptr; } + + // Default Ctor + Buffer(): + m_api(nullptr) + {} + + /// Dtor + ~Buffer() + { + if (m_api) + { + m_api->vkDestroyBuffer(m_api->m_device, m_buffer, nullptr); + m_api->vkFreeMemory(m_api->m_device, m_memory, nullptr); + } + } + + VkBuffer m_buffer; + VkDeviceMemory m_memory; + const VulkanApi* m_api; + }; + + class InputLayoutImpl : public InputLayout + { + public: + List m_vertexDescs; + int m_vertexSize; + }; + + class BufferResourceImpl: public BufferResource + { + public: + typedef BufferResource Parent; + + BufferResourceImpl(Resource::Usage initialUsage, const BufferResource::Desc& desc, VKRenderer* renderer): + Parent(desc), + m_renderer(renderer), + m_initialUsage(initialUsage) + { + assert(renderer); + } + + Resource::Usage m_initialUsage; + VKRenderer* m_renderer; + Buffer m_buffer; + Buffer m_uploadBuffer; + List m_readBuffer; ///< Stores the contents when a map read is performed + + MapFlavor m_mapFlavor = MapFlavor::Unknown; ///< If resource is mapped, records what kind of mapping else Unknown (if not mapped) + }; + + class TextureResourceImpl : public TextureResource + { + public: + typedef TextureResource Parent; + + TextureResourceImpl(const Desc& desc, Usage initialUsage, const VulkanApi* api) : + Parent(desc), + m_initialUsage(initialUsage), + m_api(api) + { + } + ~TextureResourceImpl() + { + if (m_api) + { + if (m_imageMemory != VK_NULL_HANDLE) + { + m_api->vkFreeMemory(m_api->m_device, m_imageMemory, nullptr); + } + if (m_image != VK_NULL_HANDLE) + { + m_api->vkDestroyImage(m_api->m_device, m_image, nullptr); + } + } + } + + Usage m_initialUsage; + + VkImage m_image = VK_NULL_HANDLE; + VkDeviceMemory m_imageMemory = VK_NULL_HANDLE; + + const VulkanApi* m_api; + }; + + class SamplerStateImpl : public SamplerState + { + public: + VkSampler m_sampler; + }; + + class ResourceViewImpl : public ResourceView + { + public: + enum class ViewType + { + Texture, + TexelBuffer, + PlainBuffer, + }; + ViewType m_type; + }; + + class TextureResourceViewImpl : public ResourceViewImpl + { + public: + TextureResourceViewImpl() + { + m_type = ViewType::Texture; + } + + RefPtr m_texture; + VkImageView m_view; + VkImageLayout m_layout; + }; + + class TexelBufferResourceViewImpl : public ResourceViewImpl + { + public: + TexelBufferResourceViewImpl() + { + m_type = ViewType::TexelBuffer; + } + + RefPtr m_buffer; + VkBufferView m_view; + }; + + class PlainBufferResourceViewImpl : public ResourceViewImpl + { + public: + PlainBufferResourceViewImpl() + { + m_type = ViewType::PlainBuffer; + } + + RefPtr m_buffer; + VkDeviceSize offset; + VkDeviceSize size; + }; + + class ShaderProgramImpl: public ShaderProgram + { + public: + + ShaderProgramImpl(PipelineType pipelineType): + m_pipelineType(pipelineType) + {} + + PipelineType m_pipelineType; + + VkPipelineShaderStageCreateInfo m_compute; + VkPipelineShaderStageCreateInfo m_vertex; + VkPipelineShaderStageCreateInfo m_fragment; + + List m_buffers[2]; //< To keep storage of code in scope + }; + + class DescriptorSetLayoutImpl : public DescriptorSetLayout + { + public: + DescriptorSetLayoutImpl(const VulkanApi& api) + : m_api(&api) + { + } + + ~DescriptorSetLayoutImpl() + { + if(m_descriptorSetLayout != VK_NULL_HANDLE) + { + m_api->vkDestroyDescriptorSetLayout(m_api->m_device, m_descriptorSetLayout, nullptr); + } + if (m_descriptorPool != VK_NULL_HANDLE) + { + m_api->vkDestroyDescriptorPool(m_api->m_device, m_descriptorPool, nullptr); + } + } + + VulkanApi const* m_api; + VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE; + VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE; + + struct RangeInfo + { + VkDescriptorType descriptorType; + }; + List m_ranges; + }; + + class PipelineLayoutImpl : public PipelineLayout + { + public: + PipelineLayoutImpl(const VulkanApi& api) + : m_api(&api) + { + } + + ~PipelineLayoutImpl() + { + if (m_pipelineLayout != VK_NULL_HANDLE) + { + m_api->vkDestroyPipelineLayout(m_api->m_device, m_pipelineLayout, nullptr); + } + } + + VulkanApi const* m_api; + VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; + UInt m_descriptorSetCount = 0; + }; + + class DescriptorSetImpl : public DescriptorSet + { + public: + DescriptorSetImpl(VKRenderer* renderer) + : m_renderer(renderer) + { + } + + ~DescriptorSetImpl() + { + } + + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) override; + virtual void setResource(UInt range, UInt index, ResourceView* view) override; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) override; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) override; + + RefPtr m_renderer; + RefPtr m_layout; + VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE; + }; + +#if 0 + struct BindingDetail + { + VkImageView m_srv = VK_NULL_HANDLE; + VkBufferView m_uav = VK_NULL_HANDLE; + VkSampler m_sampler = VK_NULL_HANDLE; + }; + + class BindingStateImpl: public BindingState + { + public: + typedef BindingState Parent; + + BindingStateImpl(const Desc& desc, const VulkanApi* api): + Parent(desc), + m_api(api) + { + } + ~BindingStateImpl() + { + for (int i = 0; i < int(m_bindingDetails.Count()); ++i) + { + BindingDetail& detail = m_bindingDetails[i]; + if (detail.m_sampler != VK_NULL_HANDLE) + { + m_api->vkDestroySampler(m_api->m_device, detail.m_sampler, nullptr); + } + if (detail.m_srv != VK_NULL_HANDLE) + { + m_api->vkDestroyImageView(m_api->m_device, detail.m_srv, nullptr); + } + if (detail.m_uav != VK_NULL_HANDLE) + { + m_api->vkDestroyBufferView(m_api->m_device, detail.m_uav, nullptr); + } + } + } + + const VulkanApi* m_api; + List m_bindingDetails; + }; +#endif + + struct BoundVertexBuffer + { + RefPtr m_buffer; + int m_stride; + int m_offset; + }; + + class PipelineStateImpl : public PipelineState + { + public: + PipelineStateImpl(const VulkanApi& api): + m_api(&api) + { + } + ~PipelineStateImpl() + { + if (m_pipeline != VK_NULL_HANDLE) + { + m_api->vkDestroyPipeline(m_api->m_device, m_pipeline, nullptr); + } + } + + const VulkanApi* m_api; + +// VkPrimitiveTopology m_primitiveTopology; + + RefPtr m_pipelineLayout; + +// RefPtr m_inputLayout; + RefPtr m_shaderProgram; + + VkPipeline m_pipeline = VK_NULL_HANDLE; + }; + + VkBool32 handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, + size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg); + + VkPipelineShaderStageCreateInfo compileEntryPoint( + ShaderProgram::KernelDesc const& kernelDesc, + VkShaderStageFlagBits stage, + List& bufferOut); + + static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, + size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData); + + /// Returns true if m_currentPipeline matches the current configuration +// Pipeline* _getPipeline(); +// bool _isEqual(const Pipeline& pipeline) const; +// Slang::Result _createPipeline(RefPtr& pipelineOut); + void _beginRender(); + void _endRender(); + + Slang::Result _beginPass(); + void _endPass(); + void _transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout); + + VkDebugReportCallbackEXT m_debugReportCallback; + +// RefPtr m_currentInputLayout; + +// RefPtr m_currentBindingState; + RefPtr m_currentPipelineLayout; + + RefPtr m_currentDescriptorSetImpls [kMaxDescriptorSets]; + VkDescriptorSet m_currentDescriptorSets [kMaxDescriptorSets]; + +// RefPtr m_currentProgram; + +// List > m_pipelineCache; + RefPtr m_currentPipeline; + + List m_boundVertexBuffers; + + VkPrimitiveTopology m_primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkDevice m_device = VK_NULL_HANDLE; + + VulkanModule m_module; + VulkanApi m_api; + + VulkanDeviceQueue m_deviceQueue; + VulkanSwapChain m_swapChain; + + VkRenderPass m_renderPass = VK_NULL_HANDLE; + + int m_swapChainImageIndex = -1; + + float m_clearColor[4] = { 0, 0, 0, 0 }; + + Desc m_desc; +}; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer::Buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +Result VKRenderer::Buffer::init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties) +{ + assert(!isInitialized()); + + m_api = &api; + m_memory = VK_NULL_HANDLE; + m_buffer = VK_NULL_HANDLE; + + VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + bufferCreateInfo.size = bufferSize; + bufferCreateInfo.usage = usage; + + SLANG_VK_CHECK(api.vkCreateBuffer(api.m_device, &bufferCreateInfo, nullptr, &m_buffer)); + + VkMemoryRequirements memoryReqs = {}; + api.vkGetBufferMemoryRequirements(api.m_device, m_buffer, &memoryReqs); + + int memoryTypeIndex = api.findMemoryTypeIndex(memoryReqs.memoryTypeBits, reqMemoryProperties); + assert(memoryTypeIndex >= 0); + + VkMemoryPropertyFlags actualMemoryProperites = api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; + + VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocateInfo.allocationSize = memoryReqs.size; + allocateInfo.memoryTypeIndex = memoryTypeIndex; + + SLANG_VK_CHECK(api.vkAllocateMemory(api.m_device, &allocateInfo, nullptr, &m_memory)); + SLANG_VK_CHECK(api.vkBindBufferMemory(api.m_device, m_buffer, m_memory, 0)); + + return SLANG_OK; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +#if 0 +bool VKRenderer::_isEqual(const Pipeline& pipeline) const +{ + return + pipeline.m_pipelineLayout == m_currentPipelineLayout && + pipeline.m_primitiveTopology == m_primitiveTopology && + pipeline.m_inputLayout == m_currentInputLayout && + pipeline.m_shaderProgram == m_currentProgram; +} + +VKRenderer::Pipeline* VKRenderer::_getPipeline() +{ + if (m_currentPipeline && _isEqual(*m_currentPipeline)) + { + return m_currentPipeline; + } + + // Look for a match in the cache + for (int i = 0; i < int(m_pipelineCache.Count()); ++i) + { + Pipeline* pipeline = m_pipelineCache[i]; + if (_isEqual(*pipeline)) + { + m_currentPipeline = pipeline; + return pipeline; + } + } + + RefPtr pipeline; + SLANG_RETURN_NULL_ON_FAIL(_createPipeline(pipeline)); + m_pipelineCache.Add(pipeline); + m_currentPipeline = pipeline; + return pipeline; +} + +Slang::Result VKRenderer::_createPipeline(RefPtr& pipelineOut) +{ + RefPtr pipeline(new Pipeline(m_api)); + + // Initialize the state + pipeline->m_primitiveTopology = m_primitiveTopology; + pipeline->m_pipelineLayout = m_currentPipelineLayout; + pipeline->m_shaderProgram = m_currentProgram; + pipeline->m_inputLayout = m_currentInputLayout; + + // Must be equal at this point if all the items are correctly set in pipeline + assert(_isEqual(*pipeline)); + + VkPipelineCache pipelineCache = VK_NULL_HANDLE; + + if (m_currentProgram->m_pipelineType == PipelineType::Compute) + { + // Then create a pipeline to use that layout + + VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; + computePipelineInfo.stage = m_currentProgram->m_compute; + computePipelineInfo.layout = pipeline->m_pipelineLayout->m_pipelineLayout; + + SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline->m_pipeline)); + } + else if (m_currentProgram->m_pipelineType == PipelineType::Graphics) + { + // Create the graphics pipeline + + const int width = m_swapChain.getWidth(); + const int height = m_swapChain.getHeight(); + + VkPipelineShaderStageCreateInfo shaderStages[] = { m_currentProgram->m_vertex, m_currentProgram->m_fragment }; + + // VertexBuffer/s + // Currently only handles one + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + + VkVertexInputBindingDescription vertexInputBindingDescription; + + if (m_currentInputLayout) + { + vertexInputBindingDescription.binding = 0; + vertexInputBindingDescription.stride = m_currentInputLayout->m_vertexSize; + vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + const auto& srcAttributeDescs = m_currentInputLayout->m_vertexDescs; + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription; + + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(srcAttributeDescs.Count()); + vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.Buffer(); + } + + // + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)width; + viewport.height = (float)height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = { uint32_t(width), uint32_t(height) }; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = pipeline->m_pipelineLayout->m_pipelineLayout; + pipelineInfo.renderPass = m_renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + + SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline->m_pipeline)); + } + else + { + assert(!"Unhandled program type"); + return SLANG_FAIL; + } + + pipelineOut = pipeline; + return SLANG_OK; +} +#endif + +Result VKRenderer::_beginPass() +{ + if (m_swapChainImageIndex < 0) + { + return SLANG_FAIL; + } + + const int numRenderTargets = 1; + + const VulkanSwapChain::Image& image = m_swapChain.getImages()[m_swapChainImageIndex]; + + int numAttachments = 0; + + // Start render pass + VkClearValue clearValues[kMaxAttachments]; + clearValues[numAttachments++] = VkClearValue{ m_clearColor[0], m_clearColor[1], m_clearColor[2], m_clearColor[3] }; + + bool hasDepthBuffer = false; + if (hasDepthBuffer) + { + VkClearValue& clearValue = clearValues[numAttachments++]; + + clearValue.depthStencil.depth = 1.0f; + clearValue.depthStencil.stencil = 0; + } + + const int width = m_swapChain.getWidth(); + const int height = m_swapChain.getHeight(); + + VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); + + VkRenderPassBeginInfo renderPassBegin = {}; + renderPassBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassBegin.renderPass = m_renderPass; + renderPassBegin.framebuffer = image.m_frameBuffer; + renderPassBegin.renderArea.offset.x = 0; + renderPassBegin.renderArea.offset.y = 0; + renderPassBegin.renderArea.extent.width = width; + renderPassBegin.renderArea.extent.height = height; + renderPassBegin.clearValueCount = numAttachments; + renderPassBegin.pClearValues = clearValues; + + m_api.vkCmdBeginRenderPass(cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE); + + // Set up scissor and viewport + { + VkRect2D rects[kMaxRenderTargets] = {}; + VkViewport viewports[kMaxRenderTargets] = {}; + for (int i = 0; i < numRenderTargets; ++i) + { + rects[i] = VkRect2D{ 0, 0, uint32_t(width), uint32_t(height) }; + + VkViewport& dstViewport = viewports[i]; + + dstViewport.x = 0.0f; + dstViewport.y = 0.0f; + dstViewport.width = float(width); + dstViewport.height = float(height); + dstViewport.minDepth = 0.0f; + dstViewport.maxDepth = 1.0f; + } + + m_api.vkCmdSetScissor(cmdBuffer, 0, numRenderTargets, rects); + m_api.vkCmdSetViewport(cmdBuffer, 0, numRenderTargets, viewports); + } + + return SLANG_OK; +} + +void VKRenderer::_endPass() +{ + VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); + m_api.vkCmdEndRenderPass(cmdBuffer); +} + +void VKRenderer::_beginRender() +{ + m_swapChainImageIndex = m_swapChain.nextFrontImageIndex(); + + if (m_swapChainImageIndex < 0) + { + return; + } +} + +void VKRenderer::_endRender() +{ + m_deviceQueue.flush(); +} + +Renderer* createVKRenderer() +{ + return new VKRenderer; +} + +VKRenderer::~VKRenderer() +{ + if (m_renderPass != VK_NULL_HANDLE) + { + m_api.vkDestroyRenderPass(m_device, m_renderPass, nullptr); + m_renderPass = VK_NULL_HANDLE; + } +} + + +VkBool32 VKRenderer::handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, + size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg) +{ + char const* severity = "message"; + if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) + severity = "warning"; + if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) + severity = "error"; + + // pMsg can be really big (it can be assembler dump for example) + // Use a dynamic buffer to store + size_t bufferSize = strlen(pMsg) + 1 + 1024; + List bufferArray; + bufferArray.SetSize(bufferSize); + char* buffer = bufferArray.Buffer(); + + sprintf_s(buffer, + bufferSize, + "%s: %s %d: %s\n", + pLayerPrefix, + severity, + msgCode, + pMsg); + + fprintf(stderr, "%s", buffer); + fflush(stderr); + + OutputDebugStringA(buffer); + + return VK_FALSE; +} + +/* static */VKAPI_ATTR VkBool32 VKAPI_CALL VKRenderer::debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, + size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData) +{ + return ((VKRenderer*)pUserData)->handleDebugMessage(flags, objType, srcObject, location, msgCode, pLayerPrefix, pMsg); +} + +VkPipelineShaderStageCreateInfo VKRenderer::compileEntryPoint( + ShaderProgram::KernelDesc const& kernelDesc, + VkShaderStageFlagBits stage, + List& bufferOut) +{ + char const* dataBegin = (char const*) kernelDesc.codeBegin; + char const* dataEnd = (char const*) kernelDesc.codeEnd; + + // We need to make a copy of the code, since the Slang compiler + // will free the memory after a compile request is closed. + size_t codeSize = dataEnd - dataBegin; + + bufferOut.InsertRange(0, dataBegin, codeSize); + + char* codeBegin = bufferOut.Buffer(); + + VkShaderModuleCreateInfo moduleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; + moduleCreateInfo.pCode = (uint32_t*)codeBegin; + moduleCreateInfo.codeSize = codeSize; + + VkShaderModule module; + SLANG_VK_CHECK(m_api.vkCreateShaderModule(m_device, &moduleCreateInfo, nullptr, &module)); + + VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; + shaderStageCreateInfo.stage = stage; + + shaderStageCreateInfo.module = module; + shaderStageCreateInfo.pName = "main"; + + return shaderStageCreateInfo; +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! + +SlangResult VKRenderer::initialize(const Desc& desc, void* inWindowHandle) +{ + SLANG_RETURN_ON_FAIL(m_module.init()); + SLANG_RETURN_ON_FAIL(m_api.initGlobalProcs(m_module)); + + m_desc = desc; + + VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + applicationInfo.pApplicationName = "slang-render-test"; + applicationInfo.pEngineName = "slang-render-test"; + applicationInfo.apiVersion = VK_API_VERSION_1_0; + + char const* instanceExtensions[] = + { + VK_KHR_SURFACE_EXTENSION_NAME, + +#if SLANG_WINDOWS_FAMILY + VK_KHR_WIN32_SURFACE_EXTENSION_NAME, +#else + VK_KHR_XLIB_SURFACE_EXTENSION_NAME +#endif + +#if ENABLE_VALIDATION_LAYER + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, +#endif + }; + + VkInstance instance = VK_NULL_HANDLE; + + VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + instanceCreateInfo.pApplicationInfo = &applicationInfo; + + instanceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(instanceExtensions); + instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0]; + +#if ENABLE_VALIDATION_LAYER + const char* layerNames[] = { "VK_LAYER_LUNARG_standard_validation" }; + instanceCreateInfo.enabledLayerCount = SLANG_COUNT_OF(layerNames); + instanceCreateInfo.ppEnabledLayerNames = layerNames; +#endif + + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); + SLANG_RETURN_ON_FAIL(m_api.initInstanceProcs(instance)); + +#if ENABLE_VALIDATION_LAYER + VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + + VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; + debugCreateInfo.pfnCallback = &debugMessageCallback; + debugCreateInfo.pUserData = this; + debugCreateInfo.flags = debugFlags; + + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDebugReportCallbackEXT(instance, &debugCreateInfo, nullptr, &m_debugReportCallback)); +#endif + + uint32_t numPhysicalDevices = 0; + SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, nullptr)); + + List physicalDevices; + physicalDevices.SetSize(numPhysicalDevices); + SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, physicalDevices.Buffer())); + + // TODO: allow override of selected device + uint32_t selectedDeviceIndex = 0; + + SLANG_RETURN_ON_FAIL(m_api.initPhysicalDevice(physicalDevices[selectedDeviceIndex])); + + int queueFamilyIndex = m_api.findQueue(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); + assert(queueFamilyIndex >= 0); + + float queuePriority = 0.0f; + VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; + queueCreateInfo.queueFamilyIndex = queueFamilyIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + + char const* const deviceExtensions[] = + { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + deviceCreateInfo.queueCreateInfoCount = 1; + deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; + deviceCreateInfo.pEnabledFeatures = &m_api.m_deviceFeatures; + + deviceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(deviceExtensions); + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions; + + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDevice(m_api.m_physicalDevice, &deviceCreateInfo, nullptr, &m_device)); + SLANG_RETURN_ON_FAIL(m_api.initDeviceProcs(m_device)); + + { + VkQueue queue; + m_api.vkGetDeviceQueue(m_device, queueFamilyIndex, 0, &queue); + SLANG_RETURN_ON_FAIL(m_deviceQueue.init(m_api, queue, queueFamilyIndex)); + } + + // set up swap chain + + { + VulkanSwapChain::Desc desc; + VulkanSwapChain::PlatformDesc* platformDesc = nullptr; + + desc.init(); + desc.m_format = Format::RGBA_Unorm_UInt8; + +#if SLANG_WINDOWS_FAMILY + VulkanSwapChain::WinPlatformDesc winPlatformDesc; + winPlatformDesc.m_hinstance = ::GetModuleHandle(nullptr); + winPlatformDesc.m_hwnd = (HWND)inWindowHandle; + platformDesc = &winPlatformDesc; +#endif + + SLANG_RETURN_ON_FAIL(m_swapChain.init(&m_deviceQueue, desc, platformDesc)); + } + + // depth/stencil? + + // render pass? + + { + const int numRenderTargets = 1; + bool shouldClear = true; + bool shouldClearDepth = false; + bool shouldClearStencil = false; + bool hasDepthBuffer = false; + + Format depthFormat = Format::Unknown; + VkFormat colorFormat = m_swapChain.getVkFormat(); + + int numAttachments = 0; + // We need extra space if we have depth buffer + VkAttachmentDescription attachmentDesc[kMaxRenderTargets + 1] = {}; + for (int i = 0; i < numRenderTargets; ++i) + { + VkAttachmentDescription& dst = attachmentDesc[numAttachments ++]; + + dst.flags = 0; + dst.format = colorFormat; + dst.samples = VK_SAMPLE_COUNT_1_BIT; + dst.loadOp = shouldClear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + dst.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + dst.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + dst.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + if (hasDepthBuffer) + { + VkAttachmentDescription& dst = attachmentDesc[numAttachments++]; + + dst.flags = 0; + dst.format = VulkanUtil::getVkFormat(depthFormat); + dst.samples = VK_SAMPLE_COUNT_1_BIT; + dst.loadOp = shouldClearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + dst.stencilLoadOp = shouldClearStencil ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + dst.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + dst.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + VkAttachmentReference colorAttachments[kMaxRenderTargets] = {}; + for (int i = 0; i < numRenderTargets; ++i) + { + VkAttachmentReference& dst = colorAttachments[i]; + dst.attachment = i; + dst.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + + VkAttachmentReference depthAttachment = {}; + depthAttachment.attachment = numRenderTargets; + depthAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpassDesc = {}; + subpassDesc.flags = 0; + subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDesc.inputAttachmentCount = 0u; + subpassDesc.pInputAttachments = nullptr; + subpassDesc.colorAttachmentCount = numRenderTargets; + subpassDesc.pColorAttachments = colorAttachments; + subpassDesc.pResolveAttachments = nullptr; + subpassDesc.pDepthStencilAttachment = hasDepthBuffer ? &depthAttachment : nullptr; + subpassDesc.preserveAttachmentCount = 0u; + subpassDesc.pPreserveAttachments = nullptr; + + VkRenderPassCreateInfo renderPassCreateInfo = {}; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.attachmentCount = numAttachments; + renderPassCreateInfo.pAttachments = attachmentDesc; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subpassDesc; + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &m_renderPass)); + } + + // frame buffer + SLANG_RETURN_ON_FAIL(m_swapChain.createFrameBuffers(m_renderPass)); + + _beginRender(); + + return SLANG_OK; +} + +void VKRenderer::submitGpuWork() +{ + m_deviceQueue.flush(); +} + +void VKRenderer::waitForGpu() +{ + m_deviceQueue.flushAndWait(); +} + +void VKRenderer::setClearColor(const float color[4]) +{ + for (int ii = 0; ii < 4; ++ii) + m_clearColor[ii] = color[ii]; +} + +void VKRenderer::clearFrame() +{ +} + +void VKRenderer::presentFrame() +{ + _endRender(); + + const bool vsync = true; + m_swapChain.present(vsync); + + _beginRender(); +} + +TextureResource::Desc VKRenderer::getSwapChainTextureDesc() +{ + TextureResource::Desc desc; + desc.init2D(Resource::Type::Texture2D, Format::Unknown, m_desc.width, m_desc.height, 1); + return desc; +} + +SlangResult VKRenderer::captureScreenSurface(Surface& surfaceOut) +{ + return SLANG_FAIL; +} + +static VkBufferUsageFlagBits _calcBufferUsageFlags(Resource::BindFlag::Enum bind) +{ + typedef Resource::BindFlag BindFlag; + + switch (bind) + { + case BindFlag::VertexBuffer: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + case BindFlag::IndexBuffer: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + case BindFlag::ConstantBuffer: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + case BindFlag::StreamOutput: + case BindFlag::RenderTarget: + case BindFlag::DepthStencil: + { + assert(!"Not supported yet"); + return VkBufferUsageFlagBits(0); + } + case BindFlag::UnorderedAccess: return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; + case BindFlag::PixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + case BindFlag::NonPixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + default: return VkBufferUsageFlagBits(0); + } +} + +static VkBufferUsageFlagBits _calcBufferUsageFlags(int bindFlags) +{ + int dstFlags = 0; + while (bindFlags) + { + int lsb = bindFlags & -bindFlags; + dstFlags |= _calcBufferUsageFlags(Resource::BindFlag::Enum(lsb)); + bindFlags &= ~lsb; + } + return VkBufferUsageFlagBits(dstFlags); +} + +static VkBufferUsageFlags _calcBufferUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) +{ + VkBufferUsageFlags usage = _calcBufferUsageFlags(bindFlags); + + if (cpuAccessFlags & Resource::AccessFlag::Read) + { + // If it can be read from, set this + usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + } + if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData) + { + usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + } + + return usage; +} + +static VkImageUsageFlagBits _calcImageUsageFlags(Resource::BindFlag::Enum bind) +{ + typedef Resource::BindFlag BindFlag; + + switch (bind) + { + case BindFlag::RenderTarget: return VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + case BindFlag::DepthStencil: return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + case BindFlag::NonPixelShaderResource: + case BindFlag::PixelShaderResource: + { + // Ignore + return VkImageUsageFlagBits(0); + } + default: + { + assert(!"Unsupported"); + return VkImageUsageFlagBits(0); + } + } +} + +static VkImageUsageFlagBits _calcImageUsageFlags(int bindFlags) +{ + int dstFlags = 0; + while (bindFlags) + { + int lsb = bindFlags & -bindFlags; + dstFlags |= _calcImageUsageFlags(Resource::BindFlag::Enum(lsb)); + bindFlags &= ~lsb; + } + return VkImageUsageFlagBits(dstFlags); +} + +static VkImageUsageFlags _calcImageUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) +{ + VkImageUsageFlags usage = _calcImageUsageFlags(bindFlags); + + usage |= VK_IMAGE_USAGE_SAMPLED_BIT; + + if (cpuAccessFlags & Resource::AccessFlag::Read) + { + // If it can be read from, set this + usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData) + { + usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + + return usage; +} + +void VKRenderer::_transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout) +{ + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = desc.numMipLevels; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + + VkPipelineStageFlags sourceStage; + VkPipelineStageFlags destinationStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } + else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + else + { + assert(!"unsupported layout transition!"); + return; + } + + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + m_api.vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); +} + +Result VKRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData, TextureResource** outResource) +{ + TextureResource::Desc desc(descIn); + desc.setDefaults(initialUsage); + + const VkFormat format = VulkanUtil::getVkFormat(desc.format); + if (format == VK_FORMAT_UNDEFINED) + { + assert(!"Unhandled image format"); + return SLANG_FAIL; + } + + const int arraySize = desc.calcEffectiveArraySize(); + + RefPtr texture(new TextureResourceImpl(desc, initialUsage, &m_api)); + + // Create the image + { + VkImageCreateInfo imageInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; + + switch (desc.type) + { + case Resource::Type::Texture1D: + { + imageInfo.imageType = VK_IMAGE_TYPE_1D; + imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), 1, 1 }; + break; + } + case Resource::Type::Texture2D: + { + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 }; + break; + } + case Resource::Type::TextureCube: + { + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 }; + break; + } + case Resource::Type::Texture3D: + { + // Can't have an array and 3d texture + assert(desc.arraySize <= 1); + + imageInfo.imageType = VK_IMAGE_TYPE_3D; + imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), uint32_t(descIn.size.depth) }; + break; + } + default: + { + assert(!"Unhandled type"); + return SLANG_FAIL; + } + } + + imageInfo.mipLevels = desc.numMipLevels; + imageInfo.arrayLayers = arraySize; + + imageInfo.format = format; + + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.usage = _calcImageUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData); + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.flags = 0; // Optional + + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateImage(m_device, &imageInfo, nullptr, &texture->m_image)); + } + + VkMemoryRequirements memRequirements; + m_api.vkGetImageMemoryRequirements(m_device, texture->m_image, &memRequirements); + + // Allocate the memory + { + VkMemoryPropertyFlags reqMemoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + + VkMemoryAllocateInfo allocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; + + int memoryTypeIndex = m_api.findMemoryTypeIndex(memRequirements.memoryTypeBits, reqMemoryProperties); + assert(memoryTypeIndex >= 0); + + VkMemoryPropertyFlags actualMemoryProperites = m_api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; + + allocInfo.allocationSize = memRequirements.size; + allocInfo.memoryTypeIndex = memoryTypeIndex; + + SLANG_VK_RETURN_ON_FAIL(m_api.vkAllocateMemory(m_device, &allocInfo, nullptr, &texture->m_imageMemory)); + } + + // Bind the memory to the image + m_api.vkBindImageMemory(m_device, texture->m_image, texture->m_imageMemory, 0); + + if (initData) + { + List mipSizes; + + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + const int numMipMaps = desc.numMipLevels; + assert(initData->numMips == numMipMaps); + + // Calculate how large the buffer has to be + size_t bufferSize = 0; + // Calculate how large an array entry is + for (int j = 0; j < numMipMaps; ++j) + { + const TextureResource::Size mipSize = desc.size.calcMipSize(j); + + const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); + const int numRows = Surface::calcNumRows(desc.format, mipSize.height); + + mipSizes.Add(mipSize); + + bufferSize += (rowSizeInBytes * numRows) * mipSize.depth; + } + + + // Calculate the total size taking into account the array + bufferSize *= arraySize; + + Buffer uploadBuffer; + SLANG_RETURN_ON_FAIL(uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); + + assert(mipSizes.Count() == numMipMaps); + + // Copy into upload buffer + { + int subResourceIndex = 0; + + uint8_t* dstData; + m_api.vkMapMemory(m_device, uploadBuffer.m_memory, 0, bufferSize, 0, (void**)&dstData); + + for (int i = 0; i < arraySize; ++i) + { + for (int j = 0; j < int(mipSizes.Count()); ++j) + { + const auto& mipSize = mipSizes[j]; + + const ptrdiff_t srcRowStride = initData->mipRowStrides[j]; + const int dstRowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); + const int numRows = Surface::calcNumRows(desc.format, mipSize.height); + + for (int k = 0; k < mipSize.depth; k++) + { + const uint8_t* srcData = (const uint8_t*)(initData->subResources[subResourceIndex]); + + for (int l = 0; l < numRows; l++) + { + ::memcpy(dstData, srcData, dstRowSizeInBytes); + + dstData += dstRowSizeInBytes; + srcData += srcRowStride; + } + + subResourceIndex++; + } + } + } + + m_api.vkUnmapMemory(m_device, uploadBuffer.m_memory); + } + + _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + { + size_t srcOffset = 0; + for (int i = 0; i < arraySize; ++i) + { + for (int j = 0; j < int(mipSizes.Count()); ++j) + { + const auto& mipSize = mipSizes[j]; + + const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); + const int numRows = Surface::calcNumRows(desc.format, mipSize.height); + + // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkBufferImageCopy.html + // bufferRowLength and bufferImageHeight specify the data in buffer memory as a subregion of a larger two- or three-dimensional image, + // and control the addressing calculations of data in buffer memory. If either of these values is zero, that aspect of the buffer memory + // is considered to be tightly packed according to the imageExtent. + + VkBufferImageCopy region = {}; + + region.bufferOffset = srcOffset; + region.bufferRowLength = 0; //rowSizeInBytes; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = j; + region.imageSubresource.baseArrayLayer = i; + region.imageSubresource.layerCount = 1; + region.imageOffset = { 0, 0, 0 }; + region.imageExtent = { uint32_t(mipSize.width), uint32_t(mipSize.height), uint32_t(mipSize.depth) }; + + // Do the copy (do all depths in a single go) + m_api.vkCmdCopyBufferToImage(commandBuffer, uploadBuffer.m_buffer, texture->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + // Next + srcOffset += rowSizeInBytes * numRows * mipSize.depth; + } + } + } + + _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + m_deviceQueue.flushAndWait(); + } + + *outResource = texture.detach(); + return SLANG_OK; +} + +Result VKRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData, BufferResource** outResource) +{ + BufferResource::Desc desc(descIn); + desc.setDefaults(initialUsage); + + const size_t bufferSize = desc.sizeInBytes; + + VkMemoryPropertyFlags reqMemoryProperties = 0; + + VkBufferUsageFlags usage = _calcBufferUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData); + + switch (initialUsage) + { + case Resource::Usage::ConstantBuffer: + { + reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + } + default: break; + } + + RefPtr buffer(new BufferResourceImpl(initialUsage, desc, this)); + SLANG_RETURN_ON_FAIL(buffer->m_buffer.init(m_api, desc.sizeInBytes, usage, reqMemoryProperties)); + + if ((desc.cpuAccessFlags & Resource::AccessFlag::Write) || initData) + { + SLANG_RETURN_ON_FAIL(buffer->m_uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); + } + + if (initData) + { + // TODO: only create staging buffer if the memory type + // used for the buffer doesn't let us fill things in + // directly. + // Copy into staging buffer + void* mappedData = nullptr; + SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); + ::memcpy(mappedData, initData, bufferSize); + m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); + + // Copy from staging buffer to real buffer + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); + + //flushCommandBuffer(commandBuffer); + } + + *outResource = buffer.detach(); + return SLANG_OK; +} + + +VkFilter translateFilterMode(TextureFilteringMode mode) +{ + switch (mode) + { + default: + return VkFilter(0); + +#define CASE(SRC, DST) \ + case TextureFilteringMode::SRC: return VK_FILTER_##DST + + CASE(Point, NEAREST); + CASE(Linear, LINEAR); + +#undef CASE + } +} + +VkSamplerMipmapMode translateMipFilterMode(TextureFilteringMode mode) +{ + switch (mode) + { + default: + return VkSamplerMipmapMode(0); + +#define CASE(SRC, DST) \ + case TextureFilteringMode::SRC: return VK_SAMPLER_MIPMAP_MODE_##DST + + CASE(Point, NEAREST); + CASE(Linear, LINEAR); + +#undef CASE + } +} + +VkSamplerAddressMode translateAddressingMode(TextureAddressingMode mode) +{ + switch (mode) + { + default: + return VkSamplerAddressMode(0); + +#define CASE(SRC, DST) \ + case TextureAddressingMode::SRC: return VK_SAMPLER_ADDRESS_MODE_##DST + + CASE(Wrap, REPEAT); + CASE(ClampToEdge, CLAMP_TO_EDGE); + CASE(ClampToBorder, CLAMP_TO_BORDER); + CASE(MirrorRepeat, MIRRORED_REPEAT); + CASE(MirrorOnce, MIRROR_CLAMP_TO_EDGE); + +#undef CASE + } +} + +static VkCompareOp translateComparisonFunc(ComparisonFunc func) +{ + switch (func) + { + default: + // TODO: need to report failures + return VK_COMPARE_OP_ALWAYS; + +#define CASE(FROM, TO) \ + case ComparisonFunc::FROM: return VK_COMPARE_OP_##TO + + CASE(Never, NEVER); + CASE(Less, LESS); + CASE(Equal, EQUAL); + CASE(LessEqual, LESS_OR_EQUAL); + CASE(Greater, GREATER); + CASE(NotEqual, NOT_EQUAL); + CASE(GreaterEqual, GREATER_OR_EQUAL); + CASE(Always, ALWAYS); +#undef CASE + } +} + +Result VKRenderer::createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) +{ + VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + + samplerInfo.magFilter = translateFilterMode(desc.minFilter); + samplerInfo.minFilter = translateFilterMode(desc.magFilter); + + samplerInfo.addressModeU = translateAddressingMode(desc.addressU); + samplerInfo.addressModeV = translateAddressingMode(desc.addressV); + samplerInfo.addressModeW = translateAddressingMode(desc.addressW); + + samplerInfo.anisotropyEnable = desc.maxAnisotropy > 1; + samplerInfo.maxAnisotropy = (float) desc.maxAnisotropy; + + // TODO: support translation of border color... + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = desc.reductionOp == TextureReductionOp::Comparison; + samplerInfo.compareOp = translateComparisonFunc(desc.comparisonFunc); + samplerInfo.mipmapMode = translateMipFilterMode(desc.mipFilter); + + VkSampler sampler; + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &sampler)); + + RefPtr samplerImpl = new SamplerStateImpl(); + samplerImpl->m_sampler = sampler; + *outSampler = samplerImpl.detach(); + return SLANG_OK; +} + +Result VKRenderer::createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) +{ + assert(!"unimplemented"); + return SLANG_FAIL; +} + +Result VKRenderer::createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) +{ + auto resourceImpl = (BufferResourceImpl*) buffer; + + // TODO: These should come from the `ResourceView::Desc` + VkDeviceSize offset = 0; + VkDeviceSize size = resourceImpl->getDesc().sizeInBytes; + + // There are two different cases we need to think about for buffers. + // + // One is when we have a "uniform texel buffer" or "storage texel buffer," + // in which case we need to construct a `VkBufferView` to represent the + // formatting that is applied to the buffer. This case would correspond + // to a `textureBuffer` or `imageBuffer` in GLSL, and more or less to + // `Buffer<..>` or `RWBuffer<...>` in HLSL. + // + // The other case is a `storage buffer` which is the catch-all for any + // non-formatted R/W access to a buffer. In GLSL this is a `buffer { ... }` + // declaration, while in HLSL it covers a bunch of different `RW*Buffer` + // cases. In these cases we do *not* need a `VkBufferView`, but in + // order to be compatible with other APIs that require views for any + // potentially writable access, we will have to create one anyway. + // + // We will distinguish the two cases by looking at whether the view + // is being requested with a format or not. + // + + switch(desc.type) + { + default: + assert(!"unhandled"); + return SLANG_FAIL; + + case ResourceView::Type::UnorderedAccess: + // Is this a formatted view? + // + if(desc.format == Format::Unknown) + { + // Buffer usage that doesn't involve formatting doesn't + // require a view in Vulkan. + RefPtr viewImpl = new PlainBufferResourceViewImpl(); + viewImpl->m_buffer = resourceImpl; + viewImpl->offset = 0; + viewImpl->size = size; + *outView = viewImpl.detach(); + return SLANG_OK; + } + // + // If the view is formatted, then we need to handle + // it just like we would for a "sampled" buffer: + // + // FALLTHROUGH + case ResourceView::Type::ShaderResource: + { + VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO }; + + info.format = VulkanUtil::getVkFormat(desc.format); + info.buffer = resourceImpl->m_buffer.m_buffer; + info.offset = offset; + info.range = size; + + VkBufferView view; + SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &view)); + + RefPtr viewImpl = new TexelBufferResourceViewImpl(); + viewImpl->m_buffer = resourceImpl; + viewImpl->m_view = view; + *outView = viewImpl.detach(); + return SLANG_OK; + } + break; + } +} + +Result VKRenderer::createInputLayout(const InputElementDesc* elements, UInt numElements, InputLayout** outLayout) +{ + RefPtr layout(new InputLayoutImpl); + + List& dstVertexDescs = layout->m_vertexDescs; + + size_t vertexSize = 0; + dstVertexDescs.SetSize(numElements); + + for (UInt i = 0; i < numElements; ++i) + { + const InputElementDesc& srcDesc = elements[i]; + VkVertexInputAttributeDescription& dstDesc = dstVertexDescs[i]; + + dstDesc.location = uint32_t(i); + dstDesc.binding = 0; + dstDesc.format = VulkanUtil::getVkFormat(srcDesc.format); + if (dstDesc.format == VK_FORMAT_UNDEFINED) + { + return SLANG_FAIL; + } + + dstDesc.offset = uint32_t(srcDesc.offset); + + const size_t elementSize = RendererUtil::getFormatSize(srcDesc.format); + assert(elementSize > 0); + const size_t endElement = srcDesc.offset + elementSize; + + vertexSize = (vertexSize < endElement) ? endElement : vertexSize; + } + + // Work out the overall size + layout->m_vertexSize = int(vertexSize); + *outLayout = layout.detach(); + return SLANG_OK; +} + +void* VKRenderer::map(BufferResource* bufferIn, MapFlavor flavor) +{ + BufferResourceImpl* buffer = static_cast(bufferIn); + assert(buffer->m_mapFlavor == MapFlavor::Unknown); + + // Make sure everything has completed before reading... + m_deviceQueue.flushAndWait(); + + const size_t bufferSize = buffer->getDesc().sizeInBytes; + + switch (flavor) + { + case MapFlavor::WriteDiscard: + case MapFlavor::HostWrite: + { + if (!buffer->m_uploadBuffer.isInitialized()) + { + return nullptr; + } + + void* mappedData = nullptr; + SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); + buffer->m_mapFlavor = flavor; + return mappedData; + } + case MapFlavor::HostRead: + { + // Make sure there is space in the read buffer + buffer->m_readBuffer.SetSize(bufferSize); + + // create staging buffer + Buffer staging; + + SLANG_RETURN_NULL_ON_FAIL(staging.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); + + // Copy from real buffer to staging buffer + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_buffer.m_buffer, staging.m_buffer, 1, ©Info); + + m_deviceQueue.flushAndWait(); + + // Write out the data from the buffer + void* mappedData = nullptr; + SLANG_VK_CHECK(m_api.vkMapMemory(m_device, staging.m_memory, 0, bufferSize, 0, &mappedData)); + + ::memcpy(buffer->m_readBuffer.Buffer(), mappedData, bufferSize); + m_api.vkUnmapMemory(m_device, staging.m_memory); + + buffer->m_mapFlavor = flavor; + + return buffer->m_readBuffer.Buffer(); + } + default: + return nullptr; + } +} + +void VKRenderer::unmap(BufferResource* bufferIn) +{ + BufferResourceImpl* buffer = static_cast(bufferIn); + assert(buffer->m_mapFlavor != MapFlavor::Unknown); + + const size_t bufferSize = buffer->getDesc().sizeInBytes; + + switch (buffer->m_mapFlavor) + { + case MapFlavor::WriteDiscard: + case MapFlavor::HostWrite: + { + m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); + + // Copy from staging buffer to real buffer + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + VkBufferCopy copyInfo = {}; + copyInfo.size = bufferSize; + m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); + + // TODO: is this necessary? + //m_deviceQueue.flushAndWait(); + break; + } + default: break; + } + + // Mark as no longer mapped + buffer->m_mapFlavor = MapFlavor::Unknown; +} + +void VKRenderer::setPrimitiveTopology(PrimitiveTopology topology) +{ + m_primitiveTopology = VulkanUtil::getVkPrimitiveTopology(topology); +} + +void VKRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) +{ + { + const UInt num = startSlot + slotCount; + if (num > m_boundVertexBuffers.Count()) + { + m_boundVertexBuffers.SetSize(num); + } + } + + for (UInt i = 0; i < slotCount; i++) + { + BufferResourceImpl* buffer = static_cast(buffers[i]); + if (buffer) + { + assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer); + } + + BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i]; + boundBuffer.m_buffer = buffer; + boundBuffer.m_stride = int(strides[i]); + boundBuffer.m_offset = int(offsets[i]); + } +} + +void VKRenderer::setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset) +{ +} + +void VKRenderer::setDepthStencilTarget(ResourceView* depthStencilView) +{ +} + +void VKRenderer::setPipelineState(PipelineType pipelineType, PipelineState* state) +{ + m_currentPipeline = (PipelineStateImpl*)state; +} + +void VKRenderer::draw(UInt vertexCount, UInt startVertex = 0) +{ + auto pipeline = m_currentPipeline; + if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Graphics) + { + assert(!"Invalid render pipeline"); + return; + } + + SLANG_RETURN_VOID_ON_FAIL(_beginPass()); + + // Also create descriptor sets based on the given pipeline layout + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->m_pipeline); + + auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr(); + m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayoutImpl->m_pipelineLayout, + 0, pipelineLayoutImpl->m_descriptorSetCount, + &m_currentDescriptorSets[0], + 0, nullptr); + + // Bind the vertex buffer + if (m_boundVertexBuffers.Count() > 0 && m_boundVertexBuffers[0].m_buffer) + { + const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[0]; + + VkBuffer vertexBuffers[] = { boundVertexBuffer.m_buffer->m_buffer.m_buffer }; + VkDeviceSize offsets[] = { VkDeviceSize(boundVertexBuffer.m_offset) }; + + m_api.vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + } + + m_api.vkCmdDraw(commandBuffer, static_cast(vertexCount), 1, 0, 0); + + _endPass(); +} + +void VKRenderer::drawIndexed(UInt indexCount, UInt startIndex, UInt baseVertex) +{ +} + +void VKRenderer::dispatchCompute(int x, int y, int z) +{ + auto pipeline = m_currentPipeline; + if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Compute) + { + assert(!"Invalid compute pipeline"); + return; + } + + // Also create descriptor sets based on the given pipeline layout + VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); + + m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->m_pipeline); + + auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr(); + m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayoutImpl->m_pipelineLayout, + 0, pipelineLayoutImpl->m_descriptorSetCount, + &m_currentDescriptorSets[0], + 0, nullptr); + + m_api.vkCmdDispatch(commandBuffer, x, y, z); +} + +static VkImageViewType _calcImageViewType(TextureResource::Type type, const TextureResource::Desc& desc) +{ + switch (type) + { + case Resource::Type::Texture1D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D; + case Resource::Type::Texture2D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; + case Resource::Type::TextureCube: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE; + case Resource::Type::Texture3D: + { + // Can't have an array and 3d texture + assert(desc.arraySize <= 1); + if (desc.arraySize <= 1) + { + return VK_IMAGE_VIEW_TYPE_3D; + } + break; + } + default: break; + } + + return VK_IMAGE_VIEW_TYPE_MAX_ENUM; +} + +#if 0 +BindingState* VKRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) +{ + RefPtr bindingState(new BindingStateImpl(bindingStateDesc, &m_api)); + + const auto& srcBindings = bindingStateDesc.m_bindings; + const int numBindings = int(srcBindings.Count()); + + auto& dstDetails = bindingState->m_bindingDetails; + dstDetails.SetSize(numBindings); + + for (int i = 0; i < numBindings; ++i) + { + auto& dstDetail = dstDetails[i]; + const auto& srcBinding = srcBindings[i]; + + switch (srcBinding.bindingType) + { + case BindingType::Buffer: + { + if (!srcBinding.resource || !srcBinding.resource->isBuffer()) + { + assert(!"Needs to have a buffer resource set"); + return nullptr; + } + + BufferResourceImpl* bufferResource = static_cast(srcBinding.resource.Ptr()); + const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); + + if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) + { + // VkBufferView uav + + VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO }; + + info.format = VK_FORMAT_R32_SFLOAT; + // TODO: + // Not sure how to handle typeless? + if (bufferResourceDesc.elementSize == 0) + { + info.format = VK_FORMAT_R32_SFLOAT; // DXGI_FORMAT_R32_TYPELESS ? + } + + info.buffer = bufferResource->m_buffer.m_buffer; + info.offset = 0; + info.range = bufferResourceDesc.sizeInBytes; + + SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &dstDetail.m_uav)); + } + + // TODO: Setup views. + // VkImageView srv + + + break; + } + case BindingType::Sampler: + { + VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + + samplerInfo.anisotropyEnable = VK_FALSE; + samplerInfo.maxAnisotropy = 1; + + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + + SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &dstDetail.m_sampler)); + + break; + } + case BindingType::Texture: + { + if (!srcBinding.resource || !srcBinding.resource->isTexture()) + { + assert(!"Needs to have a texture resource set"); + return nullptr; + } + + TextureResourceImpl* textureResource = static_cast(srcBinding.resource.Ptr()); + const TextureResource::Desc& texDesc = textureResource->getDesc(); + + VkImageViewType imageViewType = _calcImageViewType(textureResource->getType(), texDesc); + if (imageViewType == VK_IMAGE_VIEW_TYPE_MAX_ENUM) + { + assert(!"Invalid view type"); + return nullptr; + } + const VkFormat format = VulkanUtil::getVkFormat(texDesc.format); + if (format == VK_FORMAT_UNDEFINED) + { + assert(!"Unhandled image format"); + return nullptr; + } + + // Create the image view + + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = textureResource->m_image; + viewInfo.viewType = imageViewType; + viewInfo.format = format; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = 1; + + viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateImageView(m_device, &viewInfo, nullptr, &dstDetail.m_srv)); + + break; + } + case BindingType::CombinedTextureSampler: + { + assert(!"not implemented"); + return nullptr; + } + } + } + + return bindingState.detach();; +} +#endif + +static VkDescriptorType translateDescriptorType(DescriptorSlotType type) +{ + switch(type) + { + default: + return VK_DESCRIPTOR_TYPE_MAX_ENUM; + +#define CASE(SRC, DST) \ + case DescriptorSlotType::SRC: return VK_DESCRIPTOR_TYPE_##DST + + CASE(Sampler, SAMPLER); + CASE(CombinedImageSampler, COMBINED_IMAGE_SAMPLER); + CASE(SampledImage, SAMPLED_IMAGE); + CASE(StorageImage, STORAGE_IMAGE); + CASE(UniformTexelBuffer, UNIFORM_TEXEL_BUFFER); + CASE(StorageTexelBuffer, STORAGE_TEXEL_BUFFER); + CASE(UniformBuffer, UNIFORM_BUFFER); + CASE(StorageBuffer, STORAGE_BUFFER); + CASE(DynamicUniformBuffer, UNIFORM_BUFFER_DYNAMIC); + CASE(DynamicStorageBuffer, STORAGE_BUFFER_DYNAMIC); + CASE(InputAttachment, INPUT_ATTACHMENT); + +#undef CASE + } +} + +Result VKRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) +{ + RefPtr descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(m_api); + + Slang::List dstBindings; + + uint32_t descriptorCountForTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE] = { 0, }; + + UInt rangeCount = desc.slotRangeCount; + for(UInt rr = 0; rr < rangeCount; ++rr) + { + auto& srcRange = desc.slotRanges[rr]; + + VkDescriptorType dstDescriptorType = translateDescriptorType(srcRange.type); + + VkDescriptorSetLayoutBinding dstBinding; + dstBinding.binding = rr; + dstBinding.descriptorType = dstDescriptorType; + dstBinding.descriptorCount = srcRange.count; + dstBinding.stageFlags = VK_SHADER_STAGE_ALL; + dstBinding.pImmutableSamplers = nullptr; + + descriptorCountForTypes[dstDescriptorType] += srcRange.count; + + dstBindings.Add(dstBinding); + + DescriptorSetLayoutImpl::RangeInfo rangeInfo; + rangeInfo.descriptorType = dstDescriptorType; + descriptorSetLayoutImpl->m_ranges.Add(rangeInfo); + } + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.Count()); + descriptorSetLayoutInfo.pBindings = dstBindings.Buffer(); + + VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; + SLANG_VK_CHECK(m_api.vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout)); + + // Create a pool while we are at it, to allocate descriptor sets of this type. + + VkDescriptorPoolSize poolSizes[VK_DESCRIPTOR_TYPE_RANGE_SIZE]; + uint32_t poolSizeCount = 0; + for (int ii = 0; ii < SLANG_COUNT_OF(descriptorCountForTypes); ++ii) + { + auto descriptorCount = descriptorCountForTypes[ii]; + if (descriptorCount > 0) + { + poolSizes[poolSizeCount].type = VkDescriptorType(ii); + poolSizes[poolSizeCount].descriptorCount = descriptorCount; + poolSizeCount++; + } + } + + VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size. + descriptorPoolInfo.poolSizeCount = poolSizeCount; + descriptorPoolInfo.pPoolSizes = &poolSizes[0]; + + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; + SLANG_VK_CHECK(m_api.vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + descriptorSetLayoutImpl->m_descriptorSetLayout = descriptorSetLayout; + descriptorSetLayoutImpl->m_descriptorPool = descriptorPool; + + *outLayout = descriptorSetLayoutImpl.detach(); + return SLANG_OK; +} + +Result VKRenderer::createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) +{ + UInt descriptorSetCount = desc.descriptorSetCount; + + VkDescriptorSetLayout descriptorSetLayouts[kMaxDescriptorSets]; + for(UInt ii = 0; ii < descriptorSetCount; ++ii) + { + descriptorSetLayouts[ii] = ((DescriptorSetLayoutImpl*) desc.descriptorSets[ii].layout)->m_descriptorSetLayout; + } + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + pipelineLayoutInfo.setLayoutCount = desc.descriptorSetCount; + pipelineLayoutInfo.pSetLayouts = &descriptorSetLayouts[0]; + + VkPipelineLayout pipelineLayout; + SLANG_VK_CHECK(m_api.vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); + + RefPtr pipelineLayoutImpl = new PipelineLayoutImpl(m_api); + pipelineLayoutImpl->m_pipelineLayout = pipelineLayout; + pipelineLayoutImpl->m_descriptorSetCount = descriptorSetCount; + + *outLayout = pipelineLayoutImpl.detach(); + return SLANG_OK; +} + +Result VKRenderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) +{ + auto layoutImpl = (DescriptorSetLayoutImpl*)layout; + + VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + descriptorSetAllocInfo.descriptorPool = layoutImpl->m_descriptorPool; + descriptorSetAllocInfo.descriptorSetCount = 1; + descriptorSetAllocInfo.pSetLayouts = &layoutImpl->m_descriptorSetLayout; + + VkDescriptorSet descriptorSet; + SLANG_VK_CHECK(m_api.vkAllocateDescriptorSets(m_device, &descriptorSetAllocInfo, &descriptorSet)); + + RefPtr descriptorSetImpl = new DescriptorSetImpl(this); + descriptorSetImpl->m_layout = layoutImpl; + descriptorSetImpl->m_descriptorSet = descriptorSet; + *outDescriptorSet = descriptorSetImpl.detach(); + return SLANG_OK; +} + +void VKRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* buffer) +{ + auto bufferImpl = (BufferResourceImpl*)buffer; + + VkDescriptorBufferInfo bufferInfo = {}; + bufferInfo.buffer = bufferImpl->m_buffer.m_buffer; + bufferInfo.offset = 0; + bufferInfo.range = bufferImpl->getDesc().sizeInBytes; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.dstSet = m_descriptorSet; + writeInfo.dstBinding = range; + writeInfo.dstArrayElement = index; + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; + writeInfo.pBufferInfo = &bufferInfo; + + m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); +} + +void VKRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view) +{ + auto viewImpl = (ResourceViewImpl*)view; + switch (viewImpl->m_type) + { + case ResourceViewImpl::ViewType::Texture: + { + auto textureViewImpl = (TextureResourceViewImpl*)viewImpl; + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageView = textureViewImpl->m_view; + imageInfo.imageLayout = textureViewImpl->m_layout; + // imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.dstSet = m_descriptorSet; + writeInfo.dstBinding = range; + writeInfo.dstArrayElement = index; + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; + writeInfo.pImageInfo = &imageInfo; + + m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); + } + break; + + case ResourceViewImpl::ViewType::TexelBuffer: + { + auto bufferViewImpl = (TexelBufferResourceViewImpl*)viewImpl; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.dstSet = m_descriptorSet; + writeInfo.dstBinding = range; + writeInfo.dstArrayElement = index; + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; + writeInfo.pTexelBufferView = &bufferViewImpl->m_view; + + m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); + } + break; + + case ResourceViewImpl::ViewType::PlainBuffer: + { + auto bufferViewImpl = (PlainBufferResourceViewImpl*) viewImpl; + + VkDescriptorBufferInfo bufferInfo = {}; + bufferInfo.buffer = bufferViewImpl->m_buffer->m_buffer.m_buffer; + bufferInfo.offset = bufferViewImpl->offset; + bufferInfo.range = bufferViewImpl->size; + + VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeInfo.dstSet = m_descriptorSet; + writeInfo.dstBinding = range; + writeInfo.dstArrayElement = index; + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType; + writeInfo.pBufferInfo = &bufferInfo; + + m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr); + } + break; + + } +} + +void VKRenderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* sampler) +{ +} + +void VKRenderer::DescriptorSetImpl::setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) +{ +} + +void VKRenderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) +{ + // Ideally this should eventually be as simple as: + // + // m_api.vkCmdBindDescriptorSets( + // commandBuffer, + // translatePipelineBindPoint(pipelineType), + // layout->m_pipelineLayout, + // index, + // 1, + // ((DescriptorSetImpl*) descriptorSet)->m_descriptorSet, + // 0, + // nullptr); + // + // For now we are lazily flushing state right before drawing, so + // we will hang onto the parameters that were passed in and then + // use them later. + // + + auto descriptorSetImpl = (DescriptorSetImpl*)descriptorSet; + m_currentDescriptorSetImpls[index] = descriptorSetImpl; + m_currentDescriptorSets[index] = descriptorSetImpl->m_descriptorSet; +} + +Result VKRenderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) +{ + ShaderProgramImpl* impl = new ShaderProgramImpl(desc.pipelineType); + if( desc.pipelineType == PipelineType::Compute) + { + auto computeKernel = desc.findKernel(StageType::Compute); + impl->m_compute = compileEntryPoint(*computeKernel, VK_SHADER_STAGE_COMPUTE_BIT, impl->m_buffers[0]); + } + else + { + auto vertexKernel = desc.findKernel(StageType::Vertex); + auto fragmentKernel = desc.findKernel(StageType::Fragment); + + impl->m_vertex = compileEntryPoint(*vertexKernel, VK_SHADER_STAGE_VERTEX_BIT, impl->m_buffers[0]); + impl->m_fragment = compileEntryPoint(*fragmentKernel, VK_SHADER_STAGE_FRAGMENT_BIT, impl->m_buffers[1]); + } + *outProgram = impl; + return SLANG_OK; +} + +Result VKRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +{ + VkPipelineCache pipelineCache = VK_NULL_HANDLE; + + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; + + int width = desc.framebufferWidth; + int height = desc.framebufferHeight; + + // Shader Stages + // + // Currently only handles vertex/fragment. + + static const uint32_t kMaxShaderStages = 2; + VkPipelineShaderStageCreateInfo shaderStages[kMaxShaderStages]; + + uint32_t shaderStageCount = 0; + shaderStages[shaderStageCount++] = programImpl->m_vertex; + shaderStages[shaderStageCount++] = programImpl->m_fragment; + + // VertexBuffer/s + // Currently only handles one + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 0; + vertexInputInfo.vertexAttributeDescriptionCount = 0; + + VkVertexInputBindingDescription vertexInputBindingDescription; + + if (inputLayoutImpl) + { + vertexInputBindingDescription.binding = 0; + vertexInputBindingDescription.stride = inputLayoutImpl->m_vertexSize; + vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + const auto& srcAttributeDescs = inputLayoutImpl->m_vertexDescs; + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription; + + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(srcAttributeDescs.Count()); + vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.Buffer(); + } + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport = {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float)width; + viewport.height = (float)height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor = {}; + scissor.offset = { 0, 0 }; + scissor.extent = { uint32_t(width), uint32_t(height) }; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.pViewports = &viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout; + pipelineInfo.renderPass = m_renderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + + VkPipeline pipeline = VK_NULL_HANDLE; + SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline)); + + RefPtr pipelineStateImpl; + pipelineStateImpl->m_pipeline = pipeline; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_shaderProgram = programImpl; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + +Result VKRenderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +{ + VkPipelineCache pipelineCache = VK_NULL_HANDLE; + + auto programImpl = (ShaderProgramImpl*) desc.program; + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; + + VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; + computePipelineInfo.stage = programImpl->m_compute; + computePipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout; + + VkPipeline pipeline = VK_NULL_HANDLE; + SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline)); + + RefPtr pipelineStateImpl = new PipelineStateImpl(m_api); + pipelineStateImpl->m_pipeline = pipeline; + pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl; + pipelineStateImpl->m_shaderProgram = programImpl; + *outState = pipelineStateImpl.detach(); + return SLANG_OK; +} + + +#if 0 + else if (m_currentProgram->m_pipelineType == PipelineType::Graphics) + { + // Create the graphics pipeline + + const int width = m_swapChain.getWidth(); + const int height = m_swapChain.getHeight(); + + + + + + // + + + } + else + { + assert(!"Unhandled program type"); + return SLANG_FAIL; + } + + pipelineOut = pipeline; + return SLANG_OK; + + +#endif + +} // renderer_test diff --git a/tools/gfx/render-vk.h b/tools/gfx/render-vk.h new file mode 100644 index 000000000..14a8e403a --- /dev/null +++ b/tools/gfx/render-vk.h @@ -0,0 +1,10 @@ +// render-vk.h +#pragma once + +namespace gfx { + +class Renderer; + +Renderer* createVKRenderer(); + +} // gfx diff --git a/tools/gfx/render.cpp b/tools/gfx/render.cpp new file mode 100644 index 000000000..8f887b491 --- /dev/null +++ b/tools/gfx/render.cpp @@ -0,0 +1,391 @@ +// render.cpp +#include "render.h" + +#include "../../source/core/slang-math.h" + +namespace gfx { +using namespace Slang; + +/* static */const Resource::BindFlag::Enum Resource::s_requiredBinding[] = +{ + BindFlag::VertexBuffer, // VertexBuffer + BindFlag::IndexBuffer, // IndexBuffer + BindFlag::ConstantBuffer, // ConstantBuffer + BindFlag::StreamOutput, // StreamOut + BindFlag::RenderTarget, // RenderTager + BindFlag::DepthStencil, // DepthRead + BindFlag::DepthStencil, // DepthWrite + BindFlag::UnorderedAccess, // UnorderedAccess + BindFlag::PixelShaderResource, // PixelShaderResource + BindFlag::NonPixelShaderResource, // NonPixelShaderResource + BindFlag::Enum(BindFlag::PixelShaderResource | BindFlag::NonPixelShaderResource), // GenericRead +}; + + +/* static */void Resource::compileTimeAsserts() +{ + SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_requiredBinding) == int(Usage::CountOf)); +} + +static const Resource::DescBase s_emptyDescBase = {}; + +const Resource::DescBase& Resource::getDescBase() const +{ + if (isBuffer()) + { + return static_cast(this)->getDesc(); + } + else if (isTexture()) + { + return static_cast(this)->getDesc(); + } + return s_emptyDescBase; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RendererUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +/* static */const uint8_t RendererUtil::s_formatSize[] = +{ + 0, // Unknown, + + uint8_t(sizeof(float) * 4), // RGBA_Float32, + uint8_t(sizeof(float) * 3), // RGB_Float32, + uint8_t(sizeof(float) * 2), // RG_Float32, + uint8_t(sizeof(float) * 1), // R_Float32, + + uint8_t(sizeof(uint32_t)), // RGBA_Unorm_UInt8, + + uint8_t(sizeof(uint32_t)), // R_UInt32, + + uint8_t(sizeof(float)), // D_Float32, + uint8_t(sizeof(uint32_t)), // D_Unorm24_S8, +}; + +/* static */const BindingStyle RendererUtil::s_rendererTypeToBindingStyle[] = +{ + BindingStyle::Unknown, // Unknown, + BindingStyle::DirectX, // DirectX11, + BindingStyle::DirectX, // DirectX12, + BindingStyle::OpenGl, // OpenGl, + BindingStyle::Vulkan, // Vulkan +}; + +/* static */void RendererUtil::compileTimeAsserts() +{ + SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_formatSize) == int(Format::CountOf)); + SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_rendererTypeToBindingStyle) == int(RendererType::CountOf)); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!! BindingState::Desc !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +#if 0 +void BindingState::Desc::addSampler(const SamplerDesc& desc, const RegisterRange& registerRange) +{ + int descIndex = int(m_samplerDescs.Count()); + m_samplerDescs.Add(desc); + + Binding binding; + binding.bindingType = BindingType::Sampler; + binding.resource = nullptr; + binding.registerRange = registerRange; + binding.descIndex = descIndex; + + m_bindings.Add(binding); +} + +void BindingState::Desc::addResource(BindingType bindingType, Resource* resource, const RegisterRange& registerRange) +{ + assert(resource); + + Binding binding; + binding.bindingType = bindingType; + binding.resource = resource; + binding.descIndex = -1; + binding.registerRange = registerRange; + m_bindings.Add(binding); +} + +void BindingState::Desc::addCombinedTextureSampler(TextureResource* resource, const SamplerDesc& samplerDesc, const RegisterRange& registerRange) +{ + assert(resource); + + int samplerDescIndex = int(m_samplerDescs.Count()); + m_samplerDescs.Add(samplerDesc); + + Binding binding; + binding.bindingType = BindingType::CombinedTextureSampler; + binding.resource = resource; + binding.descIndex = samplerDescIndex; + binding.registerRange = registerRange; + m_bindings.Add(binding); +} + +void BindingState::Desc::clear() +{ + m_bindings.Clear(); + m_samplerDescs.Clear(); + m_numRenderTargets = 1; +} + +int BindingState::Desc::findBindingIndex(Resource::BindFlag::Enum bindFlag, int registerIndex) const +{ + const int numBindings = int(m_bindings.Count()); + for (int i = 0; i < numBindings; ++i) + { + const Binding& binding = m_bindings[i]; + if (binding.resource && (binding.resource->getDescBase().bindFlags & bindFlag) != 0) + { + if (binding.registerRange.hasRegister(registerIndex)) + { + return i; + } + } + } + + return -1; +} +#endif + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!! TextureResource::Size !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +int TextureResource::Size::calcMaxDimension(Type type) const +{ + switch (type) + { + case Resource::Type::Texture1D: return this->width; + case Resource::Type::Texture3D: return std::max(std::max(this->width, this->height), this->depth); + case Resource::Type::TextureCube: // fallthru + case Resource::Type::Texture2D: + { + return std::max(this->width, this->height); + } + default: return 0; + } +} + +TextureResource::Size TextureResource::Size::calcMipSize(int mipLevel) const +{ + Size size; + size.width = TextureResource::calcMipSize(this->width, mipLevel); + size.height = TextureResource::calcMipSize(this->height, mipLevel); + size.depth = TextureResource::calcMipSize(this->depth, mipLevel); + return size; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! BufferResource::Desc !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +void BufferResource::Desc::setDefaults(Usage initialUsage) +{ + if (this->bindFlags == 0) + { + this->bindFlags = Resource::s_requiredBinding[int(initialUsage)]; + } +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! TextureResource::Desc !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +int TextureResource::Desc::calcNumMipLevels() const +{ + const int maxDimensionSize = this->size.calcMaxDimension(type); + return (maxDimensionSize > 0) ? (Math::Log2Floor(maxDimensionSize) + 1) : 0; +} + +int TextureResource::Desc::calcNumSubResources() const +{ + const int numMipMaps = (this->numMipLevels > 0) ? this->numMipLevels : calcNumMipLevels(); + const int arrSize = (this->arraySize > 0) ? this->arraySize : 1; + + switch (type) + { + case Resource::Type::Texture1D: + case Resource::Type::Texture2D: + { + return numMipMaps * arrSize; + } + case Resource::Type::Texture3D: + { + // can't have arrays of 3d textures + assert(this->arraySize <= 1); + return numMipMaps * this->size.depth; + } + case Resource::Type::TextureCube: + { + // There are 6 faces to a cubemap + return numMipMaps * arrSize * 6; + } + default: return 0; + } +} + +void TextureResource::Desc::fixSize() +{ + switch (type) + { + case Resource::Type::Texture1D: + { + this->size.height = 1; + this->size.depth = 1; + break; + } + case Resource::Type::TextureCube: + case Resource::Type::Texture2D: + { + this->size.depth = 1; + break; + } + case Resource::Type::Texture3D: + { + // Can't have an array + this->arraySize = 0; + break; + } + default: break; + } +} + +void TextureResource::Desc::setDefaults(Usage initialUsage) +{ + fixSize(); + if (this->bindFlags == 0) + { + this->bindFlags = Resource::s_requiredBinding[int(initialUsage)]; + } + if (this->numMipLevels <= 0) + { + this->numMipLevels = calcNumMipLevels(); + } +} + +int TextureResource::Desc::calcEffectiveArraySize() const +{ + const int arrSize = (this->arraySize > 0) ? this->arraySize : 1; + + switch (type) + { + case Resource::Type::Texture1D: // fallthru + case Resource::Type::Texture2D: + { + return arrSize; + } + case Resource::Type::TextureCube: return arrSize * 6; + case Resource::Type::Texture3D: return 1; + default: return 0; + } +} + +void TextureResource::Desc::init(Type typeIn) +{ + this->type = typeIn; + this->size.init(); + + this->format = Format::Unknown; + this->arraySize = 0; + this->numMipLevels = 0; + this->sampleDesc.init(); + + this->bindFlags = 0; + this->cpuAccessFlags = 0; +} + +void TextureResource::Desc::init1D(Format formatIn, int widthIn, int numMipMapsIn) +{ + this->type = Type::Texture1D; + this->size.init(widthIn); + + this->format = formatIn; + this->arraySize = 0; + this->numMipLevels = numMipMapsIn; + this->sampleDesc.init(); + + this->bindFlags = 0; + this->cpuAccessFlags = 0; +} + +void TextureResource::Desc::init2D(Type typeIn, Format formatIn, int widthIn, int heightIn, int numMipMapsIn) +{ + assert(typeIn == Type::Texture2D || typeIn == Type::TextureCube); + + this->type = typeIn; + this->size.init(widthIn, heightIn); + + this->format = formatIn; + this->arraySize = 0; + this->numMipLevels = numMipMapsIn; + this->sampleDesc.init(); + + this->bindFlags = 0; + this->cpuAccessFlags = 0; +} + +void TextureResource::Desc::init3D(Format formatIn, int widthIn, int heightIn, int depthIn, int numMipMapsIn) +{ + this->type = Type::Texture3D; + this->size.init(widthIn, heightIn, depthIn); + + this->format = formatIn; + this->arraySize = 0; + this->numMipLevels = numMipMapsIn; + this->sampleDesc.init(); + + this->bindFlags = 0; + this->cpuAccessFlags = 0; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! RennderUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +ProjectionStyle RendererUtil::getProjectionStyle(RendererType type) +{ + switch (type) + { + case RendererType::DirectX11: + case RendererType::DirectX12: + { + return ProjectionStyle::DirectX; + } + case RendererType::OpenGl: return ProjectionStyle::OpenGl; + case RendererType::Vulkan: return ProjectionStyle::Vulkan; + case RendererType::Unknown: return ProjectionStyle::Unknown; + default: + { + assert(!"Unhandled type"); + return ProjectionStyle::Unknown; + } + } +} + +/* static */void RendererUtil::getIdentityProjection(ProjectionStyle style, float projMatrix[16]) +{ + switch (style) + { + case ProjectionStyle::DirectX: + case ProjectionStyle::OpenGl: + { + static const float kIdentity[] = + { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }; + ::memcpy(projMatrix, kIdentity, sizeof(kIdentity)); + break; + } + case ProjectionStyle::Vulkan: + { + static const float kIdentity[] = + { + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }; + ::memcpy(projMatrix, kIdentity, sizeof(kIdentity)); + break; + } + default: + { + assert(!"Not handled"); + } + } +} + +} // renderer_test diff --git a/tools/gfx/render.h b/tools/gfx/render.h new file mode 100644 index 000000000..b43152b68 --- /dev/null +++ b/tools/gfx/render.h @@ -0,0 +1,869 @@ +// render.h +#pragma once + +#include "window.h" + +//#include "shader-input-layout.h" + +#include "../../slang-com-helper.h" + +#include "../../source/core/smart-pointer.h" +#include "../../source/core/list.h" +#include "../../source/core/dictionary.h" + +namespace gfx { + +using Slang::RefObject; +using Slang::RefPtr; +using Slang::Dictionary; +using Slang::GetHashCode; +using Slang::combineHash; +using Slang::List; + +typedef SlangResult Result; + +// Had to move here, because Options needs types defined here +typedef intptr_t Int; +typedef uintptr_t UInt; + +// pre declare types +class Surface; + +// Declare opaque type +class InputLayout: public Slang::RefObject +{ + public: +}; + +enum class PipelineType +{ + Unknown, + Graphics, + Compute, + CountOf, +}; + +enum class StageType +{ + Unknown, + Vertex, + Hull, + Domain, + Geometry, + Fragment, + Compute, + CountOf, +}; + +enum class RendererType +{ + Unknown, + DirectX11, + DirectX12, + OpenGl, + Vulkan, + CountOf, +}; + +enum class ProjectionStyle +{ + Unknown, + OpenGl, + DirectX, + Vulkan, + CountOf, +}; + +/// The style of the binding +enum class BindingStyle +{ + Unknown, + DirectX, + OpenGl, + Vulkan, + CountOf, +}; + +class ShaderProgram: public Slang::RefObject +{ +public: + + struct KernelDesc + { + StageType stage; + void const* codeBegin; + void const* codeEnd; + + UInt getCodeSize() const { return (char const*)codeEnd - (char const*)codeBegin; } + }; + + struct Desc + { + PipelineType pipelineType; + KernelDesc const* kernels; + Int kernelCount; + + /// Find and return the kernel for `stage`, if present. + KernelDesc const* findKernel(StageType stage) const + { + for(Int ii = 0; ii < kernelCount; ++ii) + if(kernels[ii].stage == stage) + return &kernels[ii]; + return nullptr; + } + }; +}; + +struct ShaderCompileRequest +{ + struct SourceInfo + { + char const* path; + + // The data may either be source text (in which + // case it can be assumed to be nul-terminated with + // `dataEnd` pointing at the terminator), or + // raw binary data (in which case `dataEnd` points + // at the end of the buffer). + char const* dataBegin; + char const* dataEnd; + }; + + struct EntryPoint + { + char const* name = nullptr; + SourceInfo source; + }; + + SourceInfo source; + EntryPoint vertexShader; + EntryPoint fragmentShader; + EntryPoint computeShader; + Slang::List entryPointTypeArguments; +}; + +/// Different formats of things like pixels or elements of vertices +/// NOTE! Any change to this type (adding, removing, changing order) - must also be reflected in changes to RendererUtil +enum class Format +{ + Unknown, + + RGBA_Float32, + RGB_Float32, + RG_Float32, + R_Float32, + + RGBA_Unorm_UInt8, + + R_UInt32, + + D_Float32, + D_Unorm24_S8, + + CountOf, +}; + +struct InputElementDesc +{ + char const* semanticName; + UInt semanticIndex; + Format format; + UInt offset; +}; + +enum class MapFlavor +{ + Unknown, ///< Unknown mapping type + HostRead, + HostWrite, + WriteDiscard, +}; + +enum class PrimitiveTopology +{ + TriangleList, +}; + +class Resource: public Slang::RefObject +{ + public: + + /// The type of resource. + /// NOTE! The order needs to be such that all texture types are at or after Texture1D (otherwise isTexture won't work correctly) + enum class Type + { + Unknown, ///< Unknown + Buffer, ///< A buffer (like a constant/index/vertex buffer) + Texture1D, ///< A 1d texture + Texture2D, ///< A 2d texture + Texture3D, ///< A 3d texture + TextureCube, ///< A cubemap consists of 6 Texture2D like faces + CountOf, + }; + + /// Describes how a resource is to be used + enum class Usage + { + Unknown = -1, + VertexBuffer = 0, + IndexBuffer, + ConstantBuffer, + StreamOutput, + RenderTarget, + DepthRead, + DepthWrite, + UnorderedAccess, + PixelShaderResource, + NonPixelShaderResource, + GenericRead, + CountOf, + }; + + /// Binding flags describe all of the ways a resource can be bound - and therefore used + struct BindFlag + { + enum Enum + { + VertexBuffer = 0x001, + IndexBuffer = 0x002, + ConstantBuffer = 0x004, + StreamOutput = 0x008, + RenderTarget = 0x010, + DepthStencil = 0x020, + UnorderedAccess = 0x040, + PixelShaderResource = 0x080, + NonPixelShaderResource = 0x100, + }; + }; + + /// Combinations describe how a resource can be accessed (typically by the host/cpu) + struct AccessFlag + { + enum Enum + { + Read = 0x1, + Write = 0x2 + }; + }; + + /// Base class for Descs + struct DescBase + { + bool canBind(BindFlag::Enum bindFlag) const { return (bindFlags & bindFlag) != 0; } + bool hasCpuAccessFlag(AccessFlag::Enum accessFlag) { return (cpuAccessFlags & accessFlag) != 0; } + + Type type = Type::Unknown; + + int bindFlags = 0; ///< Combination of Resource::BindFlag or 0 (and will use initialUsage to set) + int cpuAccessFlags = 0; ///< Combination of Resource::AccessFlag + }; + + /// Get the type + SLANG_FORCE_INLINE Type getType() const { return m_type; } + /// True if it's a texture derived type + SLANG_FORCE_INLINE bool isTexture() const { return int(m_type) >= int(Type::Texture1D); } + /// True if it's a buffer derived type + SLANG_FORCE_INLINE bool isBuffer() const { return m_type == Type::Buffer; } + + /// Get the descBase + const DescBase& getDescBase() const; + /// Returns true if can bind with flag + bool canBind(BindFlag::Enum bindFlag) const { return getDescBase().canBind(bindFlag); } + + /// For a usage gives the required binding flags + static const BindFlag::Enum s_requiredBinding[]; /// Maps Usage to bind flags required + + protected: + Resource(Type type): + m_type(type) + {} + + static void compileTimeAsserts(); + + Type m_type; +}; + +class BufferResource: public Resource +{ + public: + typedef Resource Parent; + + struct Desc: public DescBase + { + void init(size_t sizeInBytesIn) + { + sizeInBytes = sizeInBytesIn; + elementSize = 0; + format = Format::Unknown; + } + /// Set up default parameters based on usage + void setDefaults(Usage initialUsage); + + size_t sizeInBytes; ///< Total size in bytes + int elementSize; ///< Get the element stride. If > 0, this is a structured buffer + Format format; + }; + + /// Get the buffer description + SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } + + /// Ctor + BufferResource(const Desc& desc): + Parent(Type::Buffer), + m_desc(desc) + { + } + + protected: + Desc m_desc; +}; + +class TextureResource: public Resource +{ + public: + typedef Resource Parent; + + struct SampleDesc + { + void init() + { + numSamples = 1; + quality = 0; + } + int numSamples; ///< Number of samples per pixel + int quality; ///< The quality measure for the samples + }; + + struct Size + { + void init() + { + width = height = depth = 1; + } + void init(int widthIn, int heightIn = 1, int depthIn = 1) + { + width = widthIn; + height = heightIn; + depth = depthIn; + } + /// Given the type works out the maximum dimension size + int calcMaxDimension(Type type) const; + /// Given a size, calculates the size at a mip level + Size calcMipSize(int mipLevel) const; + + int width; ///< Width in pixels + int height; ///< Height in pixels (if 2d or 3d) + int depth; ///< Depth (if 3d) + }; + + struct Desc: public DescBase + { + /// Initialize with default values + void init(Type typeIn); + /// Initialize different dimensions. For cubemap, use init2D + void init1D(Format format, int width, int numMipMaps = 0); + void init2D(Type typeIn, Format format, int width, int height, int numMipMaps = 0); + void init3D(Format format, int width, int height, int depth, int numMipMaps = 0); + + /// Given the type, calculates the number of mip maps. 0 on error + int calcNumMipLevels() const; + /// Calculate the total number of sub resources. 0 on error. + int calcNumSubResources() const; + + /// Calculate the effective array size - in essence the amount if mip map sets needed. + /// In practice takes into account if the arraySize is 0 (it's not an array, but it will still have at least one mip set) + /// and if the type is a cubemap (multiplies the amount of mip sets by 6) + int calcEffectiveArraySize() const; + + /// Use type to fix the size values (and array size). + /// For example a 1d texture, should have height and depth set to 1. + void fixSize(); + + /// Set up default parameters based on type and usage + void setDefaults(Usage initialUsage); + + Size size; + + int arraySize; ///< Array size + + int numMipLevels; ///< Number of mip levels - if 0 will create all mip levels + Format format; ///< The resources format + SampleDesc sampleDesc; ///< How the resource is sampled + }; + + /// The ordering of the subResources is + /// forall (effectiveArraySize) + /// forall (mip levels) + /// forall (depth levels) + struct Data + { + ptrdiff_t* mipRowStrides; ///< The row stride for a mip map + int numMips; ///< The number of mip maps + const void*const* subResources; ///< Pointers to each full mip subResource + int numSubResources; ///< The total amount of subResources. Typically = numMips * depth * arraySize + }; + + /// Get the description of the texture + SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } + + /// Ctor + TextureResource(const Desc& desc): + Parent(desc.type), + m_desc(desc) + { + } + + SLANG_FORCE_INLINE static int calcMipSize(int width, int mipLevel) + { + width = width >> mipLevel; + return width > 0 ? width : 1; + } + + protected: + Desc m_desc; +}; + +enum class ComparisonFunc : uint8_t +{ + Never = 0, + Less = 0x01, + Equal = 0x02, + LessEqual = 0x03, + Greater = 0x04, + NotEqual = 0x05, + GreaterEqual = 0x06, + Always = 0x07, +}; + +enum class TextureFilteringMode +{ + Point, + Linear, +}; + +enum class TextureAddressingMode +{ + Wrap, + ClampToEdge, + ClampToBorder, + MirrorRepeat, + MirrorOnce, +}; + +enum class TextureReductionOp +{ + Average, + Comparison, + Minimum, + Maximum, +}; + +class SamplerState : public Slang::RefObject +{ +public: + struct Desc + { + TextureFilteringMode minFilter = TextureFilteringMode::Linear; + TextureFilteringMode magFilter = TextureFilteringMode::Linear; + TextureFilteringMode mipFilter = TextureFilteringMode::Linear; + TextureReductionOp reductionOp = TextureReductionOp::Average; + TextureAddressingMode addressU = TextureAddressingMode::Wrap; + TextureAddressingMode addressV = TextureAddressingMode::Wrap; + TextureAddressingMode addressW = TextureAddressingMode::Wrap; + float mipLODBias = 0.0f; + uint32_t maxAnisotropy = 1; + ComparisonFunc comparisonFunc = ComparisonFunc::Never; + float borderColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + float minLOD = -FLT_MAX; + float maxLOD = FLT_MAX; + }; +}; + +enum class DescriptorSlotType +{ + Unknown, + + Sampler, + CombinedImageSampler, + SampledImage, + StorageImage, + UniformTexelBuffer, + StorageTexelBuffer, + UniformBuffer, + StorageBuffer, + DynamicUniformBuffer, + DynamicStorageBuffer, + InputAttachment, +}; + +class DescriptorSetLayout : public Slang::RefObject +{ +public: + struct SlotRangeDesc + { + DescriptorSlotType type = DescriptorSlotType::Unknown; + UInt count = 1; + + SlotRangeDesc() + {} + + SlotRangeDesc( + DescriptorSlotType type, + UInt count = 1) + : type(type) + , count(count) + {} + }; + + struct Desc + { + UInt slotRangeCount = 0; + SlotRangeDesc const* slotRanges = nullptr; + }; +}; + +class PipelineLayout : public Slang::RefObject +{ +public: + struct DescriptorSetDesc + { + DescriptorSetLayout* layout = nullptr; + + DescriptorSetDesc() + {} + + DescriptorSetDesc( + DescriptorSetLayout* layout) + : layout(layout) + {} + }; + + struct Desc + { + UInt renderTargetCount = 0; + UInt descriptorSetCount = 0; + DescriptorSetDesc const* descriptorSets = nullptr; + }; +}; + +class ResourceView : public Slang::RefObject +{ +public: + enum class Type + { + Unknown, + + RenderTarget, + DepthStencil, + ShaderResource, + UnorderedAccess, + }; + + struct Desc + { + Type type; + Format format; + }; +}; + +class DescriptorSet : public Slang::RefObject +{ +public: + virtual void setConstantBuffer(UInt range, UInt index, BufferResource* buffer) = 0; + virtual void setResource(UInt range, UInt index, ResourceView* view) = 0; + virtual void setSampler(UInt range, UInt index, SamplerState* sampler) = 0; + virtual void setCombinedTextureSampler( + UInt range, + UInt index, + ResourceView* textureView, + SamplerState* sampler) = 0; +}; + +enum class StencilOp : uint8_t +{ + Keep, + Zero, + Replace, + IncrementSaturate, + DecrementSaturate, + Invert, + IncrementWrap, + DecrementWrap, +}; + +enum class FillMode : uint8_t +{ + Solid, + Wireframe, +}; + +enum class CullMode : uint8_t +{ + None, + Front, + Back, +}; + +enum class FrontFaceMode : uint8_t +{ + CounterClockwise, + Clockwise, +}; + +struct DepthStencilOpDesc +{ + StencilOp stencilFailOp = StencilOp::Keep; + StencilOp stencilDepthFailOp = StencilOp::Keep; + StencilOp stencilPassOp = StencilOp::Keep; + ComparisonFunc stencilFunc = ComparisonFunc::Always; +}; + +struct DepthStencilDesc +{ + bool depthTestEnable = true; + bool depthWriteEnable = true; + ComparisonFunc depthFunc = ComparisonFunc::Less; + + bool stencilEnable = false; + uint32_t stencilReadMask = 0xFFFFFFFF; + uint32_t stencilWriteMask = 0xFFFFFFFF; + DepthStencilOpDesc frontFace; + DepthStencilOpDesc backFace; + + uint32_t stencilRef = 0; +}; + +struct RasterizerDesc +{ + FillMode fillMode = FillMode::Solid; + CullMode cullMode = CullMode::Back; + FrontFaceMode frontFace = FrontFaceMode::CounterClockwise; + int32_t depthBias = 0; + float depthBiasClamp = 0.0f; + float slopeScaledDepthBias = 0.0f; + bool depthClipEnable = true; + bool scissorEnable = false; + bool multisampleEnable = false; + bool antialiasedLineEnable = false; +}; + +struct GraphicsPipelineStateDesc +{ + ShaderProgram* program; + PipelineLayout* pipelineLayout; + InputLayout* inputLayout; + UInt framebufferWidth; + UInt framebufferHeight; + UInt renderTargetCount; + DepthStencilDesc depthStencil; + RasterizerDesc rasterizer; +}; + +struct ComputePipelineStateDesc +{ + ShaderProgram* program; + PipelineLayout* pipelineLayout; +}; + +class PipelineState : public Slang::RefObject +{ +public: +}; + +class Renderer: public Slang::RefObject +{ +public: + + struct Desc + { + int width; ///< Width in pixels + int height; ///< height in pixels + }; + + virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) = 0; + + virtual void setClearColor(const float color[4]) = 0; + virtual void clearFrame() = 0; + + virtual void presentFrame() = 0; + + virtual TextureResource::Desc getSwapChainTextureDesc() = 0; + + /// Create a texture resource. initData holds the initialize data to set the contents of the texture when constructed. + virtual Result createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData, TextureResource** outResource) = 0; + + /// Create a texture resource. initData holds the initialize data to set the contents of the texture when constructed. + inline RefPtr createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData = nullptr) + { + RefPtr resource; + SLANG_RETURN_NULL_ON_FAIL(createTextureResource(initialUsage, desc, initData, resource.writeRef())); + return resource; + } + + /// Create a buffer resource + virtual Result createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData, BufferResource** outResource) = 0; + + inline RefPtr createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData = nullptr) + { + RefPtr resource; + SLANG_RETURN_NULL_ON_FAIL(createBufferResource(initialUsage, desc, initData, resource.writeRef())); + return resource; + } + + virtual Result createSamplerState(SamplerState::Desc const& desc, SamplerState** outSampler) = 0; + + inline RefPtr createSamplerState(SamplerState::Desc const& desc) + { + RefPtr sampler; + SLANG_RETURN_NULL_ON_FAIL(createSamplerState(desc, sampler.writeRef())); + return sampler; + } + + virtual Result createTextureView(TextureResource* texture, ResourceView::Desc const& desc, ResourceView** outView) = 0; + + inline RefPtr createTextureView(TextureResource* texture, ResourceView::Desc const& desc) + { + RefPtr view; + SLANG_RETURN_NULL_ON_FAIL(createTextureView(texture, desc, view.writeRef())); + return view; + } + + virtual Result createBufferView(BufferResource* buffer, ResourceView::Desc const& desc, ResourceView** outView) = 0; + + inline RefPtr createBufferView(BufferResource* buffer, ResourceView::Desc const& desc) + { + RefPtr view; + SLANG_RETURN_NULL_ON_FAIL(createBufferView(buffer, desc, view.writeRef())); + return view; + } + + virtual Result createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount, InputLayout** outLayout) = 0; + + inline RefPtr createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) + { + RefPtr layout; + SLANG_RETURN_NULL_ON_FAIL(createInputLayout(inputElements, inputElementCount, layout.writeRef())); + return layout; + } + + virtual Result createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc, DescriptorSetLayout** outLayout) = 0; + + inline RefPtr createDescriptorSetLayout(const DescriptorSetLayout::Desc& desc) + { + RefPtr layout; + SLANG_RETURN_NULL_ON_FAIL(createDescriptorSetLayout(desc, layout.writeRef())); + return layout; + } + + virtual Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) = 0; + + inline RefPtr createPipelineLayout(const PipelineLayout::Desc& desc) + { + RefPtr layout; + SLANG_RETURN_NULL_ON_FAIL(createPipelineLayout(desc, layout.writeRef())); + return layout; + } + + virtual Result createDescriptorSet(DescriptorSetLayout* layout, DescriptorSet** outDescriptorSet) = 0; + + inline RefPtr createDescriptorSet(DescriptorSetLayout* layout) + { + RefPtr descriptorSet; + SLANG_RETURN_NULL_ON_FAIL(createDescriptorSet(layout, descriptorSet.writeRef())); + return descriptorSet; + } + + virtual Result createProgram(const ShaderProgram::Desc& desc, ShaderProgram** outProgram) = 0; + + inline RefPtr createProgram(const ShaderProgram::Desc& desc) + { + RefPtr program; + SLANG_RETURN_NULL_ON_FAIL(createProgram(desc, program.writeRef())); + return program; + } + + virtual Result createGraphicsPipelineState( + const GraphicsPipelineStateDesc& desc, + PipelineState** outState) = 0; + + inline RefPtr createGraphicsPipelineState( + const GraphicsPipelineStateDesc& desc) + { + RefPtr state; + SLANG_RETURN_NULL_ON_FAIL(createGraphicsPipelineState(desc, state.writeRef())); + return state; + } + + virtual Result createComputePipelineState( + const ComputePipelineStateDesc& desc, + PipelineState** outState) = 0; + + inline RefPtr createComputePipelineState( + const ComputePipelineStateDesc& desc) + { + RefPtr state; + SLANG_RETURN_NULL_ON_FAIL(createComputePipelineState(desc, state.writeRef())); + return state; + } + + /// Captures the back buffer and stores the result in surfaceOut. If the surface contains data - it will either be overwritten (if same size and format), or freed and a re-allocated. + virtual SlangResult captureScreenSurface(Surface& surfaceOut) = 0; + + virtual void* map(BufferResource* buffer, MapFlavor flavor) = 0; + virtual void unmap(BufferResource* buffer) = 0; + + virtual void setPrimitiveTopology(PrimitiveTopology topology) = 0; + + virtual void setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet) = 0; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) = 0; + inline void setVertexBuffer(UInt slot, BufferResource* buffer, UInt stride, UInt offset = 0); + + virtual void setIndexBuffer(BufferResource* buffer, Format indexFormat, UInt offset = 0) = 0; + + virtual void setDepthStencilTarget(ResourceView* depthStencilView) = 0; + + virtual void setPipelineState(PipelineType pipelineType, PipelineState* state) = 0; + + virtual void draw(UInt vertexCount, UInt startVertex = 0) = 0; + virtual void drawIndexed(UInt indexCount, UInt startIndex = 0, UInt baseVertex = 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; + + /// Get the type of this renderer + virtual RendererType getRendererType() const = 0; +}; + +// ---------------------------------------------------------------------------------------- +inline void Renderer::setVertexBuffer(UInt slot, BufferResource* buffer, UInt stride, UInt offset) +{ + setVertexBuffers(slot, 1, &buffer, &stride, &offset); +} + +/// Functions that are around Renderer and it's types +struct RendererUtil +{ + /// Gets the size in bytes of a Format type. Returns 0 if a size is not defined/invalid + SLANG_FORCE_INLINE static size_t getFormatSize(Format format) { return s_formatSize[int(format)]; } + /// Given a renderer type, gets a projection style + static ProjectionStyle getProjectionStyle(RendererType type); + + /// Given the projection style returns an 'identity' matrix, which ensures x,y mapping to pixels is the same on all targets + static void getIdentityProjection(ProjectionStyle style, float projMatrix[16]); + + /// Get the binding style from the type + static BindingStyle getBindingStyle(RendererType type) { return s_rendererTypeToBindingStyle[int(type)]; } + + private: + static void compileTimeAsserts(); + static const uint8_t s_formatSize[]; // Maps Format::XXX to a size in bytes; + static const BindingStyle s_rendererTypeToBindingStyle[]; ///< Maps a RendererType to a BindingStyle +}; + +} // renderer_test diff --git a/tools/gfx/resource-d3d12.cpp b/tools/gfx/resource-d3d12.cpp new file mode 100644 index 000000000..2e0f78371 --- /dev/null +++ b/tools/gfx/resource-d3d12.cpp @@ -0,0 +1,214 @@ +// resource-d3d12.cpp +#include "resource-d3d12.h" + +namespace gfx { +using namespace Slang; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12BarrierSubmitter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +void D3D12BarrierSubmitter::_flush() +{ + assert(m_numBarriers > 0); + + if (m_commandList) + { + m_commandList->ResourceBarrier(UINT(m_numBarriers), m_barriers); + } + m_numBarriers = 0; +} + +D3D12_RESOURCE_BARRIER& D3D12BarrierSubmitter::_expandOne() +{ + _flush(); + return m_barriers[m_numBarriers++]; +} + +void D3D12BarrierSubmitter::transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState) +{ + if (nextState != prevState) + { + D3D12_RESOURCE_BARRIER& barrier = expandOne(); + + const UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + const D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + + ::memset(&barrier, 0, sizeof(barrier)); + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = flags; + barrier.Transition.pResource = resource; + barrier.Transition.StateBefore = prevState; + barrier.Transition.StateAfter = nextState; + barrier.Transition.Subresource = subresource; + } + else + { + if (nextState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS) + { + D3D12_RESOURCE_BARRIER& barrier = expandOne(); + + ::memset(&barrier, 0, sizeof(barrier)); + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + barrier.UAV.pResource = resource; + } + } +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12ResourceBase !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +/* static */DXGI_FORMAT D3D12ResourceBase::calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource) +{ + return resource ? D3DUtil::calcFormat(usage, resource->GetDesc().Format) : DXGI_FORMAT_UNKNOWN; +} + +void D3D12ResourceBase::transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter) +{ + // Transition only if there is a resource + if (m_resource) + { + submitter.transition(m_resource, m_state, nextState); + m_state = nextState; + } +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12CounterFence !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +D3D12CounterFence::~D3D12CounterFence() +{ + if (m_event) + { + CloseHandle(m_event); + } +} + +Result D3D12CounterFence::init(ID3D12Device* device, uint64_t initialValue) +{ + m_currentValue = initialValue; + + SLANG_RETURN_ON_FAIL(device->CreateFence(m_currentValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef()))); + // Create an event handle to use for frame synchronization. + m_event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (m_event == nullptr) + { + Result res = HRESULT_FROM_WIN32(GetLastError()); + return SLANG_FAILED(res) ? res : SLANG_FAIL; + } + return SLANG_OK; +} + +UInt64 D3D12CounterFence::nextSignal(ID3D12CommandQueue* commandQueue) +{ + // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >= + m_currentValue++; + // Schedule a Signal command in the queue. + Result res = commandQueue->Signal(m_fence, m_currentValue); + if (SLANG_FAILED(res)) + { + assert(!"Signal failed"); + } + return m_currentValue; +} + +void D3D12CounterFence::waitUntilCompleted(uint64_t completedValue) +{ + // You can only wait for a value that is less than or equal to the current value + assert(completedValue <= m_currentValue); + + // Wait until the previous frame is finished. + while (m_fence->GetCompletedValue() < completedValue) + { + // Make it signal with the current value + SLANG_ASSERT_VOID_ON_FAIL(m_fence->SetEventOnCompletion(completedValue, m_event)); + WaitForSingleObject(m_event, INFINITE); + } +} + +void D3D12CounterFence::nextSignalAndWait(ID3D12CommandQueue* commandQueue) +{ + waitUntilCompleted(nextSignal(commandQueue)); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!! D3D12Resource !!!!!!!!!!!!!!!!!!!!!!!! */ + +/* static */void D3D12Resource::setDebugName(ID3D12Resource* resource, const char* name) +{ + if (resource) + { + size_t len = ::strlen(name); + List buf; + buf.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 resource; + SLANG_RETURN_ON_FAIL(device->CreateCommittedResource(&heapProps, heapFlags, &resourceDesc, initState, clearValue, IID_PPV_ARGS(resource.writeRef()))); + setResource(resource, initState); + return SLANG_OK; +} + +ID3D12Resource* D3D12Resource::detach() +{ + ID3D12Resource* resource = m_resource; + m_resource = nullptr; + return resource; +} + +void D3D12Resource::swap(ComPtr& resourceInOut) +{ + ID3D12Resource* tmp = m_resource; + m_resource = resourceInOut.detach(); + resourceInOut.attach(tmp); +} + +void D3D12Resource::setState(D3D12_RESOURCE_STATES state) +{ + m_prevState = state; + m_state = state; +} + +} // renderer_test diff --git a/tools/gfx/resource-d3d12.h b/tools/gfx/resource-d3d12.h new file mode 100644 index 000000000..1764adf9d --- /dev/null +++ b/tools/gfx/resource-d3d12.h @@ -0,0 +1,178 @@ +// resource-d3d12.h +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#include +#include + +#include "../../slang-com-ptr.h" +#include "d3d-util.h" + +namespace gfx { + +// Enables more conservative barriers - restoring the state of resources after they are used. +// Should not need to be enabled in normal builds, as the barriers should correctly sync resources +// If enabling fixes an issue it implies regular barriers are not correctly used. +#define SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS 0 + +struct D3D12BarrierSubmitter +{ + enum { MAX_BARRIERS = 8 }; + + /// Expand one space to hold a barrier + SLANG_FORCE_INLINE D3D12_RESOURCE_BARRIER& expandOne() { return (m_numBarriers < MAX_BARRIERS) ? m_barriers[m_numBarriers++] : _expandOne(); } + /// Flush barriers to command list + SLANG_FORCE_INLINE void flush() { if (m_numBarriers > 0) _flush(); } + + /// Transition resource from prevState to nextState + void transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState); + + /// Ctor + SLANG_FORCE_INLINE D3D12BarrierSubmitter(ID3D12GraphicsCommandList* commandList) : m_numBarriers(0), m_commandList(commandList) { } + /// Dtor + SLANG_FORCE_INLINE ~D3D12BarrierSubmitter() { flush(); } + +protected: + D3D12_RESOURCE_BARRIER& _expandOne(); + void _flush(); + + ID3D12GraphicsCommandList* m_commandList; + int m_numBarriers; + D3D12_RESOURCE_BARRIER m_barriers[MAX_BARRIERS]; +}; + +/*! \brief A class to simplify using Dx12 fences. + +A fence is a mechanism to track GPU work. This is achieved by having a counter that the CPU holds +called the current value. Calling nextSignal will increase the CPU counter, and add a fence +with that value to the commandQueue. When the GPU has completed all the work before the fence it will +update the completed value. This is typically used when +the CPU needs to know the GPU has finished some piece of work has completed. To do this the CPU +can check the completed value, and when it is greater or equal to the value returned by nextSignal the +CPU will know that all the work prior to when the nextSignal was added to the queue will have completed. + +NOTE! This cannot be used across threads, as for amongst other reasons SetEventOnCompletion +only works with a single value. + +Signal on the CommandQueue updates the fence on the GPU side. Signal on the fence object changes +the value on the CPU side (not used here). + +Useful article describing how Dx12 synchronization works: +https://msdn.microsoft.com/en-us/library/windows/desktop/dn899217%28v=vs.85%29.aspx +*/ +class D3D12CounterFence +{ +public: + /// Must be called before used + SlangResult init(ID3D12Device* device, uint64_t initialValue = 0); + /// Increases the counter, signals the queue and waits for the signal to be hit + void nextSignalAndWait(ID3D12CommandQueue* queue); + /// Signals with next counter value. Returns the value the signal was called on + uint64_t nextSignal(ID3D12CommandQueue* commandQueue); + /// Get the current value + SLANG_FORCE_INLINE uint64_t getCurrentValue() const { return m_currentValue; } + /// Get the completed value + SLANG_FORCE_INLINE uint64_t getCompletedValue() const { return m_fence->GetCompletedValue(); } + + /// Waits for the the specified value + void waitUntilCompleted(uint64_t completedValue); + + /// Ctor + D3D12CounterFence() :m_event(nullptr), m_currentValue(0) {} + /// Dtor + ~D3D12CounterFence(); + +protected: + HANDLE m_event; + Slang::ComPtr m_fence; + UINT64 m_currentValue; +}; + +/** The base class for resource types allows for tracking of state. It does not allow for setting of the resource though, such that +an interface can return a D3D12ResourceBase, and a client cant manipulate it's state, but it cannot replace/change the actual resource */ +struct D3D12ResourceBase +{ + /// Add a transition if necessary to the list + void transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter); + /// Get the current state + SLANG_FORCE_INLINE D3D12_RESOURCE_STATES getState() const { return m_state; } + + /// Get the associated resource + SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_resource; } + + /// True if a resource is set + SLANG_FORCE_INLINE bool isSet() const { return m_resource != nullptr; } + + /// Coercible into ID3D12Resource + SLANG_FORCE_INLINE operator ID3D12Resource*() const { return m_resource; } + + /// restore previous state +#if SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS + SLANG_FORCE_INLINE Void restore(D3D12BarrierSubmitter& submitter) { transition(m_prevState, submitter); } +#else + SLANG_FORCE_INLINE void restore(D3D12BarrierSubmitter& submitter) { SLANG_UNUSED(submitter) } +#endif + + /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible + static DXGI_FORMAT calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource); + + /// Ctor + SLANG_FORCE_INLINE D3D12ResourceBase() : + m_state(D3D12_RESOURCE_STATE_COMMON), + m_prevState(D3D12_RESOURCE_STATE_COMMON), + m_resource(nullptr) + {} + +protected: + /// This is protected so as clients cannot slice the class, and so state tracking is lost + ~D3D12ResourceBase() {} + + ID3D12Resource* m_resource; ///< The resource (ref counted) + D3D12_RESOURCE_STATES m_state; ///< The current tracked expected state, if all associated transitions have completed on ID3D12CommandList + D3D12_RESOURCE_STATES m_prevState; ///< The previous state +}; + +struct D3D12Resource : public D3D12ResourceBase +{ + + /// Dtor + ~D3D12Resource() + { + if (m_resource) + { + m_resource->Release(); + } + } + + /// Initialize as committed resource + Slang::Result initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue); + + /// Set a resource with an initial state + void setResource(ID3D12Resource* resource, D3D12_RESOURCE_STATES initialState); + /// Make the resource null + void setResourceNull(); + /// Returns the attached resource (with any ref counts) and sets to nullptr on this. + ID3D12Resource* detach(); + + /// Swaps the resource contents with the contents of the smart pointer + void swap(Slang::ComPtr& resourceInOut); + + /// Sets the current state of the resource (the current state is taken to be the future state once the command list has executed) + /// NOTE! This must be used with care, otherwise state tracking can be made incorrect. + void setState(D3D12_RESOURCE_STATES state); + + /// Set the debug name on a resource + static void setDebugName(ID3D12Resource* resource, const char* name); + + /// Set the the debug name on the resource + void setDebugName(const wchar_t* name); + /// Set the debug name + void setDebugName(const char* name); +}; + +} // renderer_test diff --git a/tools/gfx/surface.cpp b/tools/gfx/surface.cpp new file mode 100644 index 000000000..4b53d278a --- /dev/null +++ b/tools/gfx/surface.cpp @@ -0,0 +1,222 @@ +// surface.cpp +#include "surface.h" + +#include +#include + +#include "../../source/core/list.h" + +namespace gfx { +using namespace Slang; + +class MallocSurfaceAllocator: public SurfaceAllocator +{ + public: + + virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) override; + virtual void deallocate(Surface& surface) override; +}; + +static MallocSurfaceAllocator s_mallocSurfaceAllocator; + +/// Get the malloc allocator +/* static */SurfaceAllocator* SurfaceAllocator::getMallocAllocator() +{ + return &s_mallocSurfaceAllocator; +} + +Slang::Result MallocSurfaceAllocator::allocate(int width, int height, Format format, int alignment, Surface& surface) +{ + assert(surface.m_data == nullptr); + + // Calculate row size + + const int rowSizeInBytes = Surface::calcRowSize(format, width); + const int numRows = Surface::calcNumRows(format, height); + + alignment = (alignment <= 0) ? int(sizeof(void*)) : alignment; + // It must be a power of 2 + assert( ((alignment - 1) & alignment) == 0); + + // Align rowSize + const int alignedRowSizeInBytes = (rowSizeInBytes + alignment - 1) & -alignment; + + size_t totalSize = numRows * alignedRowSizeInBytes; + + uint8_t* data = (uint8_t*)::malloc(totalSize); + if (!data) + { + return SLANG_E_OUT_OF_MEMORY; + } + + surface.m_data = data; + surface.m_width = width; + surface.m_height = height; + surface.m_format = format; + surface.m_numRows = numRows; + surface.m_rowStrideInBytes = alignedRowSizeInBytes; + + surface.m_allocator = this; + return SLANG_OK; +} + +void MallocSurfaceAllocator::deallocate(Surface& surface) +{ + assert(surface.m_data); + // Make sure it's not an inverted, cos otherwise m_data is not the start address + assert(surface.m_rowStrideInBytes > 0); + ::free(surface.m_data); +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Surface !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +/* static */int Surface::calcRowSize(Format format, int width) +{ + size_t pixelSize = RendererUtil::getFormatSize(format); + if (pixelSize == 0) + { + return 0; + } + return int(pixelSize * width); +} + +/* static */int Surface::calcNumRows(Format format, int height) +{ + // Don't have any compressed types, so number of rows is same as the height + return height; +} + +void Surface::init() +{ + m_width = 0; + m_height = 0; + m_format = Format::Unknown; + m_data = nullptr; + m_numRows = 0; + m_rowStrideInBytes = 0; + // NOTE! does not clear the allocator. + // If called with an allocation memory will leak! +} + +Surface::~Surface() +{ + if (m_data && m_allocator) + { + m_allocator->deallocate(*this); + } +} + +void Surface::deallocate() +{ + if (m_data && m_allocator) + { + m_allocator->deallocate(*this); + init(); + } +} + +Result Surface::allocate(int width, int height, Format format, int alignment, SurfaceAllocator* allocator) +{ + deallocate(); + allocator = allocator ? allocator : m_allocator; + if (!allocator) + { + // An allocator needs to be set on the surface, or one passed in. + return SLANG_FAIL; + } + return allocator->allocate(width, height, format, alignment, *this); +} + +void Surface::setUnowned(int width, int height, Format format, int strideInBytes, void* data) +{ + deallocate(); + + // This is unowned + m_allocator = nullptr; + + m_width = width; + m_height = height; + m_format = format; + m_rowStrideInBytes = strideInBytes; + m_data = (uint8_t*)data; + + m_numRows = Surface::calcNumRows(format, height); + + const int rowSizeInBytes = Surface::calcRowSize(format, width); + assert((strideInBytes > 0 && rowSizeInBytes <= strideInBytes) || (strideInBytes < 0 && rowSizeInBytes <= -strideInBytes)); +} + +void Surface::zeroContents() +{ + const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); + + const int stride = m_rowStrideInBytes; + uint8_t* dst = m_data; + + for (int i = 0; i < m_numRows; i++, dst += stride) + { + ::memset(dst, 0, rowSizeInBytes); + } +} + +void Surface::flipInplaceVertically() +{ + // Can only flip when m_height matches number of rows + assert(m_numRows == m_height); + + const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); + if (rowSizeInBytes <= 0 || m_numRows <= 1) + { + return; + } + + uint8_t* top = m_data; + uint8_t* bottom = m_data + (m_numRows - 1) * m_rowStrideInBytes; + + List bufferList; + bufferList.SetSize(rowSizeInBytes); + uint8_t* buffer = bufferList.Buffer(); + + const int stride = m_rowStrideInBytes; + + const int num = m_height >> 1; + for (int i = 0; i < num; ++i, top += stride, bottom -= stride) + { + ::memcpy(buffer, top, rowSizeInBytes); + ::memcpy(top, bottom, rowSizeInBytes); + ::memcpy(bottom, buffer, rowSizeInBytes); + } +} + +SlangResult Surface::set(int width, int height, Format format, int srcRowStride, const void* data, SurfaceAllocator* allocator) +{ + if (hasContents() && m_width == width && m_height == height && m_format == format) + { + // I can just overwrite the contents that is there + } + else + { + SLANG_RETURN_ON_FAIL(allocate(width, height, format, 0, allocator)); + } + + // Okay just need to set the contents + + { + const size_t rowSize = calcRowSize(format, width); + + const uint8_t* srcRow = (const uint8_t*)data; + uint8_t* dstRow = (uint8_t*)m_data; + + for (int i = 0; i < m_numRows; i++) + { + ::memcpy(dstRow, srcRow, rowSize); + + srcRow += srcRowStride; + dstRow += m_rowStrideInBytes; + } + } + + return SLANG_OK; +} + +} // renderer_test diff --git a/tools/gfx/surface.h b/tools/gfx/surface.h new file mode 100644 index 000000000..3e0f6f0aa --- /dev/null +++ b/tools/gfx/surface.h @@ -0,0 +1,86 @@ +// surface.h +#pragma once + +#include "render.h" + +namespace gfx { + +class Surface; + +class SurfaceAllocator +{ + public: + virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) = 0; + virtual void deallocate(Surface& surface) = 0; + + /// Get the malloc allocator + static SurfaceAllocator* getMallocAllocator(); +}; + +class Surface +{ + public: + + enum + { + kDefaultAlignment = sizeof(void*) + }; + + /// Allocate + Slang::Result allocate(int width, int height, Format format, int alignment = kDefaultAlignment, SurfaceAllocator* allocator = nullptr); + + /// Deallocate contents + void deallocate(); + /// Initialize contents (zero sized, no data). Note that the allocator pointer is left as is + void init(); + + /// Set unowned + void setUnowned(int width, int height, Format format, int strideInBytes, void* data); + + /// Set the contents - the memory will be owned by this surface (ie will be freed by the allocator when goes out of scope or is deallocated) + Slang::Result set(int width, int height, Format format, int strideInBytes, const void* data, SurfaceAllocator* allocator); + + template + T* calcNextRow(T* ptr) const { return (T*)calcNextRow((void*)ptr); } + template + const T* calcNextRow(const T* ptr) const { return (const T*)calcNextRow((const void*)ptr); } + + void* calcNextRow(void* ptr) const { return (void*)(((uint8_t*)ptr) + m_rowStrideInBytes); } + const void* calcNextRow(const void* ptr) const { return (const void*)(((const uint8_t*)ptr) + m_rowStrideInBytes); } + + /// Writes zero to all of the contents + void zeroContents(); + + /// Flips the contents vertically in place + void flipInplaceVertically(); + + /// True if has some contents + bool hasContents() const { return m_data != nullptr; } + + /// Ctor + Surface() : + m_allocator(nullptr) + { + init(); + } + /// Dtor + ~Surface(); + + /// Get the size of the row in bytes + static int calcRowSize(Format format, int width); + /// Calculates the number of rows + static int calcNumRows(Format format, int height); + + int m_width; + int m_height; + Format m_format; + + uint8_t* m_data; /// The data that makes up the image. If nullptr, has no data. Pointer to first 'row' of the image. + + int m_numRows; ///< Total amount of rows (typically same as height, but in compressed formats may be less) + int m_rowStrideInBytes; ///< The number of bytes between rows + + SurfaceAllocator* m_allocator; ///< Can be null if so contents is 'unowned', if set +}; + +} // renderer_test diff --git a/tools/gfx/vector-math.h b/tools/gfx/vector-math.h new file mode 100644 index 000000000..88cb0c1d9 --- /dev/null +++ b/tools/gfx/vector-math.h @@ -0,0 +1,14 @@ +// vector-math.h +#pragma once + +// We will use the GLM library for our vector math types, just for simplicity. + +#include "../../external/glm/glm/glm.hpp" +#include "../../external/glm/glm/gtc/matrix_transform.hpp" +#include "../../external/glm/glm/gtc/constants.hpp" + +namespace gfx { + +using namespace glm; + +} // gfx diff --git a/tools/gfx/vk-api.cpp b/tools/gfx/vk-api.cpp new file mode 100644 index 000000000..4030e43ba --- /dev/null +++ b/tools/gfx/vk-api.cpp @@ -0,0 +1,138 @@ +// vk-api.cpp +#include "vk-api.h" + +#include "../../source/core/list.h" + +namespace gfx { +using namespace Slang; + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanApi !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +#define VK_API_CHECK_FUNCTION(x) && (x != nullptr) +#define VK_API_CHECK_FUNCTIONS(FUNCTION_LIST) true FUNCTION_LIST(VK_API_CHECK_FUNCTION) + +bool VulkanApi::areDefined(ProcType type) const +{ + switch (type) + { + case ProcType::Global: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_GLOBAL_PROCS); + case ProcType::Instance: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_INSTANCE_PROCS); + case ProcType::Device: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_DEVICE_PROCS); + default: + { + assert(!"Unhandled type"); + return false; + } + } +} + +Slang::Result VulkanApi::initGlobalProcs(const VulkanModule& module) +{ +#define VK_API_GET_GLOBAL_PROC(x) x = (PFN_##x)module.getFunction(#x); + + // Initialize all the global functions + VK_API_ALL_GLOBAL_PROCS(VK_API_GET_GLOBAL_PROC) + + if (!areDefined(ProcType::Global)) + { + return SLANG_FAIL; + } + m_module = &module; + return SLANG_OK; +} + +Slang::Result VulkanApi::initInstanceProcs(VkInstance instance) +{ + assert(instance && vkGetInstanceProcAddr != nullptr); + +#define VK_API_GET_INSTANCE_PROC(x) x = (PFN_##x)vkGetInstanceProcAddr(instance, #x); + + VK_API_ALL_INSTANCE_PROCS(VK_API_GET_INSTANCE_PROC) + + if (!areDefined(ProcType::Instance)) + { + return SLANG_FAIL; + } + + m_instance = instance; + return SLANG_OK; +} + +Slang::Result VulkanApi::initPhysicalDevice(VkPhysicalDevice physicalDevice) +{ + assert(m_physicalDevice == VK_NULL_HANDLE); + m_physicalDevice = physicalDevice; + + vkGetPhysicalDeviceProperties(m_physicalDevice, &m_deviceProperties); + vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_deviceFeatures); + vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_deviceMemoryProperties); + + return SLANG_OK; +} + +Slang::Result VulkanApi::initDeviceProcs(VkDevice device) +{ + assert(m_instance && device && vkGetDeviceProcAddr != nullptr); + +#define VK_API_GET_DEVICE_PROC(x) x = (PFN_##x)vkGetDeviceProcAddr(device, #x); + + VK_API_ALL_DEVICE_PROCS(VK_API_GET_DEVICE_PROC) + + if (!areDefined(ProcType::Device)) + { + return SLANG_FAIL; + } + + m_device = device; + return SLANG_OK; +} + +int VulkanApi::findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const +{ + assert(typeBits); + + const int numMemoryTypes = int(m_deviceMemoryProperties.memoryTypeCount); + + // bit holds current test bit against typeBits. Ie bit == 1 << typeBits + + uint32_t bit = 1; + for (int i = 0; i < numMemoryTypes; ++i, bit += bit) + { + auto const& memoryType = m_deviceMemoryProperties.memoryTypes[i]; + if ((typeBits & bit) && (memoryType.propertyFlags & properties) == properties) + { + return i; + } + } + + //assert(!"failed to find a usable memory type"); + return -1; +} + +int VulkanApi::findQueue(VkQueueFlags reqFlags) const +{ + assert(m_physicalDevice != VK_NULL_HANDLE); + + uint32_t numQueueFamilies = 0; + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, nullptr); + + Slang::List queueFamilies; + queueFamilies.SetSize(numQueueFamilies); + vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, queueFamilies.Buffer()); + + // Find a queue that can service our needs + //VkQueueFlags reqQueueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; + + int queueFamilyIndex = -1; + for (int i = 0; i < int(numQueueFamilies); ++i) + { + if ((queueFamilies[i].queueFlags & reqFlags) == reqFlags) + { + return i; + } + } + + return -1; +} + +} // renderer_test diff --git a/tools/gfx/vk-api.h b/tools/gfx/vk-api.h new file mode 100644 index 000000000..5ec28ef6e --- /dev/null +++ b/tools/gfx/vk-api.h @@ -0,0 +1,196 @@ +// vk-api.h +#pragma once + +#include "vk-module.h" + +namespace gfx { + +#define VK_API_GLOBAL_PROCS(x) \ + x(vkGetInstanceProcAddr) \ + x(vkCreateInstance) \ + /* */ + +#define VK_API_INSTANCE_PROCS(x) \ + x(vkCreateDevice) \ + x(vkCreateDebugReportCallbackEXT) \ + x(vkDestroyDebugReportCallbackEXT) \ + x(vkDebugReportMessageEXT) \ + x(vkEnumeratePhysicalDevices) \ + x(vkGetPhysicalDeviceProperties) \ + x(vkGetPhysicalDeviceFeatures) \ + x(vkGetPhysicalDeviceMemoryProperties) \ + x(vkGetPhysicalDeviceQueueFamilyProperties) \ + x(vkGetPhysicalDeviceFormatProperties) \ + x(vkGetDeviceProcAddr) \ + /* */ + +#define VK_API_DEVICE_PROCS(x) \ + x(vkCreateDescriptorPool) \ + x(vkDestroyDescriptorPool) \ + x(vkGetDeviceQueue) \ + x(vkQueueSubmit) \ + x(vkQueueWaitIdle) \ + x(vkCreateBuffer) \ + x(vkAllocateMemory) \ + x(vkMapMemory) \ + x(vkUnmapMemory) \ + x(vkCmdCopyBuffer) \ + x(vkDestroyBuffer) \ + x(vkFreeMemory) \ + x(vkCreateDescriptorSetLayout) \ + x(vkDestroyDescriptorSetLayout) \ + x(vkAllocateDescriptorSets) \ + x(vkUpdateDescriptorSets) \ + x(vkCreatePipelineLayout) \ + x(vkDestroyPipelineLayout) \ + x(vkCreateComputePipelines) \ + x(vkCreateGraphicsPipelines) \ + x(vkDestroyPipeline) \ + x(vkCreateShaderModule) \ + x(vkDestroyShaderModule) \ + x(vkCreateFramebuffer) \ + x(vkDestroyFramebuffer) \ + x(vkCreateImage) \ + x(vkDestroyImage) \ + x(vkCreateImageView) \ + x(vkDestroyImageView) \ + x(vkCreateRenderPass) \ + x(vkDestroyRenderPass) \ + x(vkCreateCommandPool) \ + x(vkDestroyCommandPool) \ + x(vkCreateSampler) \ + x(vkDestroySampler) \ + x(vkCreateBufferView) \ + x(vkDestroyBufferView) \ + \ + x(vkGetBufferMemoryRequirements) \ + x(vkGetImageMemoryRequirements) \ + \ + x(vkCmdBindPipeline) \ + x(vkCmdBindDescriptorSets) \ + x(vkCmdDispatch) \ + x(vkCmdDraw) \ + x(vkCmdSetScissor) \ + x(vkCmdSetViewport) \ + x(vkCmdBindVertexBuffers) \ + x(vkCmdBindIndexBuffer) \ + x(vkCmdBeginRenderPass) \ + x(vkCmdEndRenderPass) \ + x(vkCmdPipelineBarrier) \ + x(vkCmdCopyBufferToImage)\ + \ + x(vkCreateFence) \ + x(vkDestroyFence) \ + x(vkResetFences) \ + x(vkGetFenceStatus) \ + x(vkWaitForFences) \ + \ + x(vkCreateSemaphore) \ + x(vkDestroySemaphore) \ + \ + x(vkCreateEvent) \ + x(vkDestroyEvent) \ + x(vkGetEventStatus) \ + x(vkSetEvent) \ + x(vkResetEvent) \ + \ + x(vkFreeCommandBuffers) \ + x(vkAllocateCommandBuffers) \ + x(vkBeginCommandBuffer) \ + x(vkEndCommandBuffer) \ + x(vkResetCommandBuffer) \ + \ + x(vkBindImageMemory) \ + x(vkBindBufferMemory) \ + /* */ + +#if SLANG_WINDOWS_FAMILY +# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ + x(vkCreateWin32SurfaceKHR) \ + /* */ +#else +# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ + x(vkCreateXlibSurfaceKHR) \ + /* */ +#endif + +#define VK_API_INSTANCE_KHR_PROCS(x) \ + VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ + x(vkGetPhysicalDeviceSurfaceSupportKHR) \ + x(vkGetPhysicalDeviceSurfaceFormatsKHR) \ + x(vkGetPhysicalDeviceSurfacePresentModesKHR) \ + x(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ + x(vkDestroySurfaceKHR) \ + /* */ + +#define VK_API_DEVICE_KHR_PROCS(x) \ + x(vkQueuePresentKHR) \ + x(vkCreateSwapchainKHR) \ + x(vkGetSwapchainImagesKHR) \ + x(vkDestroySwapchainKHR) \ + x(vkAcquireNextImageKHR) \ + /* */ + +#define VK_API_ALL_GLOBAL_PROCS(x) \ + VK_API_GLOBAL_PROCS(x) + +#define VK_API_ALL_INSTANCE_PROCS(x) \ + VK_API_INSTANCE_PROCS(x) \ + VK_API_INSTANCE_KHR_PROCS(x) + +#define VK_API_ALL_DEVICE_PROCS(x) \ + VK_API_DEVICE_PROCS(x) \ + VK_API_DEVICE_KHR_PROCS(x) + +#define VK_API_ALL_PROCS(x) \ + VK_API_ALL_GLOBAL_PROCS(x) \ + VK_API_ALL_INSTANCE_PROCS(x) \ + VK_API_ALL_DEVICE_PROCS(x) \ + /* */ + +#define VK_API_DECLARE_PROC(NAME) PFN_##NAME NAME = nullptr; + +struct VulkanApi +{ + VK_API_ALL_PROCS(VK_API_DECLARE_PROC) + + enum class ProcType + { + Global, + Instance, + Device, + }; + + /// Returns true if all the functions in the class are defined + bool areDefined(ProcType type) const; + + /// Sets up global parameters + Slang::Result initGlobalProcs(const VulkanModule& module); + /// Initialize the instance functions + Slang::Result initInstanceProcs(VkInstance instance); + + /// Called before initDevice + Slang::Result initPhysicalDevice(VkPhysicalDevice physicalDevice); + + /// Initialize the device functions + Slang::Result initDeviceProcs(VkDevice device); + + /// Type bits control which indices are tested against bit 0 for testing at index 0 + /// properties - a memory type must have all the bits set as passed in + /// Returns -1 if couldn't find an appropriate memory type index + int findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const; + + /// Given queue required flags, finds a queue + int findQueue(VkQueueFlags reqFlags) const; + + const VulkanModule* m_module = nullptr; ///< Module this was all loaded from + VkInstance m_instance = VK_NULL_HANDLE; + VkDevice m_device = VK_NULL_HANDLE; + VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; + + VkPhysicalDeviceProperties m_deviceProperties; + VkPhysicalDeviceFeatures m_deviceFeatures; + VkPhysicalDeviceMemoryProperties m_deviceMemoryProperties; +}; + +} // renderer_test diff --git a/tools/gfx/vk-device-queue.cpp b/tools/gfx/vk-device-queue.cpp new file mode 100644 index 000000000..10a3d0e3b --- /dev/null +++ b/tools/gfx/vk-device-queue.cpp @@ -0,0 +1,199 @@ +// vk-device-queue.cpp +#include "vk-device-queue.h" + +#include +#include +#include + +namespace gfx { +using namespace Slang; + +VulkanDeviceQueue::~VulkanDeviceQueue() +{ + for (int i = 0; i < int(EventType::CountOf); ++i) + { + m_api->vkDestroySemaphore(m_api->m_device, m_semaphores[i], nullptr); + } + + for (int i = 0; i < m_numCommandBuffers; i++) + { + m_api->vkFreeCommandBuffers(m_api->m_device, m_commandPool, 1, &m_commandBuffers[i]); + m_api->vkDestroyFence(m_api->m_device, m_fences[i].fence, nullptr); + } + m_api->vkDestroyCommandPool(m_api->m_device, m_commandPool, nullptr); +} + +SlangResult VulkanDeviceQueue::init(const VulkanApi& api, VkQueue queue, int queueIndex) +{ + assert(m_api == nullptr); + m_api = &api; + + for (int i = 0; i < int(EventType::CountOf); ++i) + { + m_semaphores[i] = VK_NULL_HANDLE; + m_currentSemaphores[i] = VK_NULL_HANDLE; + } + + m_numCommandBuffers = kMaxCommandBuffers; + m_queueIndex = queueIndex; + + m_queue = queue; + + VkCommandPoolCreateInfo poolCreateInfo = {}; + poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + poolCreateInfo.queueFamilyIndex = queueIndex; + + api.vkCreateCommandPool(api.m_device, &poolCreateInfo, nullptr, &m_commandPool); + + VkCommandBufferAllocateInfo commandInfo = {}; + commandInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandInfo.commandPool = m_commandPool; + commandInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandInfo.commandBufferCount = 1; + + VkFenceCreateInfo fenceCreateInfo = {}; + fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceCreateInfo.flags = 0; // VK_FENCE_CREATE_SIGNALED_BIT; + + for (int i = 0; i < m_numCommandBuffers; i++) + { + Fence& fence = m_fences[i]; + + api.vkAllocateCommandBuffers(api.m_device, &commandInfo, &m_commandBuffers[i]); + + api.vkCreateFence(api.m_device, &fenceCreateInfo, nullptr, &fence.fence); + fence.active = false; + fence.value = 0; + } + + VkSemaphoreCreateInfo semaphoreCreateInfo = {}; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + for (int i = 0; i < int(EventType::CountOf); ++i) + { + api.vkCreateSemaphore(api.m_device, &semaphoreCreateInfo, nullptr, &m_semaphores[i]); + } + + // Second step of flush to prime command buffer + flushStepB(); + + return SLANG_OK; +} + +void VulkanDeviceQueue::flushStepA() +{ + m_api->vkEndCommandBuffer(m_commandBuffer); + + VkPipelineStageFlags stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + // Wait semaphores + if (isCurrent(EventType::BeginFrame)) + { + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &m_currentSemaphores[int(EventType::BeginFrame)]; + } + + submitInfo.pWaitDstStageMask = &stageFlags; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &m_commandBuffer; + + // Signal semaphores + if (isCurrent(EventType::EndFrame)) + { + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &m_currentSemaphores[int(EventType::EndFrame)]; + } + + Fence& fence = m_fences[m_commandBufferIndex]; + + m_api->vkQueueSubmit(m_queue, 1, &submitInfo, fence.fence); + + // mark signaled fence value + fence.value = m_nextFenceValue; + fence.active = true; + + // increment fence value + m_nextFenceValue++; + + // No longer waiting on this semaphore + makeCompleted(EventType::BeginFrame); +} + +void VulkanDeviceQueue::_updateFenceAtIndex( int fenceIndex, bool blocking) +{ + Fence& fence = m_fences[fenceIndex]; + + if (fence.active) + { + uint64_t timeout = blocking ? ~uint64_t(0) : 0; + + if (VK_SUCCESS == m_api->vkWaitForFences(m_api->m_device, 1, &fence.fence, VK_TRUE, timeout)) + { + m_api->vkResetFences(m_api->m_device, 1, &fence.fence); + + fence.active = false; + + if (fence.value > m_lastFenceCompleted) + { + m_lastFenceCompleted = fence.value; + } + } + } +} + +void VulkanDeviceQueue::flushStepB() +{ + m_commandBufferIndex = (m_commandBufferIndex + 1) % m_numCommandBuffers; + m_commandBuffer = m_commandBuffers[m_commandBufferIndex]; + + // non-blocking update of fence values + for (int i = 0; i < m_numCommandBuffers; ++i) + { + _updateFenceAtIndex(i, false); + } + + // blocking update of fence values + _updateFenceAtIndex(m_commandBufferIndex, true); + + m_api->vkResetCommandBuffer(m_commandBuffer, 0); + + //m_api.vkResetCommandPool(m_api->m_device, m_commandPool, 0); + + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + m_api->vkBeginCommandBuffer(m_commandBuffer, &beginInfo); +} + +void VulkanDeviceQueue::flush() +{ + flushStepA(); + flushStepB(); +} + +void VulkanDeviceQueue::flushAndWait() +{ + flush(); + waitForIdle(); +} + +VkSemaphore VulkanDeviceQueue::makeCurrent(EventType eventType) +{ + assert(!isCurrent(eventType)); + VkSemaphore semaphore = m_semaphores[int(eventType)]; + m_currentSemaphores[int(eventType)] = semaphore; + return semaphore; +} + +void VulkanDeviceQueue::makeCompleted(EventType eventType) +{ + m_currentSemaphores[int(eventType)] = VK_NULL_HANDLE; +} + +} // renderer_test diff --git a/tools/gfx/vk-device-queue.h b/tools/gfx/vk-device-queue.h new file mode 100644 index 000000000..d57483ec0 --- /dev/null +++ b/tools/gfx/vk-device-queue.h @@ -0,0 +1,94 @@ +// vk-swap-chain.h +#pragma once + +#include "vk-api.h" + +namespace gfx { + +struct VulkanDeviceQueue +{ + enum + { + kMaxCommandBuffers = 8, + }; + + enum class EventType + { + BeginFrame, + EndFrame, + CountOf, + }; + + /// Initialize - must be called before anything else can be done + SlangResult init(const VulkanApi& api, VkQueue queue, int queueIndex); + + /// Flushes the current command list, and steps to next (internally this is equivalent to a stepA followed by stepB) + void flush(); + /// Performs a full flush, and then waits for idle. + void flushAndWait(); + + /// Blocks until all work submitted to GPU has completed + void waitForIdle() { m_api->vkQueueWaitIdle(m_queue); } + + /// Get the graphics queue index (as set on init) + int getQueueIndex() const { return m_queueIndex; } + + /// Make the specified event 'current' - meaning it's semaphore must be waited on + VkSemaphore makeCurrent(EventType eventType); + /// Makes the event no longer required to be waited on + void makeCompleted(EventType eventType); + /// Returns true if the event is already current + SLANG_FORCE_INLINE bool isCurrent(EventType eventType) const { return m_currentSemaphores[int(eventType)] != VK_NULL_HANDLE; } + + /// Get the command buffer + VkCommandBuffer getCommandBuffer() const { return m_commandBuffer; } + + /// Get the queue + VkQueue getQueue() const { return m_queue; } + + /// Get the API + const VulkanApi* getApi() const { return m_api; } + + /// Flushes the current command list + void flushStepA(); + /// Steps to next command buffer and opens. May block if command buffer is still in use + void flushStepB(); + + /// Dtor + ~VulkanDeviceQueue(); + + protected: + + struct Fence + { + VkFence fence; + bool active; + uint64_t value; + }; + + void _updateFenceAtIndex(int fenceIndex, bool blocking); + + VkQueue m_queue = VK_NULL_HANDLE; + + VkCommandPool m_commandPool = VK_NULL_HANDLE; + int m_numCommandBuffers = 0; + int m_commandBufferIndex = 0; + // There are the same amount of command buffers as fences + VkCommandBuffer m_commandBuffers[kMaxCommandBuffers] = { VK_NULL_HANDLE }; + + Fence m_fences[kMaxCommandBuffers] = { {VK_NULL_HANDLE, 0, 0u} }; + + VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE; + + VkSemaphore m_semaphores[int(EventType::CountOf)]; + VkSemaphore m_currentSemaphores[int(EventType::CountOf)]; + + uint64_t m_lastFenceCompleted = 1; + uint64_t m_nextFenceValue = 2; + + int m_queueIndex = 0; + + const VulkanApi* m_api = nullptr; +}; + +} // renderer_test diff --git a/tools/gfx/vk-module.cpp b/tools/gfx/vk-module.cpp new file mode 100644 index 000000000..4e92a3d2c --- /dev/null +++ b/tools/gfx/vk-module.cpp @@ -0,0 +1,76 @@ +// module.cpp +#include "vk-module.h" + +#include +#include +#include + +#if SLANG_WINDOWS_FAMILY +# include +#else +# include +#endif + +namespace gfx { +using namespace Slang; + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanModule !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +Slang::Result VulkanModule::init() +{ + if (isInitialized()) + { + destroy(); + return SLANG_OK; + } + + const char* dynamicLibraryName = "Unknown"; + +#if SLANG_WINDOWS_FAMILY + dynamicLibraryName = "vulkan-1.dll"; + HMODULE module = ::LoadLibraryA(dynamicLibraryName); + m_module = (void*)module; +#else + dynamicLibraryName = "libvulkan.so.1"; + m_module = dlopen(dynamicLibraryName, RTLD_NOW); +#endif + + if (!m_module) + { + fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); + return SLANG_FAIL; + } + + return SLANG_OK; +} + +PFN_vkVoidFunction VulkanModule::getFunction(const char* name) const +{ + assert(m_module); + if (!m_module) + { + return nullptr; + } +#if SLANG_WINDOWS_FAMILY + return (PFN_vkVoidFunction)::GetProcAddress((HMODULE)m_module, name); +#else + return (PFN_vkVoidFunction)dlsym(m_module, name); +#endif +} + +void VulkanModule::destroy() +{ + if (!isInitialized()) + { + return; + } + +#if SLANG_WINDOWS_FAMILY + ::FreeLibrary((HMODULE)m_module); +#else + dlclose(m_module); +#endif + m_module = nullptr; +} + +} // renderer_test diff --git a/tools/gfx/vk-module.h b/tools/gfx/vk-module.h new file mode 100644 index 000000000..55e26f335 --- /dev/null +++ b/tools/gfx/vk-module.h @@ -0,0 +1,39 @@ +// vk-module.h +#pragma once + +#include "../../slang.h" + +#include "../../slang-com-helper.h" + +#if SLANG_WINDOWS_FAMILY +# define VK_USE_PLATFORM_WIN32_KHR 1 +#else +# define VK_USE_PLATFORM_XLIB_KHR 1 +#endif + +#define VK_NO_PROTOTYPES +#include + +namespace gfx { + +struct VulkanModule +{ + /// true if has been initialized + SLANG_FORCE_INLINE bool isInitialized() const { return m_module != nullptr; } + + /// Get a function by name + PFN_vkVoidFunction getFunction(const char* name) const; + + /// Initialize + Slang::Result init(); + /// Destroy + void destroy(); + + /// Dtor + ~VulkanModule() { destroy(); } + + protected: + void* m_module = nullptr; +}; + +} // renderer_test diff --git a/tools/gfx/vk-swap-chain.cpp b/tools/gfx/vk-swap-chain.cpp new file mode 100644 index 000000000..6e89c946c --- /dev/null +++ b/tools/gfx/vk-swap-chain.cpp @@ -0,0 +1,421 @@ +// vk-swap-chain.cpp +#include "vk-swap-chain.h" + +#include "vk-util.h" + +#include "../../source/core/list.h" + +#include +#include + +namespace gfx { +using namespace Slang; + +static int _indexOf(List& formatsIn, VkFormat format) +{ + const int numFormats = int(formatsIn.Count()); + const VkSurfaceFormatKHR* formats = formatsIn.Buffer(); + + for (int i = 0; i < numFormats; ++i) + { + if (formats[i].format == format) + { + return i; + } + } + return -1; +} + +SlangResult VulkanSwapChain::init(VulkanDeviceQueue* deviceQueue, const Desc& descIn, const PlatformDesc* platformDescIn) +{ + assert(platformDescIn); + + m_deviceQueue = deviceQueue; + m_api = deviceQueue->getApi(); + + // Make sure it's not set initially + m_format = VK_FORMAT_UNDEFINED; + + Desc desc(descIn); + +#if SLANG_WINDOWS_FAMILY + const WinPlatformDesc* platformDesc = static_cast(platformDescIn); + _setPlatformDesc(*platformDesc); + + VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.hinstance = platformDesc->m_hinstance; + surfaceCreateInfo.hwnd = platformDesc->m_hwnd; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateWin32SurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); +#else + const XPlatformDesc* platformDesc = static_cast(platformDescIn); + _setPlatformDesc(*platformDesc); + + VkXlibSurfaceCreateInfoKHR surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.dpy = platformDesc->m_display; + surfaceCreateInfo.window = platformDesc->m_window; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateXlibSurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); +#endif + + VkBool32 supported = false; + m_api->vkGetPhysicalDeviceSurfaceSupportKHR(m_api->m_physicalDevice, deviceQueue->getQueueIndex(), m_surface, &supported); + + uint32_t numSurfaceFormats = 0; + List surfaceFormats; + m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, nullptr); + surfaceFormats.SetSize(int(numSurfaceFormats)); + m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, surfaceFormats.Buffer()); + + // Look for a suitable format + List formats; + formats.Add(VulkanUtil::getVkFormat(desc.m_format)); + // HACK! To check for a different format if couldn't be found + if (descIn.m_format == Format::RGBA_Unorm_UInt8) + { + formats.Add(VK_FORMAT_B8G8R8A8_UNORM); + } + + for(int i = 0; i < int(formats.Count()); ++i) + { + VkFormat format = formats[i]; + if (_indexOf(surfaceFormats, format) >= 0) + { + m_format = format; + } + } + + if (m_format == VK_FORMAT_UNDEFINED) + { + return SLANG_FAIL; + } + + // Save the desc + m_desc = desc; + + SLANG_RETURN_ON_FAIL(_createSwapChain()); + + m_desc = desc; + return SLANG_OK; +} + +void VulkanSwapChain::getWindowSize(int* widthOut, int* heightOut) const +{ +#if SLANG_WINDOWS_FAMILY + auto platformDesc = _getPlatformDesc(); + + RECT rc; + ::GetClientRect(platformDesc->m_hwnd, &rc); + *widthOut = rc.right - rc.left; + *heightOut = rc.bottom - rc.top; +#else + auto platformDesc = _getPlatformDesc(); + + XWindowAttributes winAttr = {}; + XGetWindowAttributes(platformDesc->m_display, platformDesc->m_window, &winAttr); + + *widthOut = winAttr.width; + *heightOut = winAttr.height; +#endif +} + +SlangResult VulkanSwapChain::_createFrameBuffers(VkRenderPass renderPass) +{ + assert(renderPass != VK_NULL_HANDLE); + + for (int i = 0; i < int(m_images.Count()); ++i) + { + Image& image = m_images[i]; + VkImageView attachments[] = + { + image.m_imageView + }; + + VkFramebufferCreateInfo framebufferInfo = {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = m_width; + framebufferInfo.height = m_height; + framebufferInfo.layers = 1; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateFramebuffer(m_api->m_device, &framebufferInfo, nullptr, &image.m_frameBuffer)); + } + + return SLANG_OK; +} + +void VulkanSwapChain::_destroyFrameBuffers() +{ + for (int i = 0; i < int(m_images.Count()); ++i) + { + Image& image = m_images[i]; + if (image.m_frameBuffer != VK_NULL_HANDLE) + { + m_api->vkDestroyFramebuffer(m_api->m_device, image.m_frameBuffer, nullptr); + image.m_frameBuffer = VK_NULL_HANDLE; + } + } +} + +SlangResult VulkanSwapChain::createFrameBuffers(VkRenderPass renderPass) +{ + if (m_renderPass != VK_NULL_HANDLE) + { + _destroyFrameBuffers(); + m_renderPass = VK_NULL_HANDLE; + } + if (renderPass != VK_NULL_HANDLE) + { + SLANG_RETURN_ON_FAIL(_createFrameBuffers(renderPass)); + } + m_renderPass = renderPass; + return SLANG_OK; +} + +SlangResult VulkanSwapChain::_createSwapChain() +{ + if (hasValidSwapChain()) + { + return SLANG_OK; + } + + int width, height; + getWindowSize(&width, &height); + + VkExtent2D imageExtent = {}; + imageExtent.width = width; + imageExtent.height = height; + + m_width = width; + m_height = height; + + // catch this before throwing error + if (m_width == 0 || m_height == 0) + { + return SLANG_FAIL; + } + + // It is necessary to query the caps -> otherwise the LunarG verification layer will issue an error + { + VkSurfaceCapabilitiesKHR surfaceCaps; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_api->m_physicalDevice, m_surface, &surfaceCaps)); + } + + List presentModes; + uint32_t numPresentModes = 0; + m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, nullptr); + presentModes.SetSize(numPresentModes); + m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, presentModes.Buffer()); + + { + int numCheckPresentOptions = 3; + VkPresentModeKHR presentOptions[] = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR }; + if (m_vsync) + { + presentOptions[0] = VK_PRESENT_MODE_FIFO_KHR; + presentOptions[1] = VK_PRESENT_MODE_IMMEDIATE_KHR; + presentOptions[2] = VK_PRESENT_MODE_MAILBOX_KHR; + } + + m_presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; // Invalid + + // Find the first option that's available on the device + for (int j = 0; j < numCheckPresentOptions; j++) + { + if (presentModes.IndexOf(presentOptions[j]) != UInt(-1)) + { + m_presentMode = presentOptions[j]; + break; + } + } + + if (m_presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR) + { + return SLANG_FAIL; + } + } + + VkSwapchainKHR oldSwapchain = VK_NULL_HANDLE; + + VkSwapchainCreateInfoKHR swapchainDesc = {}; + swapchainDesc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainDesc.surface = m_surface; + swapchainDesc.minImageCount = 3; + swapchainDesc.imageFormat = m_format; + swapchainDesc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + swapchainDesc.imageExtent = imageExtent; + swapchainDesc.imageArrayLayers = 1; + swapchainDesc.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swapchainDesc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainDesc.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + swapchainDesc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchainDesc.presentMode = m_presentMode; + swapchainDesc.clipped = VK_TRUE; + swapchainDesc.oldSwapchain = oldSwapchain; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateSwapchainKHR(m_api->m_device, &swapchainDesc, nullptr, &m_swapChain)); + + uint32_t numSwapChainImages = 0; + m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, nullptr); + + { + List images; + images.SetSize(numSwapChainImages); + + m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, images.Buffer()); + + m_images.SetSize(numSwapChainImages); + for (int i = 0; i < int(numSwapChainImages); ++i) + { + Image& dstImage = m_images[i]; + dstImage.m_image = images[i]; + + } + } + + { + VkImageViewCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = m_format; + + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + for (int i = 0; i < int(numSwapChainImages); ++i) + { + Image& image = m_images[i]; + + createInfo.image = image.m_image; + + SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateImageView(m_api->m_device, &createInfo, nullptr, &image.m_imageView)); + } + } + + if (m_renderPass != VK_NULL_HANDLE) + { + _createFrameBuffers(m_renderPass); + } + + return SLANG_OK; +} + +void VulkanSwapChain::_destroySwapChain() +{ + if (!hasValidSwapChain()) + { + return; + } + + m_deviceQueue->waitForIdle(); + + if (m_renderPass != VK_NULL_HANDLE) + { + _destroyFrameBuffers(); + } + + for (int i = 0; i < int(m_images.Count()); ++i) + { + Image& image = m_images[i]; + + if (image.m_imageView != VK_NULL_HANDLE) + { + m_api->vkDestroyImageView(m_api->m_device, image.m_imageView, nullptr); + } + } + + if (m_swapChain != VK_NULL_HANDLE) + { + m_api->vkDestroySwapchainKHR(m_api->m_device, m_swapChain, nullptr); + m_swapChain = VK_NULL_HANDLE; + } + + // Mark that it is no longer used + m_images.Clear(); +} + +VulkanSwapChain::~VulkanSwapChain() +{ + _destroySwapChain(); + + if (m_surface) + { + m_api->vkDestroySurfaceKHR(m_api->m_instance, m_surface, nullptr); + m_surface = VK_NULL_HANDLE; + } +} + +int VulkanSwapChain::nextFrontImageIndex() +{ + if (!hasValidSwapChain()) + { + if (SLANG_FAILED(_createSwapChain())) + { + return -1; + } + } + + VkSemaphore beginFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::BeginFrame); + + uint32_t swapChainIndex = 0; + VkResult result = m_api->vkAcquireNextImageKHR(m_api->m_device, m_swapChain, UINT64_MAX, beginFrameSemaphore, VK_NULL_HANDLE, &swapChainIndex); + + if (result != VK_SUCCESS) + { + _destroySwapChain(); + return -1; + } + m_currentSwapChainIndex = int(swapChainIndex); + return swapChainIndex; +} + +void VulkanSwapChain::present(bool vsync) +{ + if (!hasValidSwapChain()) + { + m_deviceQueue->flush(); + return; + } + + VkSemaphore endFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::EndFrame); + + m_deviceQueue->flushStepA(); + + uint32_t swapChainIndices[] = { uint32_t(m_currentSwapChainIndex) }; + + VkPresentInfoKHR presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &m_swapChain; + presentInfo.pImageIndices = swapChainIndices; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = &endFrameSemaphore; + + VkResult result = m_api->vkQueuePresentKHR(m_deviceQueue->getQueue(), &presentInfo); + + m_deviceQueue->makeCompleted(VulkanDeviceQueue::EventType::EndFrame); + + m_deviceQueue->flushStepB(); + + if (result != VK_SUCCESS || m_vsync != vsync) + { + m_vsync = vsync; + _destroySwapChain(); + } +} + +} // renderer_test diff --git a/tools/gfx/vk-swap-chain.h b/tools/gfx/vk-swap-chain.h new file mode 100644 index 000000000..12feb0ed5 --- /dev/null +++ b/tools/gfx/vk-swap-chain.h @@ -0,0 +1,141 @@ +// vk-swap-chain.h +#pragma once + +#include "vk-api.h" +#include "vk-device-queue.h" + +#include "render.h" + +#include "../../source/core/list.h" + +namespace gfx { + +struct VulkanSwapChain +{ + /* enum + { + kMaxImages = 8, + }; */ + + /// Base class for platform specific information + struct PlatformDesc + { + }; + +#if SLANG_WINDOWS_FAMILY + struct WinPlatformDesc: public PlatformDesc + { + HINSTANCE m_hinstance; + HWND m_hwnd; + }; +#else + struct XPlatformDesc : public PlatformDesc + { + Display* m_display; + Window m_window; + }; +#endif + + struct Desc + { + void init() + { + m_format = Format::Unknown; + m_depthFormatTypeless = Format::Unknown; + m_depthFormat = Format::Unknown; + m_textureDepthFormat = Format::Unknown; + } + + Format m_format; + //bool m_enableFormat; + Format m_depthFormatTypeless; + Format m_depthFormat; + Format m_textureDepthFormat; + }; + + struct Image + { + VkImage m_image = VK_NULL_HANDLE; + VkImageView m_imageView = VK_NULL_HANDLE; + VkFramebuffer m_frameBuffer = VK_NULL_HANDLE; + }; + + + /// Must be called before the swap chain can be used + SlangResult init(VulkanDeviceQueue* deviceQueue, const Desc& desc, const PlatformDesc* platformDesc); + + /// Create the frame buffers (they must be compatible with the supplied renderPass) + SlangResult createFrameBuffers(VkRenderPass renderPass); + + /// Returned the desc used to construct the swap chain. + /// Is invalid if init hasn't returned with successful result. + const Desc& getDesc() const { return m_desc; } + + /// True if the swap chain is available + bool hasValidSwapChain() const { return m_images.Count() > 0; } + + /// Present to the display + void present(bool vsync); + + /// Get the current size of the window (in pixels written to widthOut, heightOut) + void getWindowSize(int* widthOut, int* heightOut) const; + + /// Get the VkFormat for the back buffer + VkFormat getVkFormat() const { return m_format; } + + /// Get width of the back buffers + int getWidth() const { return m_width; } + /// Get the height of the back buffer + int getHeight() const { return m_height; } + + /// Get the detail about the images + const Slang::List& getImages() const { return m_images; } + + /// Get the next front render image index. Returns -1, if image couldn't be found + int nextFrontImageIndex(); + + /// Dtor + ~VulkanSwapChain(); + + protected: + + + template + void _setPlatformDesc(const T& desc) + { + const PlatformDesc* check = &desc; + int size = (sizeof(T) + sizeof(void*) - 1) / sizeof(void*); + m_platformDescBuffer.SetSize(size); + *(T*)m_platformDescBuffer.Buffer() = desc; + } + template + const T* _getPlatformDesc() const { return static_cast((const PlatformDesc*)m_platformDescBuffer.Buffer()); } + SlangResult _createSwapChain(); + void _destroySwapChain(); + SlangResult _createFrameBuffers(VkRenderPass renderPass); + void _destroyFrameBuffers(); + + bool m_vsync = true; + int m_width = 0; + int m_height = 0; + + VkPresentModeKHR m_presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + VkFormat m_format = VK_FORMAT_UNDEFINED; ///< The format used for backbuffer. Valid after successful init. + + VkSurfaceKHR m_surface = VK_NULL_HANDLE; + VkSwapchainKHR m_swapChain = VK_NULL_HANDLE; + + VkRenderPass m_renderPass = VK_NULL_HANDLE; //< Not owned + + int m_currentSwapChainIndex = 0; + + Slang::List m_images; + + VulkanDeviceQueue* m_deviceQueue = nullptr; + const VulkanApi* m_api = nullptr; + + Desc m_desc; ///< The desc used to init this swap chain + Slang::List m_platformDescBuffer; ///< Buffer to hold the platform specific description parameters (as passed in platformDesc) +}; + +} // renderer_test diff --git a/tools/gfx/vk-util.cpp b/tools/gfx/vk-util.cpp new file mode 100644 index 000000000..e8940d1b2 --- /dev/null +++ b/tools/gfx/vk-util.cpp @@ -0,0 +1,59 @@ +// vk-util.cpp +#include "vk-util.h" + +#include +#include + +namespace gfx { + +/* static */VkFormat VulkanUtil::getVkFormat(Format format) +{ + switch (format) + { + case Format::RGBA_Float32: return VK_FORMAT_R32G32B32A32_SFLOAT; + case Format::RGB_Float32: return VK_FORMAT_R32G32B32_SFLOAT; + case Format::RG_Float32: return VK_FORMAT_R32G32_SFLOAT; + case Format::R_Float32: return VK_FORMAT_R32_SFLOAT; + case Format::RGBA_Unorm_UInt8: return VK_FORMAT_R8G8B8A8_UNORM; + case Format::R_UInt32: return VK_FORMAT_R32_UINT; + + case Format::D_Float32: return VK_FORMAT_D32_SFLOAT; + case Format::D_Unorm24_S8: return VK_FORMAT_D24_UNORM_S8_UINT; + + default: return VK_FORMAT_UNDEFINED; + } +} + +/* static */SlangResult VulkanUtil::toSlangResult(VkResult res) +{ + return (res == VK_SUCCESS) ? SLANG_OK : SLANG_FAIL; +} + +/* static */Slang::Result VulkanUtil::handleFail(VkResult res) +{ + if (res != VK_SUCCESS) + { + assert(!"Vulkan returned a failure"); + } + return toSlangResult(res); +} + +/* static */void VulkanUtil::checkFail(VkResult res) +{ + assert(res != VK_SUCCESS); + assert(!"Vulkan check failed"); + +} + +/* static */VkPrimitiveTopology VulkanUtil::getVkPrimitiveTopology(PrimitiveTopology topology) +{ + switch (topology) + { + case PrimitiveTopology::TriangleList: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + default: break; + } + assert(!"Unknown topology"); + return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; +} + +} // renderer_test diff --git a/tools/gfx/vk-util.h b/tools/gfx/vk-util.h new file mode 100644 index 000000000..edba3a7d2 --- /dev/null +++ b/tools/gfx/vk-util.h @@ -0,0 +1,41 @@ +// vk-util.h +#pragma once + +#include "vk-api.h" +#include "render.h" + +// Macros to make testing vulkan return codes simpler + +/// SLANG_VK_RETURN_ON_FAIL can be used in a similar way to SLANG_RETURN_ON_FAIL macro, except it will turn a vulkan failure into Slang::Result in the process +/// Calls handleFail which on debug builds asserts +#define SLANG_VK_RETURN_ON_FAIL(x) { VkResult _res = x; if (_res != VK_SUCCESS) { return VulkanUtil::handleFail(_res); } } + +#define SLANG_VK_RETURN_NULL_ON_FAIL(x) { VkResult _res = x; if (_res != VK_SUCCESS) { VulkanUtil::handleFail(_res); return nullptr; } } + +/// Is similar to SLANG_VK_RETURN_ON_FAIL, but does not return. Will call checkFail on failure - which asserts on debug builds. +#define SLANG_VK_CHECK(x) { VkResult _res = x; if (_res != VK_SUCCESS) { VulkanUtil::checkFail(_res); } } + +namespace gfx { + +// Utility functions for Vulkan +struct VulkanUtil +{ + /// Get the equivalent VkFormat from the format + /// Returns VK_FORMAT_UNDEFINED if a match is not found + static VkFormat getVkFormat(Format format); + + /// Called by SLANG_VK_RETURN_FAIL if a res is a failure. + /// On debug builds this will cause an assertion on failure. + static Slang::Result handleFail(VkResult res); + /// Called when a failure has occurred with SLANG_VK_CHECK - will typically assert. + static void checkFail(VkResult res); + + /// Get the VkPrimitiveTopology for the given topology. + /// Returns VK_PRIMITIVE_TOPOLOGY_MAX_ENUM on failure + static VkPrimitiveTopology getVkPrimitiveTopology(PrimitiveTopology topology); + + /// Returns Slang::Result equivalent of a VkResult + static Slang::Result toSlangResult(VkResult res); +}; + +} // renderer_test diff --git a/tools/gfx/window.cpp b/tools/gfx/window.cpp new file mode 100644 index 000000000..ee9f50813 --- /dev/null +++ b/tools/gfx/window.cpp @@ -0,0 +1,289 @@ +// window.cpp +#include "window.h" +#pragma once + +#include + +#ifdef _MSC_VER +#include +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif + +#include + + +#if _WIN32 +#include +#else +#error "The slang-graphics library currently only supports Windows platforms" +#endif + +namespace gfx { + +#if _WIN32 + +struct OSString +{ + OSString(char const* begin, char const* end) + { + _initialize(begin, end - begin); + } + + OSString(char const* begin) + { + _initialize(begin, strlen(begin)); + } + + ~OSString() + { + free(mBegin); + } + + operator WCHAR const*() + { + return mBegin; + } + +private: + WCHAR* mBegin; + WCHAR* mEnd; + + void _initialize(char const* input, size_t inputSize) + { + const DWORD dwFlags = 0; + int outputCodeUnitCount = ::MultiByteToWideChar(CP_UTF8, dwFlags, input, int(inputSize), nullptr, 0); + + WCHAR* buffer = (WCHAR*)malloc(sizeof(WCHAR) * (outputCodeUnitCount + 1)); + + ::MultiByteToWideChar(CP_UTF8, dwFlags, input, int(inputSize), buffer, outputCodeUnitCount); + buffer[outputCodeUnitCount] = 0; + + mBegin = buffer; + mEnd = buffer + outputCodeUnitCount; + } +}; + +struct ApplicationContext +{ + HINSTANCE instance; + int showCommand = SW_SHOWDEFAULT; + int resultCode = 0; +}; + +static uint64_t gTimerFrequency; + + +static void initApplication(ApplicationContext* context) +{ + LARGE_INTEGER timerFrequency; + QueryPerformanceFrequency(&timerFrequency); + gTimerFrequency = timerFrequency.QuadPart; +} + +/// Run an application given the specified callback and command-line arguments. +int runApplication( + ApplicationFunc func, + int argc, + char const* const* argv) +{ + ApplicationContext context; + context.instance = (HINSTANCE) GetModuleHandle(0); + initApplication(&context); + func(&context); + return context.resultCode; +} + +int runWindowsApplication( + ApplicationFunc func, + void* instance, + int showCommand) +{ + ApplicationContext context; + context.instance = (HINSTANCE) instance; + context.showCommand = showCommand; + initApplication(&context); + func(&context); + return context.resultCode; +} + +struct Window +{ + HWND handle; +}; + +static LRESULT CALLBACK windowProc( + HWND windowHandle, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + // TODO: Actually implement some reasonable logic here. + switch (message) + { + case WM_CLOSE: + PostQuitMessage(0); + return 0; + } + + return DefWindowProcW(windowHandle, message, wParam, lParam); +} + + +static ATOM createWindowClassAtom() +{ + WNDCLASSEXW windowClassDesc; + windowClassDesc.cbSize = sizeof(windowClassDesc); + windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + windowClassDesc.lpfnWndProc = &windowProc; + windowClassDesc.cbClsExtra = 0; + windowClassDesc.cbWndExtra = 0; + windowClassDesc.hInstance = (HINSTANCE) GetModuleHandle(0); + windowClassDesc.hIcon = 0; + windowClassDesc.hCursor = 0; + windowClassDesc.hbrBackground = 0; + windowClassDesc.lpszMenuName = 0; + windowClassDesc.lpszClassName = L"SlangGraphicsWindow"; + windowClassDesc.hIconSm = 0; + ATOM windowClassAtom = RegisterClassExW(&windowClassDesc); + return windowClassAtom; +} + +static ATOM getWindowClassAtom() +{ + static ATOM windowClassAtom = createWindowClassAtom(); + return windowClassAtom; +} + +Window* createWindow(WindowDesc const& desc) +{ + Window* window = new Window(); + + OSString windowTitle(desc.title); + + DWORD windowExtendedStyle = 0; + DWORD windowStyle = 0; + + HINSTANCE instance = (HINSTANCE) GetModuleHandle(0); + + HWND windowHandle = CreateWindowExW( + windowExtendedStyle, + (LPWSTR) getWindowClassAtom(), + windowTitle, + windowStyle, + 0, 0, // x, y + desc.width, desc.height, + NULL, // parent + NULL, // menu + instance, + window); + + if(!windowHandle) + { + delete window; + return nullptr; + } + + window->handle = windowHandle; + return window; +} + +void showWindow(Window* window) +{ + ShowWindow(window->handle, SW_SHOW); +} + +void* getPlatformWindowHandle(Window* window) +{ + return window->handle; +} + +bool dispatchEvents(ApplicationContext* context) +{ + for(;;) + { + MSG message; + + int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); + if (result != 0) + { + if (message.message == WM_QUIT) + { + context->resultCode = (int)message.wParam; + return false; + } + + TranslateMessage(&message); + DispatchMessageW(&message); + } + else + { + return true; + } + } + +} + +void exitApplication(ApplicationContext* context, int resultCode) +{ + ExitProcess(resultCode); +} + +void log(char const* message, ...) +{ + va_list args; + va_start(args, message); + + static const int kBufferSize = 1024; + char messageBuffer[kBufferSize]; + vsnprintf(messageBuffer, kBufferSize - 1, message, args); + messageBuffer[kBufferSize - 1] = 0; + + va_end(args); + + fputs(messageBuffer, stderr); + + OSString wideMessageBuffer(messageBuffer); + OutputDebugStringW(wideMessageBuffer); +} + +int reportError(char const* message, ...) +{ + va_list args; + va_start(args, message); + + static const int kBufferSize = 1024; + char messageBuffer[kBufferSize]; + vsnprintf(messageBuffer, kBufferSize - 1, message, args); + messageBuffer[kBufferSize - 1] = 0; + + va_end(args); + + fputs(messageBuffer, stderr); + + OSString wideMessageBuffer(messageBuffer); + OutputDebugStringW(wideMessageBuffer); + + return 1; +} + +uint64_t getCurrentTime() +{ + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return counter.QuadPart; +} + +uint64_t getTimerFrequency() +{ + return gTimerFrequency; +} + +#else + +// TODO: put an SDL version here + +#endif + +} // gfx diff --git a/tools/gfx/window.h b/tools/gfx/window.h new file mode 100644 index 000000000..6e557d26c --- /dev/null +++ b/tools/gfx/window.h @@ -0,0 +1,78 @@ +// window.h +#pragma once + +#include + +namespace gfx { + +struct WindowDesc +{ + char const* title; + int width; + int height; +}; + +typedef struct Window Window; + +Window* createWindow(WindowDesc const& desc); +void showWindow(Window* window); +void* getPlatformWindowHandle(Window* window); + +/// Opaque state provided by platform for a running application. +typedef struct ApplicationContext ApplicationContext; + +/// User-defined application entry-point function. +typedef void(*ApplicationFunc)(ApplicationContext* context); + +/// Dispatch any pending events for application. +/// +/// @returns `true` if application should keep running. +bool dispatchEvents(ApplicationContext* context); + +/// Exit the application with a given result code +void exitApplication(ApplicationContext* context, int resultCode); + +/// Log a message to an appropriate logging destination. +void log(char const* message, ...); + +/// Report an error to an appropriate logging destination. +int reportError(char const* message, ...); + +uint64_t getCurrentTime(); + +uint64_t getTimerFrequency(); + +/// Run an application given the specified callback and command-line arguments. +int runApplication( + ApplicationFunc func, + int argc, + char const* const* argv); + +#define GFX_CONSOLE_MAIN(APPLICATION_ENTRY) \ + int main(int argc, char** argv) { \ + return gfx::runApplication(&(APPLIATION_ENTRY), argc, argv); \ + } + +#ifdef _WIN32 + +int runWindowsApplication( + ApplicationFunc func, + void* instance, + int showCommand); + +#define GFX_UI_MAIN(APPLICATION_ENTRY) \ + int __stdcall WinMain( \ + void* instance, \ + void* /* prevInstance */, \ + void* /* commandLine */, \ + int showCommand) { \ + return gfx::runWindowsApplication(&(APPLICATION_ENTRY), instance, showCommand); \ + } + +#else + +#define GFX_UI_MAIN(APPLICATION_ENTRY) GFX_CONSOLE_MAIN(APPLICATION_ENTRY) + +#endif + +} // gfx diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp index 935b9bc98..4734c2c8f 100644 --- a/tools/render-test/main.cpp +++ b/tools/render-test/main.cpp @@ -29,6 +29,8 @@ namespace renderer_test { +using Slang::Result; + int gWindowWidth = 1024; int gWindowHeight = 768; @@ -45,7 +47,7 @@ struct Vertex float uv[2]; }; -static const Vertex kVertexData[] = +static const Vertex kVertexData[] = { { { 0, 0, 0.5 }, {1, 0, 0} , {0, 0} }, { { 0, 1, 0.5 }, {0, 0, 1} , {1, 0} }, @@ -61,15 +63,15 @@ class RenderTestApp // At initialization time, we are going to load and compile our Slang shader // code, and then create the API objects we need for rendering. - Result initialize(Renderer* renderer, ShaderCompiler* shaderCompiler); + Result initialize(Renderer* renderer, ShaderCompiler* shaderCompiler); void runCompute(); void renderFrame(); void finalize(); - BindingState* getBindingState() const { return m_bindingState; } + BindingStateImpl* getBindingState() const { return m_bindingState; } Result writeBindingOutput(const char* fileName); - + Result writeScreen(const char* filename); protected: @@ -85,7 +87,8 @@ class RenderTestApp RefPtr m_inputLayout; RefPtr m_vertexBuffer; RefPtr m_shaderProgram; - RefPtr m_bindingState; + RefPtr m_pipelineState; + RefPtr m_bindingState; ShaderInputLayout m_shaderInputLayout; ///< The binding layout int m_numAddedConstantBuffers; ///< Constant buffers can be added to the binding directly. Will be added at the end. @@ -117,22 +120,26 @@ SlangResult RenderTestApp::initialize(Renderer* renderer, ShaderCompiler* shader } { - BindingState::Desc bindingStateDesc; - SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBindingStateDesc(m_shaderInputLayout, m_renderer, bindingStateDesc)); - - //! Hack -> if bindings are specified, just set up the constant buffer binding - // Should probably be more sophisticated than this - with 'dynamic' constant buffer/s binding always being specified - // in the test file - - if ((gOptions.shaderType == Options::ShaderProgramType::Graphics || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) - && bindingStateDesc.findBindingIndex(Resource::BindFlag::ConstantBuffer, 0) < 0) + //! Hack -> if doing a graphics test, add an extra binding for our dynamic constant buffer + // + // TODO: Should probably be more sophisticated than this - with 'dynamic' constant buffer/s binding always being specified + // in the test file + RefPtr addedConstantBuffer; + switch(gOptions.shaderType) { - bindingStateDesc.addResource(BindingType::Buffer, m_constantBuffer, BindingState::RegisterRange::makeSingle(0) ); + default: + break; + case Options::ShaderProgramType::Graphics: + case Options::ShaderProgramType::GraphicsCompute: + addedConstantBuffer = m_constantBuffer; m_numAddedConstantBuffers++; + break; } - m_bindingState = m_renderer->createBindingState(bindingStateDesc); + BindingStateImpl* bindingState = nullptr; + SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBindingState(m_shaderInputLayout, m_renderer, addedConstantBuffer, &bindingState)); + m_bindingState = bindingState; } // Do other initialization that doesn't depend on the source language. @@ -156,6 +163,38 @@ SlangResult RenderTestApp::initialize(Renderer* renderer, ShaderCompiler* shader if(!m_vertexBuffer) return SLANG_FAIL; + { + switch(gOptions.shaderType) + { + default: + assert(!"unexpected test shader type"); + return SLANG_FAIL; + + case Options::ShaderProgramType::Compute: + { + ComputePipelineStateDesc desc; + desc.pipelineLayout = m_bindingState->pipelineLayout; + desc.program = m_shaderProgram; + + m_pipelineState = renderer->createComputePipelineState(desc); + } + break; + + case Options::ShaderProgramType::Graphics: + case Options::ShaderProgramType::GraphicsCompute: + { + GraphicsPipelineStateDesc desc; + desc.pipelineLayout = m_bindingState->pipelineLayout; + desc.program = m_shaderProgram; + desc.inputLayout = m_inputLayout; + desc.renderTargetCount = m_bindingState->m_numRenderTargets; + + m_pipelineState = renderer->createGraphicsPipelineState(desc); + } + break; + } + } + return SLANG_OK; } @@ -182,6 +221,16 @@ Result RenderTestApp::initializeShaders(ShaderCompiler* shaderCompiler) fclose(sourceFile); sourceText[sourceSize] = 0; + switch( gOptions.shaderType ) + { + default: + m_shaderInputLayout.numRenderTargets = 1; + break; + + case Options::ShaderProgramType::Compute: + m_shaderInputLayout.numRenderTargets = 0; + break; + } m_shaderInputLayout.Parse(sourceText); ShaderCompileRequest::SourceInfo sourceInfo; @@ -220,31 +269,27 @@ void RenderTestApp::renderFrame() { const ProjectionStyle projectionStyle = RendererUtil::getProjectionStyle(m_renderer->getRendererType()); RendererUtil::getIdentityProjection(projectionStyle, (float*)mappedData); - + m_renderer->unmap(m_constantBuffer); } - // Input Assembler (IA) + auto pipelineType = PipelineType::Graphics; - m_renderer->setInputLayout(m_inputLayout); - m_renderer->setPrimitiveTopology(PrimitiveTopology::TriangleList); + m_renderer->setPipelineState(pipelineType, m_pipelineState); + m_renderer->setPrimitiveTopology(PrimitiveTopology::TriangleList); m_renderer->setVertexBuffer(0, m_vertexBuffer, sizeof(Vertex)); - // Vertex Shader (VS) - // Pixel Shader (PS) - - m_renderer->setShaderProgram(m_shaderProgram); - m_renderer->setBindingState(m_bindingState); - // + m_bindingState->apply(m_renderer, pipelineType); m_renderer->draw(3); } void RenderTestApp::runCompute() { - m_renderer->setShaderProgram(m_shaderProgram); - m_renderer->setBindingState(m_bindingState); + auto pipelineType = PipelineType::Compute; + m_renderer->setPipelineState(pipelineType, m_pipelineState); + m_bindingState->apply(m_renderer, pipelineType); m_renderer->dispatchCompute(1, 1, 1); } @@ -265,18 +310,12 @@ Result RenderTestApp::writeBindingOutput(const char* fileName) return SLANG_FAIL; } - const BindingState::Desc& bindingStateDesc = m_bindingState->getDesc(); - // Must be the same amount of entries - assert(bindingStateDesc.m_bindings.Count() == m_shaderInputLayout.entries.Count() + m_numAddedConstantBuffers); - - const int numBindings = int(m_shaderInputLayout.entries.Count()); - - for (int i = 0; i < numBindings; ++i) + for(auto binding : m_bindingState->outputBindings) { + auto i = binding.entryIndex; const auto& layoutBinding = m_shaderInputLayout.entries[i]; - const auto& binding = bindingStateDesc.m_bindings[i]; - if (layoutBinding.isOutput) + assert(layoutBinding.isOutput); { if (binding.resource && binding.resource->isBuffer()) { @@ -524,11 +563,11 @@ SlangResult innerMain(int argc, char** argv) else { Result res = app.writeScreen(gOptions.outputPath); - + if (SLANG_FAILED(res)) { fprintf(stderr, "ERROR: failed to write screen capture to file\n"); - return res; + return res; } } return SLANG_OK; diff --git a/tools/render-test/options.h b/tools/render-test/options.h index 82c018f66..78f673796 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -9,7 +9,7 @@ namespace renderer_test { -using namespace slang_graphics; +using namespace gfx; struct Options { diff --git a/tools/render-test/png-serialize-util.h b/tools/render-test/png-serialize-util.h index dad17ae74..1ec5204f7 100644 --- a/tools/render-test/png-serialize-util.h +++ b/tools/render-test/png-serialize-util.h @@ -5,7 +5,7 @@ namespace renderer_test { -using namespace slang_graphics; +using namespace gfx; struct PngSerializeUtil { diff --git a/tools/render-test/render-test.vcxproj b/tools/render-test/render-test.vcxproj index 66ad9e7ed..91c8bd997 100644 --- a/tools/render-test/render-test.vcxproj +++ b/tools/render-test/render-test.vcxproj @@ -99,7 +99,7 @@ NotUsing Level3 _DEBUG;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;..\slang-graphics;%(AdditionalIncludeDirectories) + ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) EditAndContinue Disabled MultiThreadedDebug @@ -117,7 +117,7 @@ NotUsing Level3 _DEBUG;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;..\slang-graphics;%(AdditionalIncludeDirectories) + ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) EditAndContinue Disabled MultiThreadedDebug @@ -135,7 +135,7 @@ NotUsing Level3 NDEBUG;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;..\slang-graphics;%(AdditionalIncludeDirectories) + ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) Full true true @@ -157,7 +157,7 @@ NotUsing Level3 NDEBUG;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;..\slang-graphics;%(AdditionalIncludeDirectories) + ..\..;..\..\external;..\..\source;..\gfx;%(AdditionalIncludeDirectories) Full true true @@ -196,7 +196,7 @@ {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} - + {222F7498-B40C-4F3F-A704-DDEB91A4484A} diff --git a/tools/render-test/shader-input-layout.h b/tools/render-test/shader-input-layout.h index 19a7e59d0..92dd516a7 100644 --- a/tools/render-test/shader-input-layout.h +++ b/tools/render-test/shader-input-layout.h @@ -7,7 +7,7 @@ namespace renderer_test { -using namespace slang_graphics; +using namespace gfx; enum class ShaderInputType { diff --git a/tools/render-test/shader-renderer-util.cpp b/tools/render-test/shader-renderer-util.cpp index e46c725bc..f6c0366bb 100644 --- a/tools/render-test/shader-renderer-util.cpp +++ b/tools/render-test/shader-renderer-util.cpp @@ -5,6 +5,16 @@ namespace renderer_test { using namespace Slang; +using Slang::Result; + +void BindingStateImpl::apply(Renderer* renderer, PipelineType pipelineType) +{ + renderer->setDescriptorSet( + pipelineType, + pipelineLayout, + 0, + descriptorSet); +} /* static */Result ShaderRendererUtil::generateTextureResource(const InputTextureDesc& inputDesc, int bindFlags, Renderer* renderer, RefPtr& textureOut) { @@ -125,16 +135,27 @@ using namespace Slang; return SLANG_OK; } -static BindingState::SamplerDesc _calcSamplerDesc(const InputSamplerDesc& srcDesc) +static SamplerState::Desc _calcSamplerDesc(const InputSamplerDesc& srcDesc) { - BindingState::SamplerDesc dstDesc; - dstDesc.isCompareSampler = srcDesc.isCompareSampler; + SamplerState::Desc dstDesc; + if (srcDesc.isCompareSampler) + { + dstDesc.reductionOp = TextureReductionOp::Comparison; + dstDesc.comparisonFunc = ComparisonFunc::Less; + } return dstDesc; } -/* static */BindingState::RegisterRange ShaderRendererUtil::calcRegisterRange(Renderer* renderer, const ShaderInputLayoutEntry& entry) +static RefPtr _createSamplerState( + Renderer* renderer, + const InputSamplerDesc& srcDesc) { - typedef BindingState::RegisterRange RegisterRange; + return renderer->createSamplerState(_calcSamplerDesc(srcDesc)); +} + +/* static */BindingStateImpl::RegisterRange ShaderRendererUtil::calcRegisterRange(Renderer* renderer, const ShaderInputLayoutEntry& entry) +{ + typedef BindingStateImpl::RegisterRange RegisterRange; BindingStyle bindingStyle = RendererUtil::getBindingStyle(renderer->getRendererType()); @@ -179,71 +200,227 @@ static BindingState::SamplerDesc _calcSamplerDesc(const InputSamplerDesc& srcDes return RegisterRange::makeInvalid(); } -/* static */Result ShaderRendererUtil::createBindingStateDesc(ShaderInputLayoutEntry* srcEntries, int numEntries, Renderer* renderer, BindingState::Desc& descOut) +/* static */Result ShaderRendererUtil::createBindingState(const ShaderInputLayout& layout, Renderer* renderer, BufferResource* addedConstantBuffer, BindingStateImpl** outBindingState) { + auto srcEntries = layout.entries.Buffer(); + auto numEntries = int(layout.entries.Count()); + const int textureBindFlags = Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource; - descOut.clear(); + List slotRangeDescs; + + if(addedConstantBuffer) + { + DescriptorSetLayout::SlotRangeDesc slotRangeDesc; + slotRangeDesc.type = DescriptorSlotType::UniformBuffer; + + slotRangeDescs.Add(slotRangeDesc); + } + for (int i = 0; i < numEntries; i++) { const ShaderInputLayoutEntry& srcEntry = srcEntries[i]; - const BindingState::RegisterRange registerSet = calcRegisterRange(renderer, srcEntry); + const BindingStateImpl::RegisterRange registerSet = calcRegisterRange(renderer, srcEntry); if (!registerSet.isValid()) { assert(!"Couldn't find a binding"); return SLANG_FAIL; } + DescriptorSetLayout::SlotRangeDesc slotRangeDesc; + switch (srcEntry.type) { case ShaderInputType::Buffer: - { - const InputBufferDesc& srcBuffer = srcEntry.bufferDesc; + { + const InputBufferDesc& srcBuffer = srcEntry.bufferDesc; + + switch (srcBuffer.type) + { + case InputBufferType::ConstantBuffer: + slotRangeDesc.type = DescriptorSlotType::UniformBuffer; + break; + + case InputBufferType::StorageBuffer: + slotRangeDesc.type = DescriptorSlotType::StorageBuffer; + break; + } + } + break; - const size_t bufferSize = srcEntry.bufferData.Count() * sizeof(uint32_t); + case ShaderInputType::CombinedTextureSampler: + { + slotRangeDesc.type = DescriptorSlotType::CombinedImageSampler; + } + break; - RefPtr bufferResource; - SLANG_RETURN_ON_FAIL(createBufferResource(srcEntry.bufferDesc, srcEntry.isOutput, bufferSize, srcEntry.bufferData.Buffer(), renderer, bufferResource)); + case ShaderInputType::Texture: + { + if (srcEntry.textureDesc.isRWTexture) + { + slotRangeDesc.type = DescriptorSlotType::StorageImage; + } + else + { + slotRangeDesc.type = DescriptorSlotType::SampledImage; + } + } + break; - descOut.addBufferResource(bufferResource, registerSet); + case ShaderInputType::Sampler: + slotRangeDesc.type = DescriptorSlotType::Sampler; break; - } + + default: + assert(!"Unhandled type"); + return SLANG_FAIL; + } + slotRangeDescs.Add(slotRangeDesc); + } + + DescriptorSetLayout::Desc descriptorSetLayoutDesc; + descriptorSetLayoutDesc.slotRangeCount = slotRangeDescs.Count(); + descriptorSetLayoutDesc.slotRanges = slotRangeDescs.Buffer(); + + auto descriptorSetLayout = renderer->createDescriptorSetLayout(descriptorSetLayoutDesc); + if(!descriptorSetLayout) return SLANG_FAIL; + + List pipelineDescriptorSets; + pipelineDescriptorSets.Add(PipelineLayout::DescriptorSetDesc(descriptorSetLayout)); + + PipelineLayout::Desc pipelineLayoutDesc; + pipelineLayoutDesc.renderTargetCount = layout.numRenderTargets; + pipelineLayoutDesc.descriptorSetCount = pipelineDescriptorSets.Count(); + pipelineLayoutDesc.descriptorSets = pipelineDescriptorSets.Buffer(); + + auto pipelineLayout = renderer->createPipelineLayout(pipelineLayoutDesc); + if(!pipelineLayout) return SLANG_FAIL; + + auto descriptorSet = renderer->createDescriptorSet(descriptorSetLayout); + if(!descriptorSet) return SLANG_FAIL; + + List outputBindings; + + if(addedConstantBuffer) + { + descriptorSet->setConstantBuffer(0, 0, addedConstantBuffer); + } + for (int i = 0; i < numEntries; i++) + { + const ShaderInputLayoutEntry& srcEntry = srcEntries[i]; + + auto rangeIndex = i + (addedConstantBuffer ? 1 : 0); + + switch (srcEntry.type) + { + case ShaderInputType::Buffer: + { + const InputBufferDesc& srcBuffer = srcEntry.bufferDesc; + const size_t bufferSize = srcEntry.bufferData.Count() * sizeof(uint32_t); + + RefPtr bufferResource; + SLANG_RETURN_ON_FAIL(createBufferResource(srcEntry.bufferDesc, srcEntry.isOutput, bufferSize, srcEntry.bufferData.Buffer(), renderer, bufferResource)); + + switch(srcBuffer.type) + { + case InputBufferType::ConstantBuffer: + descriptorSet->setConstantBuffer(rangeIndex, 0, bufferResource); + break; + + case InputBufferType::StorageBuffer: + { + ResourceView::Desc viewDesc; + viewDesc.type = ResourceView::Type::UnorderedAccess; + viewDesc.format = srcBuffer.format; + auto bufferView = renderer->createBufferView( + bufferResource, + viewDesc); + descriptorSet->setResource(rangeIndex, 0, bufferView); + } + break; + } + + if(srcEntry.isOutput) + { + BindingStateImpl::OutputBinding binding; + binding.entryIndex = i; + binding.resource = bufferResource; + outputBindings.Add(binding); + } + } + break; + case ShaderInputType::CombinedTextureSampler: - { - RefPtr texture; - SLANG_RETURN_ON_FAIL(generateTextureResource(srcEntry.textureDesc, textureBindFlags, renderer, texture)); - descOut.addCombinedTextureSampler(texture, _calcSamplerDesc(srcEntry.samplerDesc), registerSet); + { + RefPtr texture; + SLANG_RETURN_ON_FAIL(generateTextureResource(srcEntry.textureDesc, textureBindFlags, renderer, texture)); + + auto sampler = _createSamplerState(renderer, srcEntry.samplerDesc); + + ResourceView::Desc viewDesc; + viewDesc.type = ResourceView::Type::ShaderResource; + auto textureView = renderer->createTextureView( + texture, + viewDesc); + + descriptorSet->setCombinedTextureSampler(rangeIndex, 0, textureView, sampler); + + if(srcEntry.isOutput) + { + BindingStateImpl::OutputBinding binding; + binding.entryIndex = i; + binding.resource = texture; + outputBindings.Add(binding); + } + } break; - } - case ShaderInputType::Texture: - { - RefPtr texture; - SLANG_RETURN_ON_FAIL(generateTextureResource(srcEntry.textureDesc, textureBindFlags, renderer, texture)); - descOut.addTextureResource(texture, registerSet); + case ShaderInputType::Texture: + { + RefPtr texture; + SLANG_RETURN_ON_FAIL(generateTextureResource(srcEntry.textureDesc, textureBindFlags, renderer, texture)); + + // TODO: support UAV textures... + + ResourceView::Desc viewDesc; + viewDesc.type = ResourceView::Type::ShaderResource; + auto textureView = renderer->createTextureView( + texture, + viewDesc); + + descriptorSet->setResource(rangeIndex, 0, textureView); + + if(srcEntry.isOutput) + { + BindingStateImpl::OutputBinding binding; + binding.entryIndex = i; + binding.resource = texture; + outputBindings.Add(binding); + } + } break; - } + case ShaderInputType::Sampler: - { - descOut.addSampler(_calcSamplerDesc(srcEntry.samplerDesc), registerSet); + { + auto sampler = _createSamplerState(renderer, srcEntry.samplerDesc); + descriptorSet->setSampler(rangeIndex, 0, sampler); + } break; - } + default: - { assert(!"Unhandled type"); return SLANG_FAIL; - } } } - return SLANG_OK; -} + BindingStateImpl* bindingState = new BindingStateImpl(); + bindingState->descriptorSet = descriptorSet; + bindingState->pipelineLayout = pipelineLayout; + bindingState->outputBindings = outputBindings; + bindingState->m_numRenderTargets = layout.numRenderTargets; -/* static */Result ShaderRendererUtil::createBindingStateDesc(const ShaderInputLayout& layout, Renderer* renderer, BindingState::Desc& descOut) -{ - SLANG_RETURN_ON_FAIL(createBindingStateDesc(layout.entries.Buffer(), int(layout.entries.Count()), renderer, descOut)); - descOut.m_numRenderTargets = layout.numRenderTargets; + *outBindingState = bindingState; return SLANG_OK; } diff --git a/tools/render-test/shader-renderer-util.h b/tools/render-test/shader-renderer-util.h index 849e68754..bbdea2af6 100644 --- a/tools/render-test/shader-renderer-util.h +++ b/tools/render-test/shader-renderer-util.h @@ -6,26 +6,68 @@ namespace renderer_test { -/// Utility class containing functions that construct items on the renderer using the ShaderInputLayout representation -struct ShaderRendererUtil +using namespace Slang; + +struct BindingStateImpl : public Slang::RefObject +{ + /// A register set consists of one or more contiguous indices. + /// To be valid index >= 0 and size >= 1 + struct RegisterRange + { + /// True if contains valid contents + bool isValid() const { return size > 0; } + /// True if valid single value + bool isSingle() const { return size == 1; } + /// Get as a single index (must be at least one index) + int getSingleIndex() const { return (size == 1) ? index : -1; } + /// Return the first index + int getFirstIndex() const { return (size > 0) ? index : -1; } + /// True if contains register index + bool hasRegister(int registerIndex) const { return registerIndex >= index && registerIndex < index + size; } + + static RegisterRange makeInvalid() { return RegisterRange{ -1, 0 }; } + static RegisterRange makeSingle(int index) { return RegisterRange{ int16_t(index), 1 }; } + static RegisterRange makeRange(int index, int size) { return RegisterRange{ int16_t(index), uint16_t(size) }; } + + int16_t index; ///< The base index + uint16_t size; ///< The amount of register indices + }; + + void apply(Renderer* renderer, PipelineType pipelineType); + + struct OutputBinding + { + RefPtr resource; + Slang::UInt entryIndex; + }; + List outputBindings; + + RefPtr pipelineLayout; + RefPtr descriptorSet; + int m_numRenderTargets = 1; +}; + +/// Utility class containing functions that construct items on the renderer using the ShaderInputLayout representation +struct ShaderRendererUtil { /// Generate a texture using the InputTextureDesc and construct a TextureResource using the Renderer with the contents static Slang::Result generateTextureResource(const InputTextureDesc& inputDesc, int bindFlags, Renderer* renderer, Slang::RefPtr& textureOut); /// Create texture resource using inputDesc, and texData to describe format, and contents static Slang::Result createTextureResource(const InputTextureDesc& inputDesc, const TextureData& texData, int bindFlags, Renderer* renderer, Slang::RefPtr& textureOut); - + /// Create the BufferResource using the renderer from the contents of inputDesc static Slang::Result createBufferResource(const InputBufferDesc& inputDesc, bool isOutput, size_t bufferSize, const void* initData, Renderer* renderer, Slang::RefPtr& bufferOut); /// Create BindingState::Desc from the contents of layout - static Slang::Result createBindingStateDesc(const ShaderInputLayout& layout, Renderer* renderer, BindingState::Desc& descOut); - /// Create BindingState::Desc from a list of ShaderInputLayout entries - static Slang::Result createBindingStateDesc(ShaderInputLayoutEntry* srcEntries, int numEntries, Renderer* renderer, BindingState::Desc& descOut); + static Slang::Result createBindingState(const ShaderInputLayout& layout, Renderer* renderer, BufferResource* addedConstantBuffer, BindingStateImpl** outBindingState); /// Get the binding register associated with this binding (or -1 if none defined) - static BindingState::RegisterRange calcRegisterRange(Renderer* renderer, const ShaderInputLayoutEntry& entry); + static BindingStateImpl::RegisterRange calcRegisterRange(Renderer* renderer, const ShaderInputLayoutEntry& entry); +private: + /// Create BindingState::Desc from a list of ShaderInputLayout entries + static Slang::Result _createBindingState(ShaderInputLayoutEntry* srcEntries, int numEntries, Renderer* renderer, BufferResource* addedConstantBuffer, BindingStateImpl** outBindingState); }; } // renderer_test diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index a6c252843..26e856295 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -11,7 +11,7 @@ namespace renderer_test { -ShaderProgram* ShaderCompiler::compileProgram( +RefPtr ShaderCompiler::compileProgram( ShaderCompileRequest const& request) { SlangSession* slangSession = spCreateSession(NULL); @@ -92,7 +92,7 @@ ShaderProgram* ShaderCompiler::compileProgram( } - ShaderProgram * shaderProgram = nullptr; + RefPtr shaderProgram; Slang::List rawTypeNames; for (auto typeName : request.entryPointTypeArguments) rawTypeNames.Add(typeName.Buffer()); diff --git a/tools/render-test/slang-support.h b/tools/render-test/slang-support.h index 8697abcb8..03de062d1 100644 --- a/tools/render-test/slang-support.h +++ b/tools/render-test/slang-support.h @@ -11,13 +11,13 @@ namespace renderer_test { struct ShaderCompiler { - Renderer* renderer; + RefPtr renderer; SlangCompileTarget target; SlangSourceLanguage sourceLanguage; SlangPassThrough passThrough; char const* profile; - ShaderProgram* compileProgram( + RefPtr compileProgram( ShaderCompileRequest const& request); }; diff --git a/tools/slang-graphics/circular-resource-heap-d3d12.cpp b/tools/slang-graphics/circular-resource-heap-d3d12.cpp deleted file mode 100644 index 8b63819a5..000000000 --- a/tools/slang-graphics/circular-resource-heap-d3d12.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "circular-resource-heap-d3d12.h" - -namespace slang_graphics { -using namespace Slang; - -D3D12CircularResourceHeap::D3D12CircularResourceHeap(): - m_fence(nullptr), - m_device(nullptr), - m_blockFreeList(sizeof(Block), SLANG_ALIGN_OF(Block), 16), - m_blocks(nullptr) -{ - m_back.m_block = nullptr; - m_back.m_position = nullptr; - m_front.m_block = nullptr; - m_front.m_position = nullptr; -} - -D3D12CircularResourceHeap::~D3D12CircularResourceHeap() -{ - _freeBlockListResources(m_blocks); -} - -void D3D12CircularResourceHeap::_freeBlockListResources(const Block* start) -{ - if (start) - { - const Block* block = start; - do - { - ID3D12Resource* resource = block->m_resource; - - resource->Unmap(0, nullptr); - resource->Release(); - - // Next in list - block = block->m_next; - - } while (block != start); - } -} - -Result D3D12CircularResourceHeap::init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence) -{ - assert(m_blocks == nullptr); - assert(desc.m_blockSize > 0); - - m_fence = fence; - m_desc = desc; - m_device = device; - - return SLANG_OK; -} - -void D3D12CircularResourceHeap::addSync(uint64_t signalValue) -{ - assert(signalValue == m_fence->getCurrentValue()); - PendingEntry entry; - entry.m_completedValue = signalValue; - entry.m_cursor = m_front; - m_pendingQueue.Add(entry); -} - -void D3D12CircularResourceHeap::updateCompleted() -{ - const uint64_t completedValue = m_fence->getCompletedValue(); - -#if 0 - while (m_pendingQueue.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 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 slang_graphics diff --git a/tools/slang-graphics/circular-resource-heap-d3d12.h b/tools/slang-graphics/circular-resource-heap-d3d12.h deleted file mode 100644 index a200d3bbc..000000000 --- a/tools/slang-graphics/circular-resource-heap-d3d12.h +++ /dev/null @@ -1,206 +0,0 @@ -#pragma once - -#include "../../slang-com-ptr.h" -#include "../../source/core/list.h" -#include "../../source/core/slang-free-list.h" - -#include "resource-d3d12.h" - -namespace slang_graphics { - -/*! \brief The D3D12CircularResourceHeap is a heap that is suited for size constrained real-time resources allocation that -is transitory in nature. It is designed to allocate resources which are used and discarded, often used where in -previous versions of DirectX the 'DISCARD' flag was used. - -The idea is to have a heap which chunks of resource can be allocated, and used for GPU execution, -and that the heap is able through the addSync/updateCompleted idiom is able to track when the usage of the resources is -completed allowing them to be reused. The heap is arranged as circularly, with new allocations made from the front, and the back -being updated as the GPU updating the back when it is informed anything using prior parts of the heap have completed. In this -arrangement all the heap between the back and the front can be thought of as in use or potentially in use by the GPU. All the heap -from the front back around to the back, is free and can be allocated from. It is the responsibility of the user of the Heap to make -sure the invariant holds, but in most normal usage it does so simply. - -Another feature of the heap is that it does not require upfront knowledge of how big a heap is needed. The backing resources will be expanded -dynamically with requests as needed. The only requirement is that know single request can be larger than m_blockSize specified in the Desc -used to initialize the heap. This is because all the backing resources are allocated to a single size. This limitation means the D3D12CircularResourceHeap -may not be the best use for example for uploading a texture - because it's design is really around transitory uploads or write backs, and so more suited -to constant buffers, vertex buffer, index buffers and the like. - -To upload a texture at program startup it is most likely better to use a D3D12ResourceScopeManager. - -\code{.cpp} - -typedef D3D12CircularResourceHeap Heap; - -Heap::Cursor cursor = heap.allocateVertexBuffer(sizeof(Vertex) * numVerts); -Memory:copy(cursor.m_position, verts, sizeof(Vertex) * numVerts); - -// Do a command using the GPU handle -m_commandList->... -// Do another command using the GPU handle - -m_commandList->... - -// Execute the command list on the command queue -{ - ID3D12CommandList* lists[] = { m_commandList }; - m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(lists), lists); -} - -// Add a sync point -const uint64_t signalValue = m_fence.nextSignal(m_commandQueue); -heap.addSync(signalValue) - -// The cursors cannot be used anymore - -// At some later point call updateCompleted. This will see where the GPU is at, and make resources available that the GPU no longer accesses. -heap.updateCompleted(); - -\endcode - -### Implementation - -Front and back can be in the same block, but ONLY if back is behind front, because we have to always be able to insert -new blocks in front of front. So it must be possible to do an block insertion between the two of them. - -|--B---F-----| |----------| - -When B and F are on top of one another it means there is nothing in the list. NOTE this also means that a move of front can never place it -top of the back. - -https://msdn.microsoft.com/en-us/library/windows/desktop/dn899125%28v=vs.85%29.aspx -https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx -*/ - -class D3D12CircularResourceHeap -{ - protected: - struct Block; - public: - typedef D3D12CircularResourceHeap ThisType; - - /// The alignment used for VERTEX_BUFFER allocations - /// Strictly speaking it seems the hardware can handle 4 byte alignment, but since often in use - /// data will be copied from CPU memory to the allocation, using 16 byte alignment is superior as allows - /// significantly faster memcpy. - /// The sample that shows sizeof(float) - 4 bytes is appropriate is at the link below. - /// https://msdn.microsoft.com/en-us/library/windows/desktop/mt426646%28v=vs.85%29.aspx - enum - { - VERTEX_BUFFER_ALIGNMENT = 16, - }; - - struct Desc - { - void init() - { - { - D3D12_HEAP_PROPERTIES& props = m_heapProperties; - - props.Type = D3D12_HEAP_TYPE_UPLOAD; - props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - props.CreationNodeMask = 1; - props.VisibleNodeMask = 1; - } - m_heapFlags = D3D12_HEAP_FLAG_NONE; - m_initialState = D3D12_RESOURCE_STATE_GENERIC_READ; - m_blockSize = 0; - } - - D3D12_HEAP_PROPERTIES m_heapProperties; - D3D12_HEAP_FLAGS m_heapFlags; - D3D12_RESOURCE_STATES m_initialState; - size_t m_blockSize; - }; - - /// Cursor position - struct Cursor - { - /// Get GpuHandle - SLANG_FORCE_INLINE D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle() const { return m_block->m_resource->GetGPUVirtualAddress() + size_t(m_position - m_block->m_start); } - /// Must have a block and position - SLANG_FORCE_INLINE bool isValid() const { return m_block != nullptr; } - /// Calculate the offset into the underlying resource - SLANG_FORCE_INLINE size_t getOffset() const { return size_t(m_position - m_block->m_start); } - /// Get the underlying resource - SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_block->m_resource; } - - Block* m_block; ///< The block index - uint8_t* m_position; ///< The current position - }; - - /// Get the desc used to initialize the heap - SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } - - /// Must be called before used - /// Block size must be at least as large as the _largest_ thing allocated - /// Also note depending on alignment of a resource allocation, the block size might also need to take into account the - /// maximum alignment use. It is a REQUIREMENT that a newly allocated resource block is large enough to hold any - /// allocation taking into account the alignment used. - Slang::Result init(ID3D12Device* device, const Desc& desc, D3D12CounterFence* fence); - - /// Get the block size - SLANG_FORCE_INLINE size_t getBlockSize() const { return m_desc.m_blockSize; } - - /// Allocate constant buffer of specified size - Cursor allocate(size_t size, size_t alignment); - - /// Allocate a constant buffer - SLANG_FORCE_INLINE Cursor allocateConstantBuffer(size_t size) { return allocate(size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); } - /// Allocate a vertex buffer - SLANG_FORCE_INLINE Cursor allocateVertexBuffer(size_t size) { return allocate(size, VERTEX_BUFFER_ALIGNMENT); } - - /// Create filled in constant buffer - SLANG_FORCE_INLINE Cursor newConstantBuffer(const void* data, size_t size) { Cursor cursor = allocateConstantBuffer(size); ::memcpy(cursor.m_position, data, size); return cursor; } - /// Create in filled in constant buffer - template - SLANG_FORCE_INLINE Cursor newConstantBuffer(const T& in) { return newConstantBuffer(&in, sizeof(T)); } - - /// Look where the GPU has got to and release anything not currently used - void updateCompleted(); - /// Add a sync point - meaning that when this point is hit in the queue - /// all of the resources up to this point will no longer be used. - void addSync(uint64_t signalValue); - - /// Get the gpu address of this cursor - D3D12_GPU_VIRTUAL_ADDRESS getGpuHandle(const Cursor& cursor) const { return cursor.m_block->m_resource->GetGPUVirtualAddress() + size_t(cursor.m_position - cursor.m_block->m_start); } - - /// Ctor - D3D12CircularResourceHeap(); - /// Dtor - ~D3D12CircularResourceHeap(); - - protected: - - struct Block - { - ID3D12Resource* m_resource; ///< The mapped resource - uint8_t* m_start; ///< Once created the resource is mapped to here - Block* m_next; ///< Points to next block in the list - }; - struct PendingEntry - { - uint64_t m_completedValue; ///< The value when this is completed - Cursor m_cursor; ///< the cursor at that point - }; - void _freeBlockListResources(const Block* block); - /// Create a new block (with associated resource), do not add the block list - Block* _newBlock(); - - Block* m_blocks; ///< Circular singly linked list of block. nullptr initially - Slang::FreeList m_blockFreeList; ///< Free list of actual allocations of blocks - Slang::List m_pendingQueue; ///< Holds the list of pending positions. When the fence value is greater than the value on the queue entry, the entry is done. - - // Allocation is made from the front, and freed from the back. - Cursor m_back; ///< Current back position. - Cursor m_front; ///< Current front position. - - Desc m_desc; ///< Describes the heap - - D3D12CounterFence* m_fence; ///< The fence to use - ID3D12Device* m_device; ///< The device that resources will be constructed on -}; - -} // namespace slang_graphics - diff --git a/tools/slang-graphics/d3d-util.cpp b/tools/slang-graphics/d3d-util.cpp deleted file mode 100644 index b2c3f87ee..000000000 --- a/tools/slang-graphics/d3d-util.cpp +++ /dev/null @@ -1,306 +0,0 @@ -// d3d-util.cpp -#include "d3d-util.h" - -#include - -// We will use the C standard library just for printing error messages. -#include - -namespace slang_graphics { -using namespace Slang; - -/* static */D3D_PRIMITIVE_TOPOLOGY D3DUtil::getPrimitiveTopology(PrimitiveTopology topology) -{ - switch (topology) - { - case PrimitiveTopology::TriangleList: - { - return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - } - default: break; - } - return D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; -} - -/* static */DXGI_FORMAT D3DUtil::getMapFormat(Format format) -{ - switch (format) - { - case Format::RGBA_Float32: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case Format::RGB_Float32: return DXGI_FORMAT_R32G32B32_FLOAT; - case Format::RG_Float32: return DXGI_FORMAT_R32G32_FLOAT; - case Format::R_Float32: return DXGI_FORMAT_R32_FLOAT; - case Format::RGBA_Unorm_UInt8: return DXGI_FORMAT_R8G8B8A8_UNORM; - case Format::R_UInt32: return DXGI_FORMAT_R32_UINT; - - case Format::D_Float32: return DXGI_FORMAT_D32_FLOAT; - case Format::D_Unorm24_S8: return DXGI_FORMAT_D24_UNORM_S8_UINT; - - default: return DXGI_FORMAT_UNKNOWN; - } -} - -/* static */DXGI_FORMAT D3DUtil::calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format) -{ - SLANG_UNUSED(usage); - if (usageFlags) - { - switch (format) - { - case DXGI_FORMAT_R32_FLOAT: /* fallthru */ - case DXGI_FORMAT_R32_UINT: - case DXGI_FORMAT_D32_FLOAT: - { - return DXGI_FORMAT_R32_TYPELESS; - } - case DXGI_FORMAT_D24_UNORM_S8_UINT: return DXGI_FORMAT_R24G8_TYPELESS; - default: break; - } - return format; - } - return format; -} - -/* static */DXGI_FORMAT D3DUtil::calcFormat(UsageType usage, DXGI_FORMAT format) -{ - switch (usage) - { - case USAGE_COUNT_OF: - case USAGE_UNKNOWN: - { - return DXGI_FORMAT_UNKNOWN; - } - case USAGE_DEPTH_STENCIL: - { - switch (format) - { - case DXGI_FORMAT_D32_FLOAT: /* fallthru */ - case DXGI_FORMAT_R32_TYPELESS: - { - return DXGI_FORMAT_D32_FLOAT; - } - case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; - case DXGI_FORMAT_R24G8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; - default: break; - } - return format; - } - case USAGE_TARGET: - { - switch (format) - { - case DXGI_FORMAT_D32_FLOAT: /* fallthru */ - case DXGI_FORMAT_D24_UNORM_S8_UINT: - { - return DXGI_FORMAT_UNKNOWN; - } - case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT; - default: break; - } - return format; - } - case USAGE_SRV: - { - switch (format) - { - case DXGI_FORMAT_D32_FLOAT: /* fallthru */ - case DXGI_FORMAT_R32_TYPELESS: - { - return DXGI_FORMAT_R32_FLOAT; - } - case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; - default: break; - } - - return format; - } - } - - assert(!"Not reachable"); - return DXGI_FORMAT_UNKNOWN; -} - -bool D3DUtil::isTypeless(DXGI_FORMAT format) -{ - switch (format) - { - case DXGI_FORMAT_R32G32B32A32_TYPELESS: - case DXGI_FORMAT_R32G32B32_TYPELESS: - case DXGI_FORMAT_R16G16B16A16_TYPELESS: - case DXGI_FORMAT_R32G32_TYPELESS: - case DXGI_FORMAT_R32G8X24_TYPELESS: - case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: - case DXGI_FORMAT_R10G10B10A2_TYPELESS: - case DXGI_FORMAT_R8G8B8A8_TYPELESS: - case DXGI_FORMAT_R16G16_TYPELESS: - case DXGI_FORMAT_R32_TYPELESS: - case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: - case DXGI_FORMAT_R24G8_TYPELESS: - case DXGI_FORMAT_R8G8_TYPELESS: - case DXGI_FORMAT_R16_TYPELESS: - case DXGI_FORMAT_R8_TYPELESS: - case DXGI_FORMAT_BC1_TYPELESS: - case DXGI_FORMAT_BC2_TYPELESS: - case DXGI_FORMAT_BC3_TYPELESS: - case DXGI_FORMAT_BC4_TYPELESS: - case DXGI_FORMAT_BC5_TYPELESS: - case DXGI_FORMAT_B8G8R8A8_TYPELESS: - case DXGI_FORMAT_BC6H_TYPELESS: - case DXGI_FORMAT_BC7_TYPELESS: - { - return true; - } - default: break; - } - return false; -} - -/* static */Int D3DUtil::getNumColorChannelBits(DXGI_FORMAT fmt) -{ - switch (fmt) - { - case DXGI_FORMAT_R32G32B32A32_TYPELESS: - case DXGI_FORMAT_R32G32B32A32_FLOAT: - case DXGI_FORMAT_R32G32B32A32_UINT: - case DXGI_FORMAT_R32G32B32A32_SINT: - case DXGI_FORMAT_R32G32B32_TYPELESS: - case DXGI_FORMAT_R32G32B32_FLOAT: - case DXGI_FORMAT_R32G32B32_UINT: - case DXGI_FORMAT_R32G32B32_SINT: - { - return 32; - } - case DXGI_FORMAT_R16G16B16A16_TYPELESS: - case DXGI_FORMAT_R16G16B16A16_FLOAT: - case DXGI_FORMAT_R16G16B16A16_UNORM: - case DXGI_FORMAT_R16G16B16A16_UINT: - case DXGI_FORMAT_R16G16B16A16_SNORM: - case DXGI_FORMAT_R16G16B16A16_SINT: - { - return 16; - } - case DXGI_FORMAT_R10G10B10A2_TYPELESS: - case DXGI_FORMAT_R10G10B10A2_UNORM: - case DXGI_FORMAT_R10G10B10A2_UINT: - case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: - { - return 10; - } - case DXGI_FORMAT_R8G8B8A8_TYPELESS: - case DXGI_FORMAT_R8G8B8A8_UNORM: - case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: - case DXGI_FORMAT_R8G8B8A8_UINT: - case DXGI_FORMAT_R8G8B8A8_SNORM: - case DXGI_FORMAT_R8G8B8A8_SINT: - case DXGI_FORMAT_B8G8R8A8_UNORM: - case DXGI_FORMAT_B8G8R8X8_UNORM: - case DXGI_FORMAT_B8G8R8A8_TYPELESS: - case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: - case DXGI_FORMAT_B8G8R8X8_TYPELESS: - case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: - { - return 8; - } - case DXGI_FORMAT_B5G6R5_UNORM: - case DXGI_FORMAT_B5G5R5A1_UNORM: - { - return 5; - } - case DXGI_FORMAT_B4G4R4A4_UNORM: - return 4; - - default: - return 0; - } -} - -// Note: this subroutine is now only used by D3D11 for generating bytecode to go into input layouts. -// -// TODO: we can probably remove that code completely by switching to a PSO-like model across all APIs. -// -/* static */Result D3DUtil::compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, ComPtr& shaderBlobOut) -{ - // Rather than statically link against the `d3dcompile` library, we - // dynamically load it. - // - // Note: A more realistic application would compile from HLSL text to D3D - // shader bytecode as part of an offline process, rather than doing it - // on-the-fly like this - // - static pD3DCompile compileFunc = nullptr; - if (!compileFunc) - { - // TODO(tfoley): maybe want to search for one of a few versions of the DLL - HMODULE compilerModule = LoadLibraryA("d3dcompiler_47.dll"); - if (!compilerModule) - { - fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n"); - return SLANG_FAIL; - } - - compileFunc = (pD3DCompile)GetProcAddress(compilerModule, "D3DCompile"); - if (!compileFunc) - { - fprintf(stderr, "error: failed load symbol 'D3DCompile'\n"); - return SLANG_FAIL; - } - } - - // For this example, we turn on debug output, and turn off all - // optimization. A real application would only use these flags - // when shader debugging is needed. - UINT flags = 0; - flags |= D3DCOMPILE_DEBUG; - flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION; - - // We will always define `__HLSL__` when compiling here, so that - // input code can react differently to being compiled as pure HLSL. - D3D_SHADER_MACRO defines[] = { - { "__HLSL__", "1" }, - { nullptr, nullptr }, - }; - - // The `D3DCompile` entry point takes a bunch of parameters, but we - // don't really need most of them for Slang-generated code. - ComPtr shaderBlob; - ComPtr errorBlob; - - HRESULT hr = compileFunc(source, strlen(source), sourcePath, &defines[0], nullptr, entryPointName, dxProfileName, flags, 0, - shaderBlob.writeRef(), errorBlob.writeRef()); - - // If the HLSL-to-bytecode compilation produced any diagnostic messages - // then we will print them out (whether or not the compilation failed). - if (errorBlob) - { - ::fputs((const char*)errorBlob->GetBufferPointer(), stderr); - ::fflush(stderr); - ::OutputDebugStringA((const char*)errorBlob->GetBufferPointer()); - } - - SLANG_RETURN_ON_FAIL(hr); - shaderBlobOut.swap(shaderBlob); - return SLANG_OK; -} - -/* static */void D3DUtil::appendWideChars(const char* in, List& out) -{ - size_t len = ::strlen(in); - - const DWORD dwFlags = 0; - int outSize = ::MultiByteToWideChar(CP_UTF8, dwFlags, in, int(len), nullptr, 0); - - if (outSize > 0) - { - const 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/slang-graphics/d3d-util.h b/tools/slang-graphics/d3d-util.h deleted file mode 100644 index b5f154e6e..000000000 --- a/tools/slang-graphics/d3d-util.h +++ /dev/null @@ -1,61 +0,0 @@ -// d3d-util.h -#pragma once - -#include - -#include "../../slang-com-helper.h" - -#include "../../slang-com-ptr.h" -#include "../../source/core/list.h" - -#include "render.h" - -#include -#include - -namespace slang_graphics { - -class D3DUtil -{ - public: - enum UsageType - { - USAGE_UNKNOWN, ///< Generally used to mark an error - USAGE_TARGET, ///< Format should be used when written as target - USAGE_DEPTH_STENCIL, ///< Format should be used when written as depth stencil - USAGE_SRV, ///< Format if being read as srv - USAGE_COUNT_OF, - }; - enum UsageFlag - { - USAGE_FLAG_MULTI_SAMPLE = 0x1, ///< If set will be used form multi sampling (such as MSAA) - USAGE_FLAG_SRV = 0x2, ///< If set means will be used as a shader resource view (SRV) - }; - - /// Get primitive topology as D3D primitive topology - static D3D_PRIMITIVE_TOPOLOGY getPrimitiveTopology(PrimitiveTopology prim); - - /// Calculate size taking into account alignment. Alignment must be a power of 2 - static UInt calcAligned(UInt size, UInt alignment) { return (size + alignment - 1) & ~(alignment - 1); } - - /// Compile HLSL code to DXBC - static Slang::Result compileHLSLShader(char const* sourcePath, char const* source, char const* entryPointName, char const* dxProfileName, Slang::ComPtr& shaderBlobOut); - - /// Given a slang pixel format returns the equivalent DXGI_ pixel format. If the format is not known, will return DXGI_FORMAT_UNKNOWN - static DXGI_FORMAT getMapFormat(Format format); - - /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible - static DXGI_FORMAT calcFormat(UsageType usage, DXGI_FORMAT format); - /// Calculate appropriate format for creating a buffer for usage and flags - static DXGI_FORMAT calcResourceFormat(UsageType usage, Int usageFlags, DXGI_FORMAT format); - /// True if the type is 'typeless' - static bool isTypeless(DXGI_FORMAT format); - - /// Returns number of bits used for color channel for format (for channels with multiple sizes, returns smallest ie RGB565 -> 5) - static Int getNumColorChannelBits(DXGI_FORMAT fmt); - - /// Append text in in, into wide char array - static void appendWideChars(const char* in, Slang::List& out); -}; - -} // renderer_test diff --git a/tools/slang-graphics/descriptor-heap-d3d12.cpp b/tools/slang-graphics/descriptor-heap-d3d12.cpp deleted file mode 100644 index 23c56d46d..000000000 --- a/tools/slang-graphics/descriptor-heap-d3d12.cpp +++ /dev/null @@ -1,47 +0,0 @@ - -#include "descriptor-heap-d3d12.h" - -namespace slang_graphics { -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 slang_graphics - diff --git a/tools/slang-graphics/descriptor-heap-d3d12.h b/tools/slang-graphics/descriptor-heap-d3d12.h deleted file mode 100644 index 6ddb583dc..000000000 --- a/tools/slang-graphics/descriptor-heap-d3d12.h +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once - - -#include -#include - -#include "../../slang-com-ptr.h" - -namespace slang_graphics { - -/*! \brief A simple class to manage an underlying Dx12 Descriptor Heap. Allocations are made linearly in order. It is not possible to free -individual allocations, but all allocations can be deallocated with 'deallocateAll'. */ -class D3D12DescriptorHeap -{ - public: - typedef D3D12DescriptorHeap ThisType; - - /// Initialize - Slang::Result init(ID3D12Device* device, int size, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags); - /// Initialize with an array of handles copying over the representation - Slang::Result init(ID3D12Device* device, const D3D12_CPU_DESCRIPTOR_HANDLE* handles, int numHandles, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags); - - /// Returns the number of slots that have been used - SLANG_FORCE_INLINE int getUsedSize() const { return m_currentIndex; } - - /// Get the total amount of descriptors possible on the heap - SLANG_FORCE_INLINE int getTotalSize() const { return m_totalSize; } - /// Allocate a descriptor. Returns the index, or -1 if none left. - SLANG_FORCE_INLINE int allocate(); - /// Allocate a number of descriptors. Returns the start index (or -1 if not possible) - SLANG_FORCE_INLINE int allocate(int numDescriptors); - - /// - SLANG_FORCE_INLINE int placeAt(int index); - - /// Deallocates all allocations, and starts allocation from the start of the underlying heap again - SLANG_FORCE_INLINE void deallocateAll() { m_currentIndex = 0; } - - /// Get the size of each - SLANG_FORCE_INLINE int getDescriptorSize() const { return m_descriptorSize; } - - /// Get the GPU heap start - SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuStart() const { return m_heap->GetGPUDescriptorHandleForHeapStart(); } - /// Get the CPU heap start - SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuStart() const { return m_heap->GetCPUDescriptorHandleForHeapStart(); } - - /// Get the GPU handle at the specified index - SLANG_FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE getGpuHandle(int index) const; - /// Get the CPU handle at the specified index - SLANG_FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE getCpuHandle(int index) const; - - /// Get the underlying heap - SLANG_FORCE_INLINE ID3D12DescriptorHeap* getHeap() const { return m_heap; } - - /// Ctor - D3D12DescriptorHeap(); - -protected: - Slang::ComPtr m_heap; ///< The underlying heap being allocated from - int m_totalSize; ///< Total amount of allocations available on the heap - int m_currentIndex; ///< The current descriptor - int m_descriptorSize; ///< The size of each descriptor -}; - -// --------------------------------------------------------------------------- -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 slang_graphics - diff --git a/tools/slang-graphics/render-d3d11.cpp b/tools/slang-graphics/render-d3d11.cpp deleted file mode 100644 index 4f9749e39..000000000 --- a/tools/slang-graphics/render-d3d11.cpp +++ /dev/null @@ -1,1101 +0,0 @@ -// render-d3d11.cpp - -#define _CRT_SECURE_NO_WARNINGS - -#include "render-d3d11.h" - -//WORKING: #include "options.h" -#include "render.h" -#include "d3d-util.h" - -#include "surface.h" - -// In order to use the Slang API, we need to include its header - -//#include - -#include "../../slang-com-ptr.h" - -// We will be rendering with Direct3D 11, so we need to include -// the Windows and D3D11 headers - -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#include -#include - -// We will use the C standard library just for printing error messages. -#include - -#ifdef _MSC_VER -#include -#if (_MSC_VER < 1900) -#define snprintf sprintf_s -#endif -#endif -// -using namespace Slang; - -namespace slang_graphics { - -class D3D11Renderer : public Renderer -{ -public: - // Renderer implementation - virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; - virtual void setClearColor(const float color[4]) override; - virtual void clearFrame() override; - virtual void presentFrame() override; - virtual TextureResource* createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData) override; - virtual BufferResource* createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& bufferDesc, const void* initData) override; - virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; - virtual InputLayout* createInputLayout( const InputElementDesc* inputElements, UInt inputElementCount) override; - virtual BindingState* createBindingState(const BindingState::Desc& desc) override; - virtual ShaderProgram* createProgram(const ShaderProgram::Desc& desc) override; - virtual void* map(BufferResource* buffer, MapFlavor flavor) override; - virtual void unmap(BufferResource* buffer) override; - virtual void setInputLayout(InputLayout* inputLayout) override; - virtual void setPrimitiveTopology(PrimitiveTopology topology) override; - virtual void setBindingState(BindingState * state); - virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; - virtual void setShaderProgram(ShaderProgram* inProgram) 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 {} - virtual RendererType getRendererType() const override { return RendererType::DirectX11; } - - protected: - - struct BindingDetail - { - ComPtr m_srv; - ComPtr m_uav; - ComPtr m_samplerState; - }; - - class BindingStateImpl: public BindingState - { - public: - typedef BindingState Parent; - - /// Ctor - BindingStateImpl(const Desc& desc): - Parent(desc) - {} - - List m_bindingDetails; - }; - - class ShaderProgramImpl: public ShaderProgram - { - public: - ComPtr m_vertexShader; - ComPtr m_pixelShader; - ComPtr m_computeShader; - }; - - class BufferResourceImpl: public BufferResource - { - public: - typedef BufferResource Parent; - - BufferResourceImpl(const Desc& desc, Usage initialUsage): - Parent(desc), - m_initialUsage(initialUsage) - { - } - - MapFlavor m_mapFlavor; - Usage m_initialUsage; - ComPtr m_buffer; - ComPtr m_staging; - }; - class TextureResourceImpl : public TextureResource - { - public: - typedef TextureResource Parent; - - TextureResourceImpl(const Desc& desc, Usage initialUsage) : - Parent(desc), - m_initialUsage(initialUsage) - { - } - Usage m_initialUsage; - ComPtr m_resource; - }; - - class InputLayoutImpl: public InputLayout - { - public: - ComPtr m_layout; - }; - - /// Capture a texture to a file - static HRESULT captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut); - - void _applyBindingState(bool isCompute); - - ComPtr m_swapChain; - ComPtr m_device; - ComPtr m_immediateContext; - ComPtr m_backBufferTexture; - - List > m_renderTargetViews; - List > m_renderTargetTextures; - - RefPtr m_currentBindings; - - Desc m_desc; - - float m_clearColor[4] = { 0, 0, 0, 0 }; -}; - -Renderer* createD3D11Renderer() -{ - return new D3D11Renderer(); -} - -/* static */HRESULT D3D11Renderer::captureTextureToSurface(ID3D11Device* device, ID3D11DeviceContext* context, ID3D11Texture2D* texture, Surface& surfaceOut) -{ - if (!context) return E_INVALIDARG; - if (!texture) return E_INVALIDARG; - - D3D11_TEXTURE2D_DESC textureDesc; - texture->GetDesc(&textureDesc); - - // Don't bother supporting MSAA for right now - if (textureDesc.SampleDesc.Count > 1) - { - fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); - return E_INVALIDARG; - } - - HRESULT hr = S_OK; - ComPtr stagingTexture; - - if (textureDesc.Usage == D3D11_USAGE_STAGING && (textureDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) - { - stagingTexture = texture; - } - else - { - // Modify the descriptor to give us a staging texture - textureDesc.BindFlags = 0; - textureDesc.MiscFlags &= ~D3D11_RESOURCE_MISC_TEXTURECUBE; - textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - textureDesc.Usage = D3D11_USAGE_STAGING; - - hr = device->CreateTexture2D(&textureDesc, 0, stagingTexture.writeRef()); - if (FAILED(hr)) - { - fprintf(stderr, "ERROR: failed to create staging texture\n"); - return hr; - } - - context->CopyResource(stagingTexture, texture); - } - - // Now just read back texels from the staging textures - { - D3D11_MAPPED_SUBRESOURCE mappedResource; - SLANG_RETURN_ON_FAIL(context->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource)); - - Result res = surfaceOut.set(textureDesc.Width, textureDesc.Height, Format::RGBA_Unorm_UInt8, mappedResource.RowPitch, mappedResource.pData, SurfaceAllocator::getMallocAllocator()); - - // Make sure to unmap - context->Unmap(stagingTexture, 0); - return res; - } -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! - -SlangResult D3D11Renderer::initialize(const Desc& desc, void* inWindowHandle) -{ - auto windowHandle = (HWND)inWindowHandle; - m_desc = desc; - - // Rather than statically link against D3D, we load it dynamically. - HMODULE d3dModule = LoadLibraryA("d3d11.dll"); - if (!d3dModule) - { - fprintf(stderr, "error: failed load 'd3d11.dll'\n"); - return SLANG_FAIL; - } - - PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN D3D11CreateDeviceAndSwapChain_ = - (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress(d3dModule, "D3D11CreateDeviceAndSwapChain"); - if (!D3D11CreateDeviceAndSwapChain_) - { - fprintf(stderr, - "error: failed load symbol 'D3D11CreateDeviceAndSwapChain'\n"); - return SLANG_FAIL; - } - - // We create our device in debug mode, just so that we can check that the - // example doesn't trigger warnings. - UINT deviceFlags = 0; - deviceFlags |= D3D11_CREATE_DEVICE_DEBUG; - - // Our swap chain uses RGBA8 with sRGB, with double buffering. - DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - - // Note(tfoley): Disabling sRGB for DX back buffer for now, so that we - // can get consistent output with OpenGL, where setting up sRGB will - // probably be more involved. - // swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - - swapChainDesc.SampleDesc.Count = 1; - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferCount = 2; - swapChainDesc.OutputWindow = windowHandle; - swapChainDesc.Windowed = TRUE; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - swapChainDesc.Flags = 0; - - // We will ask for the highest feature level that can be supported. - const D3D_FEATURE_LEVEL featureLevels[] = { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1, - }; - D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_9_1; - const int totalNumFeatureLevels = SLANG_COUNT_OF(featureLevels); - - // On a machine that does not have an up-to-date version of D3D installed, - // the `D3D11CreateDeviceAndSwapChain` call will fail with `E_INVALIDARG` - // if you ask for featuer level 11_1. The workaround is to call - // `D3D11CreateDeviceAndSwapChain` up to twice: the first time with 11_1 - // at the start of the list of requested feature levels, and the second - // time without it. - - for (int ii = 0; ii < 2; ++ii) - { - const HRESULT hr = D3D11CreateDeviceAndSwapChain_( - nullptr, // adapter (use default) - D3D_DRIVER_TYPE_REFERENCE, - //D3D_DRIVER_TYPE_HARDWARE, - nullptr, // software - deviceFlags, - &featureLevels[ii], - totalNumFeatureLevels - ii, - D3D11_SDK_VERSION, - &swapChainDesc, - m_swapChain.writeRef(), - m_device.writeRef(), - &featureLevel, - m_immediateContext.writeRef()); - - // Failures with `E_INVALIDARG` might be due to feature level 11_1 - // not being supported. - if (hr == E_INVALIDARG) - { - continue; - } - - // Other failures are real, though. - SLANG_RETURN_ON_FAIL(hr); - // We must have a swap chain - break; - } - - // After we've created the swap chain, we can request a pointer to the - // back buffer as a D3D11 texture, and create a render-target view from it. - - static const IID kIID_ID3D11Texture2D = { - 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, - 0x95, 0x35, 0xd3, 0x4f, 0x9c }; - - SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, kIID_ID3D11Texture2D, (void**)m_backBufferTexture.writeRef())); - - for (int i = 0; i < 8; i++) - { - ComPtr texture; - D3D11_TEXTURE2D_DESC textureDesc; - m_backBufferTexture->GetDesc(&textureDesc); - SLANG_RETURN_ON_FAIL(m_device->CreateTexture2D(&textureDesc, nullptr, texture.writeRef())); - - ComPtr rtv; - D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; - rtvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - rtvDesc.Texture2D.MipSlice = 0; - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - SLANG_RETURN_ON_FAIL(m_device->CreateRenderTargetView(texture, &rtvDesc, rtv.writeRef())); - - m_renderTargetViews.Add(rtv); - m_renderTargetTextures.Add(texture); - } - - m_immediateContext->OMSetRenderTargets((UINT)m_renderTargetViews.Count(), m_renderTargetViews.Buffer()->readRef(), nullptr); - - // Similarly, we are going to set up a viewport once, and then never - // switch, since this is a simple test app. - D3D11_VIEWPORT viewport; - viewport.TopLeftX = 0; - viewport.TopLeftY = 0; - viewport.Width = (float)desc.width; - viewport.Height = (float)desc.height; - viewport.MaxDepth = 1; // TODO(tfoley): use reversed depth - viewport.MinDepth = 0; - m_immediateContext->RSSetViewports(1, &viewport); - - return SLANG_OK; -} - -void D3D11Renderer::setClearColor(const float color[4]) -{ - memcpy(m_clearColor, color, sizeof(m_clearColor)); -} - -void D3D11Renderer::clearFrame() -{ - for (auto i = 0u; i < m_renderTargetViews.Count(); i++) - { - m_immediateContext->ClearRenderTargetView(m_renderTargetViews[i], m_clearColor); - } -} - -void D3D11Renderer::presentFrame() -{ - m_immediateContext->CopyResource(m_backBufferTexture, m_renderTargetTextures[0]); - m_swapChain->Present(0, 0); -} - -SlangResult D3D11Renderer::captureScreenSurface(Surface& surfaceOut) -{ - return captureTextureToSurface(m_device, m_immediateContext, m_renderTargetTextures[0], surfaceOut); -} - -static D3D11_BIND_FLAG _calcResourceFlag(Resource::BindFlag::Enum bindFlag) -{ - typedef Resource::BindFlag BindFlag; - switch (bindFlag) - { - case BindFlag::VertexBuffer: return D3D11_BIND_VERTEX_BUFFER; - case BindFlag::IndexBuffer: return D3D11_BIND_INDEX_BUFFER; - case BindFlag::ConstantBuffer: return D3D11_BIND_CONSTANT_BUFFER; - case BindFlag::StreamOutput: return D3D11_BIND_STREAM_OUTPUT; - case BindFlag::RenderTarget: return D3D11_BIND_RENDER_TARGET; - case BindFlag::DepthStencil: return D3D11_BIND_DEPTH_STENCIL; - case BindFlag::UnorderedAccess: return D3D11_BIND_UNORDERED_ACCESS; - case BindFlag::PixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; - case BindFlag::NonPixelShaderResource: return D3D11_BIND_SHADER_RESOURCE; - default: return D3D11_BIND_FLAG(0); - } -} - -static int _calcResourceBindFlags(int bindFlags) -{ - int dstFlags = 0; - while (bindFlags) - { - int lsb = bindFlags & -bindFlags; - - dstFlags |= _calcResourceFlag(Resource::BindFlag::Enum(lsb)); - bindFlags &= ~lsb; - } - return dstFlags; -} - -static int _calcResourceAccessFlags(int accessFlags) -{ - switch (accessFlags) - { - case 0: return 0; - case Resource::AccessFlag::Read: return D3D11_CPU_ACCESS_READ; - case Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_WRITE; - case Resource::AccessFlag::Read | - Resource::AccessFlag::Write: return D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; - default: assert(!"Invalid flags"); return 0; - } -} - -TextureResource* D3D11Renderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData) -{ - TextureResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); - - assert(initData); - assert(initData->numSubResources == srcDesc.numMipLevels * effectiveArraySize * srcDesc.size.depth); - - const DXGI_FORMAT format = D3DUtil::getMapFormat(srcDesc.format); - if (format == DXGI_FORMAT_UNKNOWN) - { - return nullptr; - } - - const int bindFlags = _calcResourceBindFlags(srcDesc.bindFlags); - - // Set up the initialize data - List subRes; - subRes.SetSize(srcDesc.numMipLevels * effectiveArraySize); - { - int subResourceIndex = 0; - for (int i = 0; i < effectiveArraySize; i++) - { - for (int j = 0; j < srcDesc.numMipLevels; j++) - { - const int mipHeight = TextureResource::calcMipSize(srcDesc.size.height, j); - - D3D11_SUBRESOURCE_DATA& data = subRes[subResourceIndex]; - - data.pSysMem = initData->subResources[subResourceIndex]; - - data.SysMemPitch = UINT(initData->mipRowStrides[j]); - data.SysMemSlicePitch = UINT(initData->mipRowStrides[j] * mipHeight); - - subResourceIndex++; - } - } - } - - const int accessFlags = _calcResourceAccessFlags(srcDesc.cpuAccessFlags); - - RefPtr texture(new TextureResourceImpl(srcDesc, initialUsage)); - - switch (srcDesc.type) - { - case Resource::Type::Texture1D: - { - D3D11_TEXTURE1D_DESC desc = { 0 }; - desc.BindFlags = bindFlags; - desc.CPUAccessFlags = accessFlags; - desc.Format = format; - desc.MiscFlags = 0; - desc.MipLevels = srcDesc.numMipLevels; - desc.ArraySize = effectiveArraySize; - desc.Width = srcDesc.size.width; - desc.Usage = D3D11_USAGE_DEFAULT; - - ComPtr texture1D; - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateTexture1D(&desc, subRes.Buffer(), texture1D.writeRef())); - - texture->m_resource = texture1D; - break; - } - case Resource::Type::TextureCube: - case Resource::Type::Texture2D: - { - D3D11_TEXTURE2D_DESC desc = { 0 }; - desc.BindFlags = bindFlags; - desc.CPUAccessFlags = accessFlags; - desc.Format = format; - desc.MiscFlags = 0; - desc.MipLevels = srcDesc.numMipLevels; - desc.ArraySize = effectiveArraySize; - - desc.Width = srcDesc.size.width; - desc.Height = srcDesc.size.height; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.SampleDesc.Count = srcDesc.sampleDesc.numSamples; - desc.SampleDesc.Quality = srcDesc.sampleDesc.quality; - - if (srcDesc.type == Resource::Type::TextureCube) - { - desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE; - } - - ComPtr texture2D; - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateTexture2D(&desc, subRes.Buffer(), texture2D.writeRef())); - - texture->m_resource = texture2D; - break; - } - case Resource::Type::Texture3D: - { - D3D11_TEXTURE3D_DESC desc = { 0 }; - desc.BindFlags = bindFlags; - desc.CPUAccessFlags = accessFlags; - desc.Format = format; - desc.MiscFlags = 0; - desc.MipLevels = srcDesc.numMipLevels; - desc.Width = srcDesc.size.width; - desc.Height = srcDesc.size.height; - desc.Depth = srcDesc.size.depth; - desc.Usage = D3D11_USAGE_DEFAULT; - - ComPtr texture3D; - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateTexture3D(&desc, subRes.Buffer(), texture3D.writeRef())); - - texture->m_resource = texture3D; - break; - } - default: return nullptr; - } - - return texture.detach(); -} - -BufferResource* D3D11Renderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData) -{ - BufferResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - // Make aligned to 256 bytes... not sure why, but if you remove this the tests do fail. - const size_t alignedSizeInBytes = D3DUtil::calcAligned(srcDesc.sizeInBytes, 256); - - // Hack to make the initialization never read from out of bounds memory, by copying into a buffer - List initDataBuffer; - if (initData && alignedSizeInBytes > srcDesc.sizeInBytes) - { - initDataBuffer.SetSize(alignedSizeInBytes); - ::memcpy(initDataBuffer.Buffer(), initData, srcDesc.sizeInBytes); - initData = initDataBuffer.Buffer(); - } - - D3D11_BUFFER_DESC bufferDesc = { 0 }; - bufferDesc.ByteWidth = UINT(alignedSizeInBytes); - bufferDesc.BindFlags = _calcResourceBindFlags(srcDesc.bindFlags); - // For read we'll need to do some staging - bufferDesc.CPUAccessFlags = _calcResourceAccessFlags(descIn.cpuAccessFlags & Resource::AccessFlag::Write); - bufferDesc.Usage = D3D11_USAGE_DEFAULT; - - // If written by CPU, make it dynamic - if (descIn.cpuAccessFlags & Resource::AccessFlag::Write) - { - bufferDesc.Usage = D3D11_USAGE_DYNAMIC; - } - - switch (initialUsage) - { - case Resource::Usage::ConstantBuffer: - { - // We'll just assume ConstantBuffers are dynamic for now - bufferDesc.Usage = D3D11_USAGE_DYNAMIC; - break; - } - default: break; - } - - if (bufferDesc.BindFlags & (D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE)) - { - //desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; - if (srcDesc.elementSize != 0) - { - bufferDesc.StructureByteStride = srcDesc.elementSize; - bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; - } - else - { - bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; - } - } - - D3D11_SUBRESOURCE_DATA subResourceData = { 0 }; - subResourceData.pSysMem = initData; - - RefPtr buffer(new BufferResourceImpl(srcDesc, initialUsage)); - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateBuffer(&bufferDesc, initData ? &subResourceData : nullptr, buffer->m_buffer.writeRef())); - - if (srcDesc.cpuAccessFlags & Resource::AccessFlag::Read) - { - D3D11_BUFFER_DESC bufDesc = {}; - bufDesc.BindFlags = 0; - bufDesc.ByteWidth = (UINT)alignedSizeInBytes; - bufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - bufDesc.Usage = D3D11_USAGE_STAGING; - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateBuffer(&bufDesc, nullptr, buffer->m_staging.writeRef())); - } - - return buffer.detach(); -} - -InputLayout* D3D11Renderer::createInputLayout(const InputElementDesc* inputElementsIn, UInt inputElementCount) -{ - D3D11_INPUT_ELEMENT_DESC inputElements[16] = {}; - - char hlslBuffer[1024]; - char* hlslCursor = &hlslBuffer[0]; - - hlslCursor += sprintf(hlslCursor, "float4 main(\n"); - - for (UInt ii = 0; ii < inputElementCount; ++ii) - { - inputElements[ii].SemanticName = inputElementsIn[ii].semanticName; - inputElements[ii].SemanticIndex = (UINT)inputElementsIn[ii].semanticIndex; - inputElements[ii].Format = D3DUtil::getMapFormat(inputElementsIn[ii].format); - inputElements[ii].InputSlot = 0; - inputElements[ii].AlignedByteOffset = (UINT)inputElementsIn[ii].offset; - inputElements[ii].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; - inputElements[ii].InstanceDataStepRate = 0; - - if (ii != 0) - { - hlslCursor += sprintf(hlslCursor, ",\n"); - } - - char const* typeName = "Unknown"; - switch (inputElementsIn[ii].format) - { - case Format::RGBA_Float32: - typeName = "float4"; - break; - case Format::RGB_Float32: - typeName = "float3"; - break; - case Format::RG_Float32: - typeName = "float2"; - break; - case Format::R_Float32: - typeName = "float"; - break; - default: - return nullptr; - } - - hlslCursor += sprintf(hlslCursor, "%s a%d : %s%d", - typeName, - (int)ii, - inputElementsIn[ii].semanticName, - (int)inputElementsIn[ii].semanticIndex); - } - - hlslCursor += sprintf(hlslCursor, "\n) : SV_Position { return 0; }"); - - ComPtr vertexShaderBlob; - SLANG_RETURN_NULL_ON_FAIL(D3DUtil::compileHLSLShader("inputLayout", hlslBuffer, "main", "vs_5_0", vertexShaderBlob)); - - ComPtr inputLayout; - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateInputLayout(&inputElements[0], (UINT)inputElementCount, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), - inputLayout.writeRef())); - - InputLayoutImpl* impl = new InputLayoutImpl; - impl->m_layout.swap(inputLayout); - - return impl; -} - -void* D3D11Renderer::map(BufferResource* bufferIn, MapFlavor flavor) -{ - BufferResourceImpl* bufferResource = static_cast(bufferIn); - - D3D11_MAP mapType; - ID3D11Buffer* buffer = bufferResource->m_buffer; - - switch (flavor) - { - case MapFlavor::WriteDiscard: - mapType = D3D11_MAP_WRITE_DISCARD; - break; - case MapFlavor::HostWrite: - mapType = D3D11_MAP_WRITE; - break; - case MapFlavor::HostRead: - mapType = D3D11_MAP_READ; - - buffer = bufferResource->m_staging; - if (!buffer) - { - return nullptr; - } - - // Okay copy the data over - m_immediateContext->CopyResource(buffer, bufferResource->m_buffer); - - break; - default: - return nullptr; - } - - // We update our constant buffer per-frame, just for the purposes - // of the example, but we don't actually load different data - // per-frame (we always use an identity projection). - D3D11_MAPPED_SUBRESOURCE mappedSub; - SLANG_RETURN_NULL_ON_FAIL(m_immediateContext->Map(buffer, 0, mapType, 0, &mappedSub)); - - bufferResource->m_mapFlavor = flavor; - - return mappedSub.pData; -} - -void D3D11Renderer::unmap(BufferResource* bufferIn) -{ - BufferResourceImpl* bufferResource = static_cast(bufferIn); - ID3D11Buffer* buffer = (bufferResource->m_mapFlavor == MapFlavor::HostRead) ? bufferResource->m_staging : bufferResource->m_buffer; - m_immediateContext->Unmap(buffer, 0); -} - -void D3D11Renderer::setInputLayout(InputLayout* inputLayoutIn) -{ - auto inputLayout = static_cast(inputLayoutIn); - m_immediateContext->IASetInputLayout(inputLayout->m_layout); -} - -void D3D11Renderer::setPrimitiveTopology(PrimitiveTopology topology) -{ - m_immediateContext->IASetPrimitiveTopology(D3DUtil::getPrimitiveTopology(topology)); -} - -void D3D11Renderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffersIn, const UInt* stridesIn, const UInt* offsetsIn) -{ - static const int kMaxVertexBuffers = 16; - assert(slotCount <= kMaxVertexBuffers); - - UINT vertexStrides[kMaxVertexBuffers]; - UINT vertexOffsets[kMaxVertexBuffers]; - ID3D11Buffer* dxBuffers[kMaxVertexBuffers]; - - auto buffers = (BufferResourceImpl*const*)buffersIn; - - for (UInt ii = 0; ii < slotCount; ++ii) - { - vertexStrides[ii] = (UINT)stridesIn[ii]; - vertexOffsets[ii] = (UINT)offsetsIn[ii]; - dxBuffers[ii] = buffers[ii]->m_buffer; - } - - m_immediateContext->IASetVertexBuffers((UINT)startSlot, (UINT)slotCount, dxBuffers, &vertexStrides[0], &vertexOffsets[0]); -} - -void D3D11Renderer::setShaderProgram(ShaderProgram* programIn) -{ - auto program = (ShaderProgramImpl*)programIn; - m_immediateContext->CSSetShader(program->m_computeShader, nullptr, 0); - m_immediateContext->VSSetShader(program->m_vertexShader, nullptr, 0); - m_immediateContext->PSSetShader(program->m_pixelShader, nullptr, 0); -} - -void D3D11Renderer::draw(UInt vertexCount, UInt startVertex) -{ - _applyBindingState(false); - m_immediateContext->Draw((UINT)vertexCount, (UINT)startVertex); -} - -ShaderProgram* D3D11Renderer::createProgram(const ShaderProgram::Desc& desc) -{ - if (desc.pipelineType == PipelineType::Compute) - { - auto computeKernel = desc.findKernel(StageType::Compute); - - ComPtr computeShader; - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateComputeShader(computeKernel->codeBegin, computeKernel->getCodeSize(), nullptr, computeShader.writeRef())); - - ShaderProgramImpl* shaderProgram = new ShaderProgramImpl(); - shaderProgram->m_computeShader.swap(computeShader); - return shaderProgram; - } - else - { - auto vertexKernel = desc.findKernel(StageType::Vertex); - auto fragmentKernel = desc.findKernel(StageType::Fragment); - - ComPtr vertexShader; - ComPtr pixelShader; - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateVertexShader(vertexKernel->codeBegin, vertexKernel->getCodeSize(), nullptr, vertexShader.writeRef())); - SLANG_RETURN_NULL_ON_FAIL(m_device->CreatePixelShader(fragmentKernel->codeBegin, fragmentKernel->getCodeSize(), nullptr, pixelShader.writeRef())); - - ShaderProgramImpl* shaderProgram = new ShaderProgramImpl(); - shaderProgram->m_vertexShader.swap(vertexShader); - shaderProgram->m_pixelShader.swap(pixelShader); - return shaderProgram; - } -} - -void D3D11Renderer::dispatchCompute(int x, int y, int z) -{ - _applyBindingState(true); - m_immediateContext->Dispatch(x, y, z); -} - -BindingState* D3D11Renderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - auto& dstDetail = dstDetails[i]; - const auto& srcBinding = srcBindings[i]; - - assert(srcBinding.registerRange.isSingle()); - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - assert(srcBinding.resource && srcBinding.resource->isBuffer()); - - BufferResourceImpl* buffer = static_cast(srcBinding.resource.Ptr()); - const BufferResource::Desc& bufferDesc = buffer->getDesc(); - - const int elemSize = bufferDesc.elementSize <= 0 ? 1 : bufferDesc.elementSize; - - if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - D3D11_UNORDERED_ACCESS_VIEW_DESC viewDesc; - memset(&viewDesc, 0, sizeof(viewDesc)); - viewDesc.Buffer.FirstElement = 0; - viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); - viewDesc.Buffer.Flags = 0; - viewDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; - viewDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); - - if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) - { - viewDesc.Buffer.Flags |= D3D11_BUFFER_UAV_FLAG_RAW; - viewDesc.Format = DXGI_FORMAT_R32_TYPELESS; - } - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateUnorderedAccessView(buffer->m_buffer, &viewDesc, dstDetail.m_uav.writeRef())); - } - if (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource)) - { - D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; - memset(&viewDesc, 0, sizeof(viewDesc)); - viewDesc.Buffer.FirstElement = 0; - viewDesc.Buffer.ElementWidth = elemSize; - viewDesc.Buffer.NumElements = (UINT)(bufferDesc.sizeInBytes / elemSize); - viewDesc.Buffer.ElementOffset = 0; - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; - viewDesc.Format = DXGI_FORMAT_UNKNOWN; - - if (bufferDesc.elementSize == 0) - { - viewDesc.Format = DXGI_FORMAT_R32_FLOAT; - } - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(buffer->m_buffer, &viewDesc, dstDetail.m_srv.writeRef())); - } - break; - } - case BindingType::Texture: - case BindingType::CombinedTextureSampler: - { - assert(srcBinding.resource && srcBinding.resource->isTexture()); - - TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); - - const TextureResource::Desc& textureDesc = texture->getDesc(); - - D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; - viewDesc.Format = D3DUtil::getMapFormat(textureDesc.format); - - switch (texture->getType()) - { - case Resource::Type::Texture1D: - { - if (textureDesc.arraySize <= 0) - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; - viewDesc.Texture1D.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture1D.MostDetailedMip = 0; - } - else - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; - viewDesc.Texture1DArray.ArraySize = textureDesc.arraySize; - viewDesc.Texture1DArray.FirstArraySlice = 0; - viewDesc.Texture1DArray.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture1DArray.MostDetailedMip = 0; - } - break; - } - case Resource::Type::Texture2D: - { - if (textureDesc.arraySize <= 0) - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - viewDesc.Texture2D.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture2D.MostDetailedMip = 0; - } - else - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - viewDesc.Texture2DArray.ArraySize = textureDesc.arraySize; - viewDesc.Texture2DArray.FirstArraySlice = 0; - viewDesc.Texture2DArray.MipLevels = textureDesc.numMipLevels; - viewDesc.Texture2DArray.MostDetailedMip = 0; - } - break; - } - case Resource::Type::TextureCube: - { - if (textureDesc.arraySize <= 0) - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - viewDesc.TextureCube.MipLevels = textureDesc.numMipLevels; - viewDesc.TextureCube.MostDetailedMip = 0; - } - else - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; - viewDesc.TextureCubeArray.MipLevels = textureDesc.numMipLevels; - viewDesc.TextureCubeArray.MostDetailedMip = 0; - viewDesc.TextureCubeArray.First2DArrayFace = 0; - viewDesc.TextureCubeArray.NumCubes = textureDesc.arraySize; - } - break; - } - case Resource::Type::Texture3D: - { - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - viewDesc.Texture3D.MipLevels = textureDesc.numMipLevels; // Old code fixed as one - viewDesc.Texture3D.MostDetailedMip = 0; - break; - } - default: - { - assert(!"Unhandled type"); - return nullptr; - } - } - - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateShaderResourceView(texture->m_resource, &viewDesc, dstDetail.m_srv.writeRef())); - break; - } - case BindingType::Sampler: - { - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; - - D3D11_SAMPLER_DESC desc = {}; - desc.AddressU = desc.AddressV = desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; - - if (samplerDesc.isCompareSampler) - { - desc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; - desc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - desc.MinLOD = desc.MaxLOD = 0.0f; - } - else - { - desc.Filter = D3D11_FILTER_ANISOTROPIC; - desc.MaxAnisotropy = 8; - desc.MinLOD = 0.0f; - desc.MaxLOD = 100.0f; - } - SLANG_RETURN_NULL_ON_FAIL(m_device->CreateSamplerState(&desc, dstDetail.m_samplerState.writeRef())); - break; - } - default: - { - assert(!"Unhandled type"); - return nullptr; - } - } - } - - // Done - return bindingState.detach(); -} - -void D3D11Renderer::_applyBindingState(bool isCompute) -{ - auto context = m_immediateContext.get(); - - const auto& details = m_currentBindings->m_bindingDetails; - const auto& bindings = m_currentBindings->getDesc().m_bindings; - - const int numBindings = int(bindings.Count()); - - for (int i = 0; i < numBindings; ++i) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - const int bindingIndex = binding.registerRange.getSingleIndex(); - - switch (binding.bindingType) - { - case BindingType::Buffer: - { - assert(binding.resource && binding.resource->isBuffer()); - if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) - { - ID3D11Buffer* buffer = static_cast(binding.resource.Ptr())->m_buffer; - if (isCompute) - context->CSSetConstantBuffers(bindingIndex, 1, &buffer); - else - { - context->VSSetConstantBuffers(bindingIndex, 1, &buffer); - context->PSSetConstantBuffers(bindingIndex, 1, &buffer); - } - } - else if (detail.m_uav) - { - if (isCompute) - context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); - else - context->OMSetRenderTargetsAndUnorderedAccessViews(m_currentBindings->getDesc().m_numRenderTargets, - m_renderTargetViews.Buffer()->readRef(), nullptr, bindingIndex, 1, detail.m_uav.readRef(), nullptr); - } - else - { - if (isCompute) - context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - else - { - context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - } - } - break; - } - case BindingType::Texture: - { - if (detail.m_uav) - { - if (isCompute) - context->CSSetUnorderedAccessViews(bindingIndex, 1, detail.m_uav.readRef(), nullptr); - else - context->OMSetRenderTargetsAndUnorderedAccessViews(D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, - nullptr, nullptr, bindingIndex, 1, detail.m_uav.readRef(), nullptr); - } - else - { - if (isCompute) - context->CSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - else - { - context->PSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - context->VSSetShaderResources(bindingIndex, 1, detail.m_srv.readRef()); - } - } - break; - } - case BindingType::Sampler: - { - if (isCompute) - context->CSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); - else - { - context->PSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); - context->VSSetSamplers(bindingIndex, 1, detail.m_samplerState.readRef()); - } - break; - } - default: - { - assert(!"Not implemented"); - return; - } - } - } -} - -void D3D11Renderer::setBindingState(BindingState* state) -{ - m_currentBindings = static_cast(state); -} - -} // renderer_test diff --git a/tools/slang-graphics/render-d3d11.h b/tools/slang-graphics/render-d3d11.h deleted file mode 100644 index 7b3d25e9f..000000000 --- a/tools/slang-graphics/render-d3d11.h +++ /dev/null @@ -1,10 +0,0 @@ -// render-d3d11.h -#pragma once - -namespace slang_graphics { - -class Renderer; - -Renderer* createD3D11Renderer(); - -} // slang_graphics diff --git a/tools/slang-graphics/render-d3d12.cpp b/tools/slang-graphics/render-d3d12.cpp deleted file mode 100644 index 24c9ecacb..000000000 --- a/tools/slang-graphics/render-d3d12.cpp +++ /dev/null @@ -1,2467 +0,0 @@ -// render-d3d12.cpp -#define _CRT_SECURE_NO_WARNINGS - -#include "render-d3d12.h" - -//WORKING:#include "options.h" -#include "render.h" - -#include "surface.h" - -// In order to use the Slang API, we need to include its header - -//WORKING:#include - -// We will be rendering with Direct3D 12, so we need to include -// the Windows and D3D12 headers - -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#include -#include -#include - -#include "../../slang-com-ptr.h" - -#include "resource-d3d12.h" -#include "descriptor-heap-d3d12.h" -#include "circular-resource-heap-d3d12.h" - -#include "d3d-util.h" - -// We will use the C standard library just for printing error messages. -#include - -#ifdef _MSC_VER -#include -#if (_MSC_VER < 1900) -#define snprintf sprintf_s -#endif -#endif -// - -#define ENABLE_DEBUG_LAYER 1 - -namespace slang_graphics { -using namespace Slang; - -class D3D12Renderer : public Renderer -{ -public: - // Renderer implementation - virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; - virtual void setClearColor(const float color[4]) override; - virtual void clearFrame() override; - virtual void presentFrame() override; - virtual TextureResource* createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData) override; - virtual BufferResource* createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& bufferDesc, const void* initData) override; - virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; - virtual InputLayout* createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) override; - virtual BindingState* createBindingState(const BindingState::Desc& bindingStateDesc) override; - virtual ShaderProgram* createProgram(const ShaderProgram::Desc& desc) override; - virtual void* map(BufferResource* buffer, MapFlavor flavor) override; - virtual void unmap(BufferResource* buffer) override; - virtual void setInputLayout(InputLayout* inputLayout) override; - virtual void setPrimitiveTopology(PrimitiveTopology topology) override; - virtual void setBindingState(BindingState* state); - virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; - virtual void setShaderProgram(ShaderProgram* inProgram) 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; - virtual RendererType getRendererType() const override { return RendererType::DirectX12; } - - ~D3D12Renderer(); - -protected: - static const Int kMaxNumRenderFrames = 4; - static const Int kMaxNumRenderTargets = 3; - - struct Submitter - { - virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0; - virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor) = 0; - virtual void setRootSignature(ID3D12RootSignature* rootSignature) = 0; - }; - - struct FrameInfo - { - FrameInfo() :m_fenceValue(0) {} - void reset() - { - m_commandAllocator.setNull(); - } - ComPtr m_commandAllocator; ///< The command allocator for this frame - UINT64 m_fenceValue; ///< The fence value when rendering this Frame is complete - }; - - class ShaderProgramImpl: public ShaderProgram - { - public: - PipelineType m_pipelineType; - List m_vertexShader; - List m_pixelShader; - List m_computeShader; - }; - - class BufferResourceImpl: public BufferResource - { - public: - typedef BufferResource Parent; - - enum class BackingStyle - { - Unknown, - ResourceBacked, ///< The contents is only held within the resource - MemoryBacked, ///< The current contents is held in m_memory and copied to GPU every time it's used (typically used for constant buffers) - }; - - void bindConstantBufferView(D3D12CircularResourceHeap& circularHeap, int index, Submitter* submitter) const - { - switch (m_backingStyle) - { - case BackingStyle::MemoryBacked: - { - const size_t bufferSize = m_memory.Count(); - D3D12CircularResourceHeap::Cursor cursor = circularHeap.allocateConstantBuffer(bufferSize); - ::memcpy(cursor.m_position, m_memory.Buffer(), bufferSize); - // Set the constant buffer - submitter->setRootConstantBufferView(index, circularHeap.getGpuHandle(cursor)); - break; - } - case BackingStyle::ResourceBacked: - { - // Set the constant buffer - submitter->setRootConstantBufferView(index, m_resource.getResource()->GetGPUVirtualAddress()); - break; - } - default: break; - } - } - - BufferResourceImpl(Resource::Usage initialUsage, const Desc& desc): - Parent(desc), - m_mapFlavor(MapFlavor::HostRead), - m_initialUsage(initialUsage) - { - } - - static BackingStyle _calcResourceBackingStyle(Usage usage) - { - switch (usage) - { - case Usage::ConstantBuffer: return BackingStyle::MemoryBacked; - default: return BackingStyle::ResourceBacked; - } - } - - BackingStyle m_backingStyle; ///< How the resource is 'backed' - either as a resource or cpu memory. Cpu memory is typically used for constant buffers. - D3D12Resource m_resource; ///< The resource typically in gpu memory - D3D12Resource m_uploadResource; ///< If the resource can be written to, and is in gpu memory (ie not Memory backed), will have upload resource - - Usage m_initialUsage; - - List m_memory; ///< Cpu memory buffer, used if the m_backingStyle is MemoryBacked - MapFlavor m_mapFlavor; ///< If the resource is mapped holds the current mapping flavor - }; - - class TextureResourceImpl: public TextureResource - { - public: - typedef TextureResource Parent; - - TextureResourceImpl(const Desc& desc): - Parent(desc) - { - } - - D3D12Resource m_resource; - }; - - class InputLayoutImpl: public InputLayout - { - public: - List m_elements; - List m_text; ///< Holds all strings to keep in scope - }; - - struct BindingDetail - { - int m_srvIndex = -1; - int m_uavIndex = -1; - int m_samplerIndex = -1; - }; - - class BindingStateImpl: public BindingState - { - public: - typedef BindingState Parent; - - Result init(ID3D12Device* device) - { - // Set up descriptor heaps - SLANG_RETURN_ON_FAIL(m_viewHeap.init(device, 256, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); - SLANG_RETURN_ON_FAIL(m_samplerHeap.init(device, 16, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE)); - return SLANG_OK; - } - - /// Ctor - BindingStateImpl(const Desc& desc) : - Parent(desc) - {} - - List m_bindingDetails; ///< These match 1-1 to the bindings in the m_desc - - D3D12DescriptorHeap m_viewHeap; ///< Cbv, Srv, Uav - D3D12DescriptorHeap m_samplerHeap; ///< Heap for samplers - }; - - class RenderState: public RefObject - { - public: - D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType; - RefPtr m_bindingState; - RefPtr m_inputLayout; - RefPtr m_shaderProgram; - - ComPtr m_rootSignature; - ComPtr m_pipelineState; - }; - - struct BoundVertexBuffer - { - RefPtr m_buffer; - int m_stride; - int m_offset; - }; - - 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 setRootSignature(ID3D12RootSignature* rootSignature) - { - m_commandList->SetGraphicsRootSignature(rootSignature); - } - - GraphicsSubmitter(ID3D12GraphicsCommandList* commandList): - m_commandList(commandList) - { - } - - ID3D12GraphicsCommandList* m_commandList; - }; - - struct ComputeSubmitter : public Submitter - { - virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) override - { - m_commandList->SetComputeRootConstantBufferView(index, gpuBufferLocation); - } - virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor) override - { - m_commandList->SetComputeRootDescriptorTable(index, baseDescriptor); - } - void setRootSignature(ID3D12RootSignature* rootSignature) - { - m_commandList->SetComputeRootSignature(rootSignature); - } - - ComputeSubmitter(ID3D12GraphicsCommandList* commandList) : - m_commandList(commandList) - { - } - - ID3D12GraphicsCommandList* m_commandList; - }; - - static PROC loadProc(HMODULE module, char const* name); - Result createFrameResources(); - /// Blocks until gpu has completed all work - void releaseFrameResources(); - - Result createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut); - - void beginRender(); - - void endRender(); - - void submitGpuWorkAndWait(); - void _resetCommandList(); - - Result captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut); - - FrameInfo& getFrame() { return m_frameInfos[m_frameIndex]; } - const FrameInfo& getFrame() const { return m_frameInfos[m_frameIndex]; } - - ID3D12GraphicsCommandList* getCommandList() const { return m_commandList; } - - RenderState* calcRenderState(); - /// From current bindings calculate the root signature and pipeline state - Result calcGraphicsPipelineState(ComPtr& sigOut, ComPtr& pipelineStateOut); - Result calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut); - - Result _bindRenderState(RenderState* renderState, ID3D12GraphicsCommandList* commandList, Submitter* submitter); - - Result _calcBindParameters(BindParameters& params); - RenderState* findRenderState(PipelineType pipelineType); - - PFN_D3D12_SERIALIZE_ROOT_SIGNATURE m_D3D12SerializeRootSignature = nullptr; - - D3D12CircularResourceHeap m_circularResourceHeap; - - int m_commandListOpenCount = 0; ///< If >0 the command list should be open - - List m_boundVertexBuffers; - - RefPtr m_boundShaderProgram; - RefPtr m_boundInputLayout; - RefPtr 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 - - Desc m_desc; - - bool m_isInitialized = false; - - D3D12_PRIMITIVE_TOPOLOGY_TYPE m_primitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - D3D12_PRIMITIVE_TOPOLOGY m_primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - - float m_clearColor[4] = { 0, 0, 0, 0 }; - - D3D12_VIEWPORT m_viewport = {}; - - ComPtr m_dxDebug; - - ComPtr m_device; - ComPtr m_swapChain; - ComPtr m_commandQueue; - ComPtr m_rtvHeap; - ComPtr m_commandList; - - D3D12_RECT m_scissorRect = {}; - - List > m_renderStates; ///< Holds list of all render state combinations - RenderState* m_currentRenderState = nullptr; ///< The current combination - - UINT m_rtvDescriptorSize = 0; - - ComPtr m_dsvHeap; - UINT m_dsvDescriptorSize = 0; - - // Synchronization objects. - D3D12CounterFence m_fence; - - HANDLE m_swapChainWaitableObject; - - // Frame specific data - int m_numRenderFrames = 0; - UINT m_frameIndex = 0; - FrameInfo m_frameInfos[kMaxNumRenderFrames]; - - int m_numRenderTargets = 2; - int m_renderTargetIndex = 0; - - D3D12Resource* m_backBuffers[kMaxNumRenderTargets]; - D3D12Resource* m_renderTargets[kMaxNumRenderTargets]; - - D3D12Resource m_backBufferResources[kMaxNumRenderTargets]; - D3D12Resource m_renderTargetResources[kMaxNumRenderTargets]; - - 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() -{ - return new D3D12Renderer; -} - -/* static */PROC D3D12Renderer::loadProc(HMODULE module, char const* name) -{ - PROC proc = ::GetProcAddress(module, name); - if (!proc) - { - fprintf(stderr, "error: failed load symbol '%s'\n", name); - return nullptr; - } - return proc; -} - -void D3D12Renderer::releaseFrameResources() -{ - // https://msdn.microsoft.com/en-us/library/windows/desktop/bb174577%28v=vs.85%29.aspx - - // Release the resources holding references to the swap chain (requirement of - // IDXGISwapChain::ResizeBuffers) and reset the frame fence values to the - // current fence value. - for (int i = 0; i < m_numRenderFrames; i++) - { - FrameInfo& info = m_frameInfos[i]; - info.reset(); - info.m_fenceValue = m_fence.getCurrentValue(); - } - for (int i = 0; i < m_numRenderTargets; i++) - { - m_backBuffers[i]->setResourceNull(); - m_renderTargets[i]->setResourceNull(); - } -} - -void D3D12Renderer::waitForGpu() -{ - m_fence.nextSignalAndWait(m_commandQueue); -} - -D3D12Renderer::~D3D12Renderer() -{ - if (m_isInitialized) - { - // Ensure that the GPU is no longer referencing resources that are about to be - // cleaned up by the destructor. - waitForGpu(); - } -} - -static void _initSrvDesc(Resource::Type resourceType, const TextureResource::Desc& textureDesc, const D3D12_RESOURCE_DESC& desc, DXGI_FORMAT pixelFormat, D3D12_SHADER_RESOURCE_VIEW_DESC& descOut) -{ - // create SRV - descOut = D3D12_SHADER_RESOURCE_VIEW_DESC(); - - descOut.Format = (pixelFormat == DXGI_FORMAT_UNKNOWN) ? D3DUtil::calcFormat(D3DUtil::USAGE_SRV, desc.Format) : pixelFormat; - descOut.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - if (desc.DepthOrArraySize == 1) - { - switch (desc.Dimension) - { - case D3D12_RESOURCE_DIMENSION_TEXTURE1D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; break; - case D3D12_RESOURCE_DIMENSION_TEXTURE2D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; break; - case D3D12_RESOURCE_DIMENSION_TEXTURE3D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; break; - default: assert(!"Unknown dimension"); - } - - descOut.Texture2D.MipLevels = desc.MipLevels; - descOut.Texture2D.MostDetailedMip = 0; - descOut.Texture2D.PlaneSlice = 0; - descOut.Texture2D.ResourceMinLODClamp = 0.0f; - } - else if (resourceType == Resource::Type::TextureCube) - { - if (textureDesc.arraySize > 1) - { - descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; - - descOut.TextureCubeArray.NumCubes = textureDesc.arraySize; - descOut.TextureCubeArray.First2DArrayFace = 0; - descOut.TextureCubeArray.MipLevels = desc.MipLevels; - descOut.TextureCubeArray.MostDetailedMip = 0; - descOut.TextureCubeArray.ResourceMinLODClamp = 0; - } - else - { - descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; - - descOut.TextureCube.MipLevels = desc.MipLevels; - descOut.TextureCube.MostDetailedMip = 0; - descOut.TextureCube.ResourceMinLODClamp = 0; - } - } - else - { - assert(desc.DepthOrArraySize > 1); - - switch (desc.Dimension) - { - case D3D12_RESOURCE_DIMENSION_TEXTURE1D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY; break; - case D3D12_RESOURCE_DIMENSION_TEXTURE2D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; break; - case D3D12_RESOURCE_DIMENSION_TEXTURE3D: descOut.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; break; - - default: assert(!"Unknown dimension"); - } - - descOut.Texture2DArray.ArraySize = desc.DepthOrArraySize; - descOut.Texture2DArray.MostDetailedMip = 0; - descOut.Texture2DArray.MipLevels = desc.MipLevels; - descOut.Texture2DArray.FirstArraySlice = 0; - descOut.Texture2DArray.PlaneSlice = 0; - descOut.Texture2DArray.ResourceMinLODClamp = 0; - } -} - -static void _initBufferResourceDesc(size_t bufferSize, D3D12_RESOURCE_DESC& out) -{ - out = {}; - - out.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - out.Alignment = 0; - out.Width = bufferSize; - out.Height = 1; - out.DepthOrArraySize = 1; - out.MipLevels = 1; - out.Format = DXGI_FORMAT_UNKNOWN; - out.SampleDesc.Count = 1; - out.SampleDesc.Quality = 0; - out.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - out.Flags = D3D12_RESOURCE_FLAG_NONE; -} - -Result D3D12Renderer::createBuffer(const D3D12_RESOURCE_DESC& resourceDesc, const void* srcData, D3D12Resource& uploadResource, D3D12_RESOURCE_STATES finalState, D3D12Resource& resourceOut) -{ - const size_t bufferSize = size_t(resourceDesc.Width); - - { - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - const D3D12_RESOURCE_STATES initialState = srcData ? D3D12_RESOURCE_STATE_COPY_DEST : finalState; - - SLANG_RETURN_ON_FAIL(resourceOut.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, initialState, nullptr)); - } - - { - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - D3D12_RESOURCE_DESC uploadResourceDesc(resourceDesc); - uploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; - - SLANG_RETURN_ON_FAIL(uploadResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, uploadResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr)); - } - - if (srcData) - { - // Copy data to the intermediate upload heap and then schedule a copy - // from the upload heap to the vertex buffer. - UINT8* dstData; - D3D12_RANGE readRange = {}; // We do not intend to read from this resource on the CPU. - - ID3D12Resource* dxUploadResource = uploadResource.getResource(); - - SLANG_RETURN_ON_FAIL(dxUploadResource->Map(0, &readRange, reinterpret_cast(&dstData))); - ::memcpy(dstData, srcData, 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; -} - -void D3D12Renderer::_resetCommandList() -{ - const FrameInfo& frame = getFrame(); - - ID3D12GraphicsCommandList* commandList = getCommandList(); - commandList->Reset(frame.m_commandAllocator, nullptr); - - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { m_rtvHeap->GetCPUDescriptorHandleForHeapStart().ptr + m_renderTargetIndex * m_rtvDescriptorSize }; - if (m_depthStencil) - { - commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &m_depthStencilView); - } - else - { - commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr); - } - - // Set necessary state. - commandList->RSSetViewports(1, &m_viewport); - commandList->RSSetScissorRects(1, &m_scissorRect); -} - -void D3D12Renderer::beginRender() -{ - // Should currently not be open! - assert(m_commandListOpenCount == 0); - - m_circularResourceHeap.updateCompleted(); - - getFrame().m_commandAllocator->Reset(); - - _resetCommandList(); - - // Indicate that the render target needs to be writable - { - D3D12BarrierSubmitter submitter(m_commandList); - m_renderTargets[m_renderTargetIndex]->transition(D3D12_RESOURCE_STATE_RENDER_TARGET, submitter); - } - - m_commandListOpenCount = 1; -} - -void D3D12Renderer::endRender() -{ - assert(m_commandListOpenCount == 1); - - { - const UInt64 signalValue = m_fence.nextSignal(m_commandQueue); - m_circularResourceHeap.addSync(signalValue); - } - - D3D12Resource& backBuffer = *m_backBuffers[m_renderTargetIndex]; - if (m_isMultiSampled) - { - // MSAA resolve - D3D12Resource& renderTarget = *m_renderTargets[m_renderTargetIndex]; - assert(&renderTarget != &backBuffer); - // Barriers to wait for the render target, and the backbuffer to be in correct state - { - D3D12BarrierSubmitter submitter(m_commandList); - renderTarget.transition(D3D12_RESOURCE_STATE_RESOLVE_SOURCE, submitter); - backBuffer.transition(D3D12_RESOURCE_STATE_RESOLVE_DEST, submitter); - } - - // Do the resolve... - m_commandList->ResolveSubresource(backBuffer, 0, renderTarget, 0, m_targetFormat); - } - - // Make the back buffer presentable - { - D3D12BarrierSubmitter submitter(m_commandList); - backBuffer.transition(D3D12_RESOURCE_STATE_PRESENT, submitter); - } - - SLANG_ASSERT_VOID_ON_FAIL(m_commandList->Close()); - - { - // Execute the command list. - ID3D12CommandList* commandLists[] = { m_commandList }; - m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists); - } - - assert(m_commandListOpenCount == 1); - // Must be 0 - m_commandListOpenCount = 0; -} - -void D3D12Renderer::submitGpuWork() -{ - assert(m_commandListOpenCount); - ID3D12GraphicsCommandList* commandList = getCommandList(); - - SLANG_ASSERT_VOID_ON_FAIL(commandList->Close()); - { - // Execute the command list. - ID3D12CommandList* commandLists[] = { commandList }; - m_commandQueue->ExecuteCommandLists(SLANG_COUNT_OF(commandLists), commandLists); - } - - // Reset the render target - _resetCommandList(); -} - -void D3D12Renderer::submitGpuWorkAndWait() -{ - submitGpuWork(); - waitForGpu(); -} - -Result D3D12Renderer::captureTextureToSurface(D3D12Resource& resource, Surface& surfaceOut) -{ - const D3D12_RESOURCE_STATES initialState = resource.getState(); - - const D3D12_RESOURCE_DESC desc = resource.getResource()->GetDesc(); - - // Don't bother supporting MSAA for right now - if (desc.SampleDesc.Count > 1) - { - fprintf(stderr, "ERROR: cannot capture multi-sample texture\n"); - return SLANG_FAIL; - } - - size_t bytesPerPixel = sizeof(uint32_t); - size_t rowPitch = int(desc.Width) * bytesPerPixel; - size_t bufferSize = rowPitch * int(desc.Height); - - D3D12Resource stagingResource; - { - D3D12_RESOURCE_DESC stagingDesc; - _initBufferResourceDesc(bufferSize, stagingDesc); - - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_READBACK; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - SLANG_RETURN_ON_FAIL(stagingResource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); - } - - { - D3D12BarrierSubmitter submitter(m_commandList); - resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter); - } - - // Do the copy - { - D3D12_TEXTURE_COPY_LOCATION srcLoc; - srcLoc.pResource = resource; - srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - srcLoc.SubresourceIndex = 0; - - D3D12_TEXTURE_COPY_LOCATION dstLoc; - dstLoc.pResource = stagingResource; - dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - dstLoc.PlacedFootprint.Offset = 0; - dstLoc.PlacedFootprint.Footprint.Format = desc.Format; - dstLoc.PlacedFootprint.Footprint.Width = UINT(desc.Width); - dstLoc.PlacedFootprint.Footprint.Height = UINT(desc.Height); - dstLoc.PlacedFootprint.Footprint.Depth = 1; - dstLoc.PlacedFootprint.Footprint.RowPitch = UINT(rowPitch); - - m_commandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr); - } - - { - D3D12BarrierSubmitter submitter(m_commandList); - resource.transition(initialState, submitter); - } - - // Submit the copy, and wait for copy to complete - submitGpuWorkAndWait(); - - { - ID3D12Resource* dxResource = stagingResource; - - UINT8* data; - D3D12_RANGE readRange = {0, bufferSize}; - - SLANG_RETURN_ON_FAIL(dxResource->Map(0, &readRange, reinterpret_cast(&data))); - - Result res = surfaceOut.set(int(desc.Width), int(desc.Height), Format::RGBA_Unorm_UInt8, int(rowPitch), data, SurfaceAllocator::getMallocAllocator()); - - dxResource->Unmap(0, nullptr); - return res; - } -} - -Result D3D12Renderer::calcComputePipelineState(ComPtr& signatureOut, ComPtr& pipelineStateOut) -{ - BindParameters bindParameters; - _calcBindParameters(bindParameters); - - ComPtr rootSignature; - ComPtr pipelineState; - - { - D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc; - rootSignatureDesc.NumParameters = bindParameters.m_paramIndex; - rootSignatureDesc.pParameters = bindParameters.m_parameters; - rootSignatureDesc.NumStaticSamplers = 0; - rootSignatureDesc.pStaticSamplers = nullptr; - rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; - - ComPtr signature; - ComPtr error; - SLANG_RETURN_ON_FAIL(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef())); - SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef()))); - } - - { - // Describe and create the compute pipeline state object - D3D12_COMPUTE_PIPELINE_STATE_DESC computeDesc = {}; - computeDesc.pRootSignature = rootSignature; - computeDesc.CS = { m_boundShaderProgram->m_computeShader.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& signatureOut, ComPtr& pipelineStateOut) -{ - BindParameters bindParameters; - _calcBindParameters(bindParameters); - - ComPtr rootSignature; - ComPtr 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 signature; - ComPtr error; - SLANG_RETURN_ON_FAIL(m_D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, signature.writeRef(), error.writeRef())); - SLANG_RETURN_ON_FAIL(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(rootSignature.writeRef()))); - } - - { - // Describe and create the 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; - - { - const int numRenderTargets = m_boundBindingState ? m_boundBindingState->getDesc().m_numRenderTargets : 1; - - psoDesc.DSVFormat = m_depthStencilFormat; - psoDesc.NumRenderTargets = numRenderTargets; - for (Int i = 0; i < numRenderTargets; i++) - { - psoDesc.RTVFormats[i] = m_targetFormat; - } - - psoDesc.SampleDesc.Count = 1; - psoDesc.SampleDesc.Quality = 0; - - psoDesc.SampleMask = UINT_MAX; - } - - { - auto& rs = psoDesc.RasterizerState; - rs.FillMode = D3D12_FILL_MODE_SOLID; - rs.CullMode = D3D12_CULL_MODE_NONE; - rs.FrontCounterClockwise = FALSE; - rs.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; - rs.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; - rs.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; - rs.DepthClipEnable = TRUE; - rs.MultisampleEnable = FALSE; - rs.AntialiasedLineEnable = FALSE; - rs.ForcedSampleCount = 0; - rs.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; - } - - { - D3D12_BLEND_DESC& blend = psoDesc.BlendState; - - blend.AlphaToCoverageEnable = FALSE; - blend.IndependentBlendEnable = FALSE; - const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = - { - FALSE,FALSE, - D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, - D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, - D3D12_LOGIC_OP_NOOP, - D3D12_COLOR_WRITE_ENABLE_ALL, - }; - for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) - { - blend.RenderTarget[i] = defaultRenderTargetBlendDesc; - } - } - - { - auto& ds = psoDesc.DepthStencilState; - - ds.DepthEnable = FALSE; - ds.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; - ds.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; - //ds.DepthFunc = D3D12_COMPARISON_FUNC_LESS; - ds.StencilEnable = FALSE; - ds.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; - ds.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; - const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = - { - D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS - }; - ds.FrontFace = defaultStencilOp; - ds.BackFace = defaultStencilOp; - } - } - - psoDesc.PrimitiveTopologyType = m_primitiveTopologyType; - - 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(PipelineType pipelineType) -{ - switch (pipelineType) - { - case PipelineType::Compute: - { - // Check if current state is a match - if (m_currentRenderState) - { - if (m_currentRenderState->m_bindingState == m_boundBindingState && - m_currentRenderState->m_shaderProgram == m_boundShaderProgram) - { - return m_currentRenderState; - } - } - - const int num = int(m_renderStates.Count()); - for (int i = 0; i < num; i++) - { - RenderState* renderState = m_renderStates[i]; - if (renderState->m_bindingState == m_boundBindingState && - renderState->m_shaderProgram == m_boundShaderProgram) - { - return renderState; - } - } - break; - } - case PipelineType::Graphics: - { - if (m_currentRenderState) - { - if (m_currentRenderState->m_bindingState == m_boundBindingState && - m_currentRenderState->m_inputLayout == m_boundInputLayout && - m_currentRenderState->m_shaderProgram == m_boundShaderProgram && - m_currentRenderState->m_primitiveTopologyType == m_primitiveTopologyType) - { - return m_currentRenderState; - } - } - // See if matches one in the list - { - const int num = int(m_renderStates.Count()); - for (int i = 0; i < num; i++) - { - RenderState* renderState = m_renderStates[i]; - if (renderState->m_bindingState == m_boundBindingState && - renderState->m_inputLayout == m_boundInputLayout && - renderState->m_shaderProgram == m_boundShaderProgram && - renderState->m_primitiveTopologyType == m_primitiveTopologyType) - { - // Okay we have a match - return renderState; - } - } - } - break; - } - default: break; - } - return nullptr; -} - -D3D12Renderer::RenderState* D3D12Renderer::calcRenderState() -{ - if (!m_boundShaderProgram) - { - return nullptr; - } - m_currentRenderState = findRenderState(m_boundShaderProgram->m_pipelineType); - if (m_currentRenderState) - { - return m_currentRenderState; - } - - ComPtr rootSignature; - ComPtr pipelineState; - - switch (m_boundShaderProgram->m_pipelineType) - { - case PipelineType::Compute: - { - if (SLANG_FAILED(calcComputePipelineState(rootSignature, pipelineState))) - { - return nullptr; - } - break; - } - case PipelineType::Graphics: - { - if (SLANG_FAILED(calcGraphicsPipelineState(rootSignature, pipelineState))) - { - return nullptr; - } - break; - } - default: return nullptr; - } - - RenderState* renderState = new RenderState; - - renderState->m_primitiveTopologyType = m_primitiveTopologyType; - renderState->m_bindingState = m_boundBindingState; - renderState->m_inputLayout = m_boundInputLayout; - renderState->m_shaderProgram = m_boundShaderProgram; - - renderState->m_rootSignature.swap(rootSignature); - renderState->m_pipelineState.swap(pipelineState); - - m_renderStates.Add(renderState); - - m_currentRenderState = renderState; - - return renderState; -} - -Result D3D12Renderer::_calcBindParameters(BindParameters& params) -{ - int numConstantBuffers = 0; - { - if (m_boundBindingState) - { - const int numBoundConstantBuffers = numConstantBuffers; - - const BindingState::Desc& bindingStateDesc = m_boundBindingState->getDesc(); - - const auto& bindings = bindingStateDesc.m_bindings; - const auto& details = m_boundBindingState->m_bindingDetails; - - const int numBindings = int(bindings.Count()); - - for (int i = 0; i < numBindings; i++) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - const int bindingIndex = binding.registerRange.getSingleIndex(); - - if (binding.bindingType == BindingType::Buffer) - { - assert(binding.resource && binding.resource->isBuffer()); - if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) - { - // Make sure it's not overlapping the ones we just statically defined - //assert(binding.m_binding < numBoundConstantBuffers); - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR& descriptor = param.Descriptor; - descriptor.ShaderRegister = bindingIndex; - descriptor.RegisterSpace = 0; - - numConstantBuffers++; - } - } - - if (detail.m_srvIndex >= 0) - { - D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); - - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - range.NumDescriptors = 1; - range.BaseShaderRegister = bindingIndex; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; - table.NumDescriptorRanges = 1; - table.pDescriptorRanges = ⦥ - } - - if (detail.m_uavIndex >= 0) - { - D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); - - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - range.NumDescriptors = 1; - range.BaseShaderRegister = bindingIndex; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; - table.NumDescriptorRanges = 1; - table.pDescriptorRanges = ⦥ - } - } - } - } - - // All the samplers are in one continuous section of the sampler heap - if (m_boundBindingState && m_boundBindingState->m_samplerHeap.getUsedSize() > 0) - { - D3D12_DESCRIPTOR_RANGE& range = params.nextRange(); - - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; - range.NumDescriptors = m_boundBindingState->m_samplerHeap.getUsedSize(); - range.BaseShaderRegister = 0; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - - D3D12_ROOT_PARAMETER& param = params.nextParameter(); - - param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - D3D12_ROOT_DESCRIPTOR_TABLE& table = param.DescriptorTable; - table.NumDescriptorRanges = 1; - table.pDescriptorRanges = ⦥ - } - return SLANG_OK; -} - -Result D3D12Renderer::_bindRenderState(RenderState* renderState, ID3D12GraphicsCommandList* commandList, Submitter* submitter) -{ - BindingStateImpl* bindingState = m_boundBindingState; - - submitter->setRootSignature(renderState->m_rootSignature); - commandList->SetPipelineState(renderState->m_pipelineState); - - if (bindingState) - { - ID3D12DescriptorHeap* heaps[] = - { - bindingState->m_viewHeap.getHeap(), - bindingState->m_samplerHeap.getHeap(), - }; - commandList->SetDescriptorHeaps(SLANG_COUNT_OF(heaps), heaps); - } - else - { - commandList->SetDescriptorHeaps(0, nullptr); - } - - { - int index = 0; - - int numConstantBuffers = 0; - { - if (bindingState) - { - D3D12DescriptorHeap& heap = bindingState->m_viewHeap; - const auto& details = bindingState->m_bindingDetails; - const auto& bindings = bindingState->getDesc().m_bindings; - const int numBindings = int(details.Count()); - - for (int i = 0; i < numBindings; i++) - { - const auto& detail = details[i]; - const auto& binding = bindings[i]; - - if (binding.bindingType == BindingType::Buffer) - { - assert(binding.resource && binding.resource->isBuffer()); - if (binding.resource->canBind(Resource::BindFlag::ConstantBuffer)) - { - BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); - buffer->bindConstantBufferView(m_circularResourceHeap, index++, submitter); - numConstantBuffers++; - } - } - - if (detail.m_srvIndex >= 0) - { - submitter->setRootDescriptorTable(index++, heap.getGpuHandle(detail.m_srvIndex)); - } - - if (detail.m_uavIndex >= 0) - { - submitter->setRootDescriptorTable(index++, heap.getGpuHandle(detail.m_uavIndex)); - } - } - } - } - - if (bindingState && bindingState->m_samplerHeap.getUsedSize() > 0) - { - submitter->setRootDescriptorTable(index, bindingState->m_samplerHeap.getGpuStart()); - } - } - - return SLANG_OK; -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! - -Result D3D12Renderer::initialize(const Desc& desc, void* inWindowHandle) -{ - m_hwnd = (HWND)inWindowHandle; - // Rather than statically link against D3D, we load it dynamically. - - HMODULE d3dModule = LoadLibraryA("d3d12.dll"); - if (!d3dModule) - { - fprintf(stderr, "error: failed load 'd3d12.dll'\n"); - return SLANG_FAIL; - } - - 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_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 - - m_D3D12SerializeRootSignature = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)loadProc(d3dModule, "D3D12SerializeRootSignature"); - if (!m_D3D12SerializeRootSignature) - { - return SLANG_FAIL; - } - - // Try and create DXGIFactory - ComPtr 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 - ComPtr adapter; - - LOAD_D3D_PROC(PFN_D3D12_CREATE_DEVICE, D3D12CreateDevice); - if (!D3D12CreateDevice_) - { - return SLANG_FAIL; - } - - const bool useWarp = false; - - if (useWarp) - { - SLANG_RETURN_ON_FAIL(dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(adapter.writeRef()))); - SLANG_RETURN_ON_FAIL(D3D12CreateDevice_(adapter, featureLevel, IID_PPV_ARGS(m_device.writeRef()))); - } - else - { - UINT adapterCounter = 0; - for (;;) - { - UINT adapterIndex = adapterCounter++; - - ComPtr candidateAdapter; - if (dxgiFactory->EnumAdapters1(adapterIndex, candidateAdapter.writeRef()) == DXGI_ERROR_NOT_FOUND) - break; - - DXGI_ADAPTER_DESC1 desc; - candidateAdapter->GetDesc1(&desc); - - if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) - { - // TODO: may want to allow software driver as fallback - } - else if (SUCCEEDED(D3D12CreateDevice_(candidateAdapter, featureLevel, IID_PPV_ARGS(m_device.writeRef())))) - { - // We found one! - adapter = candidateAdapter; - break; - } - } - } - - if (!adapter) - { - // Couldn't find an adapter - return SLANG_FAIL; - } - - m_numRenderFrames = 3; - m_numRenderTargets = 2; - - m_desc = desc; - - // set viewport - { - m_viewport.Width = float(m_desc.width); - m_viewport.Height = float(m_desc.height); - m_viewport.MinDepth = 0; - m_viewport.MaxDepth = 1; - m_viewport.TopLeftX = 0; - m_viewport.TopLeftY = 0; - } - - { - m_scissorRect.left = 0; - m_scissorRect.top = 0; - m_scissorRect.right = m_desc.width; - m_scissorRect.bottom = m_desc.height; - } - - // Describe and create the command queue. - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - - SLANG_RETURN_ON_FAIL(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(m_commandQueue.writeRef()))); - - // Describe the swap chain. - DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; - swapChainDesc.BufferCount = m_numRenderTargets; - swapChainDesc.BufferDesc.Width = m_desc.width; - swapChainDesc.BufferDesc.Height = m_desc.height; - swapChainDesc.BufferDesc.Format = m_targetFormat; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - swapChainDesc.OutputWindow = m_hwnd; - swapChainDesc.SampleDesc.Count = 1; - swapChainDesc.Windowed = TRUE; - - if (m_isFullSpeed) - { - m_hasVsync = false; - m_allowFullScreen = false; - } - - if (!m_hasVsync) - { - swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; - } - - // Swap chain needs the queue so that it can force a flush on it. - ComPtr swapChain; - SLANG_RETURN_ON_FAIL(dxgiFactory->CreateSwapChain(m_commandQueue, &swapChainDesc, swapChain.writeRef())); - SLANG_RETURN_ON_FAIL(swapChain->QueryInterface(m_swapChain.writeRef())); - - if (!m_hasVsync) - { - m_swapChainWaitableObject = m_swapChain->GetFrameLatencyWaitableObject(); - - int maxLatency = m_numRenderTargets - 2; - - // Make sure the maximum latency is in the range required by dx12 runtime - maxLatency = (maxLatency < 1) ? 1 : maxLatency; - maxLatency = (maxLatency > DXGI_MAX_SWAP_CHAIN_BUFFERS) ? DXGI_MAX_SWAP_CHAIN_BUFFERS : maxLatency; - - m_swapChain->SetMaximumFrameLatency(maxLatency); - } - - // This sample does not support fullscreen transitions. - SLANG_RETURN_ON_FAIL(dxgiFactory->MakeWindowAssociation(m_hwnd, DXGI_MWA_NO_ALT_ENTER)); - - m_renderTargetIndex = m_swapChain->GetCurrentBackBufferIndex(); - - // 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); - } - - { - // 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); - } - - // Setup frame resources - { - SLANG_RETURN_ON_FAIL(createFrameResources()); - } - - // Setup fence, and close the command list (as default state without begin/endRender is closed) - { - SLANG_RETURN_ON_FAIL(m_fence.init(m_device)); - // Create the command list. When command lists are created they are open, so close it. - FrameInfo& frame = m_frameInfos[m_frameIndex]; - SLANG_RETURN_ON_FAIL(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frame.m_commandAllocator, nullptr, IID_PPV_ARGS(m_commandList.writeRef()))); - m_commandList->Close(); - } - - { - D3D12CircularResourceHeap::Desc desc; - desc.init(); - // Define size - desc.m_blockSize = 65536; - // Set up the heap - m_circularResourceHeap.init(m_device, desc, &m_fence); - } - - // Setup for rendering - beginRender(); - - m_isInitialized = true; - return SLANG_OK; -} - -Result D3D12Renderer::createFrameResources() -{ - // Create back buffers - { - D3D12_CPU_DESCRIPTOR_HANDLE rtvStart(m_rtvHeap->GetCPUDescriptorHandleForHeapStart()); - - // Work out target format - D3D12_RESOURCE_DESC resourceDesc; - { - ComPtr backBuffer; - SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(0, IID_PPV_ARGS(backBuffer.writeRef()))); - resourceDesc = backBuffer->GetDesc(); - } - const DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_TARGET, m_targetUsageFlags, resourceDesc.Format); - const DXGI_FORMAT targetFormat = D3DUtil::calcFormat(D3DUtil::USAGE_TARGET, resourceFormat); - - // Set the target format - m_targetFormat = targetFormat; - - // Create a RTV, and a command allocator for each frame. - for (int i = 0; i < m_numRenderTargets; i++) - { - // Get the back buffer - ComPtr backBuffer; - SLANG_RETURN_ON_FAIL(m_swapChain->GetBuffer(UINT(i), IID_PPV_ARGS(backBuffer.writeRef()))); - - // Set up resource for back buffer - m_backBufferResources[i].setResource(backBuffer, D3D12_RESOURCE_STATE_COMMON); - m_backBuffers[i] = &m_backBufferResources[i]; - // Assume they are the same thing for now... - m_renderTargets[i] = &m_backBufferResources[i]; - - // If we are multi-sampling - create a render target separate from the back buffer - if (m_isMultiSampled) - { - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - D3D12_CLEAR_VALUE clearValue = {}; - clearValue.Format = m_targetFormat; - - // Don't know targets alignment, so just memory copy - ::memcpy(clearValue.Color, m_clearColor, sizeof(m_clearColor)); - - D3D12_RESOURCE_DESC desc(resourceDesc); - - desc.Format = resourceFormat; - desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - desc.SampleDesc.Count = m_numTargetSamples; - desc.SampleDesc.Quality = m_targetSampleQuality; - desc.Alignment = 0; - - SLANG_RETURN_ON_FAIL(m_renderTargetResources[i].initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, desc, D3D12_RESOURCE_STATE_RENDER_TARGET, &clearValue)); - m_renderTargets[i] = &m_renderTargetResources[i]; - } - - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { rtvStart.ptr + i * m_rtvDescriptorSize }; - m_device->CreateRenderTargetView(*m_renderTargets[i], nullptr, rtvHandle); - } - } - - // Set up frames - for (int i = 0; i < m_numRenderFrames; i++) - { - FrameInfo& frame = m_frameInfos[i]; - SLANG_RETURN_ON_FAIL(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(frame.m_commandAllocator.writeRef()))); - } - - { - D3D12_RESOURCE_DESC desc = m_backBuffers[0]->getResource()->GetDesc(); - assert(desc.Width == UINT64(m_desc.width) && desc.Height == UINT64(m_desc.height)); - } - - // Create the depth stencil view. - { - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - DXGI_FORMAT resourceFormat = D3DUtil::calcResourceFormat(D3DUtil::USAGE_DEPTH_STENCIL, m_depthStencilUsageFlags, m_depthStencilFormat); - DXGI_FORMAT depthStencilFormat = D3DUtil::calcFormat(D3DUtil::USAGE_DEPTH_STENCIL, resourceFormat); - - // Set the depth stencil format - m_depthStencilFormat = depthStencilFormat; - - // Setup default clear - D3D12_CLEAR_VALUE clearValue = {}; - clearValue.Format = depthStencilFormat; - clearValue.DepthStencil.Depth = 1.0f; - clearValue.DepthStencil.Stencil = 0; - - D3D12_RESOURCE_DESC resourceDesc = {}; - resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; - resourceDesc.Format = resourceFormat; - resourceDesc.Width = m_desc.width; - resourceDesc.Height = m_desc.height; - resourceDesc.DepthOrArraySize = 1; - resourceDesc.MipLevels = 1; - resourceDesc.SampleDesc.Count = m_numTargetSamples; - resourceDesc.SampleDesc.Quality = m_targetSampleQuality; - resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - resourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - resourceDesc.Alignment = 0; - - 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(m_desc.width); - m_viewport.Height = static_cast(m_desc.height); - m_viewport.MaxDepth = 1.0f; - - m_scissorRect.right = static_cast(m_desc.width); - m_scissorRect.bottom = static_cast(m_desc.height); - - return SLANG_OK; -} - -void D3D12Renderer::setClearColor(const float color[4]) -{ - memcpy(m_clearColor, color, sizeof(m_clearColor)); -} - -void D3D12Renderer::clearFrame() -{ - // Record commands - 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::captureScreenSurface(Surface& surfaceOut) -{ - return captureTextureToSurface(*m_renderTargets[m_renderTargetIndex], surfaceOut); -} - -static D3D12_RESOURCE_STATES _calcResourceState(Resource::Usage usage) -{ - typedef Resource::Usage Usage; - switch (usage) - { - case Usage::VertexBuffer: return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; - case Usage::IndexBuffer: return D3D12_RESOURCE_STATE_INDEX_BUFFER; - case Usage::ConstantBuffer: return D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; - case Usage::StreamOutput: return D3D12_RESOURCE_STATE_STREAM_OUT; - case Usage::RenderTarget: return D3D12_RESOURCE_STATE_RENDER_TARGET; - case Usage::DepthWrite: return D3D12_RESOURCE_STATE_DEPTH_WRITE; - case Usage::DepthRead: return D3D12_RESOURCE_STATE_DEPTH_READ; - case Usage::UnorderedAccess: return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; - case Usage::PixelShaderResource: return D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - case Usage::NonPixelShaderResource: return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; - case Usage::GenericRead: return D3D12_RESOURCE_STATE_GENERIC_READ; - default: return D3D12_RESOURCE_STATES(0); - } -} - -static D3D12_RESOURCE_FLAGS _calcResourceFlag(Resource::BindFlag::Enum bindFlag) -{ - typedef Resource::BindFlag BindFlag; - switch (bindFlag) - { - case BindFlag::RenderTarget: return D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; - case BindFlag::DepthStencil: return D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - case BindFlag::UnorderedAccess: return D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - default: return D3D12_RESOURCE_FLAG_NONE; - } -} - -static D3D12_RESOURCE_FLAGS _calcResourceBindFlags(Resource::Usage initialUsage, int bindFlags) -{ - int dstFlags = 0; - while (bindFlags) - { - int lsb = bindFlags & -bindFlags; - - dstFlags |= _calcResourceFlag(Resource::BindFlag::Enum(lsb)); - bindFlags &= ~lsb; - } - return D3D12_RESOURCE_FLAGS(dstFlags); -} - -static D3D12_RESOURCE_DIMENSION _calcResourceDimension(Resource::Type type) -{ - switch (type) - { - case Resource::Type::Buffer: return D3D12_RESOURCE_DIMENSION_BUFFER; - case Resource::Type::Texture1D: return D3D12_RESOURCE_DIMENSION_TEXTURE1D; - case Resource::Type::TextureCube: - case Resource::Type::Texture2D: - { - return D3D12_RESOURCE_DIMENSION_TEXTURE2D; - } - case Resource::Type::Texture3D: return D3D12_RESOURCE_DIMENSION_TEXTURE3D; - default: return D3D12_RESOURCE_DIMENSION_UNKNOWN; - } -} - -TextureResource* D3D12Renderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData) -{ - // Description of uploading on Dx12 - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn899215%28v=vs.85%29.aspx - - TextureResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - const DXGI_FORMAT pixelFormat = D3DUtil::getMapFormat(srcDesc.format); - if (pixelFormat == DXGI_FORMAT_UNKNOWN) - { - return nullptr; - } - - const int arraySize = srcDesc.calcEffectiveArraySize(); - - const D3D12_RESOURCE_DIMENSION dimension = _calcResourceDimension(srcDesc.type); - if (dimension == D3D12_RESOURCE_DIMENSION_UNKNOWN) - { - return nullptr; - } - - const int numMipMaps = srcDesc.numMipLevels; - - // Setup desc - D3D12_RESOURCE_DESC resourceDesc; - - resourceDesc.Dimension = dimension; - resourceDesc.Format = pixelFormat; - resourceDesc.Width = srcDesc.size.width; - resourceDesc.Height = srcDesc.size.height; - resourceDesc.DepthOrArraySize = (srcDesc.size.depth > 1) ? srcDesc.size.depth : arraySize; - - resourceDesc.MipLevels = numMipMaps; - resourceDesc.SampleDesc.Count = srcDesc.sampleDesc.numSamples; - resourceDesc.SampleDesc.Quality = srcDesc.sampleDesc.quality; - - resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; - resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; - resourceDesc.Alignment = 0; - - RefPtr texture(new TextureResourceImpl(srcDesc)); - - // Create the target resource - { - D3D12_HEAP_PROPERTIES heapProps; - - heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - SLANG_RETURN_NULL_ON_FAIL(texture->m_resource.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); - - texture->m_resource.setDebugName(L"Texture"); - } - - // Calculate the layout - List layouts; - layouts.SetSize(numMipMaps); - List mipRowSizeInBytes; - mipRowSizeInBytes.SetSize(numMipMaps); - List mipNumRows; - mipNumRows.SetSize(numMipMaps); - - // Since textures are effectively immutable currently initData must be set - assert(initData); - // We should have this many sub resources - assert(initData->numSubResources == numMipMaps * srcDesc.size.depth * arraySize); - - // This is just the size for one array upload -> not for the whole texure - UInt64 requiredSize = 0; - m_device->GetCopyableFootprints(&resourceDesc, 0, numMipMaps, 0, layouts.begin(), mipNumRows.begin(), mipRowSizeInBytes.begin(), &requiredSize); - - // Sub resource indexing - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn705766(v=vs.85).aspx#subresource_indexing - - int subResourceIndex = 0; - for (int i = 0; i < arraySize; i++) - { - // 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_NULL_ON_FAIL(uploadTexture.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, uploadResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr)); - - uploadTexture.setDebugName(L"TextureUpload"); - } - - ID3D12Resource* uploadResource = uploadTexture; - - uint8_t* p; - uploadResource->Map(0, nullptr, reinterpret_cast(&p)); - - for (int j = 0; j < numMipMaps; ++j) - { - const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& layout = layouts[j]; - const D3D12_SUBRESOURCE_FOOTPRINT& footprint = layout.Footprint; - - const TextureResource::Size mipSize = srcDesc.size.calcMipSize(j); - - assert(footprint.Width == mipSize.width && footprint.Height == mipSize.height && footprint.Depth == mipSize.depth); - - const ptrdiff_t dstMipRowPitch = ptrdiff_t(layouts[j].Footprint.RowPitch); - const ptrdiff_t srcMipRowPitch = ptrdiff_t(initData->mipRowStrides[j]); - - assert(dstMipRowPitch >= srcMipRowPitch); - - const uint8_t* srcRow = (const uint8_t*)initData->subResources[subResourceIndex]; - uint8_t* dstRow = p + layouts[j].Offset; - - // Copy the depth each mip - for (int l = 0; l < mipSize.depth; l++) - { - // Copy rows - for (int k = 0; k < mipSize.height; ++k) - { - ::memcpy(dstRow, srcRow, srcMipRowPitch); - - srcRow += srcMipRowPitch; - dstRow += dstMipRowPitch; - } - } - - //assert(srcRow == (const uint8_t*)(srcMip.Buffer() + srcMip.Count())); - } - uploadResource->Unmap(0, nullptr); - - for (int mipIndex = 0; mipIndex < numMipMaps; ++mipIndex) - { - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn903862(v=vs.85).aspx - - D3D12_TEXTURE_COPY_LOCATION src; - src.pResource = uploadTexture; - src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - src.PlacedFootprint = layouts[mipIndex]; - - D3D12_TEXTURE_COPY_LOCATION dst; - dst.pResource = texture->m_resource; - dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dst.SubresourceIndex = subResourceIndex; - m_commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); - - subResourceIndex++; - } - - { - // const D3D12_RESOURCE_STATES finalState = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; - const D3D12_RESOURCE_STATES finalState = _calcResourceState(initialUsage); - - D3D12BarrierSubmitter submitter(m_commandList); - texture->m_resource.transition(finalState, submitter); - } - - // Block - waiting for copy to complete (so can drop upload texture) - submitGpuWorkAndWait(); - } - - return texture.detach(); -} - -BufferResource* D3D12Renderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData) -{ - typedef BufferResourceImpl::BackingStyle Style; - - BufferResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - RefPtr buffer(new BufferResourceImpl(initialUsage, srcDesc)); - - // Save the style - buffer->m_backingStyle = BufferResourceImpl::_calcResourceBackingStyle(initialUsage); - - D3D12_RESOURCE_DESC bufferDesc; - _initBufferResourceDesc(srcDesc.sizeInBytes, bufferDesc); - - bufferDesc.Flags = _calcResourceBindFlags(initialUsage, srcDesc.bindFlags); - - switch (buffer->m_backingStyle) - { - case Style::MemoryBacked: - { - // Assume the constant buffer will change every frame. We'll just keep a copy of the contents - // in regular memory until it needed - buffer->m_memory.SetSize(UInt(srcDesc.sizeInBytes)); - // Initialize - if (initData) - { - ::memcpy(buffer->m_memory.Buffer(), initData, srcDesc.sizeInBytes); - } - break; - } - case Style::ResourceBacked: - { - const D3D12_RESOURCE_STATES initialState = _calcResourceState(initialUsage); - SLANG_RETURN_NULL_ON_FAIL(createBuffer(bufferDesc, initData, buffer->m_uploadResource, initialState, buffer->m_resource)); - break; - } - default: return nullptr; - } - - return buffer.detach(); -} - -InputLayout* D3D12Renderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) -{ - RefPtr layout(new InputLayoutImpl); - - // Work out a buffer size to hold all text - size_t textSize = 0; - for (int i = 0; i < Int(inputElementCount); ++i) - { - const char* text = inputElements[i].semanticName; - textSize += text ? (::strlen(text) + 1) : 0; - } - layout->m_text.SetSize(textSize); - char* textPos = layout->m_text.Buffer(); - - // - List& 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(BufferResource* bufferIn, MapFlavor flavor) -{ - typedef BufferResourceImpl::BackingStyle Style; - - BufferResourceImpl* buffer = static_cast(bufferIn); - buffer->m_mapFlavor = flavor; - - const size_t bufferSize = buffer->getDesc().sizeInBytes; - - switch (buffer->m_backingStyle) - { - case Style::ResourceBacked: - { - // We need this in a state so we can upload - switch (flavor) - { - case MapFlavor::HostWrite: - case MapFlavor::WriteDiscard: - { - D3D12BarrierSubmitter submitter(m_commandList); - buffer->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter); - buffer->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter); - - const D3D12_RANGE readRange = {}; - - void* uploadData; - SLANG_RETURN_NULL_ON_FAIL(buffer->m_uploadResource.getResource()->Map(0, &readRange, reinterpret_cast(&uploadData))); - return uploadData; - - break; - } - case MapFlavor::HostRead: - { - // This will be slow!!! - it blocks CPU on GPU completion - D3D12Resource& resource = buffer->m_resource; - - // Readback heap - D3D12_HEAP_PROPERTIES heapProps; - heapProps.Type = D3D12_HEAP_TYPE_READBACK; - heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapProps.CreationNodeMask = 1; - heapProps.VisibleNodeMask = 1; - - // Resource to readback to - D3D12_RESOURCE_DESC stagingDesc; - _initBufferResourceDesc(bufferSize, stagingDesc); - - D3D12Resource stageBuf; - SLANG_RETURN_NULL_ON_FAIL(stageBuf.initCommitted(m_device, heapProps, D3D12_HEAP_FLAG_NONE, stagingDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr)); - - const D3D12_RESOURCE_STATES initialState = resource.getState(); - - // Make it a source - { - D3D12BarrierSubmitter submitter(m_commandList); - resource.transition(D3D12_RESOURCE_STATE_COPY_SOURCE, submitter); - } - // Do the copy - m_commandList->CopyBufferRegion(stageBuf, 0, resource, 0, bufferSize); - // Switch it back - { - D3D12BarrierSubmitter submitter(m_commandList); - resource.transition(initialState, submitter); - } - - // Wait until complete - submitGpuWorkAndWait(); - - // Map and copy - { - UINT8* data; - D3D12_RANGE readRange = { 0, bufferSize }; - - SLANG_RETURN_NULL_ON_FAIL(stageBuf.getResource()->Map(0, &readRange, reinterpret_cast(&data))); - - // Copy to memory buffer - buffer->m_memory.SetSize(bufferSize); - ::memcpy(buffer->m_memory.Buffer(), data, bufferSize); - - stageBuf.getResource()->Unmap(0, nullptr); - } - - return buffer->m_memory.Buffer(); - } - } - break; - } - case Style::MemoryBacked: - { - return buffer->m_memory.Buffer(); - } - default: return nullptr; - } - - return nullptr; -} - -void D3D12Renderer::unmap(BufferResource* bufferIn) -{ - typedef BufferResourceImpl::BackingStyle Style; - BufferResourceImpl* buffer = static_cast(bufferIn); - - switch (buffer->m_backingStyle) - { - case Style::MemoryBacked: - { - // Don't need to do anything, as will be uploaded automatically when used - break; - } - case Style::ResourceBacked: - { - // We need this in a state so we can upload - switch (buffer->m_mapFlavor) - { - case MapFlavor::HostWrite: - case MapFlavor::WriteDiscard: - { - // Unmap - ID3D12Resource* uploadResource = buffer->m_uploadResource; - ID3D12Resource* resource = buffer->m_resource; - - uploadResource->Unmap(0, nullptr); - - const D3D12_RESOURCE_STATES initialState = buffer->m_resource.getState(); - - { - D3D12BarrierSubmitter submitter(m_commandList); - buffer->m_uploadResource.transition(D3D12_RESOURCE_STATE_GENERIC_READ, submitter); - buffer->m_resource.transition(D3D12_RESOURCE_STATE_COPY_DEST, submitter); - } - - m_commandList->CopyBufferRegion(resource, 0, uploadResource, 0, buffer->getDesc().sizeInBytes); - - { - D3D12BarrierSubmitter submitter(m_commandList); - buffer->m_resource.transition(initialState, submitter); - } - break; - } - case MapFlavor::HostRead: - { - break; - } - } - } - } -} - -void D3D12Renderer::setInputLayout(InputLayout* inputLayout) -{ - m_boundInputLayout = static_cast(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, BufferResource*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++) - { - BufferResourceImpl* buffer = static_cast(buffers[i]); - if (buffer) - { - assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer); - } - - BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i]; - boundBuffer.m_buffer = buffer; - boundBuffer.m_stride = int(strides[i]); - boundBuffer.m_offset = int(offsets[i]); - } -} - -void D3D12Renderer::setShaderProgram(ShaderProgram* inProgram) -{ - m_boundShaderProgram = static_cast(inProgram); -} - -void D3D12Renderer::draw(UInt vertexCount, UInt startVertex) -{ - ID3D12GraphicsCommandList* commandList = m_commandList; - - RenderState* renderState = calcRenderState(); - if (!renderState) - { - assert(!"Couldn't create render state"); - return; - } - - 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]; - BufferResourceImpl* buffer = boundVertexBuffer.m_buffer; - if (buffer) - { - D3D12_VERTEX_BUFFER_VIEW& vertexView = vertexViews[numVertexViews++]; - vertexView.BufferLocation = buffer->m_resource.getResource()->GetGPUVirtualAddress(); - vertexView.SizeInBytes = int(buffer->getDesc().sizeInBytes); - 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 BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc)); - - SLANG_RETURN_NULL_ON_FAIL(bindingState->init(m_device)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - const auto& srcEntry = srcBindings[i]; - auto& dstDetail = dstDetails[i]; - - const int bindingIndex = srcEntry.registerRange.getSingleIndex(); - - switch (srcEntry.bindingType) - { - case BindingType::Buffer: - { - assert(srcEntry.resource && srcEntry.resource->isBuffer()); - BufferResourceImpl* bufferResource = static_cast(srcEntry.resource.Ptr()); - const BufferResource::Desc& bufferDesc = bufferResource->getDesc(); - - const size_t bufferSize = bufferDesc.sizeInBytes; - const int elemSize = bufferDesc.elementSize <= 0 ? sizeof(uint32_t) : bufferDesc.elementSize; - - const bool createSrv = false; - - // NOTE! In this arrangement the buffer can either be a ConstantBuffer or a 'StorageBuffer'. - // If it's a storage buffer then it has a 'uav'. - // In neither circumstance is there an associated srv - // This departs a little from dx11 code - in that it will create srv and uav for a storage buffer. - if (bufferDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - dstDetail.m_uavIndex = bindingState->m_viewHeap.allocate(); - if (dstDetail.m_uavIndex < 0) - { - return nullptr; - } - - D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; - - uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; - uavDesc.Format = D3DUtil::getMapFormat(bufferDesc.format); - - uavDesc.Buffer.StructureByteStride = elemSize; - - uavDesc.Buffer.FirstElement = 0; - uavDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); - uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE; - - if (bufferDesc.elementSize == 0 && bufferDesc.format == Format::Unknown) - { - uavDesc.Buffer.Flags |= D3D12_BUFFER_UAV_FLAG_RAW; - uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; - - uavDesc.Buffer.StructureByteStride = 0; - } - else if( bufferDesc.format != Format::Unknown ) - { - uavDesc.Buffer.StructureByteStride = 0; - } - - m_device->CreateUnorderedAccessView(bufferResource->m_resource, nullptr, &uavDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_uavIndex)); - } - if (createSrv && (bufferDesc.bindFlags & (Resource::BindFlag::NonPixelShaderResource | Resource::BindFlag::PixelShaderResource))) - { - dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); - if (dstDetail.m_srvIndex < 0) - { - return nullptr; - } - - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - srvDesc.Format = DXGI_FORMAT_UNKNOWN; - srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - - srvDesc.Buffer.FirstElement = 0; - srvDesc.Buffer.NumElements = (UINT)(bufferSize / elemSize); - srvDesc.Buffer.StructureByteStride = elemSize; - srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; - - if (bufferDesc.elementSize == 0) - { - srvDesc.Format = DXGI_FORMAT_R32_FLOAT; - } - - m_device->CreateShaderResourceView(bufferResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); - } - - break; - } - case BindingType::Texture: - { - assert(srcEntry.resource && srcEntry.resource->isTexture()); - - TextureResourceImpl* textureResource = static_cast(srcEntry.resource.Ptr()); - - dstDetail.m_srvIndex = bindingState->m_viewHeap.allocate(); - if (dstDetail.m_srvIndex < 0) - { - return nullptr; - } - - { - const D3D12_RESOURCE_DESC resourceDesc = textureResource->m_resource.getResource()->GetDesc(); - const DXGI_FORMAT pixelFormat = resourceDesc.Format; - - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - _initSrvDesc(textureResource->getType(), textureResource->getDesc(), resourceDesc, pixelFormat, srvDesc); - - // Create descriptor - m_device->CreateShaderResourceView(textureResource->m_resource, &srvDesc, bindingState->m_viewHeap.getCpuHandle(dstDetail.m_srvIndex)); - } - - break; - } - case BindingType::Sampler: - { - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcEntry.descIndex]; - - const int samplerIndex = bindingIndex; - dstDetail.m_samplerIndex = samplerIndex; - bindingState->m_samplerHeap.placeAt(samplerIndex); - - D3D12_SAMPLER_DESC desc = {}; - desc.AddressU = desc.AddressV = desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - desc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; - - if (samplerDesc.isCompareSampler) - { - desc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; - desc.Filter = D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; - } - else - { - desc.Filter = D3D12_FILTER_ANISOTROPIC; - desc.MaxAnisotropy = 8; - desc.MinLOD = 0.0f; - desc.MaxLOD = 100.0f; - } - - m_device->CreateSampler(&desc, bindingState->m_samplerHeap.getCpuHandle(samplerIndex)); - - break; - } - case BindingType::CombinedTextureSampler: - { - assert(!"Not implemented"); - return nullptr; - } - } - } - - return bindingState.detach(); -} - -void D3D12Renderer::setBindingState(BindingState* state) -{ - m_boundBindingState = static_cast(state); -} - -ShaderProgram* D3D12Renderer::createProgram(const ShaderProgram::Desc& desc) -{ - RefPtr program(new ShaderProgramImpl()); - program->m_pipelineType = desc.pipelineType; - - if (desc.pipelineType == PipelineType::Compute) - { - auto computeKernel = desc.findKernel(StageType::Compute); - program->m_computeShader.InsertRange(0, (const uint8_t*) computeKernel->codeBegin, computeKernel->getCodeSize()); - } - else - { - auto vertexKernel = desc.findKernel(StageType::Vertex); - auto fragmentKernel = desc.findKernel(StageType::Fragment); - - program->m_vertexShader.InsertRange(0, (const uint8_t*) vertexKernel->codeBegin, vertexKernel->getCodeSize()); - program->m_pixelShader.InsertRange(0, (const uint8_t*) fragmentKernel->codeBegin, fragmentKernel->getCodeSize()); - } - - return program.detach(); -} - - -} // renderer_test diff --git a/tools/slang-graphics/render-d3d12.h b/tools/slang-graphics/render-d3d12.h deleted file mode 100644 index 5f0eea4d2..000000000 --- a/tools/slang-graphics/render-d3d12.h +++ /dev/null @@ -1,10 +0,0 @@ -// render-d3d12.h -#pragma once - -namespace slang_graphics { - -class Renderer; - -Renderer* createD3D12Renderer(); - -} // slang_graphics diff --git a/tools/slang-graphics/render-gl.cpp b/tools/slang-graphics/render-gl.cpp deleted file mode 100644 index f85a81ca4..000000000 --- a/tools/slang-graphics/render-gl.cpp +++ /dev/null @@ -1,1049 +0,0 @@ -// render-gl.cpp -#include "render-gl.h" - -//WORKING:#include "options.h" -#include "render.h" - -#include -#include -#include -#include "core/basic.h" -#include "core/secure-crt.h" -#include "external/stb/stb_image_write.h" - -#include "surface.h" - -// TODO(tfoley): eventually we should be able to run these -// tests on non-Windows targets to confirm that cross-compilation -// at least *works* on those platforms... -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#ifdef _MSC_VER -#include -#if (_MSC_VER < 1900) -#define snprintf sprintf_s -#endif -#endif - -#pragma comment(lib, "opengl32") - -#include -#include "external/glext.h" - -// We define an "X-macro" for mapping over loadable OpenGL -// extension entry point that we will use, so that we can -// easily write generic code to iterate over them. -#define MAP_GL_EXTENSION_FUNCS(F) \ - F(glCreateProgram, PFNGLCREATEPROGRAMPROC) \ - F(glCreateShader, PFNGLCREATESHADERPROC) \ - F(glShaderSource, PFNGLSHADERSOURCEPROC) \ - F(glCompileShader, PFNGLCOMPILESHADERPROC) \ - F(glGetShaderiv, PFNGLGETSHADERIVPROC) \ - F(glDeleteShader, PFNGLDELETESHADERPROC) \ - F(glAttachShader, PFNGLATTACHSHADERPROC) \ - F(glLinkProgram, PFNGLLINKPROGRAMPROC) \ - F(glGetProgramiv, PFNGLGETPROGRAMIVPROC) \ - F(glGetProgramInfoLog, PFNGLGETPROGRAMINFOLOGPROC) \ - F(glDeleteProgram, PFNGLDELETEPROGRAMPROC) \ - F(glGetShaderInfoLog, PFNGLGETSHADERINFOLOGPROC) \ - F(glGenBuffers, PFNGLGENBUFFERSPROC) \ - F(glBindBuffer, PFNGLBINDBUFFERPROC) \ - F(glBufferData, PFNGLBUFFERDATAPROC) \ - F(glDeleteBuffers, PFNGLDELETEBUFFERSPROC) \ - F(glMapBuffer, PFNGLMAPBUFFERPROC) \ - F(glUnmapBuffer, PFNGLUNMAPBUFFERPROC) \ - F(glUseProgram, PFNGLUSEPROGRAMPROC) \ - F(glBindBufferBase, PFNGLBINDBUFFERBASEPROC) \ - F(glVertexAttribPointer, PFNGLVERTEXATTRIBPOINTERPROC) \ - F(glEnableVertexAttribArray, PFNGLENABLEVERTEXATTRIBARRAYPROC) \ - F(glDisableVertexAttribArray, PFNGLDISABLEVERTEXATTRIBARRAYPROC) \ - F(glDebugMessageCallback, PFNGLDEBUGMESSAGECALLBACKPROC) \ - F(glDispatchCompute, PFNGLDISPATCHCOMPUTEPROC) \ - F(glActiveTexture, PFNGLACTIVETEXTUREPROC) \ - F(glCreateSamplers, PFNGLCREATESAMPLERSPROC) \ - F(glDeleteSamplers, PFNGLDELETESAMPLERSPROC) \ - F(glBindSampler, PFNGLBINDSAMPLERPROC) \ - F(glTexImage3D, PFNGLTEXIMAGE3DPROC) \ - F(glSamplerParameteri, PFNGLSAMPLERPARAMETERIPROC) \ - /* end */ - -using namespace Slang; - -namespace slang_graphics { - -class GLRenderer : public Renderer -{ -public: - - // Renderer implementation - virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; - virtual void setClearColor(const float color[4]) override; - virtual void clearFrame() override; - virtual void presentFrame() override; - virtual TextureResource* createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData) override; - virtual BufferResource* createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData) override; - virtual SlangResult captureScreenSurface(Surface& surfaceOut) override; - virtual InputLayout* createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) override; - virtual BindingState* createBindingState(const BindingState::Desc& bindingStateDesc) override; - virtual ShaderProgram* createProgram(const ShaderProgram::Desc& desc) override; - virtual void* map(BufferResource* buffer, MapFlavor flavor) override; - virtual void unmap(BufferResource* buffer) override; - virtual void setInputLayout(InputLayout* inputLayout) override; - virtual void setPrimitiveTopology(PrimitiveTopology topology) override; - virtual void setBindingState(BindingState* state); - virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; - virtual void setShaderProgram(ShaderProgram* inProgram) 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 {} - virtual RendererType getRendererType() const override { return RendererType::OpenGl; } - - protected: - enum - { - kMaxVertexStreams = 16, - }; - - struct VertexAttributeFormat - { - GLint componentCount; - GLenum componentType; - GLboolean normalized; - }; - - struct VertexAttributeDesc - { - VertexAttributeFormat format; - GLuint streamIndex; - GLsizei offset; - }; - - class InputLayoutImpl: public InputLayout - { - public: - VertexAttributeDesc m_attributes[kMaxVertexStreams]; - UInt m_attributeCount = 0; - }; - - class BufferResourceImpl: public BufferResource - { - public: - typedef BufferResource Parent; - - BufferResourceImpl(Usage initialUsage, const Desc& desc, GLRenderer* renderer, GLuint id, GLenum target): - Parent(desc), - m_renderer(renderer), - m_handle(id), - m_initialUsage(initialUsage), - m_target(target) - {} - ~BufferResourceImpl() - { - if (m_renderer) - { - m_renderer->glDeleteBuffers(1, &m_handle); - } - } - - Usage m_initialUsage; - GLRenderer* m_renderer; - GLuint m_handle; - GLenum m_target; - }; - - class TextureResourceImpl: public TextureResource - { - public: - typedef TextureResource Parent; - - TextureResourceImpl(Usage initialUsage, const Desc& desc, GLRenderer* renderer): - Parent(desc), - m_initialUsage(initialUsage), - m_renderer(renderer) - { - m_target = 0; - m_handle = 0; - } - - ~TextureResourceImpl() - { - if (m_handle) - { - glDeleteTextures(1, &m_handle); - } - } - - Usage m_initialUsage; - GLRenderer* m_renderer; - GLenum m_target; - GLuint m_handle; - }; - - struct BindingDetail - { - GLuint m_samplerHandle = 0; - }; - - class BindingStateImpl: public BindingState - { - public: - typedef BindingState Parent; - - /// Ctor - BindingStateImpl(const Desc& desc, GLRenderer* renderer): - Parent(desc), - m_renderer(renderer) - { - } - - ~BindingStateImpl() - { - if (m_renderer) - { - m_renderer->destroyBindingEntries(getDesc(), m_bindingDetails.Buffer()); - } - } - - GLRenderer* m_renderer; - List m_bindingDetails; - }; - - class ShaderProgramImpl : public ShaderProgram - { - public: - ShaderProgramImpl(GLRenderer* renderer, GLuint id): - m_renderer(renderer), - m_id(id) - { - } - ~ShaderProgramImpl() - { - if (m_renderer) - { - m_renderer->glDeleteProgram(m_id); - } - } - - GLuint m_id; - GLRenderer* m_renderer; - }; - - enum class GlPixelFormat - { - Unknown, - RGBA_Unorm_UInt8, - CountOf, - }; - - struct GlPixelFormatInfo - { - GLint internalFormat; // such as GL_RGBA8 - GLenum format; // such as GL_RGBA - GLenum formatType; // such as GL_UNSIGNED_BYTE - }; - - void destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details); - - void bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets); - void flushStateForDraw(); - GLuint loadShader(GLenum stage, char const* source); - void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message); - - /// Returns GlPixelFormat::Unknown if not an equivalent - static GlPixelFormat _getGlPixelFormat(Format format); - - static void APIENTRY staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); - static VertexAttributeFormat getVertexAttributeFormat(Format format); - - static void compileTimeAsserts(); - - HDC m_hdc; - HGLRC m_glContext; - float m_clearColor[4] = { 0, 0, 0, 0 }; - - RefPtr m_boundShaderProgram; - RefPtr m_boundInputLayout; - - GLenum m_boundPrimitiveTopology = GL_TRIANGLES; - GLuint m_boundVertexStreamBuffers[kMaxVertexStreams]; - UInt m_boundVertexStreamStrides[kMaxVertexStreams]; - UInt m_boundVertexStreamOffsets[kMaxVertexStreams]; - - Desc m_desc; - - // Declare a function pointer for each OpenGL - // extension function we need to load -#define DECLARE_GL_EXTENSION_FUNC(NAME, TYPE) TYPE NAME; - MAP_GL_EXTENSION_FUNCS(DECLARE_GL_EXTENSION_FUNC) -#undef DECLARE_GL_EXTENSION_FUNC - - static const GlPixelFormatInfo s_pixelFormatInfos[]; /// Maps GlPixelFormat to a format info -}; - -/* static */GLRenderer::GlPixelFormat GLRenderer::_getGlPixelFormat(Format format) -{ - switch (format) - { - case Format::RGBA_Unorm_UInt8: return GlPixelFormat::RGBA_Unorm_UInt8; - default: return GlPixelFormat::Unknown; - } -} - -/* static */ const GLRenderer::GlPixelFormatInfo GLRenderer::s_pixelFormatInfos[] = -{ - // internalType, format, formatType - { 0, 0, 0}, // GlPixelFormat::Unknown - { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }, // GlPixelFormat::RGBA_Unorm_UInt8 -}; - -/* static */void GLRenderer::compileTimeAsserts() -{ - SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_pixelFormatInfos) == int(GlPixelFormat::CountOf)); -} - -Renderer* createGLRenderer() -{ - return new GLRenderer(); -} - -void GLRenderer::debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message) -{ - ::OutputDebugStringA("GL: "); - ::OutputDebugStringA(message); - ::OutputDebugStringA("\n"); - - switch (type) - { - case GL_DEBUG_TYPE_ERROR: - break; - default: - break; - } -} - -/* static */void APIENTRY GLRenderer::staticDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) -{ - ((GLRenderer*)userParam)->debugCallback(source, type, id, severity, length, message); -} - -/* static */GLRenderer::VertexAttributeFormat GLRenderer::getVertexAttributeFormat(Format format) -{ - switch (format) - { - default: assert(!"unexpected"); return VertexAttributeFormat(); - -#define CASE(NAME, COUNT, TYPE, NORMALIZED) \ - case Format::NAME: do { VertexAttributeFormat result = {COUNT, TYPE, NORMALIZED}; return result; } while (0) - - CASE(RGBA_Float32, 4, GL_FLOAT, GL_FALSE); - CASE(RGB_Float32, 3, GL_FLOAT, GL_FALSE); - CASE(RG_Float32, 2, GL_FLOAT, GL_FALSE); - CASE(R_Float32, 1, GL_FLOAT, GL_FALSE); -#undef CASE - } -} - -void GLRenderer::bindBufferImpl(int target, UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* offsets) -{ - for (UInt ii = 0; ii < slotCount; ++ii) - { - UInt slot = startSlot + ii; - - BufferResourceImpl* buffer = static_cast(buffers[ii]); - GLuint bufferID = buffer ? buffer->m_handle : 0; - - assert(!offsets || !offsets[ii]); - - glBindBufferBase(target, (GLuint)slot, bufferID); - } -} - -void GLRenderer::flushStateForDraw() -{ - auto layout = m_boundInputLayout.Ptr(); - auto attrCount = layout->m_attributeCount; - for (UInt ii = 0; ii < attrCount; ++ii) - { - auto& attr = layout->m_attributes[ii]; - - auto streamIndex = attr.streamIndex; - - glBindBuffer(GL_ARRAY_BUFFER, m_boundVertexStreamBuffers[streamIndex]); - - glVertexAttribPointer( - (GLuint)ii, - attr.format.componentCount, - attr.format.componentType, - attr.format.normalized, - (GLsizei)m_boundVertexStreamStrides[streamIndex], - (GLvoid*)(attr.offset + m_boundVertexStreamOffsets[streamIndex])); - - glEnableVertexAttribArray((GLuint)ii); - } - for (UInt ii = attrCount; ii < kMaxVertexStreams; ++ii) - { - glDisableVertexAttribArray((GLuint)ii); - } -} - -GLuint GLRenderer::loadShader(GLenum stage, const char* source) -{ - // GLSL is monumentally stupid. It officially requires the `#version` directive - // to be the first thing in the file, which wouldn't be so bad but the API - // doesn't provide a way to pass a `#define` into your shader other than by - // prepending it to the whole thing. - // - // We are going to solve this problem by doing some surgery on the source - // that was passed in. - - const char* sourceBegin = source; - const char* sourceEnd = source + strlen(source); - - // Look for a version directive in the user-provided source. - const char* versionBegin = strstr(source, "#version"); - const char* versionEnd = nullptr; - if (versionBegin) - { - // If we found a directive, then scan for the end-of-line - // after it, and use that to specify the slice. - versionEnd = strchr(versionBegin, '\n'); - if (!versionEnd) - { - versionEnd = sourceEnd; - } - else - { - versionEnd = versionEnd + 1; - } - } - else - { - // If we didn't find a directive, then treat it as being - // a zero-byte slice at the start of the string - versionBegin = sourceBegin; - versionEnd = sourceBegin; - } - - enum { kMaxSourceStringCount = 16 }; - const GLchar* sourceStrings[kMaxSourceStringCount]; - GLint sourceStringLengths[kMaxSourceStringCount]; - - int sourceStringCount = 0; - - const char* stagePrelude = "\n"; - switch (stage) - { -#define CASE(NAME) case GL_##NAME##_SHADER: stagePrelude = "#define __GLSL_" #NAME "__ 1\n"; break - - CASE(VERTEX); - CASE(TESS_CONTROL); - CASE(TESS_EVALUATION); - CASE(GEOMETRY); - CASE(FRAGMENT); - CASE(COMPUTE); - -#undef CASE - } - - const char* prelude = - "#define __GLSL__ 1\n" - ; - -#define ADD_SOURCE_STRING_SPAN(BEGIN, END) \ - sourceStrings[sourceStringCount] = BEGIN; \ - sourceStringLengths[sourceStringCount++] = GLint(END - BEGIN) \ - /* end */ - -#define ADD_SOURCE_STRING(BEGIN) \ - sourceStrings[sourceStringCount] = BEGIN; \ - sourceStringLengths[sourceStringCount++] = GLint(strlen(BEGIN)) \ - /* end */ - - ADD_SOURCE_STRING_SPAN(versionBegin, versionEnd); - ADD_SOURCE_STRING(stagePrelude); - ADD_SOURCE_STRING(prelude); - ADD_SOURCE_STRING_SPAN(sourceBegin, versionBegin); - ADD_SOURCE_STRING_SPAN(versionEnd, sourceEnd); - - auto shaderID = glCreateShader(stage); - glShaderSource( - shaderID, - sourceStringCount, - &sourceStrings[0], - &sourceStringLengths[0]); - glCompileShader(shaderID); - - GLint success = GL_FALSE; - glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); - if (!success) - { - int maxSize = 0; - glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxSize); - - auto infoBuffer = (char*)malloc(maxSize); - - int infoSize = 0; - glGetShaderInfoLog(shaderID, maxSize, &infoSize, infoBuffer); - if (infoSize > 0) - { - fprintf(stderr, "%s", infoBuffer); - ::OutputDebugStringA(infoBuffer); - } - - glDeleteShader(shaderID); - return 0; - } - - return shaderID; -} - -void GLRenderer::destroyBindingEntries(const BindingState::Desc& desc, const BindingDetail* details) -{ - const auto& bindings = desc.m_bindings; - const int numBindings = int(bindings.Count()); - for (int i = 0; i < numBindings; ++i) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - if (binding.bindingType == BindingType::Sampler && detail.m_samplerHandle != 0) - { - glDeleteSamplers(1, &detail.m_samplerHandle); - } - } -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! - -SlangResult GLRenderer::initialize(const Desc& desc, void* inWindowHandle) -{ - auto windowHandle = (HWND)inWindowHandle; - m_desc = desc; - - m_hdc = ::GetDC(windowHandle); - - PIXELFORMATDESCRIPTOR pixelFormatDesc = { sizeof(PIXELFORMATDESCRIPTOR) }; - pixelFormatDesc.nVersion = 1; - pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pixelFormatDesc.iPixelType = PFD_TYPE_RGBA; - pixelFormatDesc.cColorBits = 32; - pixelFormatDesc.cDepthBits = 24; - pixelFormatDesc.cStencilBits = 8; - pixelFormatDesc.iLayerType = PFD_MAIN_PLANE; - - int pixelFormatIndex = ChoosePixelFormat(m_hdc, &pixelFormatDesc); - SetPixelFormat(m_hdc, pixelFormatIndex, &pixelFormatDesc); - - m_glContext = wglCreateContext(m_hdc); - wglMakeCurrent(m_hdc, m_glContext); - - auto renderer = glGetString(GL_RENDERER); - auto extensions = glGetString(GL_EXTENSIONS); - - // Load each of our extension functions by name - -#define LOAD_GL_EXTENSION_FUNC(NAME, TYPE) NAME = (TYPE) wglGetProcAddress(#NAME); - MAP_GL_EXTENSION_FUNCS(LOAD_GL_EXTENSION_FUNC) -#undef LOAD_GL_EXTENSION_FUNC - - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - - glViewport(0, 0, desc.width, desc.height); - - if (glDebugMessageCallback) - { - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(staticDebugCallback, this); - } - - return SLANG_OK; -} - -void GLRenderer::setClearColor(const float color[4]) -{ - glClearColor(color[0], color[1], color[2], color[3]); -} - -void GLRenderer::clearFrame() -{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); -} - -void GLRenderer::presentFrame() -{ - glFlush(); - ::SwapBuffers(m_hdc); -} - -SlangResult GLRenderer::captureScreenSurface(Surface& surfaceOut) -{ - SLANG_RETURN_ON_FAIL(surfaceOut.allocate(m_desc.width, m_desc.height, Format::RGBA_Unorm_UInt8, 1, SurfaceAllocator::getMallocAllocator())); - glReadPixels(0, 0, m_desc.width, m_desc.height, GL_RGBA, GL_UNSIGNED_BYTE, surfaceOut.m_data); - surfaceOut.flipInplaceVertically(); - return SLANG_OK; -} - -TextureResource* GLRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData) -{ - TextureResource::Desc srcDesc(descIn); - srcDesc.setDefaults(initialUsage); - - GlPixelFormat pixelFormat = _getGlPixelFormat(srcDesc.format); - if (pixelFormat == GlPixelFormat::Unknown) - { - return nullptr; - } - - const GlPixelFormatInfo& info = s_pixelFormatInfos[int(pixelFormat)]; - - const GLint internalFormat = info.internalFormat; - const GLenum format = info.format; - const GLenum formatType = info.formatType; - - RefPtr texture(new TextureResourceImpl(initialUsage, srcDesc, this)); - - GLenum target = 0; - GLuint handle = 0; - glGenTextures(1, &handle); - - const int effectiveArraySize = srcDesc.calcEffectiveArraySize(); - - assert(initData); - assert(initData->numSubResources == srcDesc.numMipLevels * srcDesc.size.depth * effectiveArraySize); - - // Set on texture so will be freed if failure - texture->m_handle = handle; - const void*const*const data = initData->subResources; - - switch (srcDesc.type) - { - case Resource::Type::Texture1D: - { - if (srcDesc.arraySize > 0) - { - target = GL_TEXTURE_1D_ARRAY; - glBindTexture(target, handle); - - int slice = 0; - for (int i = 0; i < effectiveArraySize; i++) - { - for (int j = 0; j < srcDesc.numMipLevels; j++) - { - glTexImage2D(target, j, internalFormat, srcDesc.size.width, i, 0, format, formatType, data[slice++]); - } - } - } - else - { - target = GL_TEXTURE_1D; - glBindTexture(target, handle); - for (int i = 0; i < srcDesc.numMipLevels; i++) - { - glTexImage1D(target, i, internalFormat, srcDesc.size.width, 0, format, formatType, data[i]); - } - } - break; - } - case Resource::Type::TextureCube: - case Resource::Type::Texture2D: - { - if (srcDesc.arraySize > 0) - { - if (srcDesc.type == Resource::Type::TextureCube) - { - target = GL_TEXTURE_CUBE_MAP_ARRAY; - } - else - { - target = GL_TEXTURE_2D_ARRAY; - } - - glBindTexture(target, handle); - - int slice = 0; - for (int i = 0; i < effectiveArraySize; i++) - { - for (int j = 0; j < srcDesc.numMipLevels; j++) - { - glTexImage3D(target, j, internalFormat, srcDesc.size.width, srcDesc.size.height, slice, 0, format, formatType, data[slice++]); - } - } - } - else - { - if (srcDesc.type == Resource::Type::TextureCube) - { - target = GL_TEXTURE_CUBE_MAP; - glBindTexture(target, handle); - - int slice = 0; - for (int j = 0; j < 6; j++) - { - for (int i = 0; i < srcDesc.numMipLevels; i++) - { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[slice++]); - } - } - } - else - { - target = GL_TEXTURE_2D; - glBindTexture(target, handle); - for (int i = 0; i < srcDesc.numMipLevels; i++) - { - glTexImage2D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, 0, format, formatType, data[i]); - } - } - } - break; - } - case Resource::Type::Texture3D: - { - target = GL_TEXTURE_3D; - glBindTexture(target, handle); - for (int i = 0; i < srcDesc.numMipLevels; i++) - { - glTexImage3D(target, i, internalFormat, srcDesc.size.width, srcDesc.size.height, srcDesc.size.depth, 0, format, formatType, data[i]); - } - break; - } - default: return nullptr; - } - - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); - - // Assume regular sampling (might be superseded - if a combined sampler wanted) - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); - - texture->m_target = target; - - return texture.detach(); -} - -static GLenum _calcUsage(Resource::Usage usage) -{ - typedef Resource::Usage Usage; - switch (usage) - { - case Usage::ConstantBuffer: return GL_DYNAMIC_DRAW; - default: return GL_STATIC_READ; - } -} - -static GLenum _calcTarget(Resource::Usage usage) -{ - typedef Resource::Usage Usage; - switch (usage) - { - case Usage::ConstantBuffer: return GL_UNIFORM_BUFFER; - default: return GL_SHADER_STORAGE_BUFFER; - } -} - -BufferResource* GLRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData) -{ - BufferResource::Desc desc(descIn); - desc.setDefaults(initialUsage); - - const GLenum target = _calcTarget(initialUsage); - // TODO: should derive from desc... - const GLenum usage = _calcUsage(initialUsage); - - GLuint bufferID = 0; - glGenBuffers(1, &bufferID); - glBindBuffer(target, bufferID); - - glBufferData(target, descIn.sizeInBytes, initData, usage); - - return new BufferResourceImpl(initialUsage, desc, this, bufferID, target); -} - -InputLayout* GLRenderer::createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) -{ - InputLayoutImpl* inputLayout = new InputLayoutImpl; - - inputLayout->m_attributeCount = inputElementCount; - for (UInt ii = 0; ii < inputElementCount; ++ii) - { - auto& inputAttr = inputElements[ii]; - auto& glAttr = inputLayout->m_attributes[ii]; - - glAttr.streamIndex = 0; - glAttr.format = getVertexAttributeFormat(inputAttr.format); - glAttr.offset = (GLsizei)inputAttr.offset; - } - - return (InputLayout*)inputLayout; -} - -void* GLRenderer::map(BufferResource* bufferIn, MapFlavor flavor) -{ - BufferResourceImpl* buffer = static_cast(bufferIn); - - //GLenum target = GL_UNIFORM_BUFFER; - - GLuint access = 0; - switch (flavor) - { - case MapFlavor::WriteDiscard: - case MapFlavor::HostWrite: - access = GL_WRITE_ONLY; - break; - case MapFlavor::HostRead: - access = GL_READ_ONLY; - break; - } - - glBindBuffer(buffer->m_target, buffer->m_handle); - - return glMapBuffer(buffer->m_target, access); -} - -void GLRenderer::unmap(BufferResource* bufferIn) -{ - BufferResourceImpl* buffer = static_cast(bufferIn); - glUnmapBuffer(buffer->m_target); -} - -void GLRenderer::setInputLayout(InputLayout* inputLayout) -{ - m_boundInputLayout = static_cast(inputLayout); -} - -void GLRenderer::setPrimitiveTopology(PrimitiveTopology topology) -{ - GLenum glTopology = 0; - switch (topology) - { -#define CASE(NAME, VALUE) case PrimitiveTopology::NAME: glTopology = VALUE; break - - CASE(TriangleList, GL_TRIANGLES); - -#undef CASE - } - m_boundPrimitiveTopology = glTopology; -} - -void GLRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) -{ - for (UInt ii = 0; ii < slotCount; ++ii) - { - UInt slot = startSlot + ii; - - BufferResourceImpl* buffer = static_cast(buffers[ii]); - GLuint bufferID = buffer ? buffer->m_handle : 0; - - m_boundVertexStreamBuffers[slot] = bufferID; - m_boundVertexStreamStrides[slot] = strides[ii]; - m_boundVertexStreamOffsets[slot] = offsets[ii]; - } -} - -void GLRenderer::setShaderProgram(ShaderProgram* programIn) -{ - ShaderProgramImpl* program = static_cast(programIn); - m_boundShaderProgram = program; - GLuint programID = program ? program->m_id : 0; - glUseProgram(programID); -} - -void GLRenderer::draw(UInt vertexCount, UInt startVertex = 0) -{ - flushStateForDraw(); - - glDrawArrays(m_boundPrimitiveTopology, (GLint)startVertex, (GLsizei)vertexCount); -} - -void GLRenderer::dispatchCompute(int x, int y, int z) -{ - glDispatchCompute(x, y, z); -} - -BindingState* GLRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc, this)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - auto& dstDetail = dstDetails[i]; - const auto& srcBinding = srcBindings[i]; - - - switch (srcBinding.bindingType) - { - case BindingType::Texture: - case BindingType::Buffer: - { - break; - } - case BindingType::CombinedTextureSampler: - { - assert(srcBinding.resource && srcBinding.resource->isTexture()); - TextureResourceImpl* texture = static_cast(srcBinding.resource.Ptr()); - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; - - if (samplerDesc.isCompareSampler) - { - auto target = texture->m_target; - - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - } - break; - } - case BindingType::Sampler: - { - const BindingState::SamplerDesc& samplerDesc = bindingStateDesc.m_samplerDescs[srcBinding.descIndex]; - - GLuint handle; - - glCreateSamplers(1, &handle); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, GL_REPEAT); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, GL_REPEAT); - glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, GL_REPEAT); - - if (samplerDesc.isCompareSampler) - { - glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - } - else - { - glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(handle, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); - } - - dstDetail.m_samplerHandle = handle; - break; - } - } - } - - return bindingState.detach(); -} - -void GLRenderer::setBindingState(BindingState* stateIn) -{ - BindingStateImpl* state = static_cast(stateIn); - - const auto& bindingDesc = state->getDesc(); - - const auto& details = state->m_bindingDetails; - const auto& bindings = bindingDesc.m_bindings; - const int numBindings = int(bindings.Count()); - - for (int i = 0; i < numBindings; ++i) - { - const auto& binding = bindings[i]; - const auto& detail = details[i]; - - switch (binding.bindingType) - { - case BindingType::Buffer: - { - const int bindingIndex = binding.registerRange.getSingleIndex(); - - BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); - glBindBufferBase(buffer->m_target, bindingIndex, buffer->m_handle); - break; - } - case BindingType::Sampler: - { - for (int index = binding.registerRange.index; index < binding.registerRange.index + binding.registerRange.size; ++index) - { - glBindSampler(index, detail.m_samplerHandle); - } - break; - } - case BindingType::Texture: - case BindingType::CombinedTextureSampler: - { - BufferResourceImpl* buffer = static_cast(binding.resource.Ptr()); - - const int bindingIndex = binding.registerRange.getSingleIndex(); - - glActiveTexture(GL_TEXTURE0 + bindingIndex); - glBindTexture(buffer->m_target, buffer->m_handle); - break; - } - } - } -} - -ShaderProgram* GLRenderer::createProgram(const ShaderProgram::Desc& desc) -{ - auto programID = glCreateProgram(); - if(desc.pipelineType == PipelineType::Compute ) - { - auto computeKernel = desc.findKernel(StageType::Compute); - auto computeShaderID = loadShader(GL_COMPUTE_SHADER, (char const*) computeKernel->codeBegin); - glAttachShader(programID, computeShaderID); - glLinkProgram(programID); - glDeleteShader(computeShaderID); - } - else - { - auto vertexKernel = desc.findKernel(StageType::Vertex); - auto fragmentKernel = desc.findKernel(StageType::Fragment); - - auto vertexShaderID = loadShader(GL_VERTEX_SHADER, (char const*) vertexKernel->codeBegin); - auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, (char const*) fragmentKernel->codeBegin); - - glAttachShader(programID, vertexShaderID); - glAttachShader(programID, fragmentShaderID); - - - glLinkProgram(programID); - - glDeleteShader(vertexShaderID); - glDeleteShader(fragmentShaderID); - } - GLint success = GL_FALSE; - glGetProgramiv(programID, GL_LINK_STATUS, &success); - if (!success) - { - int maxSize = 0; - glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxSize); - - auto infoBuffer = (char*)::malloc(maxSize); - - int infoSize = 0; - glGetProgramInfoLog(programID, maxSize, &infoSize, infoBuffer); - if (infoSize > 0) - { - fprintf(stderr, "%s", infoBuffer); - OutputDebugStringA(infoBuffer); - } - - ::free(infoBuffer); - - glDeleteProgram(programID); - return nullptr; - } - - return new ShaderProgramImpl(this, programID); -} - - -} // renderer_test diff --git a/tools/slang-graphics/render-gl.h b/tools/slang-graphics/render-gl.h deleted file mode 100644 index 2b211cda4..000000000 --- a/tools/slang-graphics/render-gl.h +++ /dev/null @@ -1,10 +0,0 @@ -// render-d3d11.h -#pragma once - -namespace slang_graphics { - -class Renderer; - -Renderer* createGLRenderer(); - -} // slang_graphics diff --git a/tools/slang-graphics/render-vk.cpp b/tools/slang-graphics/render-vk.cpp deleted file mode 100644 index d7cd93e67..000000000 --- a/tools/slang-graphics/render-vk.cpp +++ /dev/null @@ -1,2019 +0,0 @@ -// render-vk.cpp -#include "render-vk.h" - -//WORKING:#include "options.h" -#include "render.h" - -#include "../../source/core/smart-pointer.h" - -#include "vk-api.h" -#include "vk-util.h" -#include "vk-device-queue.h" -#include "vk-swap-chain.h" - -#include "surface.h" - -// Vulkan has a different coordinate system to ogl -// http://anki3d.org/vulkan-coordinate-system/ - -#define ENABLE_VALIDATION_LAYER 1 - -#ifdef _MSC_VER -# include -# pragma warning(disable: 4996) -# if (_MSC_VER < 1900) -# define snprintf sprintf_s -# endif -#endif - -namespace slang_graphics { -using namespace Slang; - -class VKRenderer : public Renderer -{ -public: - enum { kMaxRenderTargets = 8, kMaxAttachments = kMaxRenderTargets + 1 }; - - // Renderer implementation - virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) override; - virtual void setClearColor(const float color[4]) override; - virtual void clearFrame() override; - virtual void presentFrame() override; - virtual TextureResource* createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData) override; - virtual BufferResource* createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& bufferDesc, const void* initData) override; - virtual SlangResult captureScreenSurface(Surface& surface) override; - virtual InputLayout* createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) override; - virtual BindingState* createBindingState(const BindingState::Desc& bindingStateDesc) override; - virtual ShaderProgram* createProgram(const ShaderProgram::Desc& desc) override; - virtual void* map(BufferResource* buffer, MapFlavor flavor) override; - virtual void unmap(BufferResource* buffer) override; - virtual void setInputLayout(InputLayout* inputLayout) override; - virtual void setPrimitiveTopology(PrimitiveTopology topology) override; - virtual void setBindingState(BindingState* state); - virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) override; - virtual void setShaderProgram(ShaderProgram* inProgram) 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; - virtual RendererType getRendererType() const override { return RendererType::Vulkan; } - - /// Dtor - ~VKRenderer(); - - protected: - - class Buffer - { - public: - /// Initialize a buffer with specified size, and memory props - Result init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties); - - /// Returns true if has been initialized - bool isInitialized() const { return m_api != nullptr; } - - // Default Ctor - Buffer(): - m_api(nullptr) - {} - - /// Dtor - ~Buffer() - { - if (m_api) - { - m_api->vkDestroyBuffer(m_api->m_device, m_buffer, nullptr); - m_api->vkFreeMemory(m_api->m_device, m_memory, nullptr); - } - } - - VkBuffer m_buffer; - VkDeviceMemory m_memory; - const VulkanApi* m_api; - }; - - class InputLayoutImpl : public InputLayout - { - public: - List m_vertexDescs; - int m_vertexSize; - }; - - class BufferResourceImpl: public BufferResource - { - public: - typedef BufferResource Parent; - - BufferResourceImpl(Resource::Usage initialUsage, const BufferResource::Desc& desc, VKRenderer* renderer): - Parent(desc), - m_renderer(renderer), - m_initialUsage(initialUsage) - { - assert(renderer); - } - - Resource::Usage m_initialUsage; - VKRenderer* m_renderer; - Buffer m_buffer; - Buffer m_uploadBuffer; - List m_readBuffer; ///< Stores the contents when a map read is performed - - MapFlavor m_mapFlavor = MapFlavor::Unknown; ///< If resource is mapped, records what kind of mapping else Unknown (if not mapped) - }; - - class TextureResourceImpl : public TextureResource - { - public: - typedef TextureResource Parent; - - TextureResourceImpl(const Desc& desc, Usage initialUsage, const VulkanApi* api) : - Parent(desc), - m_initialUsage(initialUsage), - m_api(api) - { - } - ~TextureResourceImpl() - { - if (m_api) - { - if (m_imageMemory != VK_NULL_HANDLE) - { - m_api->vkFreeMemory(m_api->m_device, m_imageMemory, nullptr); - } - if (m_image != VK_NULL_HANDLE) - { - m_api->vkDestroyImage(m_api->m_device, m_image, nullptr); - } - } - } - - Usage m_initialUsage; - - VkImage m_image = VK_NULL_HANDLE; - VkDeviceMemory m_imageMemory = VK_NULL_HANDLE; - - const VulkanApi* m_api; - }; - - class ShaderProgramImpl: public ShaderProgram - { - public: - - ShaderProgramImpl(PipelineType pipelineType): - m_pipelineType(pipelineType) - {} - - PipelineType m_pipelineType; - - VkPipelineShaderStageCreateInfo m_compute; - VkPipelineShaderStageCreateInfo m_vertex; - VkPipelineShaderStageCreateInfo m_fragment; - - List m_buffers[2]; //< To keep storage of code in scope - }; - - struct BindingDetail - { - VkImageView m_srv = VK_NULL_HANDLE; - VkBufferView m_uav = VK_NULL_HANDLE; - VkSampler m_sampler = VK_NULL_HANDLE; - }; - - class BindingStateImpl: public BindingState - { - public: - typedef BindingState Parent; - - BindingStateImpl(const Desc& desc, const VulkanApi* api): - Parent(desc), - m_api(api) - { - } - ~BindingStateImpl() - { - for (int i = 0; i < int(m_bindingDetails.Count()); ++i) - { - BindingDetail& detail = m_bindingDetails[i]; - if (detail.m_sampler != VK_NULL_HANDLE) - { - m_api->vkDestroySampler(m_api->m_device, detail.m_sampler, nullptr); - } - if (detail.m_srv != VK_NULL_HANDLE) - { - m_api->vkDestroyImageView(m_api->m_device, detail.m_srv, nullptr); - } - if (detail.m_uav != VK_NULL_HANDLE) - { - m_api->vkDestroyBufferView(m_api->m_device, detail.m_uav, nullptr); - } - } - } - - const VulkanApi* m_api; - List m_bindingDetails; - }; - - struct BoundVertexBuffer - { - RefPtr m_buffer; - int m_stride; - int m_offset; - }; - - class Pipeline : public RefObject - { - public: - Pipeline(const VulkanApi& api): - m_api(&api) - { - } - ~Pipeline() - { - if (m_pipeline != VK_NULL_HANDLE) - { - m_api->vkDestroyPipeline(m_api->m_device, m_pipeline, nullptr); - } - if (m_descriptorPool != VK_NULL_HANDLE) - { - m_api->vkDestroyDescriptorPool(m_api->m_device, m_descriptorPool, nullptr); - } - if (m_pipelineLayout != VK_NULL_HANDLE) - { - m_api->vkDestroyPipelineLayout(m_api->m_device, m_pipelineLayout, nullptr); - } - if(m_descriptorSetLayout != VK_NULL_HANDLE) - { - m_api->vkDestroyDescriptorSetLayout(m_api->m_device, m_descriptorSetLayout, nullptr); - } - } - - const VulkanApi* m_api; - - VkPrimitiveTopology m_primitiveTopology; - RefPtr m_bindingState; - RefPtr m_inputLayout; - RefPtr m_shaderProgram; - - VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE; - VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; - VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE; - VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE; - VkPipeline m_pipeline = VK_NULL_HANDLE; - }; - - VkBool32 handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, - size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg); - - VkPipelineShaderStageCreateInfo compileEntryPoint( - ShaderProgram::KernelDesc const& kernelDesc, - VkShaderStageFlagBits stage, - List& bufferOut); - - static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, - size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData); - - /// Returns true if m_currentPipeline matches the current configuration - Pipeline* _getPipeline(); - bool _isEqual(const Pipeline& pipeline) const; - Slang::Result _createPipeline(RefPtr& pipelineOut); - void _beginRender(); - void _endRender(); - - Slang::Result _beginPass(); - void _endPass(); - void _transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout); - - VkDebugReportCallbackEXT m_debugReportCallback; - - RefPtr m_currentInputLayout; - RefPtr m_currentBindingState; - RefPtr m_currentProgram; - - List > m_pipelineCache; - Pipeline* m_currentPipeline = nullptr; - - List m_boundVertexBuffers; - - VkPrimitiveTopology m_primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - VkDevice m_device = VK_NULL_HANDLE; - - VulkanModule m_module; - VulkanApi m_api; - - VulkanDeviceQueue m_deviceQueue; - VulkanSwapChain m_swapChain; - - VkRenderPass m_renderPass = VK_NULL_HANDLE; - - int m_swapChainImageIndex = -1; - - float m_clearColor[4] = { 0, 0, 0, 0 }; - - Desc m_desc; -}; - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer::Buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -Result VKRenderer::Buffer::init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties) -{ - assert(!isInitialized()); - - m_api = &api; - m_memory = VK_NULL_HANDLE; - m_buffer = VK_NULL_HANDLE; - - VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - bufferCreateInfo.size = bufferSize; - bufferCreateInfo.usage = usage; - - SLANG_VK_CHECK(api.vkCreateBuffer(api.m_device, &bufferCreateInfo, nullptr, &m_buffer)); - - VkMemoryRequirements memoryReqs = {}; - api.vkGetBufferMemoryRequirements(api.m_device, m_buffer, &memoryReqs); - - int memoryTypeIndex = api.findMemoryTypeIndex(memoryReqs.memoryTypeBits, reqMemoryProperties); - assert(memoryTypeIndex >= 0); - - VkMemoryPropertyFlags actualMemoryProperites = api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; - - VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - allocateInfo.allocationSize = memoryReqs.size; - allocateInfo.memoryTypeIndex = memoryTypeIndex; - - SLANG_VK_CHECK(api.vkAllocateMemory(api.m_device, &allocateInfo, nullptr, &m_memory)); - SLANG_VK_CHECK(api.vkBindBufferMemory(api.m_device, m_buffer, m_memory, 0)); - - return SLANG_OK; -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VkRenderer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -bool VKRenderer::_isEqual(const Pipeline& pipeline) const -{ - return - pipeline.m_bindingState == m_currentBindingState && - pipeline.m_primitiveTopology == m_primitiveTopology && - pipeline.m_inputLayout == m_currentInputLayout && - pipeline.m_shaderProgram == m_currentProgram; -} - -VKRenderer::Pipeline* VKRenderer::_getPipeline() -{ - if (m_currentPipeline && _isEqual(*m_currentPipeline)) - { - return m_currentPipeline; - } - - // Look for a match in the cache - for (int i = 0; i < int(m_pipelineCache.Count()); ++i) - { - Pipeline* pipeline = m_pipelineCache[i]; - if (_isEqual(*pipeline)) - { - m_currentPipeline = pipeline; - return pipeline; - } - } - - RefPtr pipeline; - SLANG_RETURN_NULL_ON_FAIL(_createPipeline(pipeline)); - m_pipelineCache.Add(pipeline); - m_currentPipeline = pipeline; - return pipeline; -} - -Slang::Result VKRenderer::_createPipeline(RefPtr& pipelineOut) -{ - RefPtr pipeline(new Pipeline(m_api)); - - // Initialize the state - pipeline->m_primitiveTopology = m_primitiveTopology; - pipeline->m_bindingState = m_currentBindingState; - pipeline->m_shaderProgram = m_currentProgram; - pipeline->m_inputLayout = m_currentInputLayout; - - // Must be equal at this point if all the items are correctly set in pipeline - assert(_isEqual(*pipeline)); - - // First create a pipeline layout based on what is bound - - const auto& srcDetails = m_currentBindingState->m_bindingDetails; - const auto& srcBindings = m_currentBindingState->getDesc().m_bindings; - - const int numBindings = int(srcBindings.Count()); - - int numBuffers = 0; - int numImages = 0; - - int numDescriptorByType[VK_DESCRIPTOR_TYPE_RANGE_SIZE] = { 0, }; - - Slang::List dstBindings; - for (int i = 0; i < numBindings; ++i) - { - const auto& srcDetail = srcDetails[i]; - const auto& srcBinding = srcBindings[i]; - - VkDescriptorSetLayoutBinding dstBinding = {}; - - dstBinding.descriptorCount = 1; - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - BufferResourceImpl* bufferResource = static_cast(srcBinding.resource.Ptr()); - const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); - - if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - dstBinding.stageFlags = VK_SHADER_STAGE_ALL; - dstBindings.Add(dstBinding); - - numDescriptorByType[dstBinding.descriptorType] ++; - numBuffers++; - } - else if (bufferResourceDesc.bindFlags & Resource::BindFlag::ConstantBuffer) - { - dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - dstBinding.stageFlags = VK_SHADER_STAGE_ALL; - dstBindings.Add(dstBinding); - - numDescriptorByType[dstBinding.descriptorType] ++; - numBuffers++; - } - break; - } - case BindingType::Texture: - { - dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - dstBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - dstBindings.Add(dstBinding); - - numDescriptorByType[dstBinding.descriptorType] ++; - numImages++; - break; - } - case BindingType::Sampler: - { - dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; - dstBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - dstBindings.Add(dstBinding); - - numDescriptorByType[dstBinding.descriptorType] ++; - numImages++; - break; - } - - case BindingType::CombinedTextureSampler: - { - dstBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - dstBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - dstBindings.Add(dstBinding); - - numDescriptorByType[dstBinding.descriptorType] ++; - numImages++; - break; - } - default: - { - assert(!"Unhandled type"); - return SLANG_FAIL; - } - } - } - - // Create a descriptor pool for allocating sets - { -#if 0 - VkDescriptorPoolSize poolSizes[] = - { - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 128 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 128 }, - { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 128 }, - }; -#endif - - List poolSizes; - for (int i = 0; i < SLANG_COUNT_OF(numDescriptorByType); ++i) - { - int numDescriptors = numDescriptorByType[i]; - if (numDescriptors > 0) - { - const VkDescriptorPoolSize poolSize = { VkDescriptorType(i), uint32_t(numDescriptors) }; - poolSizes.Add(poolSize); - } - } - VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - - descriptorPoolInfo.maxSets = 128; // TODO: actually pick a size. - descriptorPoolInfo.poolSizeCount = uint32_t(poolSizes.Count()); - descriptorPoolInfo.pPoolSizes = poolSizes.Buffer(); - - SLANG_VK_CHECK(m_api.vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &pipeline->m_descriptorPool)); - } - - // Create the layout - { - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; - descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.Count()); - descriptorSetLayoutInfo.pBindings = dstBindings.Buffer(); - - SLANG_VK_CHECK(m_api.vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutInfo, nullptr, &pipeline->m_descriptorSetLayout)); - } - - // Create a descriptor set based on our layout - { - VkDescriptorSetAllocateInfo descriptorSetAllocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; - descriptorSetAllocInfo.descriptorPool = pipeline->m_descriptorPool; - descriptorSetAllocInfo.descriptorSetCount = 1; - descriptorSetAllocInfo.pSetLayouts = &pipeline->m_descriptorSetLayout; - - SLANG_VK_CHECK(m_api.vkAllocateDescriptorSets(m_device, &descriptorSetAllocInfo, &pipeline->m_descriptorSet)); - } - - // Fill in the descriptor set, using our binding information - - List imageInfos; - List bufferInfos; - List writes; - - // Make sure there is enough space... - imageInfos.Reserve(numImages); - bufferInfos.Reserve(numBuffers); - - int elementIndex = 0; - - for (int i = 0; i < numBindings; ++i) - { - const auto& srcDetail = srcDetails[i]; - const auto& srcBinding = srcBindings[i]; - - const int bindingIndex = srcBinding.registerRange.getSingleIndex(); - - VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - writeInfo.descriptorCount = 1; - writeInfo.dstSet = pipeline->m_descriptorSet; - writeInfo.dstBinding = bindingIndex; - writeInfo.dstArrayElement = 0; - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - assert(srcBinding.resource && srcBinding.resource->isBuffer()); - BufferResourceImpl* bufferResource = static_cast(srcBinding.resource.Ptr()); - const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); - - { - VkDescriptorBufferInfo bufferInfo; - bufferInfo.buffer = bufferResource->m_buffer.m_buffer; - bufferInfo.offset = 0; - bufferInfo.range = bufferResourceDesc.sizeInBytes; - - bufferInfos.Add(bufferInfo); - } - - writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - if (bufferResource->m_initialUsage == Resource::Usage::UnorderedAccess) - { - writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - } - else if (bufferResource->m_initialUsage == Resource::Usage::ConstantBuffer) - { - writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - } - - writeInfo.pBufferInfo = &bufferInfos.Last(); - - writes.Add(writeInfo); - break; - } - case BindingType::Texture: - { - assert(srcBinding.resource && srcBinding.resource->isTexture()); - - TextureResourceImpl* textureResource = static_cast(srcBinding.resource.Ptr()); - const TextureResource::Desc& textureResourceDesc = textureResource->getDesc(); - - { - VkDescriptorImageInfo imageInfo = {}; - imageInfo.imageView = srcDetail.m_srv; - imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageInfos.Add(imageInfo); - } - - writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - writeInfo.pImageInfo = &imageInfos.Last(); - - writes.Add(writeInfo); - break; - } - case BindingType::Sampler: - { - { - VkDescriptorImageInfo imageInfo = {}; - imageInfo.sampler = srcDetail.m_sampler; - //imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageInfos.Add(imageInfo); - } - - writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; - writeInfo.pImageInfo = &imageInfos.Last(); - - writes.Add(writeInfo); - break; - } - default: - { - assert(!"Binding not currently handled"); - return SLANG_FAIL; - } - } - } - - assert(imageInfos.Count() == numImages); - assert(bufferInfos.Count() == numBuffers); - - // Write into the descriptor set - { - m_api.vkUpdateDescriptorSets(m_device, uint32_t(writes.Count()), writes.Buffer(), 0, nullptr); - } - - // Create a pipeline layout based on our descriptor set layout(s) - - VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = &pipeline->m_descriptorSetLayout; - - SLANG_VK_CHECK(m_api.vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &pipeline->m_pipelineLayout)); - - VkPipelineCache pipelineCache = VK_NULL_HANDLE; - - if (m_currentProgram->m_pipelineType == PipelineType::Compute) - { - // Then create a pipeline to use that layout - - VkComputePipelineCreateInfo computePipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; - computePipelineInfo.stage = m_currentProgram->m_compute; - computePipelineInfo.layout = pipeline->m_pipelineLayout; - - SLANG_VK_CHECK(m_api.vkCreateComputePipelines(m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline->m_pipeline)); - } - else if (m_currentProgram->m_pipelineType == PipelineType::Graphics) - { - // Create the graphics pipeline - - const int width = m_swapChain.getWidth(); - const int height = m_swapChain.getHeight(); - - VkPipelineShaderStageCreateInfo shaderStages[] = { m_currentProgram->m_vertex, m_currentProgram->m_fragment }; - - // VertexBuffer/s - // Currently only handles one - - VkPipelineVertexInputStateCreateInfo vertexInputInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.vertexAttributeDescriptionCount = 0; - - VkVertexInputBindingDescription vertexInputBindingDescription; - - if (m_currentInputLayout) - { - vertexInputBindingDescription.binding = 0; - vertexInputBindingDescription.stride = m_currentInputLayout->m_vertexSize; - vertexInputBindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - const auto& srcAttributeDescs = m_currentInputLayout->m_vertexDescs; - - vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription; - - vertexInputInfo.vertexAttributeDescriptionCount = static_cast(srcAttributeDescs.Count()); - vertexInputInfo.pVertexAttributeDescriptions = srcAttributeDescs.Buffer(); - } - - // - - VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - VkViewport viewport = {}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = (float)width; - viewport.height = (float)height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.offset = { 0, 0 }; - scissor.extent = { uint32_t(width), uint32_t(height) }; - - VkPipelineViewportStateCreateInfo viewportState = {}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; - viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; - - VkPipelineRasterizationStateCreateInfo rasterizer = {}; - rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = VK_CULL_MODE_NONE; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - - VkPipelineMultisampleStateCreateInfo multisampling = {}; - multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo colorBlending = {}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.logicOp = VK_LOGIC_OP_COPY; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - colorBlending.blendConstants[0] = 0.0f; - colorBlending.blendConstants[1] = 0.0f; - colorBlending.blendConstants[2] = 0.0f; - colorBlending.blendConstants[3] = 0.0f; - - VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; - - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = shaderStages; - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisampling; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.layout = pipeline->m_pipelineLayout; - pipelineInfo.renderPass = m_renderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - - SLANG_VK_CHECK(m_api.vkCreateGraphicsPipelines(m_device, pipelineCache, 1, &pipelineInfo, nullptr, &pipeline->m_pipeline)); - } - else - { - assert(!"Unhandled program type"); - return SLANG_FAIL; - } - - pipelineOut = pipeline; - return SLANG_OK; -} - -Result VKRenderer::_beginPass() -{ - if (m_swapChainImageIndex < 0) - { - return SLANG_FAIL; - } - - const int numRenderTargets = 1; - - const VulkanSwapChain::Image& image = m_swapChain.getImages()[m_swapChainImageIndex]; - - int numAttachments = 0; - - // Start render pass - VkClearValue clearValues[kMaxAttachments]; - clearValues[numAttachments++] = VkClearValue{ m_clearColor[0], m_clearColor[1], m_clearColor[2], m_clearColor[3] }; - - bool hasDepthBuffer = false; - if (hasDepthBuffer) - { - VkClearValue& clearValue = clearValues[numAttachments++]; - - clearValue.depthStencil.depth = 1.0f; - clearValue.depthStencil.stencil = 0; - } - - const int width = m_swapChain.getWidth(); - const int height = m_swapChain.getHeight(); - - VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); - - VkRenderPassBeginInfo renderPassBegin = {}; - renderPassBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassBegin.renderPass = m_renderPass; - renderPassBegin.framebuffer = image.m_frameBuffer; - renderPassBegin.renderArea.offset.x = 0; - renderPassBegin.renderArea.offset.y = 0; - renderPassBegin.renderArea.extent.width = width; - renderPassBegin.renderArea.extent.height = height; - renderPassBegin.clearValueCount = numAttachments; - renderPassBegin.pClearValues = clearValues; - - m_api.vkCmdBeginRenderPass(cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE); - - // Set up scissor and viewport - { - VkRect2D rects[kMaxRenderTargets] = {}; - VkViewport viewports[kMaxRenderTargets] = {}; - for (int i = 0; i < numRenderTargets; ++i) - { - rects[i] = VkRect2D{ 0, 0, uint32_t(width), uint32_t(height) }; - - VkViewport& dstViewport = viewports[i]; - - dstViewport.x = 0.0f; - dstViewport.y = 0.0f; - dstViewport.width = float(width); - dstViewport.height = float(height); - dstViewport.minDepth = 0.0f; - dstViewport.maxDepth = 1.0f; - } - - m_api.vkCmdSetScissor(cmdBuffer, 0, numRenderTargets, rects); - m_api.vkCmdSetViewport(cmdBuffer, 0, numRenderTargets, viewports); - } - - return SLANG_OK; -} - -void VKRenderer::_endPass() -{ - VkCommandBuffer cmdBuffer = m_deviceQueue.getCommandBuffer(); - m_api.vkCmdEndRenderPass(cmdBuffer); -} - -void VKRenderer::_beginRender() -{ - m_swapChainImageIndex = m_swapChain.nextFrontImageIndex(); - - if (m_swapChainImageIndex < 0) - { - return; - } -} - -void VKRenderer::_endRender() -{ - m_deviceQueue.flush(); -} - -Renderer* createVKRenderer() -{ - return new VKRenderer; -} - -VKRenderer::~VKRenderer() -{ - if (m_renderPass != VK_NULL_HANDLE) - { - m_api.vkDestroyRenderPass(m_device, m_renderPass, nullptr); - m_renderPass = VK_NULL_HANDLE; - } -} - - -VkBool32 VKRenderer::handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, - size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg) -{ - char const* severity = "message"; - if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) - severity = "warning"; - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) - severity = "error"; - - // pMsg can be really big (it can be assembler dump for example) - // Use a dynamic buffer to store - size_t bufferSize = strlen(pMsg) + 1 + 1024; - List bufferArray; - bufferArray.SetSize(bufferSize); - char* buffer = bufferArray.Buffer(); - - sprintf_s(buffer, - bufferSize, - "%s: %s %d: %s\n", - pLayerPrefix, - severity, - msgCode, - pMsg); - - fprintf(stderr, "%s", buffer); - fflush(stderr); - - OutputDebugStringA(buffer); - - return VK_FALSE; -} - -/* static */VKAPI_ATTR VkBool32 VKAPI_CALL VKRenderer::debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, - size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData) -{ - return ((VKRenderer*)pUserData)->handleDebugMessage(flags, objType, srcObject, location, msgCode, pLayerPrefix, pMsg); -} - -VkPipelineShaderStageCreateInfo VKRenderer::compileEntryPoint( - ShaderProgram::KernelDesc const& kernelDesc, - VkShaderStageFlagBits stage, - List& bufferOut) -{ - char const* dataBegin = (char const*) kernelDesc.codeBegin; - char const* dataEnd = (char const*) kernelDesc.codeEnd; - - // We need to make a copy of the code, since the Slang compiler - // will free the memory after a compile request is closed. - size_t codeSize = dataEnd - dataBegin; - - bufferOut.InsertRange(0, dataBegin, codeSize); - - char* codeBegin = bufferOut.Buffer(); - - VkShaderModuleCreateInfo moduleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; - moduleCreateInfo.pCode = (uint32_t*)codeBegin; - moduleCreateInfo.codeSize = codeSize; - - VkShaderModule module; - SLANG_VK_CHECK(m_api.vkCreateShaderModule(m_device, &moduleCreateInfo, nullptr, &module)); - - VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; - shaderStageCreateInfo.stage = stage; - - shaderStageCreateInfo.module = module; - shaderStageCreateInfo.pName = "main"; - - return shaderStageCreateInfo; -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!! Renderer interface !!!!!!!!!!!!!!!!!!!!!!!!!! - -SlangResult VKRenderer::initialize(const Desc& desc, void* inWindowHandle) -{ - SLANG_RETURN_ON_FAIL(m_module.init()); - SLANG_RETURN_ON_FAIL(m_api.initGlobalProcs(m_module)); - - m_desc = desc; - - VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; - applicationInfo.pApplicationName = "slang-render-test"; - applicationInfo.pEngineName = "slang-render-test"; - applicationInfo.apiVersion = VK_API_VERSION_1_0; - - char const* instanceExtensions[] = - { - VK_KHR_SURFACE_EXTENSION_NAME, - -#if SLANG_WINDOWS_FAMILY - VK_KHR_WIN32_SURFACE_EXTENSION_NAME, -#else - VK_KHR_XLIB_SURFACE_EXTENSION_NAME -#endif - -#if ENABLE_VALIDATION_LAYER - VK_EXT_DEBUG_REPORT_EXTENSION_NAME, -#endif - }; - - VkInstance instance = VK_NULL_HANDLE; - - VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; - instanceCreateInfo.pApplicationInfo = &applicationInfo; - - instanceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(instanceExtensions); - instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0]; - -#if ENABLE_VALIDATION_LAYER - const char* layerNames[] = { "VK_LAYER_LUNARG_standard_validation" }; - instanceCreateInfo.enabledLayerCount = SLANG_COUNT_OF(layerNames); - instanceCreateInfo.ppEnabledLayerNames = layerNames; -#endif - - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); - SLANG_RETURN_ON_FAIL(m_api.initInstanceProcs(instance)); - -#if ENABLE_VALIDATION_LAYER - VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - - VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; - debugCreateInfo.pfnCallback = &debugMessageCallback; - debugCreateInfo.pUserData = this; - debugCreateInfo.flags = debugFlags; - - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDebugReportCallbackEXT(instance, &debugCreateInfo, nullptr, &m_debugReportCallback)); -#endif - - uint32_t numPhysicalDevices = 0; - SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, nullptr)); - - List physicalDevices; - physicalDevices.SetSize(numPhysicalDevices); - SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, physicalDevices.Buffer())); - - // TODO: allow override of selected device - uint32_t selectedDeviceIndex = 0; - - SLANG_RETURN_ON_FAIL(m_api.initPhysicalDevice(physicalDevices[selectedDeviceIndex])); - - int queueFamilyIndex = m_api.findQueue(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); - assert(queueFamilyIndex >= 0); - - float queuePriority = 0.0f; - VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; - queueCreateInfo.queueFamilyIndex = queueFamilyIndex; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &queuePriority; - - char const* const deviceExtensions[] = - { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - }; - - VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; - deviceCreateInfo.queueCreateInfoCount = 1; - deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; - deviceCreateInfo.pEnabledFeatures = &m_api.m_deviceFeatures; - - deviceCreateInfo.enabledExtensionCount = SLANG_COUNT_OF(deviceExtensions); - deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions; - - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDevice(m_api.m_physicalDevice, &deviceCreateInfo, nullptr, &m_device)); - SLANG_RETURN_ON_FAIL(m_api.initDeviceProcs(m_device)); - - { - VkQueue queue; - m_api.vkGetDeviceQueue(m_device, queueFamilyIndex, 0, &queue); - SLANG_RETURN_ON_FAIL(m_deviceQueue.init(m_api, queue, queueFamilyIndex)); - } - - // set up swap chain - - { - VulkanSwapChain::Desc desc; - VulkanSwapChain::PlatformDesc* platformDesc = nullptr; - - desc.init(); - desc.m_format = Format::RGBA_Unorm_UInt8; - -#if SLANG_WINDOWS_FAMILY - VulkanSwapChain::WinPlatformDesc winPlatformDesc; - winPlatformDesc.m_hinstance = ::GetModuleHandle(nullptr); - winPlatformDesc.m_hwnd = (HWND)inWindowHandle; - platformDesc = &winPlatformDesc; -#endif - - SLANG_RETURN_ON_FAIL(m_swapChain.init(&m_deviceQueue, desc, platformDesc)); - } - - // depth/stencil? - - // render pass? - - { - const int numRenderTargets = 1; - bool shouldClear = true; - bool shouldClearDepth = false; - bool shouldClearStencil = false; - bool hasDepthBuffer = false; - - Format depthFormat = Format::Unknown; - VkFormat colorFormat = m_swapChain.getVkFormat(); - - int numAttachments = 0; - // We need extra space if we have depth buffer - VkAttachmentDescription attachmentDesc[kMaxRenderTargets + 1] = {}; - for (int i = 0; i < numRenderTargets; ++i) - { - VkAttachmentDescription& dst = attachmentDesc[numAttachments ++]; - - dst.flags = 0; - dst.format = colorFormat; - dst.samples = VK_SAMPLE_COUNT_1_BIT; - dst.loadOp = shouldClear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - dst.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - dst.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - dst.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - } - if (hasDepthBuffer) - { - VkAttachmentDescription& dst = attachmentDesc[numAttachments++]; - - dst.flags = 0; - dst.format = VulkanUtil::getVkFormat(depthFormat); - dst.samples = VK_SAMPLE_COUNT_1_BIT; - dst.loadOp = shouldClearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - dst.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - dst.stencilLoadOp = shouldClearStencil ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - dst.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - dst.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - dst.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } - - VkAttachmentReference colorAttachments[kMaxRenderTargets] = {}; - for (int i = 0; i < numRenderTargets; ++i) - { - VkAttachmentReference& dst = colorAttachments[i]; - dst.attachment = i; - dst.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } - - VkAttachmentReference depthAttachment = {}; - depthAttachment.attachment = numRenderTargets; - depthAttachment.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpassDesc = {}; - subpassDesc.flags = 0; - subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDesc.inputAttachmentCount = 0u; - subpassDesc.pInputAttachments = nullptr; - subpassDesc.colorAttachmentCount = numRenderTargets; - subpassDesc.pColorAttachments = colorAttachments; - subpassDesc.pResolveAttachments = nullptr; - subpassDesc.pDepthStencilAttachment = hasDepthBuffer ? &depthAttachment : nullptr; - subpassDesc.preserveAttachmentCount = 0u; - subpassDesc.pPreserveAttachments = nullptr; - - VkRenderPassCreateInfo renderPassCreateInfo = {}; - renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCreateInfo.attachmentCount = numAttachments; - renderPassCreateInfo.pAttachments = attachmentDesc; - renderPassCreateInfo.subpassCount = 1; - renderPassCreateInfo.pSubpasses = &subpassDesc; - SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &m_renderPass)); - } - - // frame buffer - SLANG_RETURN_ON_FAIL(m_swapChain.createFrameBuffers(m_renderPass)); - - _beginRender(); - - return SLANG_OK; -} - -void VKRenderer::submitGpuWork() -{ - m_deviceQueue.flush(); -} - -void VKRenderer::waitForGpu() -{ - m_deviceQueue.flushAndWait(); -} - -void VKRenderer::setClearColor(const float color[4]) -{ - for (int ii = 0; ii < 4; ++ii) - m_clearColor[ii] = color[ii]; -} - -void VKRenderer::clearFrame() -{ -} - -void VKRenderer::presentFrame() -{ - _endRender(); - - const bool vsync = true; - m_swapChain.present(vsync); - - _beginRender(); -} - -SlangResult VKRenderer::captureScreenSurface(Surface& surfaceOut) -{ - return SLANG_FAIL; -} - -static VkBufferUsageFlagBits _calcBufferUsageFlags(Resource::BindFlag::Enum bind) -{ - typedef Resource::BindFlag BindFlag; - - switch (bind) - { - case BindFlag::VertexBuffer: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - case BindFlag::IndexBuffer: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - case BindFlag::ConstantBuffer: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - case BindFlag::StreamOutput: - case BindFlag::RenderTarget: - case BindFlag::DepthStencil: - { - assert(!"Not supported yet"); - return VkBufferUsageFlagBits(0); - } - case BindFlag::UnorderedAccess: return VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; - case BindFlag::PixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - case BindFlag::NonPixelShaderResource: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - default: return VkBufferUsageFlagBits(0); - } -} - -static VkBufferUsageFlagBits _calcBufferUsageFlags(int bindFlags) -{ - int dstFlags = 0; - while (bindFlags) - { - int lsb = bindFlags & -bindFlags; - dstFlags |= _calcBufferUsageFlags(Resource::BindFlag::Enum(lsb)); - bindFlags &= ~lsb; - } - return VkBufferUsageFlagBits(dstFlags); -} - -static VkBufferUsageFlags _calcBufferUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) -{ - VkBufferUsageFlags usage = _calcBufferUsageFlags(bindFlags); - - if (cpuAccessFlags & Resource::AccessFlag::Read) - { - // If it can be read from, set this - usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - } - if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData) - { - usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; - } - - return usage; -} - -static VkImageUsageFlagBits _calcImageUsageFlags(Resource::BindFlag::Enum bind) -{ - typedef Resource::BindFlag BindFlag; - - switch (bind) - { - case BindFlag::RenderTarget: return VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - case BindFlag::DepthStencil: return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - case BindFlag::NonPixelShaderResource: - case BindFlag::PixelShaderResource: - { - // Ignore - return VkImageUsageFlagBits(0); - } - default: - { - assert(!"Unsupported"); - return VkImageUsageFlagBits(0); - } - } -} - -static VkImageUsageFlagBits _calcImageUsageFlags(int bindFlags) -{ - int dstFlags = 0; - while (bindFlags) - { - int lsb = bindFlags & -bindFlags; - dstFlags |= _calcImageUsageFlags(Resource::BindFlag::Enum(lsb)); - bindFlags &= ~lsb; - } - return VkImageUsageFlagBits(dstFlags); -} - -static VkImageUsageFlags _calcImageUsageFlags(int bindFlags, int cpuAccessFlags, const void* initData) -{ - VkImageUsageFlags usage = _calcImageUsageFlags(bindFlags); - - usage |= VK_IMAGE_USAGE_SAMPLED_BIT; - - if (cpuAccessFlags & Resource::AccessFlag::Read) - { - // If it can be read from, set this - usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - } - if ((cpuAccessFlags & Resource::AccessFlag::Write) || initData) - { - usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - } - - return usage; -} - -void VKRenderer::_transitionImageLayout(VkImage image, VkFormat format, const TextureResource::Desc& desc, VkImageLayout oldLayout, VkImageLayout newLayout) -{ - VkImageMemoryBarrier barrier = {}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.oldLayout = oldLayout; - barrier.newLayout = newLayout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = desc.numMipLevels; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - - VkPipelineStageFlags sourceStage; - VkPipelineStageFlags destinationStage; - - if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) - { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - - sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - } - else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - { - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; - destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - else - { - assert(!"unsupported layout transition!"); - return; - } - - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - m_api.vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); -} - -TextureResource* VKRenderer::createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& descIn, const TextureResource::Data* initData) -{ - TextureResource::Desc desc(descIn); - desc.setDefaults(initialUsage); - - const VkFormat format = VulkanUtil::getVkFormat(desc.format); - if (format == VK_FORMAT_UNDEFINED) - { - assert(!"Unhandled image format"); - return nullptr; - } - - const int arraySize = desc.calcEffectiveArraySize(); - - RefPtr texture(new TextureResourceImpl(desc, initialUsage, &m_api)); - - // Create the image - { - VkImageCreateInfo imageInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - - switch (desc.type) - { - case Resource::Type::Texture1D: - { - imageInfo.imageType = VK_IMAGE_TYPE_1D; - imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), 1, 1 }; - break; - } - case Resource::Type::Texture2D: - { - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 }; - break; - } - case Resource::Type::TextureCube: - { - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 }; - break; - } - case Resource::Type::Texture3D: - { - // Can't have an array and 3d texture - assert(desc.arraySize <= 1); - - imageInfo.imageType = VK_IMAGE_TYPE_3D; - imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), uint32_t(descIn.size.depth) }; - break; - } - default: - { - assert(!"Unhandled type"); - return nullptr; - } - } - - imageInfo.mipLevels = desc.numMipLevels; - imageInfo.arrayLayers = arraySize; - - imageInfo.format = format; - - imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageInfo.usage = _calcImageUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData); - imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.flags = 0; // Optional - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateImage(m_device, &imageInfo, nullptr, &texture->m_image)); - } - - VkMemoryRequirements memRequirements; - m_api.vkGetImageMemoryRequirements(m_device, texture->m_image, &memRequirements); - - // Allocate the memory - { - VkMemoryPropertyFlags reqMemoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - - VkMemoryAllocateInfo allocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO}; - - int memoryTypeIndex = m_api.findMemoryTypeIndex(memRequirements.memoryTypeBits, reqMemoryProperties); - assert(memoryTypeIndex >= 0); - - VkMemoryPropertyFlags actualMemoryProperites = m_api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags; - - allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = memoryTypeIndex; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkAllocateMemory(m_device, &allocInfo, nullptr, &texture->m_imageMemory)); - } - - // Bind the memory to the image - m_api.vkBindImageMemory(m_device, texture->m_image, texture->m_imageMemory, 0); - - if (initData) - { - List mipSizes; - - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - const int numMipMaps = desc.numMipLevels; - assert(initData->numMips == numMipMaps); - - // Calculate how large the buffer has to be - size_t bufferSize = 0; - // Calculate how large an array entry is - for (int j = 0; j < numMipMaps; ++j) - { - const TextureResource::Size mipSize = desc.size.calcMipSize(j); - - const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); - const int numRows = Surface::calcNumRows(desc.format, mipSize.height); - - mipSizes.Add(mipSize); - - bufferSize += (rowSizeInBytes * numRows) * mipSize.depth; - } - - - // Calculate the total size taking into account the array - bufferSize *= arraySize; - - Buffer uploadBuffer; - SLANG_RETURN_NULL_ON_FAIL(uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); - - assert(mipSizes.Count() == numMipMaps); - - // Copy into upload buffer - { - int subResourceIndex = 0; - - uint8_t* dstData; - m_api.vkMapMemory(m_device, uploadBuffer.m_memory, 0, bufferSize, 0, (void**)&dstData); - - for (int i = 0; i < arraySize; ++i) - { - for (int j = 0; j < int(mipSizes.Count()); ++j) - { - const auto& mipSize = mipSizes[j]; - - const ptrdiff_t srcRowStride = initData->mipRowStrides[j]; - const int dstRowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); - const int numRows = Surface::calcNumRows(desc.format, mipSize.height); - - for (int k = 0; k < mipSize.depth; k++) - { - const uint8_t* srcData = (const uint8_t*)(initData->subResources[subResourceIndex]); - - for (int l = 0; l < numRows; l++) - { - ::memcpy(dstData, srcData, dstRowSizeInBytes); - - dstData += dstRowSizeInBytes; - srcData += srcRowStride; - } - - subResourceIndex++; - } - } - } - - m_api.vkUnmapMemory(m_device, uploadBuffer.m_memory); - } - - _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - { - size_t srcOffset = 0; - for (int i = 0; i < arraySize; ++i) - { - for (int j = 0; j < int(mipSizes.Count()); ++j) - { - const auto& mipSize = mipSizes[j]; - - const int rowSizeInBytes = Surface::calcRowSize(desc.format, mipSize.width); - const int numRows = Surface::calcNumRows(desc.format, mipSize.height); - - // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkBufferImageCopy.html - // bufferRowLength and bufferImageHeight specify the data in buffer memory as a subregion of a larger two- or three-dimensional image, - // and control the addressing calculations of data in buffer memory. If either of these values is zero, that aspect of the buffer memory - // is considered to be tightly packed according to the imageExtent. - - VkBufferImageCopy region = {}; - - region.bufferOffset = srcOffset; - region.bufferRowLength = 0; //rowSizeInBytes; - region.bufferImageHeight = 0; - - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = j; - region.imageSubresource.baseArrayLayer = i; - region.imageSubresource.layerCount = 1; - region.imageOffset = { 0, 0, 0 }; - region.imageExtent = { uint32_t(mipSize.width), uint32_t(mipSize.height), uint32_t(mipSize.depth) }; - - // Do the copy (do all depths in a single go) - m_api.vkCmdCopyBufferToImage(commandBuffer, uploadBuffer.m_buffer, texture->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - - // Next - srcOffset += rowSizeInBytes * numRows * mipSize.depth; - } - } - } - - _transitionImageLayout(texture->m_image, format, texture->getDesc(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - m_deviceQueue.flushAndWait(); - } - - return texture.detach(); -} - -BufferResource* VKRenderer::createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& descIn, const void* initData) -{ - BufferResource::Desc desc(descIn); - desc.setDefaults(initialUsage); - - const size_t bufferSize = desc.sizeInBytes; - - VkMemoryPropertyFlags reqMemoryProperties = 0; - - VkBufferUsageFlags usage = _calcBufferUsageFlags(desc.bindFlags, desc.cpuAccessFlags, initData); - - switch (initialUsage) - { - case Resource::Usage::ConstantBuffer: - { - reqMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - break; - } - default: break; - } - - RefPtr buffer(new BufferResourceImpl(initialUsage, desc, this)); - SLANG_RETURN_NULL_ON_FAIL(buffer->m_buffer.init(m_api, desc.sizeInBytes, usage, reqMemoryProperties)); - - if ((desc.cpuAccessFlags & Resource::AccessFlag::Write) || initData) - { - SLANG_RETURN_NULL_ON_FAIL(buffer->m_uploadBuffer.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); - } - - if (initData) - { - // TODO: only create staging buffer if the memory type - // used for the buffer doesn't let us fill things in - // directly. - // Copy into staging buffer - void* mappedData = nullptr; - SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); - ::memcpy(mappedData, initData, bufferSize); - m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); - - // Copy from staging buffer to real buffer - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - VkBufferCopy copyInfo = {}; - copyInfo.size = bufferSize; - m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); - - //flushCommandBuffer(commandBuffer); - } - - return buffer.detach(); -} - -InputLayout* VKRenderer::createInputLayout(const InputElementDesc* elements, UInt numElements) -{ - RefPtr layout(new InputLayoutImpl); - - List& dstVertexDescs = layout->m_vertexDescs; - - size_t vertexSize = 0; - dstVertexDescs.SetSize(numElements); - - for (UInt i = 0; i < numElements; ++i) - { - const InputElementDesc& srcDesc = elements[i]; - VkVertexInputAttributeDescription& dstDesc = dstVertexDescs[i]; - - dstDesc.location = uint32_t(i); - dstDesc.binding = 0; - dstDesc.format = VulkanUtil::getVkFormat(srcDesc.format); - if (dstDesc.format == VK_FORMAT_UNDEFINED) - { - return nullptr; - } - - dstDesc.offset = uint32_t(srcDesc.offset); - - const size_t elementSize = RendererUtil::getFormatSize(srcDesc.format); - assert(elementSize > 0); - const size_t endElement = srcDesc.offset + elementSize; - - vertexSize = (vertexSize < endElement) ? endElement : vertexSize; - } - - // Work out the overall size - layout->m_vertexSize = int(vertexSize); - return layout.detach(); -} - -void* VKRenderer::map(BufferResource* bufferIn, MapFlavor flavor) -{ - BufferResourceImpl* buffer = static_cast(bufferIn); - assert(buffer->m_mapFlavor == MapFlavor::Unknown); - - // Make sure everything has completed before reading... - m_deviceQueue.flushAndWait(); - - const size_t bufferSize = buffer->getDesc().sizeInBytes; - - switch (flavor) - { - case MapFlavor::WriteDiscard: - case MapFlavor::HostWrite: - { - if (!buffer->m_uploadBuffer.isInitialized()) - { - return nullptr; - } - - void* mappedData = nullptr; - SLANG_VK_CHECK(m_api.vkMapMemory(m_device, buffer->m_uploadBuffer.m_memory, 0, bufferSize, 0, &mappedData)); - buffer->m_mapFlavor = flavor; - return mappedData; - } - case MapFlavor::HostRead: - { - // Make sure there is space in the read buffer - buffer->m_readBuffer.SetSize(bufferSize); - - // create staging buffer - Buffer staging; - - SLANG_RETURN_NULL_ON_FAIL(staging.init(m_api, bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)); - - // Copy from real buffer to staging buffer - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - VkBufferCopy copyInfo = {}; - copyInfo.size = bufferSize; - m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_buffer.m_buffer, staging.m_buffer, 1, ©Info); - - m_deviceQueue.flushAndWait(); - - // Write out the data from the buffer - void* mappedData = nullptr; - SLANG_VK_CHECK(m_api.vkMapMemory(m_device, staging.m_memory, 0, bufferSize, 0, &mappedData)); - - ::memcpy(buffer->m_readBuffer.Buffer(), mappedData, bufferSize); - m_api.vkUnmapMemory(m_device, staging.m_memory); - - buffer->m_mapFlavor = flavor; - - return buffer->m_readBuffer.Buffer(); - } - default: - return nullptr; - } -} - -void VKRenderer::unmap(BufferResource* bufferIn) -{ - BufferResourceImpl* buffer = static_cast(bufferIn); - assert(buffer->m_mapFlavor != MapFlavor::Unknown); - - const size_t bufferSize = buffer->getDesc().sizeInBytes; - - switch (buffer->m_mapFlavor) - { - case MapFlavor::WriteDiscard: - case MapFlavor::HostWrite: - { - m_api.vkUnmapMemory(m_device, buffer->m_uploadBuffer.m_memory); - - // Copy from staging buffer to real buffer - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - VkBufferCopy copyInfo = {}; - copyInfo.size = bufferSize; - m_api.vkCmdCopyBuffer(commandBuffer, buffer->m_uploadBuffer.m_buffer, buffer->m_buffer.m_buffer, 1, ©Info); - - // TODO: is this necessary? - //m_deviceQueue.flushAndWait(); - break; - } - default: break; - } - - // Mark as no longer mapped - buffer->m_mapFlavor = MapFlavor::Unknown; -} - -void VKRenderer::setInputLayout(InputLayout* inputLayout) -{ - m_currentInputLayout = static_cast(inputLayout); -} - -void VKRenderer::setPrimitiveTopology(PrimitiveTopology topology) -{ - m_primitiveTopology = VulkanUtil::getVkPrimitiveTopology(topology); -} - -void VKRenderer::setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) -{ - { - const UInt num = startSlot + slotCount; - if (num > m_boundVertexBuffers.Count()) - { - m_boundVertexBuffers.SetSize(num); - } - } - - for (UInt i = 0; i < slotCount; i++) - { - BufferResourceImpl* buffer = static_cast(buffers[i]); - if (buffer) - { - assert(buffer->m_initialUsage == Resource::Usage::VertexBuffer); - } - - BoundVertexBuffer& boundBuffer = m_boundVertexBuffers[startSlot + i]; - boundBuffer.m_buffer = buffer; - boundBuffer.m_stride = int(strides[i]); - boundBuffer.m_offset = int(offsets[i]); - } -} - -void VKRenderer::setShaderProgram(ShaderProgram* program) -{ - m_currentProgram = (ShaderProgramImpl*)program; -} - -void VKRenderer::draw(UInt vertexCount, UInt startVertex = 0) -{ - Pipeline* pipeline = _getPipeline(); - if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Graphics) - { - assert(!"Invalid render pipeline"); - return; - } - - SLANG_RETURN_VOID_ON_FAIL(_beginPass()); - - // Also create descriptor sets based on the given pipeline layout - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->m_pipeline); - m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->m_pipelineLayout, - 0, 1, &pipeline->m_descriptorSet, 0, nullptr); - - // Bind the vertex buffer - if (m_boundVertexBuffers.Count() > 0 && m_boundVertexBuffers[0].m_buffer) - { - const BoundVertexBuffer& boundVertexBuffer = m_boundVertexBuffers[0]; - - VkBuffer vertexBuffers[] = { boundVertexBuffer.m_buffer->m_buffer.m_buffer }; - VkDeviceSize offsets[] = { VkDeviceSize(boundVertexBuffer.m_offset) }; - - m_api.vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); - } - - m_api.vkCmdDraw(commandBuffer, static_cast(vertexCount), 1, 0, 0); - - _endPass(); -} - -void VKRenderer::dispatchCompute(int x, int y, int z) -{ - Pipeline* pipeline = _getPipeline(); - if (!pipeline || pipeline->m_shaderProgram->m_pipelineType != PipelineType::Compute) - { - assert(!"Invalid render pipeline"); - return; - } - - // Also create descriptor sets based on the given pipeline layout - VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer(); - - m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->m_pipeline); - - m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->m_pipelineLayout, - 0, 1, &pipeline->m_descriptorSet, 0, nullptr); - - m_api.vkCmdDispatch(commandBuffer, x, y, z); -} - -static VkImageViewType _calcImageViewType(TextureResource::Type type, const TextureResource::Desc& desc) -{ - switch (type) - { - case Resource::Type::Texture1D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D; - case Resource::Type::Texture2D: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; - case Resource::Type::TextureCube: return desc.arraySize > 1 ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE; - case Resource::Type::Texture3D: - { - // Can't have an array and 3d texture - assert(desc.arraySize <= 1); - if (desc.arraySize <= 1) - { - return VK_IMAGE_VIEW_TYPE_3D; - } - break; - } - default: break; - } - - return VK_IMAGE_VIEW_TYPE_MAX_ENUM; -} - - -BindingState* VKRenderer::createBindingState(const BindingState::Desc& bindingStateDesc) -{ - RefPtr bindingState(new BindingStateImpl(bindingStateDesc, &m_api)); - - const auto& srcBindings = bindingStateDesc.m_bindings; - const int numBindings = int(srcBindings.Count()); - - auto& dstDetails = bindingState->m_bindingDetails; - dstDetails.SetSize(numBindings); - - for (int i = 0; i < numBindings; ++i) - { - auto& dstDetail = dstDetails[i]; - const auto& srcBinding = srcBindings[i]; - - switch (srcBinding.bindingType) - { - case BindingType::Buffer: - { - if (!srcBinding.resource || !srcBinding.resource->isBuffer()) - { - assert(!"Needs to have a buffer resource set"); - return nullptr; - } - - BufferResourceImpl* bufferResource = static_cast(srcBinding.resource.Ptr()); - const BufferResource::Desc& bufferResourceDesc = bufferResource->getDesc(); - - if (bufferResourceDesc.bindFlags & Resource::BindFlag::UnorderedAccess) - { - // VkBufferView uav - - VkBufferViewCreateInfo info = { VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO }; - - info.format = VK_FORMAT_R32_SFLOAT; - // TODO: - // Not sure how to handle typeless? - if (bufferResourceDesc.elementSize == 0) - { - info.format = VK_FORMAT_R32_SFLOAT; // DXGI_FORMAT_R32_TYPELESS ? - } - - info.buffer = bufferResource->m_buffer.m_buffer; - info.offset = 0; - info.range = bufferResourceDesc.sizeInBytes; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateBufferView(m_device, &info, nullptr, &dstDetail.m_uav)); - } - - // TODO: Setup views. - // VkImageView srv - - - break; - } - case BindingType::Sampler: - { - VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - - samplerInfo.magFilter = VK_FILTER_LINEAR; - samplerInfo.minFilter = VK_FILTER_LINEAR; - - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - - samplerInfo.anisotropyEnable = VK_FALSE; - samplerInfo.maxAnisotropy = 1; - - samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - samplerInfo.unnormalizedCoordinates = VK_FALSE; - samplerInfo.compareEnable = VK_FALSE; - samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateSampler(m_device, &samplerInfo, nullptr, &dstDetail.m_sampler)); - - break; - } - case BindingType::Texture: - { - if (!srcBinding.resource || !srcBinding.resource->isTexture()) - { - assert(!"Needs to have a texture resource set"); - return nullptr; - } - - TextureResourceImpl* textureResource = static_cast(srcBinding.resource.Ptr()); - const TextureResource::Desc& texDesc = textureResource->getDesc(); - - VkImageViewType imageViewType = _calcImageViewType(textureResource->getType(), texDesc); - if (imageViewType == VK_IMAGE_VIEW_TYPE_MAX_ENUM) - { - assert(!"Invalid view type"); - return nullptr; - } - const VkFormat format = VulkanUtil::getVkFormat(texDesc.format); - if (format == VK_FORMAT_UNDEFINED) - { - assert(!"Unhandled image format"); - return nullptr; - } - - // Create the image view - - VkImageViewCreateInfo viewInfo = {}; - viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - viewInfo.image = textureResource->m_image; - viewInfo.viewType = imageViewType; - viewInfo.format = format; - viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = 1; - - viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - SLANG_VK_RETURN_NULL_ON_FAIL(m_api.vkCreateImageView(m_device, &viewInfo, nullptr, &dstDetail.m_srv)); - - break; - } - case BindingType::CombinedTextureSampler: - { - assert(!"not implemented"); - return nullptr; - } - } - } - - return bindingState.detach();; -} - -void VKRenderer::setBindingState(BindingState* state) -{ - m_currentBindingState = static_cast(state); -} - -ShaderProgram* VKRenderer::createProgram(const ShaderProgram::Desc& desc) -{ - ShaderProgramImpl* impl = new ShaderProgramImpl(desc.pipelineType); - if( desc.pipelineType == PipelineType::Compute) - { - auto computeKernel = desc.findKernel(StageType::Compute); - impl->m_compute = compileEntryPoint(*computeKernel, VK_SHADER_STAGE_COMPUTE_BIT, impl->m_buffers[0]); - } - else - { - auto vertexKernel = desc.findKernel(StageType::Vertex); - auto fragmentKernel = desc.findKernel(StageType::Fragment); - - impl->m_vertex = compileEntryPoint(*vertexKernel, VK_SHADER_STAGE_VERTEX_BIT, impl->m_buffers[0]); - impl->m_fragment = compileEntryPoint(*fragmentKernel, VK_SHADER_STAGE_FRAGMENT_BIT, impl->m_buffers[1]); - } - return impl; -} - -} // renderer_test diff --git a/tools/slang-graphics/render-vk.h b/tools/slang-graphics/render-vk.h deleted file mode 100644 index 720f35a2c..000000000 --- a/tools/slang-graphics/render-vk.h +++ /dev/null @@ -1,10 +0,0 @@ -// render-vk.h -#pragma once - -namespace slang_graphics { - -class Renderer; - -Renderer* createVKRenderer(); - -} // slang_graphics diff --git a/tools/slang-graphics/render.cpp b/tools/slang-graphics/render.cpp deleted file mode 100644 index 3595f73c1..000000000 --- a/tools/slang-graphics/render.cpp +++ /dev/null @@ -1,390 +0,0 @@ -// render.cpp -#include "render.h" - -#include "../../source/core/slang-math.h" - -namespace slang_graphics { -using namespace Slang; - -/* static */const Resource::BindFlag::Enum Resource::s_requiredBinding[] = -{ - BindFlag::VertexBuffer, // VertexBuffer - BindFlag::IndexBuffer, // IndexBuffer - BindFlag::ConstantBuffer, // ConstantBuffer - BindFlag::StreamOutput, // StreamOut - BindFlag::RenderTarget, // RenderTager - BindFlag::DepthStencil, // DepthRead - BindFlag::DepthStencil, // DepthWrite - BindFlag::UnorderedAccess, // UnorderedAccess - BindFlag::PixelShaderResource, // PixelShaderResource - BindFlag::NonPixelShaderResource, // NonPixelShaderResource - BindFlag::Enum(BindFlag::PixelShaderResource | BindFlag::NonPixelShaderResource), // GenericRead -}; - - -/* static */void Resource::compileTimeAsserts() -{ - SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_requiredBinding) == int(Usage::CountOf)); -} - -static const Resource::DescBase s_emptyDescBase = {}; - -const Resource::DescBase& Resource::getDescBase() const -{ - if (isBuffer()) - { - return static_cast(this)->getDesc(); - } - else if (isTexture()) - { - return static_cast(this)->getDesc(); - } - return s_emptyDescBase; -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RendererUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -/* static */const uint8_t RendererUtil::s_formatSize[] = -{ - 0, // Unknown, - - uint8_t(sizeof(float) * 4), // RGBA_Float32, - uint8_t(sizeof(float) * 3), // RGB_Float32, - uint8_t(sizeof(float) * 2), // RG_Float32, - uint8_t(sizeof(float) * 1), // R_Float32, - - uint8_t(sizeof(uint32_t)), // RGBA_Unorm_UInt8, - - uint8_t(sizeof(uint32_t)), // R_UInt32, - - uint8_t(sizeof(float)), // D_Float32, - uint8_t(sizeof(uint32_t)), // D_Unorm24_S8, -}; - -/* static */const BindingStyle RendererUtil::s_rendererTypeToBindingStyle[] = -{ - BindingStyle::Unknown, // Unknown, - BindingStyle::DirectX, // DirectX11, - BindingStyle::DirectX, // DirectX12, - BindingStyle::OpenGl, // OpenGl, - BindingStyle::Vulkan, // Vulkan -}; - -/* static */void RendererUtil::compileTimeAsserts() -{ - SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_formatSize) == int(Format::CountOf)); - SLANG_COMPILE_TIME_ASSERT(SLANG_COUNT_OF(s_rendererTypeToBindingStyle) == int(RendererType::CountOf)); -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!! BindingState::Desc !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -void BindingState::Desc::addSampler(const SamplerDesc& desc, const RegisterRange& registerRange) -{ - int descIndex = int(m_samplerDescs.Count()); - m_samplerDescs.Add(desc); - - Binding binding; - binding.bindingType = BindingType::Sampler; - binding.resource = nullptr; - binding.registerRange = registerRange; - binding.descIndex = descIndex; - - m_bindings.Add(binding); -} - -void BindingState::Desc::addResource(BindingType bindingType, Resource* resource, const RegisterRange& registerRange) -{ - assert(resource); - - Binding binding; - binding.bindingType = bindingType; - binding.resource = resource; - binding.descIndex = -1; - binding.registerRange = registerRange; - m_bindings.Add(binding); -} - -void BindingState::Desc::addCombinedTextureSampler(TextureResource* resource, const SamplerDesc& samplerDesc, const RegisterRange& registerRange) -{ - assert(resource); - - int samplerDescIndex = int(m_samplerDescs.Count()); - m_samplerDescs.Add(samplerDesc); - - Binding binding; - binding.bindingType = BindingType::CombinedTextureSampler; - binding.resource = resource; - binding.descIndex = samplerDescIndex; - binding.registerRange = registerRange; - m_bindings.Add(binding); -} - -void BindingState::Desc::clear() -{ - m_bindings.Clear(); - m_samplerDescs.Clear(); - m_numRenderTargets = 1; -} - -int BindingState::Desc::findBindingIndex(Resource::BindFlag::Enum bindFlag, int registerIndex) const -{ - const int numBindings = int(m_bindings.Count()); - for (int i = 0; i < numBindings; ++i) - { - const Binding& binding = m_bindings[i]; - if (binding.resource && (binding.resource->getDescBase().bindFlags & bindFlag) != 0) - { - if (binding.registerRange.hasRegister(registerIndex)) - { - return i; - } - } - } - - return -1; -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!! TextureResource::Size !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -int TextureResource::Size::calcMaxDimension(Type type) const -{ - switch (type) - { - case Resource::Type::Texture1D: return this->width; - case Resource::Type::Texture3D: return std::max(std::max(this->width, this->height), this->depth); - case Resource::Type::TextureCube: // fallthru - case Resource::Type::Texture2D: - { - return std::max(this->width, this->height); - } - default: return 0; - } -} - -TextureResource::Size TextureResource::Size::calcMipSize(int mipLevel) const -{ - Size size; - size.width = TextureResource::calcMipSize(this->width, mipLevel); - size.height = TextureResource::calcMipSize(this->height, mipLevel); - size.depth = TextureResource::calcMipSize(this->depth, mipLevel); - return size; -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!! BufferResource::Desc !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -void BufferResource::Desc::setDefaults(Usage initialUsage) -{ - if (this->bindFlags == 0) - { - this->bindFlags = Resource::s_requiredBinding[int(initialUsage)]; - } -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!! TextureResource::Desc !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -int TextureResource::Desc::calcNumMipLevels() const -{ - const int maxDimensionSize = this->size.calcMaxDimension(type); - return (maxDimensionSize > 0) ? (Math::Log2Floor(maxDimensionSize) + 1) : 0; -} - -int TextureResource::Desc::calcNumSubResources() const -{ - const int numMipMaps = (this->numMipLevels > 0) ? this->numMipLevels : calcNumMipLevels(); - const int arrSize = (this->arraySize > 0) ? this->arraySize : 1; - - switch (type) - { - case Resource::Type::Texture1D: - case Resource::Type::Texture2D: - { - return numMipMaps * arrSize; - } - case Resource::Type::Texture3D: - { - // can't have arrays of 3d textures - assert(this->arraySize <= 1); - return numMipMaps * this->size.depth; - } - case Resource::Type::TextureCube: - { - // There are 6 faces to a cubemap - return numMipMaps * arrSize * 6; - } - default: return 0; - } -} - -void TextureResource::Desc::fixSize() -{ - switch (type) - { - case Resource::Type::Texture1D: - { - this->size.height = 1; - this->size.depth = 1; - break; - } - case Resource::Type::TextureCube: - case Resource::Type::Texture2D: - { - this->size.depth = 1; - break; - } - case Resource::Type::Texture3D: - { - // Can't have an array - this->arraySize = 0; - break; - } - default: break; - } -} - -void TextureResource::Desc::setDefaults(Usage initialUsage) -{ - fixSize(); - if (this->bindFlags == 0) - { - this->bindFlags = Resource::s_requiredBinding[int(initialUsage)]; - } - if (this->numMipLevels <= 0) - { - this->numMipLevels = calcNumMipLevels(); - } -} - -int TextureResource::Desc::calcEffectiveArraySize() const -{ - const int arrSize = (this->arraySize > 0) ? this->arraySize : 1; - - switch (type) - { - case Resource::Type::Texture1D: // fallthru - case Resource::Type::Texture2D: - { - return arrSize; - } - case Resource::Type::TextureCube: return arrSize * 6; - case Resource::Type::Texture3D: return 1; - default: return 0; - } -} - -void TextureResource::Desc::init(Type typeIn) -{ - this->type = typeIn; - this->size.init(); - - this->format = Format::Unknown; - this->arraySize = 0; - this->numMipLevels = 0; - this->sampleDesc.init(); - - this->bindFlags = 0; - this->cpuAccessFlags = 0; -} - -void TextureResource::Desc::init1D(Format formatIn, int widthIn, int numMipMapsIn) -{ - this->type = Type::Texture1D; - this->size.init(widthIn); - - this->format = format; - this->arraySize = 0; - this->numMipLevels = numMipMapsIn; - this->sampleDesc.init(); - - this->bindFlags = 0; - this->cpuAccessFlags = 0; -} - -void TextureResource::Desc::init2D(Type typeIn, Format formatIn, int widthIn, int heightIn, int numMipMapsIn) -{ - assert(typeIn == Type::Texture2D || typeIn == Type::TextureCube); - - this->type = type; - this->size.init(widthIn, heightIn); - - this->format = format; - this->arraySize = 0; - this->numMipLevels = numMipMapsIn; - this->sampleDesc.init(); - - this->bindFlags = 0; - this->cpuAccessFlags = 0; -} - -void TextureResource::Desc::init3D(Format formatIn, int widthIn, int heightIn, int depthIn, int numMipMapsIn) -{ - this->type = Type::Texture3D; - this->size.init(widthIn, heightIn, depthIn); - - this->format = format; - this->arraySize = 0; - this->numMipLevels = numMipMapsIn; - this->sampleDesc.init(); - - this->bindFlags = 0; - this->cpuAccessFlags = 0; -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!! RennderUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -ProjectionStyle RendererUtil::getProjectionStyle(RendererType type) -{ - switch (type) - { - case RendererType::DirectX11: - case RendererType::DirectX12: - { - return ProjectionStyle::DirectX; - } - case RendererType::OpenGl: return ProjectionStyle::OpenGl; - case RendererType::Vulkan: return ProjectionStyle::Vulkan; - case RendererType::Unknown: return ProjectionStyle::Unknown; - default: - { - assert(!"Unhandled type"); - return ProjectionStyle::Unknown; - } - } -} - -/* static */void RendererUtil::getIdentityProjection(ProjectionStyle style, float projMatrix[16]) -{ - switch (style) - { - case ProjectionStyle::DirectX: - case ProjectionStyle::OpenGl: - { - static const float kIdentity[] = - { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - }; - ::memcpy(projMatrix, kIdentity, sizeof(kIdentity)); - break; - } - case ProjectionStyle::Vulkan: - { - static const float kIdentity[] = - { - 1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - }; - ::memcpy(projMatrix, kIdentity, sizeof(kIdentity)); - break; - } - default: - { - assert(!"Not handled"); - } - } -} - -} // renderer_test diff --git a/tools/slang-graphics/render.h b/tools/slang-graphics/render.h deleted file mode 100644 index 92e9c0930..000000000 --- a/tools/slang-graphics/render.h +++ /dev/null @@ -1,583 +0,0 @@ -// render.h -#pragma once - -#include "window.h" - -//#include "shader-input-layout.h" - -#include "../../slang-com-helper.h" - -#include "../../source/core/smart-pointer.h" -#include "../../source/core/list.h" - -namespace slang_graphics { - -// Had to move here, because Options needs types defined here -typedef intptr_t Int; -typedef uintptr_t UInt; - -// pre declare types -class Surface; - -// Declare opaque type -class InputLayout: public Slang::RefObject -{ - public: -}; - -enum class PipelineType -{ - Unknown, - Graphics, - Compute, - CountOf, -}; - -enum class StageType -{ - Unknown, - Vertex, - Hull, - Domain, - Geometry, - Fragment, - Compute, - CountOf, -}; - -enum class RendererType -{ - Unknown, - DirectX11, - DirectX12, - OpenGl, - Vulkan, - CountOf, -}; - -enum class ProjectionStyle -{ - Unknown, - OpenGl, - DirectX, - Vulkan, - CountOf, -}; - -/// The style of the binding -enum class BindingStyle -{ - Unknown, - DirectX, - OpenGl, - Vulkan, - CountOf, -}; - -class ShaderProgram: public Slang::RefObject -{ -public: - - struct KernelDesc - { - StageType stage; - void const* codeBegin; - void const* codeEnd; - - UInt getCodeSize() const { return (char const*)codeEnd - (char const*)codeBegin; } - }; - - struct Desc - { - PipelineType pipelineType; - KernelDesc const* kernels; - Int kernelCount; - - /// Find and return the kernel for `stage`, if present. - KernelDesc const* findKernel(StageType stage) const - { - for(Int ii = 0; ii < kernelCount; ++ii) - if(kernels[ii].stage == stage) - return &kernels[ii]; - return nullptr; - } - }; -}; - -struct ShaderCompileRequest -{ - struct SourceInfo - { - char const* path; - - // The data may either be source text (in which - // case it can be assumed to be nul-terminated with - // `dataEnd` pointing at the terminator), or - // raw binary data (in which case `dataEnd` points - // at the end of the buffer). - char const* dataBegin; - char const* dataEnd; - }; - - struct EntryPoint - { - char const* name = nullptr; - SourceInfo source; - }; - - SourceInfo source; - EntryPoint vertexShader; - EntryPoint fragmentShader; - EntryPoint computeShader; - Slang::List entryPointTypeArguments; -}; - -/// Different formats of things like pixels or elements of vertices -/// NOTE! Any change to this type (adding, removing, changing order) - must also be reflected in changes to RendererUtil -enum class Format -{ - Unknown, - - RGBA_Float32, - RGB_Float32, - RG_Float32, - R_Float32, - - RGBA_Unorm_UInt8, - - R_UInt32, - - D_Float32, - D_Unorm24_S8, - - CountOf, -}; - -struct InputElementDesc -{ - char const* semanticName; - UInt semanticIndex; - Format format; - UInt offset; -}; - -enum class MapFlavor -{ - Unknown, ///< Unknown mapping type - HostRead, - HostWrite, - WriteDiscard, -}; - -enum class PrimitiveTopology -{ - TriangleList, -}; - -class Resource: public Slang::RefObject -{ - public: - - /// The type of resource. - /// NOTE! The order needs to be such that all texture types are at or after Texture1D (otherwise isTexture won't work correctly) - enum class Type - { - Unknown, ///< Unknown - Buffer, ///< A buffer (like a constant/index/vertex buffer) - Texture1D, ///< A 1d texture - Texture2D, ///< A 2d texture - Texture3D, ///< A 3d texture - TextureCube, ///< A cubemap consists of 6 Texture2D like faces - CountOf, - }; - - /// Describes how a resource is to be used - enum class Usage - { - Unknown = -1, - VertexBuffer = 0, - IndexBuffer, - ConstantBuffer, - StreamOutput, - RenderTarget, - DepthRead, - DepthWrite, - UnorderedAccess, - PixelShaderResource, - NonPixelShaderResource, - GenericRead, - CountOf, - }; - - /// Binding flags describe all of the ways a resource can be bound - and therefore used - struct BindFlag - { - enum Enum - { - VertexBuffer = 0x001, - IndexBuffer = 0x002, - ConstantBuffer = 0x004, - StreamOutput = 0x008, - RenderTarget = 0x010, - DepthStencil = 0x020, - UnorderedAccess = 0x040, - PixelShaderResource = 0x080, - NonPixelShaderResource = 0x100, - }; - }; - - /// Combinations describe how a resource can be accessed (typically by the host/cpu) - struct AccessFlag - { - enum Enum - { - Read = 0x1, - Write = 0x2 - }; - }; - - /// Base class for Descs - struct DescBase - { - bool canBind(BindFlag::Enum bindFlag) const { return (bindFlags & bindFlag) != 0; } - bool hasCpuAccessFlag(AccessFlag::Enum accessFlag) { return (cpuAccessFlags & accessFlag) != 0; } - - Type type = Type::Unknown; - - int bindFlags = 0; ///< Combination of Resource::BindFlag or 0 (and will use initialUsage to set) - int cpuAccessFlags = 0; ///< Combination of Resource::AccessFlag - }; - - /// Get the type - SLANG_FORCE_INLINE Type getType() const { return m_type; } - /// True if it's a texture derived type - SLANG_FORCE_INLINE bool isTexture() const { return int(m_type) >= int(Type::Texture1D); } - /// True if it's a buffer derived type - SLANG_FORCE_INLINE bool isBuffer() const { return m_type == Type::Buffer; } - - /// Get the descBase - const DescBase& getDescBase() const; - /// Returns true if can bind with flag - bool canBind(BindFlag::Enum bindFlag) const { return getDescBase().canBind(bindFlag); } - - /// For a usage gives the required binding flags - static const BindFlag::Enum s_requiredBinding[]; /// Maps Usage to bind flags required - - protected: - Resource(Type type): - m_type(type) - {} - - static void compileTimeAsserts(); - - Type m_type; -}; - -class BufferResource: public Resource -{ - public: - typedef Resource Parent; - - struct Desc: public DescBase - { - void init(size_t sizeInBytesIn) - { - sizeInBytes = sizeInBytesIn; - elementSize = 0; - format = Format::Unknown; - } - /// Set up default parameters based on usage - void setDefaults(Usage initialUsage); - - size_t sizeInBytes; ///< Total size in bytes - int elementSize; ///< Get the element stride. If > 0, this is a structured buffer - Format format; - }; - - /// Get the buffer description - SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } - - /// Ctor - BufferResource(const Desc& desc): - Parent(Type::Buffer), - m_desc(desc) - { - } - - protected: - Desc m_desc; -}; - -class TextureResource: public Resource -{ - public: - typedef Resource Parent; - - struct SampleDesc - { - void init() - { - numSamples = 1; - quality = 0; - } - int numSamples; ///< Number of samples per pixel - int quality; ///< The quality measure for the samples - }; - - struct Size - { - void init() - { - width = height = depth = 1; - } - void init(int widthIn, int heightIn = 1, int depthIn = 1) - { - width = widthIn; - height = heightIn; - depth = depthIn; - } - /// Given the type works out the maximum dimension size - int calcMaxDimension(Type type) const; - /// Given a size, calculates the size at a mip level - Size calcMipSize(int mipLevel) const; - - int width; ///< Width in pixels - int height; ///< Height in pixels (if 2d or 3d) - int depth; ///< Depth (if 3d) - }; - - struct Desc: public DescBase - { - /// Initialize with default values - void init(Type typeIn); - /// Initialize different dimensions. For cubemap, use init2D - void init1D(Format format, int width, int numMipMaps = 0); - void init2D(Type typeIn, Format format, int width, int height, int numMipMaps = 0); - void init3D(Format format, int width, int height, int depth, int numMipMaps = 0); - - /// Given the type, calculates the number of mip maps. 0 on error - int calcNumMipLevels() const; - /// Calculate the total number of sub resources. 0 on error. - int calcNumSubResources() const; - - /// Calculate the effective array size - in essence the amount if mip map sets needed. - /// In practice takes into account if the arraySize is 0 (it's not an array, but it will still have at least one mip set) - /// and if the type is a cubemap (multiplies the amount of mip sets by 6) - int calcEffectiveArraySize() const; - - /// Use type to fix the size values (and array size). - /// For example a 1d texture, should have height and depth set to 1. - void fixSize(); - - /// Set up default parameters based on type and usage - void setDefaults(Usage initialUsage); - - Size size; - - int arraySize; ///< Array size - - int numMipLevels; ///< Number of mip levels - if 0 will create all mip levels - Format format; ///< The resources format - SampleDesc sampleDesc; ///< How the resource is sampled - }; - - /// The ordering of the subResources is - /// forall (effectiveArraySize) - /// forall (mip levels) - /// forall (depth levels) - struct Data - { - ptrdiff_t* mipRowStrides; ///< The row stride for a mip map - int numMips; ///< The number of mip maps - const void*const* subResources; ///< Pointers to each full mip subResource - int numSubResources; ///< The total amount of subResources. Typically = numMips * depth * arraySize - }; - - /// Get the description of the texture - SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } - - /// Ctor - TextureResource(const Desc& desc): - Parent(desc.type), - m_desc(desc) - { - } - - SLANG_FORCE_INLINE static int calcMipSize(int width, int mipLevel) - { - width = width >> mipLevel; - return width > 0 ? width : 1; - } - - protected: - Desc m_desc; -}; - -enum class BindingType -{ - Unknown, - Sampler, - Buffer, - Texture, - CombinedTextureSampler, - CountOf, -}; - -class BindingState : public Slang::RefObject -{ -public: - /// A register set consists of one or more contiguous indices. - /// To be valid index >= 0 and size >= 1 - struct RegisterRange - { - /// True if contains valid contents - bool isValid() const { return size > 0; } - /// True if valid single value - bool isSingle() const { return size == 1; } - /// Get as a single index (must be at least one index) - int getSingleIndex() const { return (size == 1) ? index : -1; } - /// Return the first index - int getFirstIndex() const { return (size > 0) ? index : -1; } - /// True if contains register index - bool hasRegister(int registerIndex) const { return registerIndex >= index && registerIndex < index + size; } - - static RegisterRange makeInvalid() { return RegisterRange{ -1, 0 }; } - static RegisterRange makeSingle(int index) { return RegisterRange{ int16_t(index), 1 }; } - static RegisterRange makeRange(int index, int size) { return RegisterRange{ int16_t(index), uint16_t(size) }; } - - int16_t index; ///< The base index - uint16_t size; ///< The amount of register indices - }; - - struct SamplerDesc - { - bool isCompareSampler; - }; - - struct Binding - { - BindingType bindingType; ///< Type of binding - int descIndex; ///< The description index associated with type. -1 if not used. For example if bindingType is Sampler, the descIndex is into m_samplerDescs. - Slang::RefPtr resource; ///< Associated resource. nullptr if not used - RegisterRange registerRange; /// Defines the registers for binding - }; - - struct Desc - { - /// Add a resource - assumed that the binding will match the Desc of the resource - void addResource(BindingType bindingType, Resource* resource, const RegisterRange& registerRange); - /// Add a sampler - void addSampler(const SamplerDesc& desc, const RegisterRange& registerRange); - /// Add a BufferResource - void addBufferResource(BufferResource* resource, const RegisterRange& registerRange) { addResource(BindingType::Buffer, resource, registerRange); } - /// Add a texture - void addTextureResource(TextureResource* resource, const RegisterRange& registerRange) { addResource(BindingType::Texture, resource, registerRange); } - /// Add combined texture a - void addCombinedTextureSampler(TextureResource* resource, const SamplerDesc& samplerDesc, const RegisterRange& registerRange); - - /// Returns the bind index, that has the bind flag, and indexes the specified register - int findBindingIndex(Resource::BindFlag::Enum bindFlag, int registerIndex) const; - - /// Clear the contents - void clear(); - - Slang::List m_bindings; ///< All of the bindings in order - Slang::List m_samplerDescs; ///< Holds the SamplerDesc for the binding - indexed by the descIndex member of Binding - - int m_numRenderTargets = 1; - }; - - /// Get the Desc used to create this binding - SLANG_FORCE_INLINE const Desc& getDesc() const { return m_desc; } - - protected: - BindingState(const Desc& desc): - m_desc(desc) - { - } - - Desc m_desc; -}; - -class Renderer: public Slang::RefObject -{ -public: - - struct Desc - { - int width; ///< Width in pixels - int height; ///< height in pixels - }; - - virtual SlangResult initialize(const Desc& desc, void* inWindowHandle) = 0; - - virtual void setClearColor(const float color[4]) = 0; - virtual void clearFrame() = 0; - - virtual void presentFrame() = 0; - - /// Create a texture resource. initData holds the initialize data to set the contents of the texture when constructed. - virtual TextureResource* createTextureResource(Resource::Usage initialUsage, const TextureResource::Desc& desc, const TextureResource::Data* initData = nullptr) { return nullptr; } - /// Create a buffer resource - virtual BufferResource* createBufferResource(Resource::Usage initialUsage, const BufferResource::Desc& desc, const void* initData = nullptr) { return nullptr; } - - /// Captures the back buffer and stores the result in surfaceOut. If the surface contains data - it will either be overwritten (if same size and format), or freed and a re-allocated. - virtual SlangResult captureScreenSurface(Surface& surfaceOut) = 0; - - virtual InputLayout* createInputLayout(const InputElementDesc* inputElements, UInt inputElementCount) = 0; - virtual BindingState* createBindingState(const BindingState::Desc& desc) { return nullptr; } - - virtual ShaderProgram* createProgram(const ShaderProgram::Desc& desc) = 0; - - virtual void* map(BufferResource* buffer, MapFlavor flavor) = 0; - virtual void unmap(BufferResource* buffer) = 0; - - virtual void setInputLayout(InputLayout* inputLayout) = 0; - virtual void setPrimitiveTopology(PrimitiveTopology topology) = 0; - virtual void setBindingState(BindingState* state) = 0; - virtual void setVertexBuffers(UInt startSlot, UInt slotCount, BufferResource*const* buffers, const UInt* strides, const UInt* offsets) = 0; - - inline void setVertexBuffer(UInt slot, BufferResource* buffer, UInt stride, UInt offset = 0); - - virtual void setShaderProgram(ShaderProgram* program) = 0; - - 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; - - /// Get the type of this renderer - virtual RendererType getRendererType() const = 0; -}; - -// ---------------------------------------------------------------------------------------- -inline void Renderer::setVertexBuffer(UInt slot, BufferResource* buffer, UInt stride, UInt offset) -{ - setVertexBuffers(slot, 1, &buffer, &stride, &offset); -} - -/// Functions that are around Renderer and it's types -struct RendererUtil -{ - /// Gets the size in bytes of a Format type. Returns 0 if a size is not defined/invalid - SLANG_FORCE_INLINE static size_t getFormatSize(Format format) { return s_formatSize[int(format)]; } - /// Given a renderer type, gets a projection style - static ProjectionStyle getProjectionStyle(RendererType type); - - /// Given the projection style returns an 'identity' matrix, which ensures x,y mapping to pixels is the same on all targets - static void getIdentityProjection(ProjectionStyle style, float projMatrix[16]); - - /// Get the binding style from the type - static BindingStyle getBindingStyle(RendererType type) { return s_rendererTypeToBindingStyle[int(type)]; } - - private: - static void compileTimeAsserts(); - static const uint8_t s_formatSize[]; // Maps Format::XXX to a size in bytes; - static const BindingStyle s_rendererTypeToBindingStyle[]; ///< Maps a RendererType to a BindingStyle -}; - -} // renderer_test diff --git a/tools/slang-graphics/resource-d3d12.cpp b/tools/slang-graphics/resource-d3d12.cpp deleted file mode 100644 index bb39d2529..000000000 --- a/tools/slang-graphics/resource-d3d12.cpp +++ /dev/null @@ -1,214 +0,0 @@ -// resource-d3d12.cpp -#include "resource-d3d12.h" - -namespace slang_graphics { -using namespace Slang; - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12BarrierSubmitter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -void D3D12BarrierSubmitter::_flush() -{ - assert(m_numBarriers > 0); - - if (m_commandList) - { - m_commandList->ResourceBarrier(UINT(m_numBarriers), m_barriers); - } - m_numBarriers = 0; -} - -D3D12_RESOURCE_BARRIER& D3D12BarrierSubmitter::_expandOne() -{ - _flush(); - return m_barriers[m_numBarriers++]; -} - -void D3D12BarrierSubmitter::transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState) -{ - if (nextState != prevState) - { - D3D12_RESOURCE_BARRIER& barrier = expandOne(); - - const UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - const D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - - ::memset(&barrier, 0, sizeof(barrier)); - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = flags; - barrier.Transition.pResource = resource; - barrier.Transition.StateBefore = prevState; - barrier.Transition.StateAfter = nextState; - barrier.Transition.Subresource = subresource; - } - else - { - if (nextState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS) - { - D3D12_RESOURCE_BARRIER& barrier = expandOne(); - - ::memset(&barrier, 0, sizeof(barrier)); - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; - barrier.UAV.pResource = resource; - } - } -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12ResourceBase !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -/* static */DXGI_FORMAT D3D12ResourceBase::calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource) -{ - return resource ? D3DUtil::calcFormat(usage, resource->GetDesc().Format) : DXGI_FORMAT_UNKNOWN; -} - -void D3D12ResourceBase::transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter) -{ - // Transition only if there is a resource - if (m_resource) - { - submitter.transition(m_resource, m_state, nextState); - m_state = nextState; - } -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! D3D12CounterFence !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ - -D3D12CounterFence::~D3D12CounterFence() -{ - if (m_event) - { - CloseHandle(m_event); - } -} - -Result D3D12CounterFence::init(ID3D12Device* device, uint64_t initialValue) -{ - m_currentValue = initialValue; - - SLANG_RETURN_ON_FAIL(device->CreateFence(m_currentValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_fence.writeRef()))); - // Create an event handle to use for frame synchronization. - m_event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (m_event == nullptr) - { - Result res = HRESULT_FROM_WIN32(GetLastError()); - return SLANG_FAILED(res) ? res : SLANG_FAIL; - } - return SLANG_OK; -} - -UInt64 D3D12CounterFence::nextSignal(ID3D12CommandQueue* commandQueue) -{ - // Increment the fence value. Save on the frame - we'll know that frame is done when the fence value >= - m_currentValue++; - // Schedule a Signal command in the queue. - Result res = commandQueue->Signal(m_fence, m_currentValue); - if (SLANG_FAILED(res)) - { - assert(!"Signal failed"); - } - return m_currentValue; -} - -void D3D12CounterFence::waitUntilCompleted(uint64_t completedValue) -{ - // You can only wait for a value that is less than or equal to the current value - assert(completedValue <= m_currentValue); - - // Wait until the previous frame is finished. - while (m_fence->GetCompletedValue() < completedValue) - { - // Make it signal with the current value - SLANG_ASSERT_VOID_ON_FAIL(m_fence->SetEventOnCompletion(completedValue, m_event)); - WaitForSingleObject(m_event, INFINITE); - } -} - -void D3D12CounterFence::nextSignalAndWait(ID3D12CommandQueue* commandQueue) -{ - waitUntilCompleted(nextSignal(commandQueue)); -} - -/* !!!!!!!!!!!!!!!!!!!!!!!!! D3D12Resource !!!!!!!!!!!!!!!!!!!!!!!! */ - -/* static */void D3D12Resource::setDebugName(ID3D12Resource* resource, const char* name) -{ - if (resource) - { - size_t len = ::strlen(name); - List buf; - buf.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 resource; - SLANG_RETURN_ON_FAIL(device->CreateCommittedResource(&heapProps, heapFlags, &resourceDesc, initState, clearValue, IID_PPV_ARGS(resource.writeRef()))); - setResource(resource, initState); - return SLANG_OK; -} - -ID3D12Resource* D3D12Resource::detach() -{ - ID3D12Resource* resource = m_resource; - m_resource = nullptr; - return resource; -} - -void D3D12Resource::swap(ComPtr& resourceInOut) -{ - ID3D12Resource* tmp = m_resource; - m_resource = resourceInOut.detach(); - resourceInOut.attach(tmp); -} - -void D3D12Resource::setState(D3D12_RESOURCE_STATES state) -{ - m_prevState = state; - m_state = state; -} - -} // renderer_test diff --git a/tools/slang-graphics/resource-d3d12.h b/tools/slang-graphics/resource-d3d12.h deleted file mode 100644 index 6040291cc..000000000 --- a/tools/slang-graphics/resource-d3d12.h +++ /dev/null @@ -1,178 +0,0 @@ -// resource-d3d12.h -#pragma once - -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#include -#include - -#include "../../slang-com-ptr.h" -#include "d3d-util.h" - -namespace slang_graphics { - -// Enables more conservative barriers - restoring the state of resources after they are used. -// Should not need to be enabled in normal builds, as the barriers should correctly sync resources -// If enabling fixes an issue it implies regular barriers are not correctly used. -#define SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS 0 - -struct D3D12BarrierSubmitter -{ - enum { MAX_BARRIERS = 8 }; - - /// Expand one space to hold a barrier - SLANG_FORCE_INLINE D3D12_RESOURCE_BARRIER& expandOne() { return (m_numBarriers < MAX_BARRIERS) ? m_barriers[m_numBarriers++] : _expandOne(); } - /// Flush barriers to command list - SLANG_FORCE_INLINE void flush() { if (m_numBarriers > 0) _flush(); } - - /// Transition resource from prevState to nextState - void transition(ID3D12Resource* resource, D3D12_RESOURCE_STATES prevState, D3D12_RESOURCE_STATES nextState); - - /// Ctor - SLANG_FORCE_INLINE D3D12BarrierSubmitter(ID3D12GraphicsCommandList* commandList) : m_numBarriers(0), m_commandList(commandList) { } - /// Dtor - SLANG_FORCE_INLINE ~D3D12BarrierSubmitter() { flush(); } - -protected: - D3D12_RESOURCE_BARRIER& _expandOne(); - void _flush(); - - ID3D12GraphicsCommandList* m_commandList; - int m_numBarriers; - D3D12_RESOURCE_BARRIER m_barriers[MAX_BARRIERS]; -}; - -/*! \brief A class to simplify using Dx12 fences. - -A fence is a mechanism to track GPU work. This is achieved by having a counter that the CPU holds -called the current value. Calling nextSignal will increase the CPU counter, and add a fence -with that value to the commandQueue. When the GPU has completed all the work before the fence it will -update the completed value. This is typically used when -the CPU needs to know the GPU has finished some piece of work has completed. To do this the CPU -can check the completed value, and when it is greater or equal to the value returned by nextSignal the -CPU will know that all the work prior to when the nextSignal was added to the queue will have completed. - -NOTE! This cannot be used across threads, as for amongst other reasons SetEventOnCompletion -only works with a single value. - -Signal on the CommandQueue updates the fence on the GPU side. Signal on the fence object changes -the value on the CPU side (not used here). - -Useful article describing how Dx12 synchronization works: -https://msdn.microsoft.com/en-us/library/windows/desktop/dn899217%28v=vs.85%29.aspx -*/ -class D3D12CounterFence -{ -public: - /// Must be called before used - SlangResult init(ID3D12Device* device, uint64_t initialValue = 0); - /// Increases the counter, signals the queue and waits for the signal to be hit - void nextSignalAndWait(ID3D12CommandQueue* queue); - /// Signals with next counter value. Returns the value the signal was called on - uint64_t nextSignal(ID3D12CommandQueue* commandQueue); - /// Get the current value - SLANG_FORCE_INLINE uint64_t getCurrentValue() const { return m_currentValue; } - /// Get the completed value - SLANG_FORCE_INLINE uint64_t getCompletedValue() const { return m_fence->GetCompletedValue(); } - - /// Waits for the the specified value - void waitUntilCompleted(uint64_t completedValue); - - /// Ctor - D3D12CounterFence() :m_event(nullptr), m_currentValue(0) {} - /// Dtor - ~D3D12CounterFence(); - -protected: - HANDLE m_event; - Slang::ComPtr m_fence; - UINT64 m_currentValue; -}; - -/** The base class for resource types allows for tracking of state. It does not allow for setting of the resource though, such that -an interface can return a D3D12ResourceBase, and a client cant manipulate it's state, but it cannot replace/change the actual resource */ -struct D3D12ResourceBase -{ - /// Add a transition if necessary to the list - void transition(D3D12_RESOURCE_STATES nextState, D3D12BarrierSubmitter& submitter); - /// Get the current state - SLANG_FORCE_INLINE D3D12_RESOURCE_STATES getState() const { return m_state; } - - /// Get the associated resource - SLANG_FORCE_INLINE ID3D12Resource* getResource() const { return m_resource; } - - /// True if a resource is set - SLANG_FORCE_INLINE bool isSet() const { return m_resource != nullptr; } - - /// Coercible into ID3D12Resource - SLANG_FORCE_INLINE operator ID3D12Resource*() const { return m_resource; } - - /// restore previous state -#if SLANG_ENABLE_CONSERVATIVE_RESOURCE_BARRIERS - SLANG_FORCE_INLINE Void restore(D3D12BarrierSubmitter& submitter) { transition(m_prevState, submitter); } -#else - SLANG_FORCE_INLINE void restore(D3D12BarrierSubmitter& submitter) { SLANG_UNUSED(submitter) } -#endif - - /// Given the usage, flags, and format will return the most suitable format. Will return DXGI_UNKNOWN if combination is not possible - static DXGI_FORMAT calcFormat(D3DUtil::UsageType usage, ID3D12Resource* resource); - - /// Ctor - SLANG_FORCE_INLINE D3D12ResourceBase() : - m_state(D3D12_RESOURCE_STATE_COMMON), - m_prevState(D3D12_RESOURCE_STATE_COMMON), - m_resource(nullptr) - {} - -protected: - /// This is protected so as clients cannot slice the class, and so state tracking is lost - ~D3D12ResourceBase() {} - - ID3D12Resource* m_resource; ///< The resource (ref counted) - D3D12_RESOURCE_STATES m_state; ///< The current tracked expected state, if all associated transitions have completed on ID3D12CommandList - D3D12_RESOURCE_STATES m_prevState; ///< The previous state -}; - -struct D3D12Resource : public D3D12ResourceBase -{ - - /// Dtor - ~D3D12Resource() - { - if (m_resource) - { - m_resource->Release(); - } - } - - /// Initialize as committed resource - Slang::Result initCommitted(ID3D12Device* device, const D3D12_HEAP_PROPERTIES& heapProps, D3D12_HEAP_FLAGS heapFlags, const D3D12_RESOURCE_DESC& resourceDesc, D3D12_RESOURCE_STATES initState, const D3D12_CLEAR_VALUE * clearValue); - - /// Set a resource with an initial state - void setResource(ID3D12Resource* resource, D3D12_RESOURCE_STATES initialState); - /// Make the resource null - void setResourceNull(); - /// Returns the attached resource (with any ref counts) and sets to nullptr on this. - ID3D12Resource* detach(); - - /// Swaps the resource contents with the contents of the smart pointer - void swap(Slang::ComPtr& resourceInOut); - - /// Sets the current state of the resource (the current state is taken to be the future state once the command list has executed) - /// NOTE! This must be used with care, otherwise state tracking can be made incorrect. - void setState(D3D12_RESOURCE_STATES state); - - /// Set the debug name on a resource - static void setDebugName(ID3D12Resource* resource, const char* name); - - /// Set the the debug name on the resource - void setDebugName(const wchar_t* name); - /// Set the debug name - void setDebugName(const char* name); -}; - -} // renderer_test diff --git a/tools/slang-graphics/slang-graphics.vcxproj b/tools/slang-graphics/slang-graphics.vcxproj deleted file mode 100644 index ce7502326..000000000 --- a/tools/slang-graphics/slang-graphics.vcxproj +++ /dev/null @@ -1,212 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {222F7498-B40C-4F3F-A704-DDEB91A4484A} - true - Win32Proj - slang-graphics - 10.0.14393.0 - - - - StaticLibrary - true - Unicode - v140 - - - StaticLibrary - true - Unicode - v140 - - - StaticLibrary - false - Unicode - v140 - - - StaticLibrary - false - Unicode - v140 - - - - - - - - - - - - - - - - - - - ..\..\bin\windows-x86\debug\ - ..\..\intermediate\windows-x86\debug\slang-graphics\ - slang-graphics - .lib - - - ..\..\bin\windows-x64\debug\ - ..\..\intermediate\windows-x64\debug\slang-graphics\ - slang-graphics - .lib - - - ..\..\bin\windows-x86\release\ - ..\..\intermediate\windows-x86\release\slang-graphics\ - slang-graphics - .lib - - - ..\..\bin\windows-x64\release\ - ..\..\intermediate\windows-x64\release\slang-graphics\ - slang-graphics - .lib - - - - NotUsing - Level3 - _DEBUG;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories) - EditAndContinue - Disabled - MultiThreadedDebug - - - Windows - true - - - "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../bin/windows-x86/debug/" - - - - - NotUsing - Level3 - _DEBUG;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories) - EditAndContinue - Disabled - MultiThreadedDebug - - - Windows - true - - - "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../bin/windows-x64/debug/" - - - - - NotUsing - Level3 - NDEBUG;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - - - Windows - true - true - - - "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x86/" "../../bin/windows-x86/release/" - - - - - NotUsing - Level3 - NDEBUG;%(PreprocessorDefinitions) - ..\..;..\..\external;..\..\source;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - - - Windows - true - true - - - "$(SolutionDir)tools\copy-hlsl-libs.bat" "$(WindowsSdkDir)Redist/D3D/x64/" "../../bin/windows-x64/release/" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tools/slang-graphics/slang-graphics.vcxproj.filters b/tools/slang-graphics/slang-graphics.vcxproj.filters deleted file mode 100644 index b1e4c42a3..000000000 --- a/tools/slang-graphics/slang-graphics.vcxproj.filters +++ /dev/null @@ -1,111 +0,0 @@ - - - - - {21EB8090-0D4E-1035-B6D3-48EBA215DCB7} - - - {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/tools/slang-graphics/surface.cpp b/tools/slang-graphics/surface.cpp deleted file mode 100644 index 9d91f8778..000000000 --- a/tools/slang-graphics/surface.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// surface.cpp -#include "surface.h" - -#include -#include - -#include "../../source/core/list.h" - -namespace slang_graphics { -using namespace Slang; - -class MallocSurfaceAllocator: public SurfaceAllocator -{ - public: - - virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) override; - virtual void deallocate(Surface& surface) override; -}; - -static MallocSurfaceAllocator s_mallocSurfaceAllocator; - -/// Get the malloc allocator -/* static */SurfaceAllocator* SurfaceAllocator::getMallocAllocator() -{ - return &s_mallocSurfaceAllocator; -} - -Slang::Result MallocSurfaceAllocator::allocate(int width, int height, Format format, int alignment, Surface& surface) -{ - assert(surface.m_data == nullptr); - - // Calculate row size - - const int rowSizeInBytes = Surface::calcRowSize(format, width); - const int numRows = Surface::calcNumRows(format, height); - - alignment = (alignment <= 0) ? int(sizeof(void*)) : alignment; - // It must be a power of 2 - assert( ((alignment - 1) & alignment) == 0); - - // Align rowSize - const int alignedRowSizeInBytes = (rowSizeInBytes + alignment - 1) & -alignment; - - size_t totalSize = numRows * alignedRowSizeInBytes; - - uint8_t* data = (uint8_t*)::malloc(totalSize); - if (!data) - { - return SLANG_E_OUT_OF_MEMORY; - } - - surface.m_data = data; - surface.m_width = width; - surface.m_height = height; - surface.m_format = format; - surface.m_numRows = numRows; - surface.m_rowStrideInBytes = alignedRowSizeInBytes; - - surface.m_allocator = this; - return SLANG_OK; -} - -void MallocSurfaceAllocator::deallocate(Surface& surface) -{ - assert(surface.m_data); - // Make sure it's not an inverted, cos otherwise m_data is not the start address - assert(surface.m_rowStrideInBytes > 0); - ::free(surface.m_data); -} - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Surface !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -/* static */int Surface::calcRowSize(Format format, int width) -{ - size_t pixelSize = RendererUtil::getFormatSize(format); - if (pixelSize == 0) - { - return 0; - } - return int(pixelSize * width); -} - -/* static */int Surface::calcNumRows(Format format, int height) -{ - // Don't have any compressed types, so number of rows is same as the height - return height; -} - -void Surface::init() -{ - m_width = 0; - m_height = 0; - m_format = Format::Unknown; - m_data = nullptr; - m_numRows = 0; - m_rowStrideInBytes = 0; - // NOTE! does not clear the allocator. - // If called with an allocation memory will leak! -} - -Surface::~Surface() -{ - if (m_data && m_allocator) - { - m_allocator->deallocate(*this); - } -} - -void Surface::deallocate() -{ - if (m_data && m_allocator) - { - m_allocator->deallocate(*this); - init(); - } -} - -Result Surface::allocate(int width, int height, Format format, int alignment, SurfaceAllocator* allocator) -{ - deallocate(); - allocator = allocator ? allocator : m_allocator; - if (!allocator) - { - // An allocator needs to be set on the surface, or one passed in. - return SLANG_FAIL; - } - return allocator->allocate(width, height, format, alignment, *this); -} - -void Surface::setUnowned(int width, int height, Format format, int strideInBytes, void* data) -{ - deallocate(); - - // This is unowned - m_allocator = nullptr; - - m_width = width; - m_height = height; - m_format = format; - m_rowStrideInBytes = strideInBytes; - m_data = (uint8_t*)data; - - m_numRows = Surface::calcNumRows(format, height); - - const int rowSizeInBytes = Surface::calcRowSize(format, width); - assert((strideInBytes > 0 && rowSizeInBytes <= strideInBytes) || (strideInBytes < 0 && rowSizeInBytes <= -strideInBytes)); -} - -void Surface::zeroContents() -{ - const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); - - const int stride = m_rowStrideInBytes; - uint8_t* dst = m_data; - - for (int i = 0; i < m_numRows; i++, dst += stride) - { - ::memset(dst, 0, rowSizeInBytes); - } -} - -void Surface::flipInplaceVertically() -{ - // Can only flip when m_height matches number of rows - assert(m_numRows == m_height); - - const int rowSizeInBytes = Surface::calcRowSize(m_format, m_width); - if (rowSizeInBytes <= 0 || m_numRows <= 1) - { - return; - } - - uint8_t* top = m_data; - uint8_t* bottom = m_data + (m_numRows - 1) * m_rowStrideInBytes; - - List bufferList; - bufferList.SetSize(rowSizeInBytes); - uint8_t* buffer = bufferList.Buffer(); - - const int stride = m_rowStrideInBytes; - - const int num = m_height >> 1; - for (int i = 0; i < num; ++i, top += stride, bottom -= stride) - { - ::memcpy(buffer, top, rowSizeInBytes); - ::memcpy(top, bottom, rowSizeInBytes); - ::memcpy(bottom, buffer, rowSizeInBytes); - } -} - -SlangResult Surface::set(int width, int height, Format format, int srcRowStride, const void* data, SurfaceAllocator* allocator) -{ - if (hasContents() && m_width == width && m_height == height && m_format == format) - { - // I can just overwrite the contents that is there - } - else - { - SLANG_RETURN_ON_FAIL(allocate(width, height, format, 0, allocator)); - } - - // Okay just need to set the contents - - { - const size_t rowSize = calcRowSize(format, width); - - const uint8_t* srcRow = (const uint8_t*)data; - uint8_t* dstRow = (uint8_t*)m_data; - - for (int i = 0; i < m_numRows; i++) - { - ::memcpy(dstRow, srcRow, rowSize); - - srcRow += srcRowStride; - dstRow += m_rowStrideInBytes; - } - } - - return SLANG_OK; -} - -} // renderer_test diff --git a/tools/slang-graphics/surface.h b/tools/slang-graphics/surface.h deleted file mode 100644 index 026ba25ed..000000000 --- a/tools/slang-graphics/surface.h +++ /dev/null @@ -1,86 +0,0 @@ -// surface.h -#pragma once - -#include "render.h" - -namespace slang_graphics { - -class Surface; - -class SurfaceAllocator -{ - public: - virtual Slang::Result allocate(int width, int height, Format format, int alignment, Surface& surface) = 0; - virtual void deallocate(Surface& surface) = 0; - - /// Get the malloc allocator - static SurfaceAllocator* getMallocAllocator(); -}; - -class Surface -{ - public: - - enum - { - kDefaultAlignment = sizeof(void*) - }; - - /// Allocate - Slang::Result allocate(int width, int height, Format format, int alignment = kDefaultAlignment, SurfaceAllocator* allocator = nullptr); - - /// Deallocate contents - void deallocate(); - /// Initialize contents (zero sized, no data). Note that the allocator pointer is left as is - void init(); - - /// Set unowned - void setUnowned(int width, int height, Format format, int strideInBytes, void* data); - - /// Set the contents - the memory will be owned by this surface (ie will be freed by the allocator when goes out of scope or is deallocated) - Slang::Result set(int width, int height, Format format, int strideInBytes, const void* data, SurfaceAllocator* allocator); - - template - T* calcNextRow(T* ptr) const { return (T*)calcNextRow((void*)ptr); } - template - const T* calcNextRow(const T* ptr) const { return (const T*)calcNextRow((const void*)ptr); } - - void* calcNextRow(void* ptr) const { return (void*)(((uint8_t*)ptr) + m_rowStrideInBytes); } - const void* calcNextRow(const void* ptr) const { return (const void*)(((const uint8_t*)ptr) + m_rowStrideInBytes); } - - /// Writes zero to all of the contents - void zeroContents(); - - /// Flips the contents vertically in place - void flipInplaceVertically(); - - /// True if has some contents - bool hasContents() const { return m_data != nullptr; } - - /// Ctor - Surface() : - m_allocator(nullptr) - { - init(); - } - /// Dtor - ~Surface(); - - /// Get the size of the row in bytes - static int calcRowSize(Format format, int width); - /// Calculates the number of rows - static int calcNumRows(Format format, int height); - - int m_width; - int m_height; - Format m_format; - - uint8_t* m_data; /// The data that makes up the image. If nullptr, has no data. Pointer to first 'row' of the image. - - int m_numRows; ///< Total amount of rows (typically same as height, but in compressed formats may be less) - int m_rowStrideInBytes; ///< The number of bytes between rows - - SurfaceAllocator* m_allocator; ///< Can be null if so contents is 'unowned', if set -}; - -} // renderer_test diff --git a/tools/slang-graphics/vk-api.cpp b/tools/slang-graphics/vk-api.cpp deleted file mode 100644 index 0ffbf46eb..000000000 --- a/tools/slang-graphics/vk-api.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// vk-api.cpp -#include "vk-api.h" - -#include "../../source/core/list.h" - -namespace slang_graphics { -using namespace Slang; - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanApi !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -#define VK_API_CHECK_FUNCTION(x) && (x != nullptr) -#define VK_API_CHECK_FUNCTIONS(FUNCTION_LIST) true FUNCTION_LIST(VK_API_CHECK_FUNCTION) - -bool VulkanApi::areDefined(ProcType type) const -{ - switch (type) - { - case ProcType::Global: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_GLOBAL_PROCS); - case ProcType::Instance: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_INSTANCE_PROCS); - case ProcType::Device: return VK_API_CHECK_FUNCTIONS(VK_API_ALL_DEVICE_PROCS); - default: - { - assert(!"Unhandled type"); - return false; - } - } -} - -Slang::Result VulkanApi::initGlobalProcs(const VulkanModule& module) -{ -#define VK_API_GET_GLOBAL_PROC(x) x = (PFN_##x)module.getFunction(#x); - - // Initialize all the global functions - VK_API_ALL_GLOBAL_PROCS(VK_API_GET_GLOBAL_PROC) - - if (!areDefined(ProcType::Global)) - { - return SLANG_FAIL; - } - m_module = &module; - return SLANG_OK; -} - -Slang::Result VulkanApi::initInstanceProcs(VkInstance instance) -{ - assert(instance && vkGetInstanceProcAddr != nullptr); - -#define VK_API_GET_INSTANCE_PROC(x) x = (PFN_##x)vkGetInstanceProcAddr(instance, #x); - - VK_API_ALL_INSTANCE_PROCS(VK_API_GET_INSTANCE_PROC) - - if (!areDefined(ProcType::Instance)) - { - return SLANG_FAIL; - } - - m_instance = instance; - return SLANG_OK; -} - -Slang::Result VulkanApi::initPhysicalDevice(VkPhysicalDevice physicalDevice) -{ - assert(m_physicalDevice == VK_NULL_HANDLE); - m_physicalDevice = physicalDevice; - - vkGetPhysicalDeviceProperties(m_physicalDevice, &m_deviceProperties); - vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_deviceFeatures); - vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_deviceMemoryProperties); - - return SLANG_OK; -} - -Slang::Result VulkanApi::initDeviceProcs(VkDevice device) -{ - assert(m_instance && device && vkGetDeviceProcAddr != nullptr); - -#define VK_API_GET_DEVICE_PROC(x) x = (PFN_##x)vkGetDeviceProcAddr(device, #x); - - VK_API_ALL_DEVICE_PROCS(VK_API_GET_DEVICE_PROC) - - if (!areDefined(ProcType::Device)) - { - return SLANG_FAIL; - } - - m_device = device; - return SLANG_OK; -} - -int VulkanApi::findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const -{ - assert(typeBits); - - const int numMemoryTypes = int(m_deviceMemoryProperties.memoryTypeCount); - - // bit holds current test bit against typeBits. Ie bit == 1 << typeBits - - uint32_t bit = 1; - for (int i = 0; i < numMemoryTypes; ++i, bit += bit) - { - auto const& memoryType = m_deviceMemoryProperties.memoryTypes[i]; - if ((typeBits & bit) && (memoryType.propertyFlags & properties) == properties) - { - return i; - } - } - - //assert(!"failed to find a usable memory type"); - return -1; -} - -int VulkanApi::findQueue(VkQueueFlags reqFlags) const -{ - assert(m_physicalDevice != VK_NULL_HANDLE); - - uint32_t numQueueFamilies = 0; - vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, nullptr); - - Slang::List queueFamilies; - queueFamilies.SetSize(numQueueFamilies); - vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &numQueueFamilies, queueFamilies.Buffer()); - - // Find a queue that can service our needs - //VkQueueFlags reqQueueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; - - int queueFamilyIndex = -1; - for (int i = 0; i < int(numQueueFamilies); ++i) - { - if ((queueFamilies[i].queueFlags & reqFlags) == reqFlags) - { - return i; - } - } - - return -1; -} - -} // renderer_test diff --git a/tools/slang-graphics/vk-api.h b/tools/slang-graphics/vk-api.h deleted file mode 100644 index 0cbd3faf7..000000000 --- a/tools/slang-graphics/vk-api.h +++ /dev/null @@ -1,196 +0,0 @@ -// vk-api.h -#pragma once - -#include "vk-module.h" - -namespace slang_graphics { - -#define VK_API_GLOBAL_PROCS(x) \ - x(vkGetInstanceProcAddr) \ - x(vkCreateInstance) \ - /* */ - -#define VK_API_INSTANCE_PROCS(x) \ - x(vkCreateDevice) \ - x(vkCreateDebugReportCallbackEXT) \ - x(vkDestroyDebugReportCallbackEXT) \ - x(vkDebugReportMessageEXT) \ - x(vkEnumeratePhysicalDevices) \ - x(vkGetPhysicalDeviceProperties) \ - x(vkGetPhysicalDeviceFeatures) \ - x(vkGetPhysicalDeviceMemoryProperties) \ - x(vkGetPhysicalDeviceQueueFamilyProperties) \ - x(vkGetPhysicalDeviceFormatProperties) \ - x(vkGetDeviceProcAddr) \ - /* */ - -#define VK_API_DEVICE_PROCS(x) \ - x(vkCreateDescriptorPool) \ - x(vkDestroyDescriptorPool) \ - x(vkGetDeviceQueue) \ - x(vkQueueSubmit) \ - x(vkQueueWaitIdle) \ - x(vkCreateBuffer) \ - x(vkAllocateMemory) \ - x(vkMapMemory) \ - x(vkUnmapMemory) \ - x(vkCmdCopyBuffer) \ - x(vkDestroyBuffer) \ - x(vkFreeMemory) \ - x(vkCreateDescriptorSetLayout) \ - x(vkDestroyDescriptorSetLayout) \ - x(vkAllocateDescriptorSets) \ - x(vkUpdateDescriptorSets) \ - x(vkCreatePipelineLayout) \ - x(vkDestroyPipelineLayout) \ - x(vkCreateComputePipelines) \ - x(vkCreateGraphicsPipelines) \ - x(vkDestroyPipeline) \ - x(vkCreateShaderModule) \ - x(vkDestroyShaderModule) \ - x(vkCreateFramebuffer) \ - x(vkDestroyFramebuffer) \ - x(vkCreateImage) \ - x(vkDestroyImage) \ - x(vkCreateImageView) \ - x(vkDestroyImageView) \ - x(vkCreateRenderPass) \ - x(vkDestroyRenderPass) \ - x(vkCreateCommandPool) \ - x(vkDestroyCommandPool) \ - x(vkCreateSampler) \ - x(vkDestroySampler) \ - x(vkCreateBufferView) \ - x(vkDestroyBufferView) \ - \ - x(vkGetBufferMemoryRequirements) \ - x(vkGetImageMemoryRequirements) \ - \ - x(vkCmdBindPipeline) \ - x(vkCmdBindDescriptorSets) \ - x(vkCmdDispatch) \ - x(vkCmdDraw) \ - x(vkCmdSetScissor) \ - x(vkCmdSetViewport) \ - x(vkCmdBindVertexBuffers) \ - x(vkCmdBindIndexBuffer) \ - x(vkCmdBeginRenderPass) \ - x(vkCmdEndRenderPass) \ - x(vkCmdPipelineBarrier) \ - x(vkCmdCopyBufferToImage)\ - \ - x(vkCreateFence) \ - x(vkDestroyFence) \ - x(vkResetFences) \ - x(vkGetFenceStatus) \ - x(vkWaitForFences) \ - \ - x(vkCreateSemaphore) \ - x(vkDestroySemaphore) \ - \ - x(vkCreateEvent) \ - x(vkDestroyEvent) \ - x(vkGetEventStatus) \ - x(vkSetEvent) \ - x(vkResetEvent) \ - \ - x(vkFreeCommandBuffers) \ - x(vkAllocateCommandBuffers) \ - x(vkBeginCommandBuffer) \ - x(vkEndCommandBuffer) \ - x(vkResetCommandBuffer) \ - \ - x(vkBindImageMemory) \ - x(vkBindBufferMemory) \ - /* */ - -#if SLANG_WINDOWS_FAMILY -# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ - x(vkCreateWin32SurfaceKHR) \ - /* */ -#else -# define VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ - x(vkCreateXlibSurfaceKHR) \ - /* */ -#endif - -#define VK_API_INSTANCE_KHR_PROCS(x) \ - VK_API_INSTANCE_PLATFORM_KHR_PROCS(x) \ - x(vkGetPhysicalDeviceSurfaceSupportKHR) \ - x(vkGetPhysicalDeviceSurfaceFormatsKHR) \ - x(vkGetPhysicalDeviceSurfacePresentModesKHR) \ - x(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ - x(vkDestroySurfaceKHR) \ - /* */ - -#define VK_API_DEVICE_KHR_PROCS(x) \ - x(vkQueuePresentKHR) \ - x(vkCreateSwapchainKHR) \ - x(vkGetSwapchainImagesKHR) \ - x(vkDestroySwapchainKHR) \ - x(vkAcquireNextImageKHR) \ - /* */ - -#define VK_API_ALL_GLOBAL_PROCS(x) \ - VK_API_GLOBAL_PROCS(x) - -#define VK_API_ALL_INSTANCE_PROCS(x) \ - VK_API_INSTANCE_PROCS(x) \ - VK_API_INSTANCE_KHR_PROCS(x) - -#define VK_API_ALL_DEVICE_PROCS(x) \ - VK_API_DEVICE_PROCS(x) \ - VK_API_DEVICE_KHR_PROCS(x) - -#define VK_API_ALL_PROCS(x) \ - VK_API_ALL_GLOBAL_PROCS(x) \ - VK_API_ALL_INSTANCE_PROCS(x) \ - VK_API_ALL_DEVICE_PROCS(x) \ - /* */ - -#define VK_API_DECLARE_PROC(NAME) PFN_##NAME NAME = nullptr; - -struct VulkanApi -{ - VK_API_ALL_PROCS(VK_API_DECLARE_PROC) - - enum class ProcType - { - Global, - Instance, - Device, - }; - - /// Returns true if all the functions in the class are defined - bool areDefined(ProcType type) const; - - /// Sets up global parameters - Slang::Result initGlobalProcs(const VulkanModule& module); - /// Initialize the instance functions - Slang::Result initInstanceProcs(VkInstance instance); - - /// Called before initDevice - Slang::Result initPhysicalDevice(VkPhysicalDevice physicalDevice); - - /// Initialize the device functions - Slang::Result initDeviceProcs(VkDevice device); - - /// Type bits control which indices are tested against bit 0 for testing at index 0 - /// properties - a memory type must have all the bits set as passed in - /// Returns -1 if couldn't find an appropriate memory type index - int findMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const; - - /// Given queue required flags, finds a queue - int findQueue(VkQueueFlags reqFlags) const; - - const VulkanModule* m_module = nullptr; ///< Module this was all loaded from - VkInstance m_instance = VK_NULL_HANDLE; - VkDevice m_device = VK_NULL_HANDLE; - VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; - - VkPhysicalDeviceProperties m_deviceProperties; - VkPhysicalDeviceFeatures m_deviceFeatures; - VkPhysicalDeviceMemoryProperties m_deviceMemoryProperties; -}; - -} // renderer_test diff --git a/tools/slang-graphics/vk-device-queue.cpp b/tools/slang-graphics/vk-device-queue.cpp deleted file mode 100644 index 9e978117f..000000000 --- a/tools/slang-graphics/vk-device-queue.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// vk-device-queue.cpp -#include "vk-device-queue.h" - -#include -#include -#include - -namespace slang_graphics { -using namespace Slang; - -VulkanDeviceQueue::~VulkanDeviceQueue() -{ - for (int i = 0; i < int(EventType::CountOf); ++i) - { - m_api->vkDestroySemaphore(m_api->m_device, m_semaphores[i], nullptr); - } - - for (int i = 0; i < m_numCommandBuffers; i++) - { - m_api->vkFreeCommandBuffers(m_api->m_device, m_commandPool, 1, &m_commandBuffers[i]); - m_api->vkDestroyFence(m_api->m_device, m_fences[i].fence, nullptr); - } - m_api->vkDestroyCommandPool(m_api->m_device, m_commandPool, nullptr); -} - -SlangResult VulkanDeviceQueue::init(const VulkanApi& api, VkQueue queue, int queueIndex) -{ - assert(m_api == nullptr); - m_api = &api; - - for (int i = 0; i < int(EventType::CountOf); ++i) - { - m_semaphores[i] = VK_NULL_HANDLE; - m_currentSemaphores[i] = VK_NULL_HANDLE; - } - - m_numCommandBuffers = kMaxCommandBuffers; - m_queueIndex = queueIndex; - - m_queue = queue; - - VkCommandPoolCreateInfo poolCreateInfo = {}; - poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - - poolCreateInfo.queueFamilyIndex = queueIndex; - - api.vkCreateCommandPool(api.m_device, &poolCreateInfo, nullptr, &m_commandPool); - - VkCommandBufferAllocateInfo commandInfo = {}; - commandInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - commandInfo.commandPool = m_commandPool; - commandInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - commandInfo.commandBufferCount = 1; - - VkFenceCreateInfo fenceCreateInfo = {}; - fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceCreateInfo.flags = 0; // VK_FENCE_CREATE_SIGNALED_BIT; - - for (int i = 0; i < m_numCommandBuffers; i++) - { - Fence& fence = m_fences[i]; - - api.vkAllocateCommandBuffers(api.m_device, &commandInfo, &m_commandBuffers[i]); - - api.vkCreateFence(api.m_device, &fenceCreateInfo, nullptr, &fence.fence); - fence.active = false; - fence.value = 0; - } - - VkSemaphoreCreateInfo semaphoreCreateInfo = {}; - semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - for (int i = 0; i < int(EventType::CountOf); ++i) - { - api.vkCreateSemaphore(api.m_device, &semaphoreCreateInfo, nullptr, &m_semaphores[i]); - } - - // Second step of flush to prime command buffer - flushStepB(); - - return SLANG_OK; -} - -void VulkanDeviceQueue::flushStepA() -{ - m_api->vkEndCommandBuffer(m_commandBuffer); - - VkPipelineStageFlags stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - // Wait semaphores - if (isCurrent(EventType::BeginFrame)) - { - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &m_currentSemaphores[int(EventType::BeginFrame)]; - } - - submitInfo.pWaitDstStageMask = &stageFlags; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &m_commandBuffer; - - // Signal semaphores - if (isCurrent(EventType::EndFrame)) - { - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &m_currentSemaphores[int(EventType::EndFrame)]; - } - - Fence& fence = m_fences[m_commandBufferIndex]; - - m_api->vkQueueSubmit(m_queue, 1, &submitInfo, fence.fence); - - // mark signaled fence value - fence.value = m_nextFenceValue; - fence.active = true; - - // increment fence value - m_nextFenceValue++; - - // No longer waiting on this semaphore - makeCompleted(EventType::BeginFrame); -} - -void VulkanDeviceQueue::_updateFenceAtIndex( int fenceIndex, bool blocking) -{ - Fence& fence = m_fences[fenceIndex]; - - if (fence.active) - { - uint64_t timeout = blocking ? ~uint64_t(0) : 0; - - if (VK_SUCCESS == m_api->vkWaitForFences(m_api->m_device, 1, &fence.fence, VK_TRUE, timeout)) - { - m_api->vkResetFences(m_api->m_device, 1, &fence.fence); - - fence.active = false; - - if (fence.value > m_lastFenceCompleted) - { - m_lastFenceCompleted = fence.value; - } - } - } -} - -void VulkanDeviceQueue::flushStepB() -{ - m_commandBufferIndex = (m_commandBufferIndex + 1) % m_numCommandBuffers; - m_commandBuffer = m_commandBuffers[m_commandBufferIndex]; - - // non-blocking update of fence values - for (int i = 0; i < m_numCommandBuffers; ++i) - { - _updateFenceAtIndex(i, false); - } - - // blocking update of fence values - _updateFenceAtIndex(m_commandBufferIndex, true); - - m_api->vkResetCommandBuffer(m_commandBuffer, 0); - - //m_api.vkResetCommandPool(m_api->m_device, m_commandPool, 0); - - VkCommandBufferBeginInfo beginInfo = {}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - m_api->vkBeginCommandBuffer(m_commandBuffer, &beginInfo); -} - -void VulkanDeviceQueue::flush() -{ - flushStepA(); - flushStepB(); -} - -void VulkanDeviceQueue::flushAndWait() -{ - flush(); - waitForIdle(); -} - -VkSemaphore VulkanDeviceQueue::makeCurrent(EventType eventType) -{ - assert(!isCurrent(eventType)); - VkSemaphore semaphore = m_semaphores[int(eventType)]; - m_currentSemaphores[int(eventType)] = semaphore; - return semaphore; -} - -void VulkanDeviceQueue::makeCompleted(EventType eventType) -{ - m_currentSemaphores[int(eventType)] = VK_NULL_HANDLE; -} - -} // renderer_test diff --git a/tools/slang-graphics/vk-device-queue.h b/tools/slang-graphics/vk-device-queue.h deleted file mode 100644 index 01ed16f5d..000000000 --- a/tools/slang-graphics/vk-device-queue.h +++ /dev/null @@ -1,94 +0,0 @@ -// vk-swap-chain.h -#pragma once - -#include "vk-api.h" - -namespace slang_graphics { - -struct VulkanDeviceQueue -{ - enum - { - kMaxCommandBuffers = 8, - }; - - enum class EventType - { - BeginFrame, - EndFrame, - CountOf, - }; - - /// Initialize - must be called before anything else can be done - SlangResult init(const VulkanApi& api, VkQueue queue, int queueIndex); - - /// Flushes the current command list, and steps to next (internally this is equivalent to a stepA followed by stepB) - void flush(); - /// Performs a full flush, and then waits for idle. - void flushAndWait(); - - /// Blocks until all work submitted to GPU has completed - void waitForIdle() { m_api->vkQueueWaitIdle(m_queue); } - - /// Get the graphics queue index (as set on init) - int getQueueIndex() const { return m_queueIndex; } - - /// Make the specified event 'current' - meaning it's semaphore must be waited on - VkSemaphore makeCurrent(EventType eventType); - /// Makes the event no longer required to be waited on - void makeCompleted(EventType eventType); - /// Returns true if the event is already current - SLANG_FORCE_INLINE bool isCurrent(EventType eventType) const { return m_currentSemaphores[int(eventType)] != VK_NULL_HANDLE; } - - /// Get the command buffer - VkCommandBuffer getCommandBuffer() const { return m_commandBuffer; } - - /// Get the queue - VkQueue getQueue() const { return m_queue; } - - /// Get the API - const VulkanApi* getApi() const { return m_api; } - - /// Flushes the current command list - void flushStepA(); - /// Steps to next command buffer and opens. May block if command buffer is still in use - void flushStepB(); - - /// Dtor - ~VulkanDeviceQueue(); - - protected: - - struct Fence - { - VkFence fence; - bool active; - uint64_t value; - }; - - void _updateFenceAtIndex(int fenceIndex, bool blocking); - - VkQueue m_queue = VK_NULL_HANDLE; - - VkCommandPool m_commandPool = VK_NULL_HANDLE; - int m_numCommandBuffers = 0; - int m_commandBufferIndex = 0; - // There are the same amount of command buffers as fences - VkCommandBuffer m_commandBuffers[kMaxCommandBuffers] = { VK_NULL_HANDLE }; - - Fence m_fences[kMaxCommandBuffers] = { {VK_NULL_HANDLE, 0, 0u} }; - - VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE; - - VkSemaphore m_semaphores[int(EventType::CountOf)]; - VkSemaphore m_currentSemaphores[int(EventType::CountOf)]; - - uint64_t m_lastFenceCompleted = 1; - uint64_t m_nextFenceValue = 2; - - int m_queueIndex = 0; - - const VulkanApi* m_api = nullptr; -}; - -} // renderer_test diff --git a/tools/slang-graphics/vk-module.cpp b/tools/slang-graphics/vk-module.cpp deleted file mode 100644 index 460e6550c..000000000 --- a/tools/slang-graphics/vk-module.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// module.cpp -#include "vk-module.h" - -#include -#include -#include - -#if SLANG_WINDOWS_FAMILY -# include -#else -# include -#endif - -namespace slang_graphics { -using namespace Slang; - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VulkanModule !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -Slang::Result VulkanModule::init() -{ - if (isInitialized()) - { - destroy(); - return SLANG_OK; - } - - const char* dynamicLibraryName = "Unknown"; - -#if SLANG_WINDOWS_FAMILY - dynamicLibraryName = "vulkan-1.dll"; - HMODULE module = ::LoadLibraryA(dynamicLibraryName); - m_module = (void*)module; -#else - dynamicLibraryName = "libvulkan.so.1"; - m_module = dlopen(dynamicLibraryName, RTLD_NOW); -#endif - - if (!m_module) - { - fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); - return SLANG_FAIL; - } - - return SLANG_OK; -} - -PFN_vkVoidFunction VulkanModule::getFunction(const char* name) const -{ - assert(m_module); - if (!m_module) - { - return nullptr; - } -#if SLANG_WINDOWS_FAMILY - return (PFN_vkVoidFunction)::GetProcAddress((HMODULE)m_module, name); -#else - return (PFN_vkVoidFunction)dlsym(m_module, name); -#endif -} - -void VulkanModule::destroy() -{ - if (!isInitialized()) - { - return; - } - -#if SLANG_WINDOWS_FAMILY - ::FreeLibrary((HMODULE)m_module); -#else - dlclose(m_module); -#endif - m_module = nullptr; -} - -} // renderer_test diff --git a/tools/slang-graphics/vk-module.h b/tools/slang-graphics/vk-module.h deleted file mode 100644 index c72334db5..000000000 --- a/tools/slang-graphics/vk-module.h +++ /dev/null @@ -1,39 +0,0 @@ -// vk-module.h -#pragma once - -#include "../../slang.h" - -#include "../../slang-com-helper.h" - -#if SLANG_WINDOWS_FAMILY -# define VK_USE_PLATFORM_WIN32_KHR 1 -#else -# define VK_USE_PLATFORM_XLIB_KHR 1 -#endif - -#define VK_NO_PROTOTYPES -#include - -namespace slang_graphics { - -struct VulkanModule -{ - /// true if has been initialized - SLANG_FORCE_INLINE bool isInitialized() const { return m_module != nullptr; } - - /// Get a function by name - PFN_vkVoidFunction getFunction(const char* name) const; - - /// Initialize - Slang::Result init(); - /// Destroy - void destroy(); - - /// Dtor - ~VulkanModule() { destroy(); } - - protected: - void* m_module = nullptr; -}; - -} // renderer_test diff --git a/tools/slang-graphics/vk-swap-chain.cpp b/tools/slang-graphics/vk-swap-chain.cpp deleted file mode 100644 index a6704e9d7..000000000 --- a/tools/slang-graphics/vk-swap-chain.cpp +++ /dev/null @@ -1,421 +0,0 @@ -// vk-swap-chain.cpp -#include "vk-swap-chain.h" - -#include "vk-util.h" - -#include "../../source/core/list.h" - -#include -#include - -namespace slang_graphics { -using namespace Slang; - -static int _indexOf(List& formatsIn, VkFormat format) -{ - const int numFormats = int(formatsIn.Count()); - const VkSurfaceFormatKHR* formats = formatsIn.Buffer(); - - for (int i = 0; i < numFormats; ++i) - { - if (formats[i].format == format) - { - return i; - } - } - return -1; -} - -SlangResult VulkanSwapChain::init(VulkanDeviceQueue* deviceQueue, const Desc& descIn, const PlatformDesc* platformDescIn) -{ - assert(platformDescIn); - - m_deviceQueue = deviceQueue; - m_api = deviceQueue->getApi(); - - // Make sure it's not set initially - m_format = VK_FORMAT_UNDEFINED; - - Desc desc(descIn); - -#if SLANG_WINDOWS_FAMILY - const WinPlatformDesc* platformDesc = static_cast(platformDescIn); - _setPlatformDesc(*platformDesc); - - VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.hinstance = platformDesc->m_hinstance; - surfaceCreateInfo.hwnd = platformDesc->m_hwnd; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateWin32SurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); -#else - const XPlatformDesc* platformDesc = static_cast(platformDescIn); - _setPlatformDesc(*platformDesc); - - VkXlibSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.dpy = platformDesc->m_display; - surfaceCreateInfo.window = platformDesc->m_window; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateXlibSurfaceKHR(m_api->m_instance, &surfaceCreateInfo, nullptr, &m_surface)); -#endif - - VkBool32 supported = false; - m_api->vkGetPhysicalDeviceSurfaceSupportKHR(m_api->m_physicalDevice, deviceQueue->getQueueIndex(), m_surface, &supported); - - uint32_t numSurfaceFormats = 0; - List surfaceFormats; - m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, nullptr); - surfaceFormats.SetSize(int(numSurfaceFormats)); - m_api->vkGetPhysicalDeviceSurfaceFormatsKHR(m_api->m_physicalDevice, m_surface, &numSurfaceFormats, surfaceFormats.Buffer()); - - // Look for a suitable format - List formats; - formats.Add(VulkanUtil::getVkFormat(desc.m_format)); - // HACK! To check for a different format if couldn't be found - if (descIn.m_format == Format::RGBA_Unorm_UInt8) - { - formats.Add(VK_FORMAT_B8G8R8A8_UNORM); - } - - for(int i = 0; i < int(formats.Count()); ++i) - { - VkFormat format = formats[i]; - if (_indexOf(surfaceFormats, format) >= 0) - { - m_format = format; - } - } - - if (m_format == VK_FORMAT_UNDEFINED) - { - return SLANG_FAIL; - } - - // Save the desc - m_desc = desc; - - SLANG_RETURN_ON_FAIL(_createSwapChain()); - - m_desc = desc; - return SLANG_OK; -} - -void VulkanSwapChain::getWindowSize(int* widthOut, int* heightOut) const -{ -#if SLANG_WINDOWS_FAMILY - auto platformDesc = _getPlatformDesc(); - - RECT rc; - ::GetClientRect(platformDesc->m_hwnd, &rc); - *widthOut = rc.right - rc.left; - *heightOut = rc.bottom - rc.top; -#else - auto platformDesc = _getPlatformDesc(); - - XWindowAttributes winAttr = {}; - XGetWindowAttributes(platformDesc->m_display, platformDesc->m_window, &winAttr); - - *widthOut = winAttr.width; - *heightOut = winAttr.height; -#endif -} - -SlangResult VulkanSwapChain::_createFrameBuffers(VkRenderPass renderPass) -{ - assert(renderPass != VK_NULL_HANDLE); - - for (int i = 0; i < int(m_images.Count()); ++i) - { - Image& image = m_images[i]; - VkImageView attachments[] = - { - image.m_imageView - }; - - VkFramebufferCreateInfo framebufferInfo = {}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = m_width; - framebufferInfo.height = m_height; - framebufferInfo.layers = 1; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateFramebuffer(m_api->m_device, &framebufferInfo, nullptr, &image.m_frameBuffer)); - } - - return SLANG_OK; -} - -void VulkanSwapChain::_destroyFrameBuffers() -{ - for (int i = 0; i < int(m_images.Count()); ++i) - { - Image& image = m_images[i]; - if (image.m_frameBuffer != VK_NULL_HANDLE) - { - m_api->vkDestroyFramebuffer(m_api->m_device, image.m_frameBuffer, nullptr); - image.m_frameBuffer = VK_NULL_HANDLE; - } - } -} - -SlangResult VulkanSwapChain::createFrameBuffers(VkRenderPass renderPass) -{ - if (m_renderPass != VK_NULL_HANDLE) - { - _destroyFrameBuffers(); - m_renderPass = VK_NULL_HANDLE; - } - if (renderPass != VK_NULL_HANDLE) - { - SLANG_RETURN_ON_FAIL(_createFrameBuffers(renderPass)); - } - m_renderPass = renderPass; - return SLANG_OK; -} - -SlangResult VulkanSwapChain::_createSwapChain() -{ - if (hasValidSwapChain()) - { - return SLANG_OK; - } - - int width, height; - getWindowSize(&width, &height); - - VkExtent2D imageExtent = {}; - imageExtent.width = width; - imageExtent.height = height; - - m_width = width; - m_height = height; - - // catch this before throwing error - if (m_width == 0 || m_height == 0) - { - return SLANG_FAIL; - } - - // It is necessary to query the caps -> otherwise the LunarG verification layer will issue an error - { - VkSurfaceCapabilitiesKHR surfaceCaps; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_api->m_physicalDevice, m_surface, &surfaceCaps)); - } - - List presentModes; - uint32_t numPresentModes = 0; - m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, nullptr); - presentModes.SetSize(numPresentModes); - m_api->vkGetPhysicalDeviceSurfacePresentModesKHR(m_api->m_physicalDevice, m_surface, &numPresentModes, presentModes.Buffer()); - - { - int numCheckPresentOptions = 3; - VkPresentModeKHR presentOptions[] = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR }; - if (m_vsync) - { - presentOptions[0] = VK_PRESENT_MODE_FIFO_KHR; - presentOptions[1] = VK_PRESENT_MODE_IMMEDIATE_KHR; - presentOptions[2] = VK_PRESENT_MODE_MAILBOX_KHR; - } - - m_presentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; // Invalid - - // Find the first option that's available on the device - for (int j = 0; j < numCheckPresentOptions; j++) - { - if (presentModes.IndexOf(presentOptions[j]) != UInt(-1)) - { - m_presentMode = presentOptions[j]; - break; - } - } - - if (m_presentMode == VK_PRESENT_MODE_MAX_ENUM_KHR) - { - return SLANG_FAIL; - } - } - - VkSwapchainKHR oldSwapchain = VK_NULL_HANDLE; - - VkSwapchainCreateInfoKHR swapchainDesc = {}; - swapchainDesc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swapchainDesc.surface = m_surface; - swapchainDesc.minImageCount = 3; - swapchainDesc.imageFormat = m_format; - swapchainDesc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; - swapchainDesc.imageExtent = imageExtent; - swapchainDesc.imageArrayLayers = 1; - swapchainDesc.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - swapchainDesc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swapchainDesc.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - swapchainDesc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swapchainDesc.presentMode = m_presentMode; - swapchainDesc.clipped = VK_TRUE; - swapchainDesc.oldSwapchain = oldSwapchain; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateSwapchainKHR(m_api->m_device, &swapchainDesc, nullptr, &m_swapChain)); - - uint32_t numSwapChainImages = 0; - m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, nullptr); - - { - List images; - images.SetSize(numSwapChainImages); - - m_api->vkGetSwapchainImagesKHR(m_api->m_device, m_swapChain, &numSwapChainImages, images.Buffer()); - - m_images.SetSize(numSwapChainImages); - for (int i = 0; i < int(numSwapChainImages); ++i) - { - Image& dstImage = m_images[i]; - dstImage.m_image = images[i]; - - } - } - - { - VkImageViewCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = m_format; - - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - for (int i = 0; i < int(numSwapChainImages); ++i) - { - Image& image = m_images[i]; - - createInfo.image = image.m_image; - - SLANG_VK_RETURN_ON_FAIL(m_api->vkCreateImageView(m_api->m_device, &createInfo, nullptr, &image.m_imageView)); - } - } - - if (m_renderPass != VK_NULL_HANDLE) - { - _createFrameBuffers(m_renderPass); - } - - return SLANG_OK; -} - -void VulkanSwapChain::_destroySwapChain() -{ - if (!hasValidSwapChain()) - { - return; - } - - m_deviceQueue->waitForIdle(); - - if (m_renderPass != VK_NULL_HANDLE) - { - _destroyFrameBuffers(); - } - - for (int i = 0; i < int(m_images.Count()); ++i) - { - Image& image = m_images[i]; - - if (image.m_imageView != VK_NULL_HANDLE) - { - m_api->vkDestroyImageView(m_api->m_device, image.m_imageView, nullptr); - } - } - - if (m_swapChain != VK_NULL_HANDLE) - { - m_api->vkDestroySwapchainKHR(m_api->m_device, m_swapChain, nullptr); - m_swapChain = VK_NULL_HANDLE; - } - - // Mark that it is no longer used - m_images.Clear(); -} - -VulkanSwapChain::~VulkanSwapChain() -{ - _destroySwapChain(); - - if (m_surface) - { - m_api->vkDestroySurfaceKHR(m_api->m_instance, m_surface, nullptr); - m_surface = VK_NULL_HANDLE; - } -} - -int VulkanSwapChain::nextFrontImageIndex() -{ - if (!hasValidSwapChain()) - { - if (SLANG_FAILED(_createSwapChain())) - { - return -1; - } - } - - VkSemaphore beginFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::BeginFrame); - - uint32_t swapChainIndex = 0; - VkResult result = m_api->vkAcquireNextImageKHR(m_api->m_device, m_swapChain, UINT64_MAX, beginFrameSemaphore, VK_NULL_HANDLE, &swapChainIndex); - - if (result != VK_SUCCESS) - { - _destroySwapChain(); - return -1; - } - m_currentSwapChainIndex = int(swapChainIndex); - return swapChainIndex; -} - -void VulkanSwapChain::present(bool vsync) -{ - if (!hasValidSwapChain()) - { - m_deviceQueue->flush(); - return; - } - - VkSemaphore endFrameSemaphore = m_deviceQueue->makeCurrent(VulkanDeviceQueue::EventType::EndFrame); - - m_deviceQueue->flushStepA(); - - uint32_t swapChainIndices[] = { uint32_t(m_currentSwapChainIndex) }; - - VkPresentInfoKHR presentInfo = {}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &m_swapChain; - presentInfo.pImageIndices = swapChainIndices; - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = &endFrameSemaphore; - - VkResult result = m_api->vkQueuePresentKHR(m_deviceQueue->getQueue(), &presentInfo); - - m_deviceQueue->makeCompleted(VulkanDeviceQueue::EventType::EndFrame); - - m_deviceQueue->flushStepB(); - - if (result != VK_SUCCESS || m_vsync != vsync) - { - m_vsync = vsync; - _destroySwapChain(); - } -} - -} // renderer_test diff --git a/tools/slang-graphics/vk-swap-chain.h b/tools/slang-graphics/vk-swap-chain.h deleted file mode 100644 index 7c04af70c..000000000 --- a/tools/slang-graphics/vk-swap-chain.h +++ /dev/null @@ -1,141 +0,0 @@ -// vk-swap-chain.h -#pragma once - -#include "vk-api.h" -#include "vk-device-queue.h" - -#include "render.h" - -#include "../../source/core/list.h" - -namespace slang_graphics { - -struct VulkanSwapChain -{ - /* enum - { - kMaxImages = 8, - }; */ - - /// Base class for platform specific information - struct PlatformDesc - { - }; - -#if SLANG_WINDOWS_FAMILY - struct WinPlatformDesc: public PlatformDesc - { - HINSTANCE m_hinstance; - HWND m_hwnd; - }; -#else - struct XPlatformDesc : public PlatformDesc - { - Display* m_display; - Window m_window; - }; -#endif - - struct Desc - { - void init() - { - m_format = Format::Unknown; - m_depthFormatTypeless = Format::Unknown; - m_depthFormat = Format::Unknown; - m_textureDepthFormat = Format::Unknown; - } - - Format m_format; - //bool m_enableFormat; - Format m_depthFormatTypeless; - Format m_depthFormat; - Format m_textureDepthFormat; - }; - - struct Image - { - VkImage m_image = VK_NULL_HANDLE; - VkImageView m_imageView = VK_NULL_HANDLE; - VkFramebuffer m_frameBuffer = VK_NULL_HANDLE; - }; - - - /// Must be called before the swap chain can be used - SlangResult init(VulkanDeviceQueue* deviceQueue, const Desc& desc, const PlatformDesc* platformDesc); - - /// Create the frame buffers (they must be compatible with the supplied renderPass) - SlangResult createFrameBuffers(VkRenderPass renderPass); - - /// Returned the desc used to construct the swap chain. - /// Is invalid if init hasn't returned with successful result. - const Desc& getDesc() const { return m_desc; } - - /// True if the swap chain is available - bool hasValidSwapChain() const { return m_images.Count() > 0; } - - /// Present to the display - void present(bool vsync); - - /// Get the current size of the window (in pixels written to widthOut, heightOut) - void getWindowSize(int* widthOut, int* heightOut) const; - - /// Get the VkFormat for the back buffer - VkFormat getVkFormat() const { return m_format; } - - /// Get width of the back buffers - int getWidth() const { return m_width; } - /// Get the height of the back buffer - int getHeight() const { return m_height; } - - /// Get the detail about the images - const Slang::List& getImages() const { return m_images; } - - /// Get the next front render image index. Returns -1, if image couldn't be found - int nextFrontImageIndex(); - - /// Dtor - ~VulkanSwapChain(); - - protected: - - - template - void _setPlatformDesc(const T& desc) - { - const PlatformDesc* check = &desc; - int size = (sizeof(T) + sizeof(void*) - 1) / sizeof(void*); - m_platformDescBuffer.SetSize(size); - *(T*)m_platformDescBuffer.Buffer() = desc; - } - template - const T* _getPlatformDesc() const { return static_cast((const PlatformDesc*)m_platformDescBuffer.Buffer()); } - SlangResult _createSwapChain(); - void _destroySwapChain(); - SlangResult _createFrameBuffers(VkRenderPass renderPass); - void _destroyFrameBuffers(); - - bool m_vsync = true; - int m_width = 0; - int m_height = 0; - - VkPresentModeKHR m_presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; - VkFormat m_format = VK_FORMAT_UNDEFINED; ///< The format used for backbuffer. Valid after successful init. - - VkSurfaceKHR m_surface = VK_NULL_HANDLE; - VkSwapchainKHR m_swapChain = VK_NULL_HANDLE; - - VkRenderPass m_renderPass = VK_NULL_HANDLE; //< Not owned - - int m_currentSwapChainIndex = 0; - - Slang::List m_images; - - VulkanDeviceQueue* m_deviceQueue = nullptr; - const VulkanApi* m_api = nullptr; - - Desc m_desc; ///< The desc used to init this swap chain - Slang::List m_platformDescBuffer; ///< Buffer to hold the platform specific description parameters (as passed in platformDesc) -}; - -} // renderer_test diff --git a/tools/slang-graphics/vk-util.cpp b/tools/slang-graphics/vk-util.cpp deleted file mode 100644 index 374a3876d..000000000 --- a/tools/slang-graphics/vk-util.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// vk-util.cpp -#include "vk-util.h" - -#include -#include - -namespace slang_graphics { - -/* static */VkFormat VulkanUtil::getVkFormat(Format format) -{ - switch (format) - { - case Format::RGBA_Float32: return VK_FORMAT_R32G32B32A32_SFLOAT; - case Format::RGB_Float32: return VK_FORMAT_R32G32B32_SFLOAT; - case Format::RG_Float32: return VK_FORMAT_R32G32_SFLOAT; - case Format::R_Float32: return VK_FORMAT_R32_SFLOAT; - case Format::RGBA_Unorm_UInt8: return VK_FORMAT_R8G8B8A8_UNORM; - case Format::R_UInt32: return VK_FORMAT_R32_UINT; - - case Format::D_Float32: return VK_FORMAT_D32_SFLOAT; - case Format::D_Unorm24_S8: return VK_FORMAT_D24_UNORM_S8_UINT; - - default: return VK_FORMAT_UNDEFINED; - } -} - -/* static */SlangResult VulkanUtil::toSlangResult(VkResult res) -{ - return (res == VK_SUCCESS) ? SLANG_OK : SLANG_FAIL; -} - -/* static */Slang::Result VulkanUtil::handleFail(VkResult res) -{ - if (res != VK_SUCCESS) - { - assert(!"Vulkan returned a failure"); - } - return toSlangResult(res); -} - -/* static */void VulkanUtil::checkFail(VkResult res) -{ - assert(res != VK_SUCCESS); - assert(!"Vulkan check failed"); - -} - -/* static */VkPrimitiveTopology VulkanUtil::getVkPrimitiveTopology(PrimitiveTopology topology) -{ - switch (topology) - { - case PrimitiveTopology::TriangleList: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - default: break; - } - assert(!"Unknown topology"); - return VK_PRIMITIVE_TOPOLOGY_MAX_ENUM; -} - -} // renderer_test diff --git a/tools/slang-graphics/vk-util.h b/tools/slang-graphics/vk-util.h deleted file mode 100644 index 420c0a57a..000000000 --- a/tools/slang-graphics/vk-util.h +++ /dev/null @@ -1,41 +0,0 @@ -// vk-util.h -#pragma once - -#include "vk-api.h" -#include "render.h" - -// Macros to make testing vulkan return codes simpler - -/// SLANG_VK_RETURN_ON_FAIL can be used in a similar way to SLANG_RETURN_ON_FAIL macro, except it will turn a vulkan failure into Slang::Result in the process -/// Calls handleFail which on debug builds asserts -#define SLANG_VK_RETURN_ON_FAIL(x) { VkResult _res = x; if (_res != VK_SUCCESS) { return VulkanUtil::handleFail(_res); } } - -#define SLANG_VK_RETURN_NULL_ON_FAIL(x) { VkResult _res = x; if (_res != VK_SUCCESS) { VulkanUtil::handleFail(_res); return nullptr; } } - -/// Is similar to SLANG_VK_RETURN_ON_FAIL, but does not return. Will call checkFail on failure - which asserts on debug builds. -#define SLANG_VK_CHECK(x) { VkResult _res = x; if (_res != VK_SUCCESS) { VulkanUtil::checkFail(_res); } } - -namespace slang_graphics { - -// Utility functions for Vulkan -struct VulkanUtil -{ - /// Get the equivalent VkFormat from the format - /// Returns VK_FORMAT_UNDEFINED if a match is not found - static VkFormat getVkFormat(Format format); - - /// Called by SLANG_VK_RETURN_FAIL if a res is a failure. - /// On debug builds this will cause an assertion on failure. - static Slang::Result handleFail(VkResult res); - /// Called when a failure has occurred with SLANG_VK_CHECK - will typically assert. - static void checkFail(VkResult res); - - /// Get the VkPrimitiveTopology for the given topology. - /// Returns VK_PRIMITIVE_TOPOLOGY_MAX_ENUM on failure - static VkPrimitiveTopology getVkPrimitiveTopology(PrimitiveTopology topology); - - /// Returns Slang::Result equivalent of a VkResult - static Slang::Result toSlangResult(VkResult res); -}; - -} // renderer_test diff --git a/tools/slang-graphics/window.cpp b/tools/slang-graphics/window.cpp deleted file mode 100644 index 7aef88c12..000000000 --- a/tools/slang-graphics/window.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// window.cpp -#include "window.h" -#pragma once - -#include - -#ifdef _MSC_VER -#include -#if (_MSC_VER < 1900) -#define snprintf sprintf_s -#endif -#endif - - -#if _WIN32 -#include -#else -#error "The slang-graphics library currently only supports Windows platforms" -#endif - -namespace slang_graphics { - -#if _WIN32 - -struct OSString -{ - OSString(char const* begin, char const* end) - { - _initialize(begin, end - begin); - } - - OSString(char const* begin) - { - _initialize(begin, strlen(begin)); - } - - ~OSString() - { - free(mBegin); - } - - operator WCHAR const*() - { - return mBegin; - } - -private: - WCHAR* mBegin; - WCHAR* mEnd; - - void _initialize(char const* input, size_t inputSize) - { - const DWORD dwFlags = 0; - int outputCodeUnitCount = ::MultiByteToWideChar(CP_UTF8, dwFlags, input, int(inputSize), nullptr, 0); - - WCHAR* buffer = (WCHAR*)malloc(sizeof(WCHAR) * (outputCodeUnitCount + 1)); - - ::MultiByteToWideChar(CP_UTF8, dwFlags, input, int(inputSize), buffer, outputCodeUnitCount); - buffer[outputCodeUnitCount] = 0; - - mBegin = buffer; - mEnd = buffer + outputCodeUnitCount; - } -}; - -struct ApplicationContext -{ - HINSTANCE instance; - int showCommand = SW_SHOWDEFAULT; - int resultCode = 0; -}; - -/// Run an application given the specified callback and command-line arguments. -int runApplication( - ApplicationFunc func, - int argc, - char const* const* argv) -{ - ApplicationContext context; - context.instance = (HINSTANCE) GetModuleHandle(0); - func(&context); - return context.resultCode; -} - -int runWindowsApplication( - ApplicationFunc func, - void* instance, - int showCommand) -{ - ApplicationContext context; - context.instance = (HINSTANCE) instance; - context.showCommand = showCommand; - func(&context); - return context.resultCode; -} - -struct Window -{ - HWND handle; -}; - -static LRESULT CALLBACK windowProc( - HWND windowHandle, - UINT message, - WPARAM wParam, - LPARAM lParam) -{ - // TODO: Actually implement some reasonable logic here. - switch (message) - { - case WM_CLOSE: - PostQuitMessage(0); - return 0; - } - - return DefWindowProcW(windowHandle, message, wParam, lParam); -} - - -static ATOM createWindowClassAtom() -{ - WNDCLASSEXW windowClassDesc; - windowClassDesc.cbSize = sizeof(windowClassDesc); - windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; - windowClassDesc.lpfnWndProc = &windowProc; - windowClassDesc.cbClsExtra = 0; - windowClassDesc.cbWndExtra = 0; - windowClassDesc.hInstance = (HINSTANCE) GetModuleHandle(0); - windowClassDesc.hIcon = 0; - windowClassDesc.hCursor = 0; - windowClassDesc.hbrBackground = 0; - windowClassDesc.lpszMenuName = 0; - windowClassDesc.lpszClassName = L"SlangGraphicsWindow"; - windowClassDesc.hIconSm = 0; - ATOM windowClassAtom = RegisterClassExW(&windowClassDesc); - return windowClassAtom; -} - -static ATOM getWindowClassAtom() -{ - static ATOM windowClassAtom = createWindowClassAtom(); - return windowClassAtom; -} - -Window* createWindow(WindowDesc const& desc) -{ - Window* window = new Window(); - - OSString windowTitle(desc.title); - - DWORD windowExtendedStyle = 0; - DWORD windowStyle = 0; - - HINSTANCE instance = (HINSTANCE) GetModuleHandle(0); - - HWND windowHandle = CreateWindowExW( - windowExtendedStyle, - (LPWSTR) getWindowClassAtom(), - windowTitle, - windowStyle, - 0, 0, // x, y - desc.width, desc.height, - NULL, // parent - NULL, // menu - instance, - window); - - if(!windowHandle) - { - delete window; - return nullptr; - } - - window->handle = windowHandle; - return window; -} - -void showWindow(Window* window) -{ - ShowWindow(window->handle, SW_SHOW); -} - -void* getPlatformWindowHandle(Window* window) -{ - return window->handle; -} - -bool dispatchEvents(ApplicationContext* context) -{ - for(;;) - { - MSG message; - - int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); - if (result != 0) - { - if (message.message == WM_QUIT) - { - context->resultCode = (int)message.wParam; - return false; - } - - TranslateMessage(&message); - DispatchMessageW(&message); - } - else - { - return true; - } - } - -} - -void exitApplication(ApplicationContext* context, int resultCode) -{ - ExitProcess(resultCode); -} - -int reportError(char const* message, ...) -{ - va_list args; - va_start(args, message); - - static const int kBufferSize = 1024; - char messageBuffer[kBufferSize]; - vsnprintf(messageBuffer, kBufferSize - 1, message, args); - messageBuffer[kBufferSize - 1] = 0; - - va_end(args); - - fputs(messageBuffer, stderr); - - OSString wideMessageBuffer(messageBuffer); - OutputDebugStringW(wideMessageBuffer); - - return 1; -} - -#else - -// TODO: put an SDL version here - -#endif - -} // slang_graphics diff --git a/tools/slang-graphics/window.h b/tools/slang-graphics/window.h deleted file mode 100644 index 91c8286d5..000000000 --- a/tools/slang-graphics/window.h +++ /dev/null @@ -1,69 +0,0 @@ -// window.h -#pragma once - -namespace slang_graphics { - -struct WindowDesc -{ - char const* title; - int width; - int height; -}; - -typedef struct Window Window; - -Window* createWindow(WindowDesc const& desc); -void showWindow(Window* window); -void* getPlatformWindowHandle(Window* window); - -/// Opaque state provided by platform for a running application. -typedef struct ApplicationContext ApplicationContext; - -/// User-defined application entry-point function. -typedef void(*ApplicationFunc)(ApplicationContext* context); - -/// Dispatch any pending events for application. -/// -/// @returns `true` if application should keep running. -bool dispatchEvents(ApplicationContext* context); - -/// Exit the application with a given result code -void exitApplication(ApplicationContext* context, int resultCode); - -/// Report an error to an appropriate logging destination. -int reportError(char const* message, ...); - -/// Run an application given the specified callback and command-line arguments. -int runApplication( - ApplicationFunc func, - int argc, - char const* const* argv); - -#define SG_CONSOLE_MAIN(APPLICATION_ENTRY) \ - int main(int argc, char** argv) { \ - return slang_graphics::runApplication(&(APPLIATION_ENTRY), argc, argv); \ - } - -#ifdef _WIN32 - -int runWindowsApplication( - ApplicationFunc func, - void* instance, - int showCommand); - -#define SG_UI_MAIN(APPLICATION_ENTRY) \ - int __stdcall WinMain( \ - void* instance, \ - void* /* prevInstance */, \ - void* /* commandLine */, \ - int showCommand) { \ - return slang_graphics::runWindowsApplication(&(APPLICATION_ENTRY), instance, showCommand); \ - } - -#else - -#define SG_UI_MAIN(APPLICATION_ENTRY) SG_CONSOLE_MAIN(APPLICATION_ENTRY) - -#endif - -} // slang_graphics -- cgit v1.2.3