RendererDX12.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2024 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <TestFramework.h>
  5. #include <Renderer/DX12/RendererDX12.h>
  6. #include <Renderer/DX12/RenderPrimitiveDX12.h>
  7. #include <Renderer/DX12/PipelineStateDX12.h>
  8. #include <Renderer/DX12/VertexShaderDX12.h>
  9. #include <Renderer/DX12/PixelShaderDX12.h>
  10. #include <Renderer/DX12/TextureDX12.h>
  11. #include <Renderer/DX12/RenderInstancesDX12.h>
  12. #include <Renderer/DX12/FatalErrorIfFailedDX12.h>
  13. #include <Window/ApplicationWindowWin.h>
  14. #include <Jolt/Core/Profiler.h>
  15. #include <Utils/ReadData.h>
  16. #include <Utils/Log.h>
  17. #include <Utils/AssetStream.h>
  18. #include <d3dcompiler.h>
  19. #ifdef JPH_DEBUG
  20. #include <d3d12sdklayers.h>
  21. #endif
  22. RendererDX12::~RendererDX12()
  23. {
  24. // Ensure that the GPU is no longer referencing resources that are about to be cleaned up by the destructor.
  25. WaitForGpu();
  26. // Don't add more stuff to the delay reference list
  27. mIsExiting = true;
  28. CloseHandle(mFenceEvent);
  29. }
  30. void RendererDX12::WaitForGpu()
  31. {
  32. // Schedule a Signal command in the queue
  33. UINT64 current_fence_value = mFenceValues[mFrameIndex];
  34. FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), current_fence_value));
  35. // Wait until the fence has been processed
  36. FatalErrorIfFailed(mFence->SetEventOnCompletion(current_fence_value, mFenceEvent));
  37. WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
  38. // Increment the fence value for all frames
  39. for (uint n = 0; n < cFrameCount; ++n)
  40. mFenceValues[n] = current_fence_value + 1;
  41. // Release all used resources
  42. for (Array<ComPtr<ID3D12Object>> &list : mDelayReleased)
  43. list.clear();
  44. // Anything that's not used yet can be removed, delayed objects are now available
  45. mResourceCache.clear();
  46. mDelayCached[mFrameIndex].swap(mResourceCache);
  47. }
  48. void RendererDX12::CreateRenderTargets()
  49. {
  50. // Create render targets and views
  51. for (uint n = 0; n < cFrameCount; ++n)
  52. {
  53. mRenderTargetViews[n] = mRTVHeap.Allocate();
  54. FatalErrorIfFailed(mSwapChain->GetBuffer(n, IID_PPV_ARGS(&mRenderTargets[n])));
  55. mDevice->CreateRenderTargetView(mRenderTargets[n].Get(), nullptr, mRenderTargetViews[n]);
  56. }
  57. }
  58. void RendererDX12::CreateDepthBuffer()
  59. {
  60. // Free any previous depth stencil view
  61. if (mDepthStencilView.ptr != 0)
  62. mDSVHeap.Free(mDepthStencilView);
  63. // Free any previous depth stencil buffer
  64. mDepthStencilBuffer.Reset();
  65. // Allocate depth stencil buffer
  66. D3D12_CLEAR_VALUE clear_value = {};
  67. clear_value.Format = DXGI_FORMAT_D32_FLOAT;
  68. clear_value.DepthStencil.Depth = 0.0f;
  69. clear_value.DepthStencil.Stencil = 0;
  70. D3D12_HEAP_PROPERTIES heap_properties = {};
  71. heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
  72. heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
  73. heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
  74. heap_properties.CreationNodeMask = 1;
  75. heap_properties.VisibleNodeMask = 1;
  76. D3D12_RESOURCE_DESC depth_stencil_desc = {};
  77. depth_stencil_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
  78. depth_stencil_desc.Alignment = 0;
  79. depth_stencil_desc.Width = mWindow->GetWindowWidth();
  80. depth_stencil_desc.Height = mWindow->GetWindowHeight();
  81. depth_stencil_desc.DepthOrArraySize = 1;
  82. depth_stencil_desc.MipLevels = 1;
  83. depth_stencil_desc.Format = DXGI_FORMAT_D32_FLOAT;
  84. depth_stencil_desc.SampleDesc.Count = 1;
  85. depth_stencil_desc.SampleDesc.Quality = 0;
  86. depth_stencil_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
  87. depth_stencil_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
  88. FatalErrorIfFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &depth_stencil_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clear_value, IID_PPV_ARGS(&mDepthStencilBuffer)));
  89. // Allocate depth stencil view
  90. D3D12_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_desc = {};
  91. depth_stencil_view_desc.Format = DXGI_FORMAT_D32_FLOAT;
  92. depth_stencil_view_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
  93. depth_stencil_view_desc.Flags = D3D12_DSV_FLAG_NONE;
  94. mDepthStencilView = mDSVHeap.Allocate();
  95. mDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &depth_stencil_view_desc, mDepthStencilView);
  96. }
  97. void RendererDX12::Initialize(ApplicationWindow *inWindow)
  98. {
  99. Renderer::Initialize(inWindow);
  100. #if defined(JPH_DEBUG)
  101. // Enable the D3D12 debug layer
  102. ComPtr<ID3D12Debug> debug_controller;
  103. if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller))))
  104. debug_controller->EnableDebugLayer();
  105. #endif
  106. // Create DXGI factory
  107. FatalErrorIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mDXGIFactory)));
  108. // Find adapter
  109. ComPtr<IDXGIAdapter1> adapter;
  110. HRESULT result = E_FAIL;
  111. // First check if we have the Windows 1803 IDXGIFactory6 interface
  112. ComPtr<IDXGIFactory6> factory6;
  113. if (SUCCEEDED(mDXGIFactory->QueryInterface(IID_PPV_ARGS(&factory6))))
  114. {
  115. for (UINT index = 0; DXGI_ERROR_NOT_FOUND != factory6->EnumAdapterByGpuPreference(index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)); ++index)
  116. {
  117. DXGI_ADAPTER_DESC1 desc;
  118. adapter->GetDesc1(&desc);
  119. // We don't want software renderers
  120. if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
  121. continue;
  122. // Check to see whether the adapter supports Direct3D 12
  123. result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice));
  124. if (SUCCEEDED(result))
  125. break;
  126. }
  127. }
  128. else
  129. {
  130. // Fall back to the older method that may not get the fastest GPU
  131. for (UINT index = 0; DXGI_ERROR_NOT_FOUND != mDXGIFactory->EnumAdapters1(index, &adapter); ++index)
  132. {
  133. DXGI_ADAPTER_DESC1 desc;
  134. adapter->GetDesc1(&desc);
  135. // We don't want software renderers
  136. if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
  137. continue;
  138. // Check to see whether the adapter supports Direct3D 12
  139. result = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice));
  140. if (SUCCEEDED(result))
  141. break;
  142. }
  143. }
  144. // Check if we managed to obtain a device
  145. FatalErrorIfFailed(result);
  146. #ifdef JPH_DEBUG
  147. // Enable breaking on errors
  148. ComPtr<ID3D12InfoQueue> info_queue;
  149. if (SUCCEEDED(mDevice.As(&info_queue)))
  150. {
  151. info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
  152. info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
  153. info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
  154. // Disable an error that triggers on Windows 11 with a hybrid graphic system
  155. // See: https://stackoverflow.com/questions/69805245/directx-12-application-is-crashing-in-windows-11
  156. D3D12_MESSAGE_ID hide[] =
  157. {
  158. D3D12_MESSAGE_ID_RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE,
  159. };
  160. D3D12_INFO_QUEUE_FILTER filter = { };
  161. filter.DenyList.NumIDs = static_cast<UINT>( std::size( hide ) );
  162. filter.DenyList.pIDList = hide;
  163. info_queue->AddStorageFilterEntries( &filter );
  164. }
  165. #endif // JPH_DEBUG
  166. // Disable full screen transitions
  167. FatalErrorIfFailed(mDXGIFactory->MakeWindowAssociation(static_cast<ApplicationWindowWin *>(mWindow)->GetWindowHandle(), DXGI_MWA_NO_ALT_ENTER));
  168. // Create heaps
  169. mRTVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 2);
  170. mDSVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 4);
  171. mSRVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128);
  172. // Create a command queue
  173. D3D12_COMMAND_QUEUE_DESC queue_desc = {};
  174. queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
  175. queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
  176. FatalErrorIfFailed(mDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mCommandQueue)));
  177. // Create a command allocator for each frame
  178. for (uint n = 0; n < cFrameCount; n++)
  179. FatalErrorIfFailed(mDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocators[n])));
  180. // Describe and create the swap chain
  181. DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
  182. swap_chain_desc.BufferCount = cFrameCount;
  183. swap_chain_desc.BufferDesc.Width = mWindow->GetWindowWidth();
  184. swap_chain_desc.BufferDesc.Height = mWindow->GetWindowHeight();
  185. swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  186. swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  187. swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
  188. swap_chain_desc.OutputWindow = static_cast<ApplicationWindowWin *>(mWindow)->GetWindowHandle();
  189. swap_chain_desc.SampleDesc.Count = 1;
  190. swap_chain_desc.Windowed = TRUE;
  191. ComPtr<IDXGISwapChain> swap_chain;
  192. FatalErrorIfFailed(mDXGIFactory->CreateSwapChain(mCommandQueue.Get(), &swap_chain_desc, &swap_chain));
  193. FatalErrorIfFailed(swap_chain.As(&mSwapChain));
  194. mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
  195. CreateRenderTargets();
  196. CreateDepthBuffer();
  197. // Create a root signature suitable for all our shaders
  198. D3D12_ROOT_PARAMETER params[3] = {};
  199. // Mapping a constant buffer to slot 0 for the vertex shader
  200. params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
  201. params[0].Descriptor.ShaderRegister = 0;
  202. params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
  203. // Mapping a constant buffer to slot 1 in the pixel shader
  204. params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
  205. params[1].Descriptor.ShaderRegister = 1;
  206. params[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
  207. // Mapping a texture to slot 2 in the pixel shader
  208. D3D12_DESCRIPTOR_RANGE range = {};
  209. range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
  210. range.BaseShaderRegister = 2;
  211. range.NumDescriptors = 1;
  212. params[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
  213. params[2].DescriptorTable.NumDescriptorRanges = 1;
  214. params[2].DescriptorTable.pDescriptorRanges = &range;
  215. params[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
  216. D3D12_STATIC_SAMPLER_DESC samplers[3] = {};
  217. // Sampler 0: Non-wrapping linear filtering
  218. samplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
  219. samplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
  220. samplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
  221. samplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
  222. samplers[0].MipLODBias = 0.0f;
  223. samplers[0].MaxAnisotropy = 1;
  224. samplers[0].ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
  225. samplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
  226. samplers[0].MinLOD = 0.0f;
  227. samplers[0].MaxLOD = D3D12_FLOAT32_MAX;
  228. samplers[0].ShaderRegister = 0;
  229. samplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
  230. // Sampler 1: Wrapping and linear filtering
  231. samplers[1] = samplers[0];
  232. samplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
  233. samplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
  234. samplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
  235. samplers[1].ShaderRegister = 1;
  236. // Sampler 2: Point filtering, using SampleCmp mode to compare if sampled value >= reference value (for shadows)
  237. samplers[2] = samplers[0];
  238. samplers[2].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
  239. samplers[2].ComparisonFunc = D3D12_COMPARISON_FUNC_GREATER_EQUAL;
  240. samplers[2].ShaderRegister = 2;
  241. D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {};
  242. root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
  243. root_signature_desc.NumParameters = ARRAYSIZE(params);
  244. root_signature_desc.pParameters = params;
  245. root_signature_desc.NumStaticSamplers = ARRAYSIZE(samplers);
  246. root_signature_desc.pStaticSamplers = samplers;
  247. ComPtr<ID3DBlob> signature;
  248. ComPtr<ID3DBlob> error;
  249. FatalErrorIfFailed(D3D12SerializeRootSignature(&root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
  250. FatalErrorIfFailed(mDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&mRootSignature)));
  251. // Create the command list
  252. FatalErrorIfFailed(mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocators[mFrameIndex].Get(), nullptr, IID_PPV_ARGS(&mCommandList)));
  253. // Command lists are created in the recording state, but there is nothing to record yet. The main loop expects it to be closed, so close it now
  254. FatalErrorIfFailed(mCommandList->Close());
  255. // Create synchronization object
  256. FatalErrorIfFailed(mDevice->CreateFence(mFenceValues[mFrameIndex], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)));
  257. // Increment fence value so we don't skip waiting the first time a command list is executed
  258. mFenceValues[mFrameIndex]++;
  259. // Create an event handle to use for frame synchronization
  260. mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
  261. if (mFenceEvent == nullptr)
  262. FatalErrorIfFailed(HRESULT_FROM_WIN32(GetLastError()));
  263. // Initialize the queue used to upload resources to the GPU
  264. mUploadQueue.Initialize(mDevice.Get());
  265. // Create constant buffer. One per frame to avoid overwriting the constant buffer while the GPU is still using it.
  266. for (uint n = 0; n < cFrameCount; ++n)
  267. {
  268. mVertexShaderConstantBufferProjection[n] = CreateConstantBuffer(sizeof(VertexShaderConstantBuffer));
  269. mVertexShaderConstantBufferOrtho[n] = CreateConstantBuffer(sizeof(VertexShaderConstantBuffer));
  270. mPixelShaderConstantBuffer[n] = CreateConstantBuffer(sizeof(PixelShaderConstantBuffer));
  271. }
  272. // Create depth only texture (no color buffer, as seen from light)
  273. mShadowMap = new TextureDX12(this, cShadowMapSize, cShadowMapSize);
  274. }
  275. void RendererDX12::OnWindowResize()
  276. {
  277. // Wait for the previous frame to be rendered
  278. WaitForGpu();
  279. // Free the render targets and views to allow resizing the swap chain
  280. for (uint n = 0; n < cFrameCount; ++n)
  281. {
  282. mRTVHeap.Free(mRenderTargetViews[n]);
  283. mRenderTargets[n].Reset();
  284. }
  285. // Resize the swap chain buffers
  286. FatalErrorIfFailed(mSwapChain->ResizeBuffers(cFrameCount, mWindow->GetWindowWidth(), mWindow->GetWindowHeight(), DXGI_FORMAT_R8G8B8A8_UNORM, 0));
  287. // Back buffer index may have changed after the resize (it always seems to go to 0 again)
  288. mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
  289. // Since we may have switched frame index and we know everything is done, we need to update the fence value for our other frame as completed
  290. for (uint n = 0; n < cFrameCount; ++n)
  291. if (mFrameIndex != n)
  292. mFenceValues[n] = mFence->GetCompletedValue();
  293. // Recreate render targets
  294. CreateRenderTargets();
  295. // Recreate depth buffer
  296. CreateDepthBuffer();
  297. }
  298. bool RendererDX12::BeginFrame(const CameraState &inCamera, float inWorldScale)
  299. {
  300. JPH_PROFILE_FUNCTION();
  301. Renderer::BeginFrame(inCamera, inWorldScale);
  302. // Reset command allocator
  303. FatalErrorIfFailed(mCommandAllocators[mFrameIndex]->Reset());
  304. // Reset command list
  305. FatalErrorIfFailed(mCommandList->Reset(mCommandAllocators[mFrameIndex].Get(), nullptr));
  306. // Set root signature
  307. mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
  308. // Set SRV heap
  309. ID3D12DescriptorHeap *heaps[] = { mSRVHeap.Get() };
  310. mCommandList->SetDescriptorHeaps(_countof(heaps), heaps);
  311. // Indicate that the back buffer will be used as a render target.
  312. D3D12_RESOURCE_BARRIER barrier;
  313. barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
  314. barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
  315. barrier.Transition.pResource = mRenderTargets[mFrameIndex].Get();
  316. barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
  317. barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
  318. barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
  319. mCommandList->ResourceBarrier(1, &barrier);
  320. // Clear the back buffer.
  321. const float blue[] = { 0.098f, 0.098f, 0.439f, 1.000f };
  322. mCommandList->ClearRenderTargetView(mRenderTargetViews[mFrameIndex], blue, 0, nullptr);
  323. mCommandList->ClearDepthStencilView(mDepthStencilView, D3D12_CLEAR_FLAG_DEPTH, 0.0f, 0, 0, nullptr);
  324. // Set constants for vertex shader in projection mode
  325. VertexShaderConstantBuffer *vs = mVertexShaderConstantBufferProjection[mFrameIndex]->Map<VertexShaderConstantBuffer>();
  326. *vs = mVSBuffer;
  327. mVertexShaderConstantBufferProjection[mFrameIndex]->Unmap();
  328. // Set constants for vertex shader in ortho mode
  329. vs = mVertexShaderConstantBufferOrtho[mFrameIndex]->Map<VertexShaderConstantBuffer>();
  330. *vs = mVSBufferOrtho;
  331. mVertexShaderConstantBufferOrtho[mFrameIndex]->Unmap();
  332. // Switch to 3d projection mode
  333. SetProjectionMode();
  334. // Set constants for pixel shader
  335. PixelShaderConstantBuffer *ps = mPixelShaderConstantBuffer[mFrameIndex]->Map<PixelShaderConstantBuffer>();
  336. *ps = mPSBuffer;
  337. mPixelShaderConstantBuffer[mFrameIndex]->Unmap();
  338. // Set the pixel shader constant buffer data.
  339. mPixelShaderConstantBuffer[mFrameIndex]->Bind(1);
  340. // Start drawing the shadow pass
  341. mShadowMap->SetAsRenderTarget(true);
  342. return true;
  343. }
  344. void RendererDX12::EndShadowPass()
  345. {
  346. JPH_PROFILE_FUNCTION();
  347. // Finish drawing the shadow pass
  348. mShadowMap->SetAsRenderTarget(false);
  349. // Set the main back buffer as render target
  350. mCommandList->OMSetRenderTargets(1, &mRenderTargetViews[mFrameIndex], FALSE, &mDepthStencilView);
  351. // Set viewport
  352. D3D12_VIEWPORT viewport = { 0.0f, 0.0f, static_cast<float>(mWindow->GetWindowWidth()), static_cast<float>(mWindow->GetWindowHeight()), 0.0f, 1.0f };
  353. mCommandList->RSSetViewports(1, &viewport);
  354. // Set scissor rect
  355. D3D12_RECT scissor_rect = { 0, 0, static_cast<LONG>(mWindow->GetWindowWidth()), static_cast<LONG>(mWindow->GetWindowHeight()) };
  356. mCommandList->RSSetScissorRects(1, &scissor_rect);
  357. }
  358. void RendererDX12::EndFrame()
  359. {
  360. JPH_PROFILE_FUNCTION();
  361. Renderer::EndFrame();
  362. // Indicate that the back buffer will now be used to present.
  363. D3D12_RESOURCE_BARRIER barrier;
  364. barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
  365. barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
  366. barrier.Transition.pResource = mRenderTargets[mFrameIndex].Get();
  367. barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
  368. barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
  369. barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
  370. mCommandList->ResourceBarrier(1, &barrier);
  371. // Close the command list
  372. FatalErrorIfFailed(mCommandList->Close());
  373. // Execute the command list
  374. ID3D12CommandList* command_lists[] = { mCommandList.Get() };
  375. mCommandQueue->ExecuteCommandLists(_countof(command_lists), command_lists);
  376. // Present the frame
  377. FatalErrorIfFailed(mSwapChain->Present(1, 0));
  378. // Schedule a Signal command in the queue
  379. UINT64 current_fence_value = mFenceValues[mFrameIndex];
  380. FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), current_fence_value));
  381. // Update the frame index
  382. mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
  383. // If the next frame is not ready to be rendered yet, wait until it is ready
  384. UINT64 completed_value = mFence->GetCompletedValue();
  385. if (completed_value < mFenceValues[mFrameIndex])
  386. {
  387. FatalErrorIfFailed(mFence->SetEventOnCompletion(mFenceValues[mFrameIndex], mFenceEvent));
  388. WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
  389. }
  390. // Release all used resources
  391. mDelayReleased[mFrameIndex].clear();
  392. // Anything that's not used yet can be removed, delayed objects are now available
  393. mResourceCache.clear();
  394. mDelayCached[mFrameIndex].swap(mResourceCache);
  395. // Set the fence value for the next frame.
  396. mFenceValues[mFrameIndex] = current_fence_value + 1;
  397. }
  398. void RendererDX12::SetProjectionMode()
  399. {
  400. JPH_ASSERT(mInFrame);
  401. mVertexShaderConstantBufferProjection[mFrameIndex]->Bind(0);
  402. }
  403. void RendererDX12::SetOrthoMode()
  404. {
  405. JPH_ASSERT(mInFrame);
  406. mVertexShaderConstantBufferOrtho[mFrameIndex]->Bind(0);
  407. }
  408. Ref<Texture> RendererDX12::CreateTexture(const Surface *inSurface)
  409. {
  410. return new TextureDX12(this, inSurface);
  411. }
  412. Ref<VertexShader> RendererDX12::CreateVertexShader(const char *inName)
  413. {
  414. UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
  415. #ifdef JPH_DEBUG
  416. flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
  417. #endif
  418. const D3D_SHADER_MACRO defines[] =
  419. {
  420. { nullptr, nullptr }
  421. };
  422. // Read shader source file
  423. String file_name = String("Shaders/DX/") + inName + ".hlsl";
  424. Array<uint8> data = ReadData(file_name.c_str());
  425. // Compile source
  426. ComPtr<ID3DBlob> shader_blob, error_blob;
  427. HRESULT hr = D3DCompile(&data[0],
  428. (uint)data.size(),
  429. (AssetStream::sGetAssetsBasePath() + file_name).c_str(),
  430. defines,
  431. D3D_COMPILE_STANDARD_FILE_INCLUDE,
  432. "main",
  433. "vs_5_0",
  434. flags,
  435. 0,
  436. shader_blob.GetAddressOf(),
  437. error_blob.GetAddressOf());
  438. if (FAILED(hr))
  439. {
  440. // Throw error if compilation failed
  441. if (error_blob)
  442. OutputDebugStringA((const char *)error_blob->GetBufferPointer());
  443. FatalError("Failed to compile vertex shader");
  444. }
  445. return new VertexShaderDX12(shader_blob);
  446. }
  447. Ref<PixelShader> RendererDX12::CreatePixelShader(const char *inName)
  448. {
  449. UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
  450. #ifdef JPH_DEBUG
  451. flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
  452. #endif
  453. const D3D_SHADER_MACRO defines[] =
  454. {
  455. { nullptr, nullptr }
  456. };
  457. // Read shader source file
  458. String file_name = String("Shaders/DX/") + inName + ".hlsl";
  459. Array<uint8> data = ReadData(file_name.c_str());
  460. // Compile source
  461. ComPtr<ID3DBlob> shader_blob, error_blob;
  462. HRESULT hr = D3DCompile(&data[0],
  463. (uint)data.size(),
  464. (AssetStream::sGetAssetsBasePath() + file_name).c_str(),
  465. defines,
  466. D3D_COMPILE_STANDARD_FILE_INCLUDE,
  467. "main",
  468. "ps_5_0",
  469. flags,
  470. 0,
  471. shader_blob.GetAddressOf(),
  472. error_blob.GetAddressOf());
  473. if (FAILED(hr))
  474. {
  475. // Throw error if compilation failed
  476. if (error_blob)
  477. OutputDebugStringA((const char *)error_blob->GetBufferPointer());
  478. FatalError("Failed to compile pixel shader");
  479. }
  480. return new PixelShaderDX12(shader_blob);
  481. }
  482. unique_ptr<ConstantBufferDX12> RendererDX12::CreateConstantBuffer(uint inBufferSize)
  483. {
  484. return make_unique<ConstantBufferDX12>(this, inBufferSize);
  485. }
  486. unique_ptr<PipelineState> RendererDX12::CreatePipelineState(const VertexShader *inVertexShader, const PipelineState::EInputDescription *inInputDescription, uint inInputDescriptionCount, const PixelShader *inPixelShader, PipelineState::EDrawPass inDrawPass, PipelineState::EFillMode inFillMode, PipelineState::ETopology inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode)
  487. {
  488. return make_unique<PipelineStateDX12>(this, static_cast<const VertexShaderDX12 *>(inVertexShader), inInputDescription, inInputDescriptionCount, static_cast<const PixelShaderDX12 *>(inPixelShader), inDrawPass, inFillMode, inTopology, inDepthTest, inBlendMode, inCullMode);
  489. }
  490. RenderPrimitive *RendererDX12::CreateRenderPrimitive(PipelineState::ETopology inType)
  491. {
  492. return new RenderPrimitiveDX12(this, inType);
  493. }
  494. RenderInstances *RendererDX12::CreateRenderInstances()
  495. {
  496. return new RenderInstancesDX12(this);
  497. }
  498. ComPtr<ID3D12Resource> RendererDX12::CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, uint64 inSize)
  499. {
  500. // Create a new resource
  501. D3D12_RESOURCE_DESC desc;
  502. desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
  503. desc.Alignment = 0;
  504. desc.Width = inSize;
  505. desc.Height = 1;
  506. desc.DepthOrArraySize = 1;
  507. desc.MipLevels = 1;
  508. desc.Format = DXGI_FORMAT_UNKNOWN;
  509. desc.SampleDesc.Count = 1;
  510. desc.SampleDesc.Quality = 0;
  511. desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
  512. desc.Flags = D3D12_RESOURCE_FLAG_NONE;
  513. D3D12_HEAP_PROPERTIES heap_properties = {};
  514. heap_properties.Type = inHeapType;
  515. heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
  516. heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
  517. heap_properties.CreationNodeMask = 1;
  518. heap_properties.VisibleNodeMask = 1;
  519. ComPtr<ID3D12Resource> resource;
  520. FatalErrorIfFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &desc, inResourceState, nullptr, IID_PPV_ARGS(&resource)));
  521. return resource;
  522. }
  523. void RendererDX12::CopyD3DResource(ID3D12Resource *inDest, const void *inSrc, uint64 inSize)
  524. {
  525. // Copy data to destination buffer
  526. void *data;
  527. D3D12_RANGE range = { 0, 0 }; // We're not going to read
  528. FatalErrorIfFailed(inDest->Map(0, &range, &data));
  529. memcpy(data, inSrc, size_t(inSize));
  530. inDest->Unmap(0, nullptr);
  531. }
  532. void RendererDX12::CopyD3DResource(ID3D12Resource *inDest, ID3D12Resource *inSrc, uint64 inSize)
  533. {
  534. // Start a commandlist for the upload
  535. ID3D12GraphicsCommandList *list = mUploadQueue.Start();
  536. // Copy the data to the GPU
  537. list->CopyBufferRegion(inDest, 0, inSrc, 0, inSize);
  538. // Change the state of the resource to generic read
  539. D3D12_RESOURCE_BARRIER barrier;
  540. barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
  541. barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
  542. barrier.Transition.pResource = inDest;
  543. barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
  544. barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_GENERIC_READ;
  545. barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
  546. list->ResourceBarrier(1, &barrier);
  547. // Wait for copying to finish
  548. mUploadQueue.ExecuteAndWait();
  549. }
  550. ComPtr<ID3D12Resource> RendererDX12::CreateD3DResourceOnDefaultHeap(const void *inData, uint64 inSize)
  551. {
  552. ComPtr<ID3D12Resource> upload = CreateD3DResourceOnUploadHeap(inSize);
  553. ComPtr<ID3D12Resource> resource = CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COMMON, inSize);
  554. CopyD3DResource(upload.Get(), inData, inSize);
  555. CopyD3DResource(resource.Get(), upload.Get(), inSize);
  556. RecycleD3DResourceOnUploadHeap(upload.Get(), inSize);
  557. return resource;
  558. }
  559. ComPtr<ID3D12Resource> RendererDX12::CreateD3DResourceOnUploadHeap(uint64 inSize)
  560. {
  561. // Try cache first
  562. ResourceCache::iterator i = mResourceCache.find(inSize);
  563. if (i != mResourceCache.end() && !i->second.empty())
  564. {
  565. ComPtr<ID3D12Resource> resource = i->second.back();
  566. i->second.pop_back();
  567. return resource;
  568. }
  569. return CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, inSize);
  570. }
  571. void RendererDX12::RecycleD3DResourceOnUploadHeap(ID3D12Resource *inResource, uint64 inSize)
  572. {
  573. if (!mIsExiting)
  574. mDelayCached[mFrameIndex][inSize].push_back(inResource);
  575. }
  576. void RendererDX12::RecycleD3DObject(ID3D12Object *inResource)
  577. {
  578. if (!mIsExiting)
  579. mDelayReleased[mFrameIndex].push_back(inResource);
  580. }
  581. #ifndef JPH_ENABLE_VULKAN
  582. Renderer *Renderer::sCreate()
  583. {
  584. return new RendererDX12;
  585. }
  586. #endif