From eb6d22242ae2c90beaa031fb4fe4434583c82ad9 Mon Sep 17 00:00:00 2001 From: Austin Eng Date: Mon, 5 Jun 2017 17:13:58 -0400 Subject: [PATCH] add D3D12Binding with swap chain --- examples/D3D12Binding.cpp | 158 ++++++++++++++++++++++++++++- src/backend/CMakeLists.txt | 51 ++++++++++ src/backend/d3d12/D3D12Backend.cpp | 61 ++++++++++- src/backend/d3d12/D3D12Backend.h | 21 +++- src/backend/d3d12/d3d12_platform.h | 24 +++++ third_party/CMakeLists.txt | 2 + 6 files changed, 312 insertions(+), 5 deletions(-) create mode 100644 src/backend/d3d12/d3d12_platform.h diff --git a/examples/D3D12Binding.cpp b/examples/D3D12Binding.cpp index a68cf5f8a5..8efa0ee851 100644 --- a/examples/D3D12Binding.cpp +++ b/examples/D3D12Binding.cpp @@ -14,20 +14,174 @@ #include "BackendBinding.h" +#define GLFW_EXPOSE_NATIVE_WIN32 +#include "GLFW/glfw3.h" +#include "GLFW/glfw3native.h" + +#include +#include +#include +#include + +#define ASSERT assert + +using Microsoft::WRL::ComPtr; + namespace backend { namespace d3d12 { - void Init(nxtProcTable* procs, nxtDevice* device); + void Init(ComPtr d3d12Device, nxtProcTable* procs, nxtDevice* device); + ComPtr GetCommandQueue(nxtDevice device); + void SetNextRenderTarget(nxtDevice device, ComPtr renderTargetResource, D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor); } } class D3D12Binding : public BackendBinding { public: void SetupGLFWWindowHints() override { + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); } + void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override { - backend::d3d12::Init(procs, device); + uint32_t dxgiFactoryFlags = 0; +#ifdef _DEBUG + // Enable the debug layer (requires the Graphics Tools "optional feature"). + // NOTE: Enabling the debug layer after device creation will invalidate the active device. + { + ComPtr debugController; + if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) + { + debugController->EnableDebugLayer(); + + // Enable additional debug layers. + dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; + } + } +#endif + + ASSERT_SUCCESS(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory))); + ASSERT(GetHardwareAdapter(factory.Get(), &hardwareAdapter)); + ASSERT_SUCCESS(D3D12CreateDevice( + hardwareAdapter.Get(), + D3D_FEATURE_LEVEL_11_0, + IID_PPV_ARGS(&d3d12Device) + )); + + backend::d3d12::Init(d3d12Device, procs, device); + backendDevice = *device; + commandQueue = backend::d3d12::GetCommandQueue(backendDevice); + + int width, height; + glfwGetWindowSize(window, &width, &height); + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; + swapChainDesc.Width = width; + swapChainDesc.Height = height; + swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = kFrameCount; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.SampleDesc.Count = 1; + + HWND win32Window = glfwGetWin32Window(window); + ComPtr swapChain1; + ASSERT_SUCCESS(factory->CreateSwapChainForHwnd( + commandQueue.Get(), + win32Window, + &swapChainDesc, + nullptr, + nullptr, + &swapChain1 + )); + ASSERT_SUCCESS(swapChain1.As(&swapChain)); + frameIndex = swapChain->GetCurrentBackBufferIndex(); + + // Describe and create a render target view (RTV) descriptor heap. + D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; + rtvHeapDesc.NumDescriptors = kFrameCount; + rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + ASSERT_SUCCESS(d3d12Device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&renderTargetViewHeap))); + + rtvDescriptorSize = d3d12Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + // Create a RTV for each frame. + D3D12_CPU_DESCRIPTOR_HANDLE renderTargetViewHandle = renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart(); + for (uint32_t n = 0; n < kFrameCount; ++n) { + ASSERT_SUCCESS(swapChain->GetBuffer(n, IID_PPV_ARGS(&renderTargetResources[n]))); + d3d12Device->CreateRenderTargetView(renderTargetResources[n].Get(), nullptr, renderTargetViewHandle); + renderTargetViewHandle.ptr += rtvDescriptorSize; + } + + ASSERT_SUCCESS(d3d12Device->CreateFence(fenceValues[frameIndex], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence))); + fenceValues[frameIndex]++; + fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + ASSERT(fenceEvent != nullptr); + + SetNextRenderTarget(); } void SwapBuffers() override { + // Present the rendered frame + ASSERT_SUCCESS(swapChain->Present(1, 0)); + + const uint64_t currentFenceValue = fenceValues[frameIndex]; + ASSERT_SUCCESS(commandQueue->Signal(fence.Get(), currentFenceValue)); + + frameIndex = swapChain->GetCurrentBackBufferIndex(); + + SetNextRenderTarget(); + + // If the next frame is not ready to be rendered yet, wait until it is ready. + if (fence->GetCompletedValue() < fenceValues[frameIndex]) { + ASSERT_SUCCESS(fence->SetEventOnCompletion(fenceValues[frameIndex], fenceEvent)); + WaitForSingleObjectEx(fenceEvent, INFINITE, FALSE); + } + + // Set the fence value for the next frame. + fenceValues[frameIndex] = currentFenceValue + 1; + } + + private: + nxtDevice backendDevice = nullptr; + + static constexpr int kFrameCount = 2; + uint32_t frameIndex = 0; + uint32_t rtvDescriptorSize; + ComPtr factory; + ComPtr hardwareAdapter; + ComPtr d3d12Device; + ComPtr commandQueue; + ComPtr swapChain; + ComPtr renderTargetViewHeap; + ComPtr renderTargetResources[kFrameCount]; + uint64_t fenceValues[kFrameCount] = { 0, 0 }; + ComPtr fence; + HANDLE fenceEvent; + + void SetNextRenderTarget() { + D3D12_CPU_DESCRIPTOR_HANDLE renderTargetViewHandle = renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart(); + renderTargetViewHandle.ptr += rtvDescriptorSize * frameIndex; + backend::d3d12::SetNextRenderTarget(backendDevice, renderTargetResources[frameIndex], renderTargetViewHandle); + } + + static void ASSERT_SUCCESS(HRESULT hr) { + assert(SUCCEEDED(hr)); + } + + static bool GetHardwareAdapter(IDXGIFactory4* factory, IDXGIAdapter1** hardwareAdapter) { + *hardwareAdapter = nullptr; + for (uint32_t adapterIndex = 0; ; ++adapterIndex) { + IDXGIAdapter1* adapter = nullptr; + if (factory->EnumAdapters1(adapterIndex, &adapter) == DXGI_ERROR_NOT_FOUND) { + break; // No more adapters to enumerate. + } + + // Check to see if the adapter supports Direct3D 12, but don't create the actual device yet. + if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) { + *hardwareAdapter = adapter; + return true; + } + adapter->Release(); + } + return false; } }; diff --git a/src/backend/CMakeLists.txt b/src/backend/CMakeLists.txt index 46abf6b9c4..06eddafce2 100644 --- a/src/backend/CMakeLists.txt +++ b/src/backend/CMakeLists.txt @@ -156,7 +156,58 @@ if (WIN32) ${GENERATOR_COMMON_ARGS} -T d3d12 ) + # WIN10_SDK_PATH will be something like C:\Program Files (x86)\Windows Kits\10 + get_filename_component(WIN10_SDK_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0;InstallationFolder]" ABSOLUTE CACHE) + # TEMP_WIN10_SDK_VERSION will be something like ${CMAKE_CURRENT_SOURCE_DIR}\10.0.14393 or ${CMAKE_CURRENT_SOURCE_DIR}\10.0.14393.0 + get_filename_component(TEMP_WIN10_SDK_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0;ProductVersion]" ABSOLUTE CACHE) + + # strip off ${CMAKE_CURRENT_SOURCE_DIR} to get just the version number + get_filename_component(WIN10_SDK_VERSION ${TEMP_WIN10_SDK_VERSION} NAME) + # WIN10_SDK_VERSION will be something like 10.0.14393 or 10.0.14393.0; we need the one that matches the directory name. + if (IS_DIRECTORY "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}.0") + set(WIN10_SDK_VERSION "${WIN10_SDK_VERSION}.0") + endif() + + # Find the d3d12 and dxgi include path, it will typically look something like this. + # C:\Program Files (x86)\Windows Kits\10\Include\10.0.10586.0\um\d3d12.h + # C:\Program Files (x86)\Windows Kits\10\Include\10.0.10586.0\shared\dxgi1_4.h + find_path(D3D12_INCLUDE_DIR # Set variable D3D12_INCLUDE_DIR + d3d12.h # Find a path with d3d12.h + HINTS "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}/um" + DOC "path to WIN10 SDK header files" + HINTS + ) + + find_path(DXGI_INCLUDE_DIR # Set variable DXGI_INCLUDE_DIR + dxgi1_4.h # Find a path with dxgi1_4.h + HINTS "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}/shared" + DOC "path to WIN10 SDK header files" + HINTS + ) + + if (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM" ) + set(WIN10_SDK_LIB_PATH ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm) + elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM64" ) + set(WIN10_SDK_LIB_PATH ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm64) + elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*Win64" ) + set(WIN10_SDK_LIB_PATH ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x64) + else() + set(WIN10_SDK_LIB_PATH ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x86) + endif() + + find_library(D3D12_LIBRARY NAMES d3d12.lib HINTS ${WIN10_SDK_LIB_PATH}) + find_library(DXGI_LIBRARY NAMES dxgi.lib HINTS ${WIN10_SDK_LIB_PATH}) + find_library(D3DCOMPILER_LIBRARY NAMES d3dcompiler.lib HINTS ${WIN10_SDK_LIB_PATH}) + + set(D3D12_LIBRARIES + ${D3D12_LIBRARY} + ${DXGI_LIBRARY} + ${D3DCOMPILER_LIBRARY} + ) + + target_link_libraries(d3d12_autogen glfw nxtcpp ${D3D12_LIBRARIES}) + target_include_directories(d3d12_autogen SYSTEM PRIVATE ${D3D12_INCLUDE_DIR} ${DXGI_INCLUDE_DIR}) target_include_directories(d3d12_autogen PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(d3d12_autogen PUBLIC ${GENERATED_DIR}) SetCXX14(d3d12_autogen) diff --git a/src/backend/d3d12/D3D12Backend.cpp b/src/backend/d3d12/D3D12Backend.cpp index 6b1c94daef..b82a0fa841 100644 --- a/src/backend/d3d12/D3D12Backend.cpp +++ b/src/backend/d3d12/D3D12Backend.cpp @@ -20,17 +20,74 @@ namespace d3d12 { nxtProcTable GetNonValidatingProcs(); nxtProcTable GetValidatingProcs(); - void Init(nxtProcTable* procs, nxtDevice* device) { + void Init(ComPtr d3d12Device, nxtProcTable* procs, nxtDevice* device) { *device = nullptr; *procs = GetValidatingProcs(); + *device = reinterpret_cast(new Device(d3d12Device)); } - Device::Device() { + ComPtr GetCommandQueue(nxtDevice device) { + Device* backendDevice = reinterpret_cast(device); + return backendDevice->GetCommandQueue(); + } + + void SetNextRenderTarget(nxtDevice device, ComPtr renderTargetResource, D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor) { + Device* backendDevice = reinterpret_cast(device); + backendDevice->SetNextRenderTarget(renderTargetResource, renderTargetDescriptor); + } + + void ASSERT_SUCCESS(HRESULT hr) { + ASSERT(SUCCEEDED(hr)); + } + + Device::Device(ComPtr d3d12Device) : d3d12Device(d3d12Device) { + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + ASSERT_SUCCESS(d3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue))); + + // Create an empty root signature. + D3D12_ROOT_SIGNATURE_DESC rootSignatureDescriptor; + rootSignatureDescriptor.NumParameters = 0; + rootSignatureDescriptor.pParameters = nullptr; + rootSignatureDescriptor.NumStaticSamplers = 0; + rootSignatureDescriptor.pStaticSamplers = nullptr; + rootSignatureDescriptor.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + + ComPtr signature; + ComPtr error; + ASSERT_SUCCESS(D3D12SerializeRootSignature(&rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); + ASSERT_SUCCESS(d3d12Device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&rootSignature))); } Device::~Device() { } + ComPtr Device::GetD3D12Device() { + return d3d12Device; + } + + ComPtr Device::GetRootSignature() { + return rootSignature; + } + + ComPtr Device::GetCommandQueue() { + return commandQueue; + } + + Microsoft::WRL::ComPtr Device::GetNextRenderTarget() { + return renderTargetResource; + } + + D3D12_CPU_DESCRIPTOR_HANDLE Device::GetNextRenderTargetDescriptor() { + return renderTargetDescriptor; + } + + void Device::SetNextRenderTarget(ComPtr renderTargetResource, D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor) { + this->renderTargetResource = renderTargetResource; + this->renderTargetDescriptor = renderTargetDescriptor; + } + BindGroupBase* Device::CreateBindGroup(BindGroupBuilder* builder) { return new BindGroup(this, builder); } diff --git a/src/backend/d3d12/D3D12Backend.h b/src/backend/d3d12/D3D12Backend.h index 1615bb00ec..85b4fac312 100644 --- a/src/backend/d3d12/D3D12Backend.h +++ b/src/backend/d3d12/D3D12Backend.h @@ -34,6 +34,8 @@ #include "common/Texture.h" #include "common/ToBackend.h" +#include "d3d12_platform.h" + namespace backend { namespace d3d12 { @@ -78,10 +80,12 @@ namespace d3d12 { return ToBackendBase(common); } + void ASSERT_SUCCESS(HRESULT hr); + // Definition of backend types class Device : public DeviceBase { public: - Device(); + Device(Microsoft::WRL::ComPtr d3d12Device); ~Device(); BindGroupBase* CreateBindGroup(BindGroupBuilder* builder) override; @@ -101,9 +105,24 @@ namespace d3d12 { TextureBase* CreateTexture(TextureBuilder* builder) override; TextureViewBase* CreateTextureView(TextureViewBuilder* builder) override; + Microsoft::WRL::ComPtr GetD3D12Device(); + Microsoft::WRL::ComPtr GetRootSignature(); + Microsoft::WRL::ComPtr GetCommandQueue(); + Microsoft::WRL::ComPtr GetNextRenderTarget(); + D3D12_CPU_DESCRIPTOR_HANDLE GetNextRenderTargetDescriptor(); + + void SetNextRenderTarget(Microsoft::WRL::ComPtr renderTargetResource, D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor); + // NXT API void Reference(); void Release(); + + private: + Microsoft::WRL::ComPtr d3d12Device; + Microsoft::WRL::ComPtr commandQueue; + Microsoft::WRL::ComPtr rootSignature; + Microsoft::WRL::ComPtr renderTargetResource; + D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor; }; diff --git a/src/backend/d3d12/d3d12_platform.h b/src/backend/d3d12/d3d12_platform.h new file mode 100644 index 0000000000..6b5d362660 --- /dev/null +++ b/src/backend/d3d12/d3d12_platform.h @@ -0,0 +1,24 @@ +// Copyright 2017 The NXT Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BACKEND_D3D12_D3D12_H_ +#define BACKEND_D3D12_D3D12_H_ + +#include +#include +#include + +using Microsoft::WRL::ComPtr; + +#endif // BACKEND_D3D12_D3D12_H_ diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 8c0c7d9a65..704fefe124 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -62,6 +62,8 @@ add_library(spirv-cross STATIC ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv.hpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_msl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_msl.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_hlsl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_hlsl.hpp ) target_include_directories(spirv-cross PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) SetCXX14(spirv-cross)