Renderer.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <TestFramework.h>
  4. #include <Renderer/Renderer.h>
  5. #include <Renderer/Texture.h>
  6. #include <Renderer/FatalErrorIfFailed.h>
  7. #include <Jolt/Core/Profiler.h>
  8. #include <Utils/ReadData.h>
  9. #include <Utils/Log.h>
  10. #pragma warning (push, 0)
  11. #pragma warning (disable : 4668) // DirectXMath.h(22): warning C4668: '_MANAGED' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
  12. #include <d3dcompiler.h>
  13. #include <ShellScalingApi.h>
  14. #include <DirectXMath.h>
  15. #pragma warning (pop)
  16. #pragma comment(lib, "dxgi.lib")
  17. #pragma comment(lib, "d3d12.lib")
  18. #pragma comment(lib, "d3dcompiler.lib")
  19. static Renderer *sRenderer = nullptr;
  20. struct VertexShaderConstantBuffer
  21. {
  22. DirectX::XMFLOAT4X4 mView;
  23. DirectX::XMFLOAT4X4 mProjection;
  24. DirectX::XMFLOAT4X4 mLightView;
  25. DirectX::XMFLOAT4X4 mLightProjection;
  26. };
  27. struct PixelShaderConstantBuffer
  28. {
  29. DirectX::XMFLOAT4 mCameraPos;
  30. DirectX::XMFLOAT4 mLightPos;
  31. };
  32. //--------------------------------------------------------------------------------------
  33. // Called every time the application receives a message
  34. //--------------------------------------------------------------------------------------
  35. static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  36. {
  37. PAINTSTRUCT ps;
  38. switch (message)
  39. {
  40. case WM_PAINT:
  41. BeginPaint(hWnd, &ps);
  42. EndPaint(hWnd, &ps);
  43. break;
  44. case WM_SIZE:
  45. if (sRenderer != nullptr)
  46. sRenderer->OnWindowResize();
  47. break;
  48. case WM_DESTROY:
  49. PostQuitMessage(0);
  50. break;
  51. default:
  52. return DefWindowProc(hWnd, message, wParam, lParam);
  53. }
  54. return 0;
  55. }
  56. Renderer::~Renderer()
  57. {
  58. // Ensure that the GPU is no longer referencing resources that are about to be cleaned up by the destructor.
  59. WaitForGpu();
  60. // Don't add more stuff to the delay reference list
  61. mIsExiting = true;
  62. CloseHandle(mFenceEvent);
  63. }
  64. void Renderer::WaitForGpu()
  65. {
  66. // Schedule a Signal command in the queue
  67. UINT64 current_fence_value = mFenceValues[mFrameIndex];
  68. FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), current_fence_value));
  69. // Wait until the fence has been processed
  70. FatalErrorIfFailed(mFence->SetEventOnCompletion(current_fence_value, mFenceEvent));
  71. WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
  72. // Increment the fence value for all frames
  73. for (uint n = 0; n < cFrameCount; ++n)
  74. mFenceValues[n] = current_fence_value + 1;
  75. // Release all used resources
  76. for (Array<ComPtr<ID3D12Object>> &list : mDelayReleased)
  77. list.clear();
  78. // Anything that's not used yet can be removed, delayed objects are now available
  79. mResourceCache.clear();
  80. mDelayCached[mFrameIndex].swap(mResourceCache);
  81. }
  82. void Renderer::CreateRenterTargets()
  83. {
  84. // Create render targets and views
  85. for (uint n = 0; n < cFrameCount; ++n)
  86. {
  87. mRenderTargetViews[n] = mRTVHeap.Allocate();
  88. FatalErrorIfFailed(mSwapChain->GetBuffer(n, IID_PPV_ARGS(&mRenderTargets[n])));
  89. mDevice->CreateRenderTargetView(mRenderTargets[n].Get(), nullptr, mRenderTargetViews[n]);
  90. }
  91. }
  92. void Renderer::CreateDepthBuffer()
  93. {
  94. // Free any previous depth stencil view
  95. if (mDepthStencilView.ptr != 0)
  96. mDSVHeap.Free(mDepthStencilView);
  97. // Free any previous depth stencil buffer
  98. mDepthStencilBuffer.Reset();
  99. // Allocate depth stencil buffer
  100. D3D12_CLEAR_VALUE clear_value = {};
  101. clear_value.Format = DXGI_FORMAT_D32_FLOAT;
  102. clear_value.DepthStencil.Depth = 1.0f;
  103. clear_value.DepthStencil.Stencil = 0;
  104. D3D12_HEAP_PROPERTIES heap_properties = {};
  105. heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
  106. heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
  107. heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
  108. heap_properties.CreationNodeMask = 1;
  109. heap_properties.VisibleNodeMask = 1;
  110. D3D12_RESOURCE_DESC depth_stencil_desc = {};
  111. depth_stencil_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
  112. depth_stencil_desc.Alignment = 0;
  113. depth_stencil_desc.Width = mWindowWidth;
  114. depth_stencil_desc.Height = mWindowHeight;
  115. depth_stencil_desc.DepthOrArraySize = 1;
  116. depth_stencil_desc.MipLevels = 1;
  117. depth_stencil_desc.Format = DXGI_FORMAT_D32_FLOAT;
  118. depth_stencil_desc.SampleDesc.Count = 1;
  119. depth_stencil_desc.SampleDesc.Quality = 0;
  120. depth_stencil_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
  121. depth_stencil_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
  122. FatalErrorIfFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &depth_stencil_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clear_value, IID_PPV_ARGS(&mDepthStencilBuffer)));
  123. // Allocate depth stencil view
  124. D3D12_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_desc = {};
  125. depth_stencil_view_desc.Format = DXGI_FORMAT_D32_FLOAT;
  126. depth_stencil_view_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
  127. depth_stencil_view_desc.Flags = D3D12_DSV_FLAG_NONE;
  128. mDepthStencilView = mDSVHeap.Allocate();
  129. mDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &depth_stencil_view_desc, mDepthStencilView);
  130. }
  131. void Renderer::Initialize()
  132. {
  133. // Prevent this window from auto scaling
  134. SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
  135. // Register class
  136. WNDCLASSEX wcex;
  137. wcex.cbSize = sizeof(WNDCLASSEX);
  138. wcex.style = CS_HREDRAW | CS_VREDRAW;
  139. wcex.lpfnWndProc = WndProc;
  140. wcex.cbClsExtra = 0;
  141. wcex.cbWndExtra = 0;
  142. wcex.hInstance = GetModuleHandle(nullptr);
  143. wcex.hIcon = nullptr;
  144. wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
  145. wcex.hbrBackground = nullptr;
  146. wcex.lpszMenuName = nullptr;
  147. wcex.lpszClassName = L"TestFrameworkClass";
  148. wcex.hIconSm = nullptr;
  149. if (!RegisterClassEx(&wcex))
  150. FatalError("Failed to register window class");
  151. // Create window
  152. RECT rc = { 0, 0, mWindowWidth, mWindowHeight };
  153. AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
  154. mhWnd = CreateWindow(L"TestFrameworkClass", L"TestFramework", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
  155. rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, wcex.hInstance, nullptr);
  156. if (!mhWnd)
  157. FatalError("Failed to create window");
  158. // Show window
  159. ShowWindow(mhWnd, SW_SHOW);
  160. #if defined(_DEBUG)
  161. // Enable the D3D12 debug layer
  162. ComPtr<ID3D12Debug> debug_controller;
  163. if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller))))
  164. debug_controller->EnableDebugLayer();
  165. #endif
  166. // Create DXGI factory
  167. FatalErrorIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&mDXGIFactory)));
  168. // Find adapter
  169. ComPtr<IDXGIAdapter1> adapter;
  170. // First check if we have the Windows 1803 IDXGIFactory6 interface
  171. ComPtr<IDXGIFactory6> factory6;
  172. if (SUCCEEDED(mDXGIFactory->QueryInterface(IID_PPV_ARGS(&factory6))))
  173. {
  174. for (UINT index = 0; DXGI_ERROR_NOT_FOUND != factory6->EnumAdapterByGpuPreference(index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)); ++index)
  175. {
  176. DXGI_ADAPTER_DESC1 desc;
  177. adapter->GetDesc1(&desc);
  178. // We don't want software renderers
  179. if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
  180. continue;
  181. // Check to see whether the adapter supports Direct3D 12
  182. if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice))))
  183. break;
  184. }
  185. }
  186. else
  187. {
  188. // Fall back to the older method that may not get the fastest GPU
  189. for (UINT index = 0; DXGI_ERROR_NOT_FOUND != mDXGIFactory->EnumAdapters1(index, &adapter); ++index)
  190. {
  191. DXGI_ADAPTER_DESC1 desc;
  192. adapter->GetDesc1(&desc);
  193. // We don't want software renderers
  194. if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
  195. continue;
  196. // Check to see whether the adapter supports Direct3D 12
  197. if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&mDevice))))
  198. break;
  199. }
  200. }
  201. #ifdef _DEBUG
  202. // Enable breaking on errors
  203. ComPtr<ID3D12InfoQueue> info_queue;
  204. if (SUCCEEDED(mDevice.As(&info_queue)))
  205. {
  206. info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
  207. info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
  208. info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE);
  209. // Disable an error that triggers on Windows 11 with a hybrid graphic system
  210. // See: https://stackoverflow.com/questions/69805245/directx-12-application-is-crashing-in-windows-11
  211. D3D12_MESSAGE_ID hide[] =
  212. {
  213. D3D12_MESSAGE_ID_RESOURCE_BARRIER_MISMATCHING_COMMAND_LIST_TYPE,
  214. };
  215. D3D12_INFO_QUEUE_FILTER filter = { };
  216. filter.DenyList.NumIDs = static_cast<UINT>( std::size( hide ) );
  217. filter.DenyList.pIDList = hide;
  218. info_queue->AddStorageFilterEntries( &filter );
  219. }
  220. #endif // _DEBUG
  221. // Disable full screen transitions
  222. FatalErrorIfFailed(mDXGIFactory->MakeWindowAssociation(mhWnd, DXGI_MWA_NO_ALT_ENTER));
  223. // Create heaps
  224. mRTVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 2);
  225. mDSVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 4);
  226. mSRVHeap.Init(mDevice.Get(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 128);
  227. // Create a command queue
  228. D3D12_COMMAND_QUEUE_DESC queue_desc = {};
  229. queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
  230. queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
  231. FatalErrorIfFailed(mDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&mCommandQueue)));
  232. // Create a command allocator for each frame
  233. for (uint n = 0; n < cFrameCount; n++)
  234. FatalErrorIfFailed(mDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocators[n])));
  235. // Describe and create the swap chain
  236. DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
  237. swap_chain_desc.BufferCount = cFrameCount;
  238. swap_chain_desc.BufferDesc.Width = mWindowWidth;
  239. swap_chain_desc.BufferDesc.Height = mWindowHeight;
  240. swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  241. swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  242. swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
  243. swap_chain_desc.OutputWindow = mhWnd;
  244. swap_chain_desc.SampleDesc.Count = 1;
  245. swap_chain_desc.Windowed = TRUE;
  246. ComPtr<IDXGISwapChain> swap_chain;
  247. FatalErrorIfFailed(mDXGIFactory->CreateSwapChain(mCommandQueue.Get(), &swap_chain_desc, &swap_chain));
  248. FatalErrorIfFailed(swap_chain.As(&mSwapChain));
  249. mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
  250. CreateRenterTargets();
  251. CreateDepthBuffer();
  252. // Create a root signature suitable for all our shaders
  253. D3D12_ROOT_PARAMETER params[3] = {};
  254. // Mapping a constant buffer to slot 0 for the vertex shader
  255. params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
  256. params[0].Descriptor.ShaderRegister = 0;
  257. params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
  258. // Mapping a constant buffer to slot 1 in the pixel shader
  259. params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
  260. params[1].Descriptor.ShaderRegister = 1;
  261. params[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
  262. // Mapping a texture to slot 2 in the pixel shader
  263. D3D12_DESCRIPTOR_RANGE range = {};
  264. range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
  265. range.BaseShaderRegister = 2;
  266. range.NumDescriptors = 1;
  267. params[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
  268. params[2].DescriptorTable.NumDescriptorRanges = 1;
  269. params[2].DescriptorTable.pDescriptorRanges = &range;
  270. params[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
  271. D3D12_STATIC_SAMPLER_DESC samplers[3] = {};
  272. // Sampler 0: Non-wrapping linear filtering
  273. samplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
  274. samplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
  275. samplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
  276. samplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
  277. samplers[0].MipLODBias = 0.0f;
  278. samplers[0].MaxAnisotropy = 1;
  279. samplers[0].ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
  280. samplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
  281. samplers[0].MinLOD = 0.0f;
  282. samplers[0].MaxLOD = D3D12_FLOAT32_MAX;
  283. samplers[0].ShaderRegister = 0;
  284. samplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
  285. // Sampler 1: Wrapping and linear filtering
  286. samplers[1] = samplers[0];
  287. samplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
  288. samplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
  289. samplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
  290. samplers[1].ShaderRegister = 1;
  291. // Sampler 2: Point filtering, using SampleCmp mode to compare if sampled value <= reference value (for shadows)
  292. samplers[2] = samplers[0];
  293. samplers[2].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
  294. samplers[2].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
  295. samplers[2].ShaderRegister = 2;
  296. D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {};
  297. root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
  298. root_signature_desc.NumParameters = ARRAYSIZE(params);
  299. root_signature_desc.pParameters = params;
  300. root_signature_desc.NumStaticSamplers = ARRAYSIZE(samplers);
  301. root_signature_desc.pStaticSamplers = samplers;
  302. ComPtr<ID3DBlob> signature;
  303. ComPtr<ID3DBlob> error;
  304. FatalErrorIfFailed(D3D12SerializeRootSignature(&root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
  305. FatalErrorIfFailed(mDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&mRootSignature)));
  306. // Create the command list
  307. FatalErrorIfFailed(mDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mCommandAllocators[mFrameIndex].Get(), nullptr, IID_PPV_ARGS(&mCommandList)));
  308. // 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
  309. FatalErrorIfFailed(mCommandList->Close());
  310. // Create synchronization object
  311. FatalErrorIfFailed(mDevice->CreateFence(mFenceValues[mFrameIndex], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)));
  312. // Increment fence value so we don't skip waiting the first time a command list is executed
  313. mFenceValues[mFrameIndex]++;
  314. // Create an event handle to use for frame synchronization
  315. mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
  316. if (mFenceEvent == nullptr)
  317. FatalErrorIfFailed(HRESULT_FROM_WIN32(GetLastError()));
  318. // Initialize the queue used to upload resources to the GPU
  319. mUploadQueue.Initialize(mDevice.Get());
  320. // Create constant buffer. One per frame to avoid overwriting the constant buffer while the GPU is still using it.
  321. for (uint n = 0; n < cFrameCount; ++n)
  322. {
  323. mVertexShaderConstantBufferProjection[n] = CreateConstantBuffer(sizeof(VertexShaderConstantBuffer));
  324. mVertexShaderConstantBufferOrtho[n] = CreateConstantBuffer(sizeof(VertexShaderConstantBuffer));
  325. mPixelShaderConstantBuffer[n] = CreateConstantBuffer(sizeof(PixelShaderConstantBuffer));
  326. }
  327. // Store global renderer now that we're done initializing
  328. sRenderer = this;
  329. }
  330. void Renderer::OnWindowResize()
  331. {
  332. // Wait for the previous frame to be rendered
  333. WaitForGpu();
  334. // Get new window size
  335. RECT rc;
  336. GetClientRect(mhWnd, &rc);
  337. mWindowWidth = max<LONG>(rc.right - rc.left, 8);
  338. mWindowHeight = max<LONG>(rc.bottom - rc.top, 8);
  339. // Free the render targets and views to allow resizing the swap chain
  340. for (uint n = 0; n < cFrameCount; ++n)
  341. {
  342. mRTVHeap.Free(mRenderTargetViews[n]);
  343. mRenderTargets[n].Reset();
  344. }
  345. // Resize the swap chain buffers
  346. FatalErrorIfFailed(mSwapChain->ResizeBuffers(cFrameCount, mWindowWidth, mWindowHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
  347. // Back buffer index may have changed after the resize (it always seems to go to 0 again)
  348. mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
  349. // 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
  350. for (uint n = 0; n < cFrameCount; ++n)
  351. if (mFrameIndex != n)
  352. mFenceValues[n] = mFence->GetCompletedValue();
  353. // Recreate render targets
  354. CreateRenterTargets();
  355. // Recreate depth buffer
  356. CreateDepthBuffer();
  357. }
  358. void Renderer::BeginFrame(const CameraState &inCamera, float inWorldScale)
  359. {
  360. JPH_PROFILE_FUNCTION();
  361. // Store state
  362. mCameraState = inCamera;
  363. // Reset command allocator
  364. FatalErrorIfFailed(mCommandAllocators[mFrameIndex]->Reset());
  365. // Reset command list
  366. FatalErrorIfFailed(mCommandList->Reset(mCommandAllocators[mFrameIndex].Get(), nullptr));
  367. // Set root signature
  368. mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
  369. // Set SRV heap
  370. ID3D12DescriptorHeap *heaps[] = { mSRVHeap.Get() };
  371. mCommandList->SetDescriptorHeaps(_countof(heaps), heaps);
  372. // Indicate that the back buffer will be used as a render target.
  373. D3D12_RESOURCE_BARRIER barrier;
  374. barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
  375. barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
  376. barrier.Transition.pResource = mRenderTargets[mFrameIndex].Get();
  377. barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
  378. barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
  379. barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
  380. mCommandList->ResourceBarrier(1, &barrier);
  381. // Set the main back buffer as render target
  382. SetRenderTarget(nullptr);
  383. // Clear the back buffer.
  384. const float blue[] = { 0.098f, 0.098f, 0.439f, 1.000f };
  385. mCommandList->ClearRenderTargetView(mRenderTargetViews[mFrameIndex], blue, 0, nullptr);
  386. mCommandList->ClearDepthStencilView(mDepthStencilView, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
  387. // Light properties
  388. Vec3 light_pos = inWorldScale * Vec3(250, 250, 250);
  389. Vec3 light_tgt = Vec3::sZero();
  390. Vec3 light_up = Vec3(0, 1, 0);
  391. Vec3 light_fwd = (light_tgt - light_pos).Normalized();
  392. float light_fov = DegreesToRadians(20.0f);
  393. float light_near = 1.0f;
  394. float light_far = 1000.0f;
  395. // Camera properties
  396. float camera_fovy = inCamera.mFOVY;
  397. float camera_aspect = static_cast<float>(GetWindowWidth()) / GetWindowHeight();
  398. float camera_fovx = 2.0f * ATan(camera_aspect * Tan(0.5f * camera_fovy));
  399. float camera_near = 0.01f * inWorldScale;
  400. float camera_far = inCamera.mFarPlane * inWorldScale;
  401. // Set constants for vertex shader in projection mode
  402. VertexShaderConstantBuffer *vs = mVertexShaderConstantBufferProjection[mFrameIndex]->Map<VertexShaderConstantBuffer>();
  403. // Camera projection and view
  404. DirectX::XMStoreFloat4x4(
  405. &vs->mProjection,
  406. DirectX::XMMatrixPerspectiveFovRH(
  407. camera_fovy,
  408. camera_aspect,
  409. camera_near,
  410. camera_far));
  411. Vec3 tgt = inCamera.mPos + inCamera.mForward;
  412. DirectX::XMStoreFloat4x4(
  413. &vs->mView,
  414. DirectX::XMMatrixLookAtRH(reinterpret_cast<const DirectX::XMVECTOR &>(inCamera.mPos), reinterpret_cast<const DirectX::XMVECTOR &>(tgt), reinterpret_cast<const DirectX::XMVECTOR &>(inCamera.mUp)));
  415. // Light projection and view
  416. DirectX::XMStoreFloat4x4(
  417. &vs->mLightProjection,
  418. DirectX::XMMatrixPerspectiveFovRH(
  419. light_fov,
  420. 1.0f,
  421. light_near,
  422. light_far));
  423. DirectX::XMStoreFloat4x4(
  424. &vs->mLightView,
  425. DirectX::XMMatrixLookAtRH(reinterpret_cast<const DirectX::XMVECTOR &>(light_pos), reinterpret_cast<const DirectX::XMVECTOR &>(light_tgt), reinterpret_cast<const DirectX::XMVECTOR &>(light_up)));
  426. mVertexShaderConstantBufferProjection[mFrameIndex]->Unmap();
  427. // Set constants for vertex shader in ortho mode
  428. vs = mVertexShaderConstantBufferOrtho[mFrameIndex]->Map<VertexShaderConstantBuffer>();
  429. // Camera projection and view
  430. DirectX::XMStoreFloat4x4(
  431. &vs->mProjection,
  432. DirectX::XMMatrixOrthographicOffCenterRH(
  433. 0.0f,
  434. float(mWindowWidth),
  435. float(mWindowHeight),
  436. 0.0f,
  437. 0.0f,
  438. 1.0f));
  439. DirectX::XMStoreFloat4x4(
  440. &vs->mView,
  441. DirectX::XMMatrixIdentity());
  442. // Light projection and view are unused in ortho mode
  443. DirectX::XMStoreFloat4x4(
  444. &vs->mLightView,
  445. DirectX::XMMatrixIdentity());
  446. DirectX::XMStoreFloat4x4(
  447. &vs->mLightProjection,
  448. DirectX::XMMatrixIdentity());
  449. mVertexShaderConstantBufferOrtho[mFrameIndex]->Unmap();
  450. // Switch to 3d projection mode
  451. SetProjectionMode();
  452. // Set constants for pixel shader
  453. PixelShaderConstantBuffer *ps = mPixelShaderConstantBuffer[mFrameIndex]->Map<PixelShaderConstantBuffer>();
  454. ps->mCameraPos = DirectX::XMFLOAT4(inCamera.mPos.GetX(), inCamera.mPos.GetY(), inCamera.mPos.GetZ(), 0);
  455. ps->mLightPos = DirectX::XMFLOAT4(light_pos.GetX(), light_pos.GetY(), light_pos.GetZ(), 0);
  456. mPixelShaderConstantBuffer[mFrameIndex]->Unmap();
  457. // Set the pixel shader constant buffer data.
  458. mPixelShaderConstantBuffer[mFrameIndex]->Bind(1);
  459. // Calculate camera frustum
  460. mCameraFrustum = Frustum(inCamera.mPos, inCamera.mForward, inCamera.mUp, camera_fovx, camera_fovy, camera_near, camera_far);
  461. // Calculate light frustum
  462. mLightFrustum = Frustum(light_pos, light_fwd, light_up, light_fov, light_fov, light_near, light_far);
  463. }
  464. void Renderer::EndFrame()
  465. {
  466. JPH_PROFILE_FUNCTION();
  467. // Indicate that the back buffer will now be used to present.
  468. D3D12_RESOURCE_BARRIER barrier;
  469. barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
  470. barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
  471. barrier.Transition.pResource = mRenderTargets[mFrameIndex].Get();
  472. barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
  473. barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
  474. barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
  475. mCommandList->ResourceBarrier(1, &barrier);
  476. // Close the command list
  477. FatalErrorIfFailed(mCommandList->Close());
  478. // Execute the command list
  479. ID3D12CommandList* command_lists[] = { mCommandList.Get() };
  480. mCommandQueue->ExecuteCommandLists(_countof(command_lists), command_lists);
  481. // Present the frame
  482. FatalErrorIfFailed(mSwapChain->Present(1, 0));
  483. // Schedule a Signal command in the queue
  484. UINT64 current_fence_value = mFenceValues[mFrameIndex];
  485. FatalErrorIfFailed(mCommandQueue->Signal(mFence.Get(), current_fence_value));
  486. // Update the frame index
  487. mFrameIndex = mSwapChain->GetCurrentBackBufferIndex();
  488. // If the next frame is not ready to be rendered yet, wait until it is ready
  489. UINT64 completed_value = mFence->GetCompletedValue();
  490. if (completed_value < mFenceValues[mFrameIndex])
  491. {
  492. FatalErrorIfFailed(mFence->SetEventOnCompletion(mFenceValues[mFrameIndex], mFenceEvent));
  493. WaitForSingleObjectEx(mFenceEvent, INFINITE, FALSE);
  494. }
  495. // Release all used resources
  496. mDelayReleased[mFrameIndex].clear();
  497. // Anything that's not used yet can be removed, delayed objects are now available
  498. mResourceCache.clear();
  499. mDelayCached[mFrameIndex].swap(mResourceCache);
  500. // Set the fence value for the next frame.
  501. mFenceValues[mFrameIndex] = current_fence_value + 1;
  502. }
  503. void Renderer::SetProjectionMode()
  504. {
  505. mVertexShaderConstantBufferProjection[mFrameIndex]->Bind(0);
  506. }
  507. void Renderer::SetOrthoMode()
  508. {
  509. mVertexShaderConstantBufferOrtho[mFrameIndex]->Bind(0);
  510. }
  511. Ref<Texture> Renderer::CreateTexture(const Surface *inSurface)
  512. {
  513. return new Texture(this, inSurface);
  514. }
  515. Ref<Texture> Renderer::CreateRenderTarget(int inWidth, int inHeight)
  516. {
  517. return new Texture(this, inWidth, inHeight);
  518. }
  519. void Renderer::SetRenderTarget(Texture *inRenderTarget)
  520. {
  521. // Unset the previous render target
  522. if (mRenderTargetTexture != nullptr)
  523. mRenderTargetTexture->SetAsRenderTarget(false);
  524. mRenderTargetTexture = nullptr;
  525. if (inRenderTarget == nullptr)
  526. {
  527. // Set the main back buffer as render target
  528. mCommandList->OMSetRenderTargets(1, &mRenderTargetViews[mFrameIndex], FALSE, &mDepthStencilView);
  529. // Set viewport
  530. D3D12_VIEWPORT viewport = { 0.0f, 0.0f, static_cast<float>(mWindowWidth), static_cast<float>(mWindowHeight), 0.0f, 1.0f };
  531. mCommandList->RSSetViewports(1, &viewport);
  532. // Set scissor rect
  533. D3D12_RECT scissor_rect = { 0, 0, static_cast<LONG>(mWindowWidth), static_cast<LONG>(mWindowHeight) };
  534. mCommandList->RSSetScissorRects(1, &scissor_rect);
  535. }
  536. else
  537. {
  538. // Use the texture as render target
  539. inRenderTarget->SetAsRenderTarget(true);
  540. mRenderTargetTexture = inRenderTarget;
  541. }
  542. }
  543. ComPtr<ID3DBlob> Renderer::CreateVertexShader(const char *inFileName)
  544. {
  545. UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
  546. #ifdef _DEBUG
  547. flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
  548. #endif
  549. const D3D_SHADER_MACRO defines[] =
  550. {
  551. { nullptr, nullptr }
  552. };
  553. // Read shader source file
  554. Array<uint8> data = ReadData(inFileName);
  555. // Compile source
  556. ComPtr<ID3DBlob> shader_blob, error_blob;
  557. HRESULT hr = D3DCompile(&data[0],
  558. (uint)data.size(),
  559. inFileName,
  560. defines,
  561. D3D_COMPILE_STANDARD_FILE_INCLUDE,
  562. "main",
  563. "vs_5_0",
  564. flags,
  565. 0,
  566. shader_blob.GetAddressOf(),
  567. error_blob.GetAddressOf());
  568. if (FAILED(hr))
  569. {
  570. // Throw error if compilation failed
  571. if (error_blob)
  572. OutputDebugStringA((const char *)error_blob->GetBufferPointer());
  573. FatalError("Failed to compile vertex shader");
  574. }
  575. return shader_blob;
  576. }
  577. ComPtr<ID3DBlob> Renderer::CreatePixelShader(const char *inFileName)
  578. {
  579. UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
  580. #ifdef _DEBUG
  581. flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
  582. #endif
  583. const D3D_SHADER_MACRO defines[] =
  584. {
  585. { nullptr, nullptr }
  586. };
  587. // Read shader source file
  588. Array<uint8> data = ReadData(inFileName);
  589. // Compile source
  590. ComPtr<ID3DBlob> shader_blob, error_blob;
  591. HRESULT hr = D3DCompile(&data[0],
  592. (uint)data.size(),
  593. inFileName,
  594. defines,
  595. D3D_COMPILE_STANDARD_FILE_INCLUDE,
  596. "main",
  597. "ps_5_0",
  598. flags,
  599. 0,
  600. shader_blob.GetAddressOf(),
  601. error_blob.GetAddressOf());
  602. if (FAILED(hr))
  603. {
  604. // Throw error if compilation failed
  605. if (error_blob)
  606. OutputDebugStringA((const char *)error_blob->GetBufferPointer());
  607. FatalError("Failed to compile pixel shader");
  608. }
  609. return shader_blob;
  610. }
  611. unique_ptr<ConstantBuffer> Renderer::CreateConstantBuffer(uint inBufferSize)
  612. {
  613. return make_unique<ConstantBuffer>(this, inBufferSize);
  614. }
  615. unique_ptr<PipelineState> Renderer::CreatePipelineState(ID3DBlob *inVertexShader, const D3D12_INPUT_ELEMENT_DESC *inInputDescription, uint inInputDescriptionCount, ID3DBlob *inPixelShader, D3D12_FILL_MODE inFillMode, D3D12_PRIMITIVE_TOPOLOGY_TYPE inTopology, PipelineState::EDepthTest inDepthTest, PipelineState::EBlendMode inBlendMode, PipelineState::ECullMode inCullMode)
  616. {
  617. return make_unique<PipelineState>(this, inVertexShader, inInputDescription, inInputDescriptionCount, inPixelShader, inFillMode, inTopology, inDepthTest, inBlendMode, inCullMode);
  618. }
  619. ComPtr<ID3D12Resource> Renderer::CreateD3DResource(D3D12_HEAP_TYPE inHeapType, D3D12_RESOURCE_STATES inResourceState, uint64 inSize)
  620. {
  621. // Create a new resource
  622. D3D12_RESOURCE_DESC desc;
  623. desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
  624. desc.Alignment = 0;
  625. desc.Width = inSize;
  626. desc.Height = 1;
  627. desc.DepthOrArraySize = 1;
  628. desc.MipLevels = 1;
  629. desc.Format = DXGI_FORMAT_UNKNOWN;
  630. desc.SampleDesc.Count = 1;
  631. desc.SampleDesc.Quality = 0;
  632. desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
  633. desc.Flags = D3D12_RESOURCE_FLAG_NONE;
  634. D3D12_HEAP_PROPERTIES heap_properties = {};
  635. heap_properties.Type = inHeapType;
  636. heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
  637. heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
  638. heap_properties.CreationNodeMask = 1;
  639. heap_properties.VisibleNodeMask = 1;
  640. ComPtr<ID3D12Resource> resource;
  641. FatalErrorIfFailed(mDevice->CreateCommittedResource(&heap_properties, D3D12_HEAP_FLAG_NONE, &desc, inResourceState, nullptr, IID_PPV_ARGS(&resource)));
  642. return resource;
  643. }
  644. void Renderer::CopyD3DResource(ID3D12Resource *inDest, const void *inSrc, uint64 inSize)
  645. {
  646. // Copy data to destination buffer
  647. void *data;
  648. D3D12_RANGE range = { 0, 0 }; // We're not going to read
  649. FatalErrorIfFailed(inDest->Map(0, &range, &data));
  650. memcpy(data, inSrc, size_t(inSize));
  651. inDest->Unmap(0, nullptr);
  652. }
  653. void Renderer::CopyD3DResource(ID3D12Resource *inDest, ID3D12Resource *inSrc, uint64 inSize)
  654. {
  655. // Start a commandlist for the upload
  656. ID3D12GraphicsCommandList *list = mUploadQueue.Start();
  657. // Copy the data to the GPU
  658. list->CopyBufferRegion(inDest, 0, inSrc, 0, inSize);
  659. // Change the state of the resource to generic read
  660. D3D12_RESOURCE_BARRIER barrier;
  661. barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
  662. barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
  663. barrier.Transition.pResource = inDest;
  664. barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
  665. barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_GENERIC_READ;
  666. barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
  667. list->ResourceBarrier(1, &barrier);
  668. // Wait for copying to finish
  669. mUploadQueue.ExecuteAndWait();
  670. }
  671. ComPtr<ID3D12Resource> Renderer::CreateD3DResourceOnDefaultHeap(const void *inData, uint64 inSize)
  672. {
  673. ComPtr<ID3D12Resource> upload = CreateD3DResourceOnUploadHeap(inSize);
  674. ComPtr<ID3D12Resource> resource = CreateD3DResource(D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_STATE_COPY_DEST, inSize);
  675. CopyD3DResource(upload.Get(), inData, inSize);
  676. CopyD3DResource(resource.Get(), upload.Get(), inSize);
  677. RecycleD3DResourceOnUploadHeap(upload.Get(), inSize);
  678. return resource;
  679. }
  680. ComPtr<ID3D12Resource> Renderer::CreateD3DResourceOnUploadHeap(uint64 inSize)
  681. {
  682. // Try cache first
  683. ResourceCache::iterator i = mResourceCache.find(inSize);
  684. if (i != mResourceCache.end() && !i->second.empty())
  685. {
  686. ComPtr<ID3D12Resource> resource = i->second.back();
  687. i->second.pop_back();
  688. return resource;
  689. }
  690. return CreateD3DResource(D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, inSize);
  691. }
  692. void Renderer::RecycleD3DResourceOnUploadHeap(ID3D12Resource *inResource, uint64 inSize)
  693. {
  694. if (!mIsExiting)
  695. mDelayCached[mFrameIndex][inSize].push_back(inResource);
  696. }
  697. void Renderer::RecycleD3DObject(ID3D12Object *inResource)
  698. {
  699. if (!mIsExiting)
  700. mDelayReleased[mFrameIndex].push_back(inResource);
  701. }