1 // Copyright 2019 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Common/Logging/Log.h"
6 
7 #include "VideoBackends/D3D12/BoundingBox.h"
8 #include "VideoBackends/D3D12/Common.h"
9 #include "VideoBackends/D3D12/DXContext.h"
10 #include "VideoBackends/D3D12/DXPipeline.h"
11 #include "VideoBackends/D3D12/DXShader.h"
12 #include "VideoBackends/D3D12/DXTexture.h"
13 #include "VideoBackends/D3D12/DXVertexFormat.h"
14 #include "VideoBackends/D3D12/DescriptorHeapManager.h"
15 #include "VideoBackends/D3D12/PerfQuery.h"
16 #include "VideoBackends/D3D12/Renderer.h"
17 #include "VideoBackends/D3D12/SwapChain.h"
18 #include "VideoCommon/VideoConfig.h"
19 
20 namespace DX12
21 {
Renderer(std::unique_ptr<SwapChain> swap_chain,float backbuffer_scale)22 Renderer::Renderer(std::unique_ptr<SwapChain> swap_chain, float backbuffer_scale)
23     : ::Renderer(swap_chain ? swap_chain->GetWidth() : 0, swap_chain ? swap_chain->GetHeight() : 0,
24                  backbuffer_scale,
25                  swap_chain ? swap_chain->GetFormat() : AbstractTextureFormat::Undefined),
26       m_swap_chain(std::move(swap_chain))
27 {
28   m_state.root_signature = g_dx_context->GetGXRootSignature();
29 
30   // Textures must be populated with null descriptors, since we copy directly from this array.
31   for (u32 i = 0; i < MAX_TEXTURES; i++)
32   {
33     m_state.textures[i].ptr = g_dx_context->GetNullSRVDescriptor().cpu_handle.ptr;
34     m_state.samplers.states[i] = RenderState::GetPointSamplerState();
35   }
36 }
37 
38 Renderer::~Renderer() = default;
39 
IsHeadless() const40 bool Renderer::IsHeadless() const
41 {
42   return !m_swap_chain;
43 }
44 
Initialize()45 bool Renderer::Initialize()
46 {
47   if (!::Renderer::Initialize())
48     return false;
49 
50   m_bounding_box = BoundingBox::Create();
51   if (!m_bounding_box)
52     return false;
53 
54   SetPixelShaderUAV(m_bounding_box->GetGPUDescriptor().cpu_handle);
55   return true;
56 }
57 
Shutdown()58 void Renderer::Shutdown()
59 {
60   m_bounding_box.reset();
61   m_swap_chain.reset();
62 
63   ::Renderer::Shutdown();
64 }
65 
CreateTexture(const TextureConfig & config)66 std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
67 {
68   return DXTexture::Create(config);
69 }
70 
CreateStagingTexture(StagingTextureType type,const TextureConfig & config)71 std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
72                                                                        const TextureConfig& config)
73 {
74   return DXStagingTexture::Create(type, config);
75 }
76 
CreateFramebuffer(AbstractTexture * color_attachment,AbstractTexture * depth_attachment)77 std::unique_ptr<AbstractFramebuffer> Renderer::CreateFramebuffer(AbstractTexture* color_attachment,
78                                                                  AbstractTexture* depth_attachment)
79 {
80   return DXFramebuffer::Create(static_cast<DXTexture*>(color_attachment),
81                                static_cast<DXTexture*>(depth_attachment));
82 }
83 
CreateShaderFromSource(ShaderStage stage,std::string_view source)84 std::unique_ptr<AbstractShader> Renderer::CreateShaderFromSource(ShaderStage stage,
85                                                                  std::string_view source)
86 {
87   return DXShader::CreateFromSource(stage, source);
88 }
89 
CreateShaderFromBinary(ShaderStage stage,const void * data,size_t length)90 std::unique_ptr<AbstractShader> Renderer::CreateShaderFromBinary(ShaderStage stage,
91                                                                  const void* data, size_t length)
92 {
93   return DXShader::CreateFromBytecode(stage, DXShader::CreateByteCode(data, length));
94 }
95 
96 std::unique_ptr<NativeVertexFormat>
CreateNativeVertexFormat(const PortableVertexDeclaration & vtx_decl)97 Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
98 {
99   return std::make_unique<DXVertexFormat>(vtx_decl);
100 }
101 
CreatePipeline(const AbstractPipelineConfig & config,const void * cache_data,size_t cache_data_length)102 std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelineConfig& config,
103                                                            const void* cache_data,
104                                                            size_t cache_data_length)
105 {
106   return DXPipeline::Create(config, cache_data, cache_data_length);
107 }
108 
BBoxRead(int index)109 u16 Renderer::BBoxRead(int index)
110 {
111   return static_cast<u16>(m_bounding_box->Get(index));
112 }
113 
BBoxWrite(int index,u16 value)114 void Renderer::BBoxWrite(int index, u16 value)
115 {
116   m_bounding_box->Set(index, value);
117 }
118 
BBoxFlush()119 void Renderer::BBoxFlush()
120 {
121   m_bounding_box->Flush();
122   m_bounding_box->Invalidate();
123 }
124 
Flush()125 void Renderer::Flush()
126 {
127   ExecuteCommandList(false);
128 }
129 
WaitForGPUIdle()130 void Renderer::WaitForGPUIdle()
131 {
132   ExecuteCommandList(true);
133 }
134 
ClearScreen(const MathUtil::Rectangle<int> & rc,bool color_enable,bool alpha_enable,bool z_enable,u32 color,u32 z)135 void Renderer::ClearScreen(const MathUtil::Rectangle<int>& rc, bool color_enable, bool alpha_enable,
136                            bool z_enable, u32 color, u32 z)
137 {
138   // Use a fast path without the shader if both color/alpha are enabled.
139   const bool fast_color_clear = color_enable && (alpha_enable || !EFBHasAlphaChannel());
140   if (fast_color_clear || z_enable)
141   {
142     MathUtil::Rectangle<int> native_rc = ConvertEFBRectangle(rc);
143     native_rc.ClampUL(0, 0, m_current_framebuffer->GetWidth(), m_current_framebuffer->GetHeight());
144     const D3D12_RECT d3d_clear_rc{native_rc.left, native_rc.top, native_rc.right, native_rc.bottom};
145 
146     if (fast_color_clear)
147     {
148       static_cast<DXTexture*>(m_current_framebuffer->GetColorAttachment())
149           ->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
150 
151       const std::array<float, 4> clear_color = {
152           {static_cast<float>((color >> 16) & 0xFF) / 255.0f,
153            static_cast<float>((color >> 8) & 0xFF) / 255.0f,
154            static_cast<float>((color >> 0) & 0xFF) / 255.0f,
155            static_cast<float>((color >> 24) & 0xFF) / 255.0f}};
156       g_dx_context->GetCommandList()->ClearRenderTargetView(
157           static_cast<const DXFramebuffer*>(m_current_framebuffer)->GetRTVDescriptor().cpu_handle,
158           clear_color.data(), 1, &d3d_clear_rc);
159       color_enable = false;
160       alpha_enable = false;
161     }
162 
163     if (z_enable)
164     {
165       static_cast<DXTexture*>(m_current_framebuffer->GetDepthAttachment())
166           ->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
167 
168       // D3D does not support reversed depth ranges.
169       const float clear_depth = 1.0f - static_cast<float>(z & 0xFFFFFF) / 16777216.0f;
170       g_dx_context->GetCommandList()->ClearDepthStencilView(
171           static_cast<const DXFramebuffer*>(m_current_framebuffer)->GetDSVDescriptor().cpu_handle,
172           D3D12_CLEAR_FLAG_DEPTH, clear_depth, 0, 1, &d3d_clear_rc);
173       z_enable = false;
174     }
175   }
176 
177   // Anything left over, fall back to clear triangle.
178   if (color_enable || alpha_enable || z_enable)
179     ::Renderer::ClearScreen(rc, color_enable, alpha_enable, z_enable, color, z);
180 }
181 
SetPipeline(const AbstractPipeline * pipeline)182 void Renderer::SetPipeline(const AbstractPipeline* pipeline)
183 {
184   const DXPipeline* dx_pipeline = static_cast<const DXPipeline*>(pipeline);
185   if (m_current_pipeline == dx_pipeline)
186     return;
187 
188   m_current_pipeline = dx_pipeline;
189   m_dirty_bits |= DirtyState_Pipeline;
190 
191   if (dx_pipeline)
192   {
193     if (dx_pipeline->GetRootSignature() != m_state.root_signature)
194     {
195       m_state.root_signature = dx_pipeline->GetRootSignature();
196       m_dirty_bits |= DirtyState_RootSignature | DirtyState_PS_CBV | DirtyState_VS_CBV |
197                       DirtyState_GS_CBV | DirtyState_SRV_Descriptor |
198                       DirtyState_Sampler_Descriptor | DirtyState_UAV_Descriptor;
199     }
200     if (dx_pipeline->UseIntegerRTV() != m_state.using_integer_rtv)
201     {
202       m_state.using_integer_rtv = dx_pipeline->UseIntegerRTV();
203       m_dirty_bits |= DirtyState_Framebuffer;
204     }
205     if (dx_pipeline->GetPrimitiveTopology() != m_state.primitive_topology)
206     {
207       m_state.primitive_topology = dx_pipeline->GetPrimitiveTopology();
208       m_dirty_bits |= DirtyState_PrimitiveTopology;
209     }
210   }
211 }
212 
BindFramebuffer(DXFramebuffer * fb)213 void Renderer::BindFramebuffer(DXFramebuffer* fb)
214 {
215   if (fb->HasColorBuffer())
216   {
217     static_cast<DXTexture*>(fb->GetColorAttachment())
218         ->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
219   }
220   if (fb->HasDepthBuffer())
221   {
222     static_cast<DXTexture*>(fb->GetDepthAttachment())
223         ->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
224   }
225 
226   g_dx_context->GetCommandList()->OMSetRenderTargets(
227       fb->GetRTVDescriptorCount(),
228       m_state.using_integer_rtv ? fb->GetIntRTVDescriptorArray() : fb->GetRTVDescriptorArray(),
229       FALSE, fb->GetDSVDescriptorArray());
230   m_current_framebuffer = fb;
231   m_dirty_bits &= ~DirtyState_Framebuffer;
232 }
233 
SetFramebuffer(AbstractFramebuffer * framebuffer)234 void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer)
235 {
236   if (m_current_framebuffer == framebuffer)
237     return;
238 
239   m_current_framebuffer = framebuffer;
240   m_dirty_bits |= DirtyState_Framebuffer;
241 }
242 
SetAndDiscardFramebuffer(AbstractFramebuffer * framebuffer)243 void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer)
244 {
245   BindFramebuffer(static_cast<DXFramebuffer*>(framebuffer));
246 
247   static const D3D12_DISCARD_REGION dr = {0, nullptr, 0, 1};
248   if (framebuffer->HasColorBuffer())
249   {
250     g_dx_context->GetCommandList()->DiscardResource(
251         static_cast<DXTexture*>(framebuffer->GetColorAttachment())->GetResource(), &dr);
252   }
253   if (framebuffer->HasDepthBuffer())
254   {
255     g_dx_context->GetCommandList()->DiscardResource(
256         static_cast<DXTexture*>(framebuffer->GetDepthAttachment())->GetResource(), &dr);
257   }
258 }
259 
SetAndClearFramebuffer(AbstractFramebuffer * framebuffer,const ClearColor & color_value,float depth_value)260 void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer,
261                                       const ClearColor& color_value, float depth_value)
262 {
263   DXFramebuffer* dxfb = static_cast<DXFramebuffer*>(framebuffer);
264   BindFramebuffer(dxfb);
265 
266   static const D3D12_DISCARD_REGION dr = {0, nullptr, 0, 1};
267   if (framebuffer->HasColorBuffer())
268   {
269     g_dx_context->GetCommandList()->ClearRenderTargetView(dxfb->GetRTVDescriptor().cpu_handle,
270                                                           color_value.data(), 0, nullptr);
271   }
272   if (framebuffer->HasDepthBuffer())
273   {
274     g_dx_context->GetCommandList()->ClearDepthStencilView(
275         dxfb->GetDSVDescriptor().cpu_handle, D3D12_CLEAR_FLAG_DEPTH, depth_value, 0, 0, nullptr);
276   }
277 }
278 
SetScissorRect(const MathUtil::Rectangle<int> & rc)279 void Renderer::SetScissorRect(const MathUtil::Rectangle<int>& rc)
280 {
281   if (m_state.scissor.left == rc.left && m_state.scissor.right == rc.right &&
282       m_state.scissor.top == rc.top && m_state.scissor.bottom == rc.bottom)
283   {
284     return;
285   }
286 
287   m_state.scissor.left = rc.left;
288   m_state.scissor.right = rc.right;
289   m_state.scissor.top = rc.top;
290   m_state.scissor.bottom = rc.bottom;
291   m_dirty_bits |= DirtyState_ScissorRect;
292 }
293 
SetTexture(u32 index,const AbstractTexture * texture)294 void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
295 {
296   const DXTexture* dxtex = static_cast<const DXTexture*>(texture);
297   if (m_state.textures[index].ptr == dxtex->GetSRVDescriptor().cpu_handle.ptr)
298     return;
299 
300   m_state.textures[index].ptr = dxtex->GetSRVDescriptor().cpu_handle.ptr;
301   if (dxtex)
302     dxtex->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
303 
304   m_dirty_bits |= DirtyState_Textures;
305 }
306 
SetSamplerState(u32 index,const SamplerState & state)307 void Renderer::SetSamplerState(u32 index, const SamplerState& state)
308 {
309   if (m_state.samplers.states[index] == state)
310     return;
311 
312   m_state.samplers.states[index] = state;
313   m_dirty_bits |= DirtyState_Samplers;
314 }
315 
SetComputeImageTexture(AbstractTexture * texture,bool read,bool write)316 void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write)
317 {
318   const DXTexture* dxtex = static_cast<const DXTexture*>(texture);
319   if (m_state.compute_image_texture == dxtex)
320     return;
321 
322   m_state.compute_image_texture = dxtex;
323   m_dirty_bits |= DirtyState_ComputeImageTexture;
324 }
325 
UnbindTexture(const AbstractTexture * texture)326 void Renderer::UnbindTexture(const AbstractTexture* texture)
327 {
328   const auto srv_shadow_descriptor =
329       static_cast<const DXTexture*>(texture)->GetSRVDescriptor().cpu_handle;
330   for (u32 i = 0; i < MAX_TEXTURES; i++)
331   {
332     if (m_state.textures[i].ptr == srv_shadow_descriptor.ptr)
333     {
334       m_state.textures[i].ptr = g_dx_context->GetNullSRVDescriptor().cpu_handle.ptr;
335       m_dirty_bits |= DirtyState_Textures;
336     }
337   }
338   if (m_state.compute_image_texture == texture)
339   {
340     m_state.compute_image_texture = nullptr;
341     m_dirty_bits |= DirtyState_ComputeImageTexture;
342   }
343 }
344 
SetViewport(float x,float y,float width,float height,float near_depth,float far_depth)345 void Renderer::SetViewport(float x, float y, float width, float height, float near_depth,
346                            float far_depth)
347 {
348   if (m_state.viewport.TopLeftX == x && m_state.viewport.TopLeftY == y &&
349       m_state.viewport.Width == width && m_state.viewport.Height == height &&
350       near_depth == m_state.viewport.MinDepth && far_depth == m_state.viewport.MaxDepth)
351   {
352     return;
353   }
354 
355   m_state.viewport.TopLeftX = x;
356   m_state.viewport.TopLeftY = y;
357   m_state.viewport.Width = width;
358   m_state.viewport.Height = height;
359   m_state.viewport.MinDepth = near_depth;
360   m_state.viewport.MaxDepth = far_depth;
361   m_dirty_bits |= DirtyState_Viewport;
362 }
363 
Draw(u32 base_vertex,u32 num_vertices)364 void Renderer::Draw(u32 base_vertex, u32 num_vertices)
365 {
366   if (!ApplyState())
367     return;
368 
369   g_dx_context->GetCommandList()->DrawInstanced(num_vertices, 1, base_vertex, 0);
370 }
371 
DrawIndexed(u32 base_index,u32 num_indices,u32 base_vertex)372 void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
373 {
374   if (!ApplyState())
375     return;
376 
377   g_dx_context->GetCommandList()->DrawIndexedInstanced(num_indices, 1, base_index, base_vertex, 0);
378 }
379 
DispatchComputeShader(const AbstractShader * shader,u32 groups_x,u32 groups_y,u32 groups_z)380 void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groups_x, u32 groups_y,
381                                      u32 groups_z)
382 {
383   SetRootSignatures();
384   SetDescriptorHeaps();
385   UpdateDescriptorTables();
386 
387   if (m_dirty_bits & DirtyState_ComputeImageTexture && !UpdateComputeUAVDescriptorTable())
388   {
389     ExecuteCommandList(false);
390     SetRootSignatures();
391     SetDescriptorHeaps();
392     UpdateDescriptorTables();
393     UpdateComputeUAVDescriptorTable();
394   }
395 
396   // Share graphics and compute state. No need to track now since dispatches are infrequent.
397   auto* const cmdlist = g_dx_context->GetCommandList();
398   cmdlist->SetPipelineState(static_cast<const DXShader*>(shader)->GetComputePipeline());
399   cmdlist->SetComputeRootConstantBufferView(CS_ROOT_PARAMETER_CBV, m_state.constant_buffers[0]);
400   cmdlist->SetComputeRootDescriptorTable(CS_ROOT_PARAMETER_SRV, m_state.srv_descriptor_base);
401   cmdlist->SetComputeRootDescriptorTable(CS_ROOT_PARAMETER_SAMPLERS,
402                                          m_state.sampler_descriptor_base);
403   cmdlist->SetComputeRootDescriptorTable(CS_ROOT_PARAMETER_UAV,
404                                          m_state.compute_uav_descriptor_base);
405   cmdlist->Dispatch(groups_x, groups_y, groups_z);
406 
407   // Compute and graphics state share the same pipeline object? :(
408   m_dirty_bits |= DirtyState_Pipeline;
409 }
410 
BindBackbuffer(const ClearColor & clear_color)411 void Renderer::BindBackbuffer(const ClearColor& clear_color)
412 {
413   CheckForSwapChainChanges();
414   SetAndClearFramebuffer(m_swap_chain->GetCurrentFramebuffer(), clear_color);
415 }
416 
CheckForSwapChainChanges()417 void Renderer::CheckForSwapChainChanges()
418 {
419   const bool surface_changed = m_surface_changed.TestAndClear();
420   const bool surface_resized =
421       m_surface_resized.TestAndClear() || m_swap_chain->CheckForFullscreenChange();
422   if (!surface_changed && !surface_resized)
423     return;
424 
425   // The swap chain could be in use from a previous frame.
426   WaitForGPUIdle();
427   if (surface_changed)
428   {
429     m_swap_chain->ChangeSurface(m_new_surface_handle);
430     m_new_surface_handle = nullptr;
431   }
432   else
433   {
434     m_swap_chain->ResizeSwapChain();
435   }
436 
437   m_backbuffer_width = m_swap_chain->GetWidth();
438   m_backbuffer_height = m_swap_chain->GetHeight();
439 }
440 
PresentBackbuffer()441 void Renderer::PresentBackbuffer()
442 {
443   m_current_framebuffer = nullptr;
444 
445   m_swap_chain->GetCurrentTexture()->TransitionToState(D3D12_RESOURCE_STATE_PRESENT);
446   ExecuteCommandList(false);
447 
448   m_swap_chain->Present();
449 }
450 
OnConfigChanged(u32 bits)451 void Renderer::OnConfigChanged(u32 bits)
452 {
453   ::Renderer::OnConfigChanged(bits);
454 
455   // For quad-buffered stereo we need to change the layer count, so recreate the swap chain.
456   if (m_swap_chain && bits & CONFIG_CHANGE_BIT_STEREO_MODE)
457   {
458     ExecuteCommandList(true);
459     m_swap_chain->SetStereo(SwapChain::WantsStereo());
460   }
461 
462   // Wipe sampler cache if force texture filtering or anisotropy changes.
463   if (bits & (CONFIG_CHANGE_BIT_ANISOTROPY | CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING))
464   {
465     ExecuteCommandList(true);
466     g_dx_context->GetSamplerHeapManager().Clear();
467     g_dx_context->ResetSamplerAllocators();
468   }
469 
470   // If the host config changed (e.g. bbox/per-pixel-shading), recreate the root signature.
471   if (bits & CONFIG_CHANGE_BIT_HOST_CONFIG)
472     g_dx_context->RecreateGXRootSignature();
473 }
474 
ExecuteCommandList(bool wait_for_completion)475 void Renderer::ExecuteCommandList(bool wait_for_completion)
476 {
477   PerfQuery::GetInstance()->ResolveQueries();
478   g_dx_context->ExecuteCommandList(wait_for_completion);
479   m_dirty_bits = DirtyState_All;
480 }
481 
SetConstantBuffer(u32 index,D3D12_GPU_VIRTUAL_ADDRESS address)482 void Renderer::SetConstantBuffer(u32 index, D3D12_GPU_VIRTUAL_ADDRESS address)
483 {
484   if (m_state.constant_buffers[index] == address)
485     return;
486 
487   m_state.constant_buffers[index] = address;
488   m_dirty_bits |= DirtyState_PS_CBV << index;
489 }
490 
SetTextureDescriptor(u32 index,D3D12_CPU_DESCRIPTOR_HANDLE handle)491 void Renderer::SetTextureDescriptor(u32 index, D3D12_CPU_DESCRIPTOR_HANDLE handle)
492 {
493   if (m_state.textures[index].ptr == handle.ptr)
494     return;
495 
496   m_state.textures[index].ptr = handle.ptr;
497   m_dirty_bits |= DirtyState_Textures;
498 }
499 
SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle)500 void Renderer::SetPixelShaderUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle)
501 {
502   if (m_state.ps_uav.ptr == handle.ptr)
503     return;
504 
505   m_state.ps_uav = handle;
506   m_dirty_bits |= DirtyState_PS_UAV;
507 }
508 
SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address,u32 stride,u32 size)509 void Renderer::SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 stride, u32 size)
510 {
511   if (m_state.vertex_buffer.BufferLocation == address &&
512       m_state.vertex_buffer.StrideInBytes == stride && m_state.vertex_buffer.SizeInBytes == size)
513   {
514     return;
515   }
516 
517   m_state.vertex_buffer.BufferLocation = address;
518   m_state.vertex_buffer.StrideInBytes = stride;
519   m_state.vertex_buffer.SizeInBytes = size;
520   m_dirty_bits |= DirtyState_VertexBuffer;
521 }
522 
SetIndexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address,u32 size,DXGI_FORMAT format)523 void Renderer::SetIndexBuffer(D3D12_GPU_VIRTUAL_ADDRESS address, u32 size, DXGI_FORMAT format)
524 {
525   if (m_state.index_buffer.BufferLocation == address && m_state.index_buffer.SizeInBytes == size &&
526       m_state.index_buffer.Format == format)
527   {
528     return;
529   }
530 
531   m_state.index_buffer.BufferLocation = address;
532   m_state.index_buffer.SizeInBytes = size;
533   m_state.index_buffer.Format = format;
534   m_dirty_bits |= DirtyState_IndexBuffer;
535 }
536 
ApplyState()537 bool Renderer::ApplyState()
538 {
539   if (!m_current_framebuffer || !m_current_pipeline)
540     return false;
541 
542   // Updating the descriptor tables can cause command list execution if no descriptors remain.
543   SetRootSignatures();
544   SetDescriptorHeaps();
545   UpdateDescriptorTables();
546 
547   // Clear bits before actually changing state. Some state (e.g. cbuffers) can't be set
548   // if utility pipelines are bound.
549   const u32 dirty_bits = m_dirty_bits;
550   m_dirty_bits &= ~(
551       DirtyState_Framebuffer | DirtyState_Pipeline | DirtyState_Viewport | DirtyState_ScissorRect |
552       DirtyState_PS_UAV | DirtyState_PS_CBV | DirtyState_VS_CBV | DirtyState_GS_CBV |
553       DirtyState_SRV_Descriptor | DirtyState_Sampler_Descriptor | DirtyState_UAV_Descriptor |
554       DirtyState_VertexBuffer | DirtyState_IndexBuffer | DirtyState_PrimitiveTopology);
555 
556   auto* const cmdlist = g_dx_context->GetCommandList();
557   if (dirty_bits & DirtyState_Pipeline)
558     cmdlist->SetPipelineState(static_cast<const DXPipeline*>(m_current_pipeline)->GetPipeline());
559 
560   if (dirty_bits & DirtyState_Framebuffer)
561     BindFramebuffer(static_cast<DXFramebuffer*>(m_current_framebuffer));
562 
563   if (dirty_bits & DirtyState_Viewport)
564     cmdlist->RSSetViewports(1, &m_state.viewport);
565 
566   if (dirty_bits & DirtyState_ScissorRect)
567     cmdlist->RSSetScissorRects(1, &m_state.scissor);
568 
569   if (dirty_bits & DirtyState_VertexBuffer)
570     cmdlist->IASetVertexBuffers(0, 1, &m_state.vertex_buffer);
571 
572   if (dirty_bits & DirtyState_IndexBuffer)
573     cmdlist->IASetIndexBuffer(&m_state.index_buffer);
574 
575   if (dirty_bits & DirtyState_PrimitiveTopology)
576     cmdlist->IASetPrimitiveTopology(m_state.primitive_topology);
577 
578   if (dirty_bits & DirtyState_SRV_Descriptor)
579     cmdlist->SetGraphicsRootDescriptorTable(ROOT_PARAMETER_PS_SRV, m_state.srv_descriptor_base);
580 
581   if (dirty_bits & DirtyState_Sampler_Descriptor)
582   {
583     cmdlist->SetGraphicsRootDescriptorTable(ROOT_PARAMETER_PS_SAMPLERS,
584                                             m_state.sampler_descriptor_base);
585   }
586 
587   if (static_cast<const DXPipeline*>(m_current_pipeline)->GetUsage() == AbstractPipelineUsage::GX)
588   {
589     if (dirty_bits & DirtyState_VS_CBV)
590     {
591       cmdlist->SetGraphicsRootConstantBufferView(ROOT_PARAMETER_VS_CBV,
592                                                  m_state.constant_buffers[1]);
593 
594       if (g_ActiveConfig.bEnablePixelLighting)
595       {
596         cmdlist->SetGraphicsRootConstantBufferView(
597             g_ActiveConfig.bBBoxEnable ? ROOT_PARAMETER_PS_CBV2 : ROOT_PARAMETER_PS_UAV_OR_CBV2,
598             m_state.constant_buffers[1]);
599       }
600     }
601 
602     if (dirty_bits & DirtyState_GS_CBV)
603     {
604       cmdlist->SetGraphicsRootConstantBufferView(ROOT_PARAMETER_GS_CBV,
605                                                  m_state.constant_buffers[2]);
606     }
607 
608     if (dirty_bits & DirtyState_UAV_Descriptor && g_ActiveConfig.bBBoxEnable)
609     {
610       cmdlist->SetGraphicsRootDescriptorTable(ROOT_PARAMETER_PS_UAV_OR_CBV2,
611                                               m_state.uav_descriptor_base);
612     }
613   }
614 
615   if (dirty_bits & DirtyState_PS_CBV)
616   {
617     cmdlist->SetGraphicsRootConstantBufferView(ROOT_PARAMETER_PS_CBV, m_state.constant_buffers[0]);
618   }
619 
620   return true;
621 }
622 
SetRootSignatures()623 void Renderer::SetRootSignatures()
624 {
625   const u32 dirty_bits = m_dirty_bits;
626   if (dirty_bits & DirtyState_RootSignature)
627     g_dx_context->GetCommandList()->SetGraphicsRootSignature(m_state.root_signature);
628   if (dirty_bits & DirtyState_ComputeRootSignature)
629   {
630     g_dx_context->GetCommandList()->SetComputeRootSignature(
631         g_dx_context->GetComputeRootSignature());
632   }
633   m_dirty_bits &= ~(DirtyState_RootSignature | DirtyState_ComputeRootSignature);
634 }
635 
SetDescriptorHeaps()636 void Renderer::SetDescriptorHeaps()
637 {
638   if (m_dirty_bits & DirtyState_DescriptorHeaps)
639   {
640     g_dx_context->GetCommandList()->SetDescriptorHeaps(g_dx_context->GetGPUDescriptorHeapCount(),
641                                                        g_dx_context->GetGPUDescriptorHeaps());
642     m_dirty_bits &= ~DirtyState_DescriptorHeaps;
643   }
644 }
645 
UpdateDescriptorTables()646 void Renderer::UpdateDescriptorTables()
647 {
648   // Samplers force a full sync because any of the samplers could be in use.
649   const bool texture_update_failed =
650       (m_dirty_bits & DirtyState_Textures) && !UpdateSRVDescriptorTable();
651   const bool sampler_update_failed =
652       (m_dirty_bits & DirtyState_Samplers) && !UpdateSamplerDescriptorTable();
653   const bool uav_update_failed = (m_dirty_bits & DirtyState_PS_UAV) && !UpdateUAVDescriptorTable();
654   if (texture_update_failed || sampler_update_failed || uav_update_failed)
655   {
656     WARN_LOG(VIDEO, "Executing command list while waiting for temporary %s",
657              texture_update_failed ? "descriptors" : "samplers");
658     ExecuteCommandList(false);
659     SetRootSignatures();
660     SetDescriptorHeaps();
661     UpdateSRVDescriptorTable();
662     UpdateSamplerDescriptorTable();
663     UpdateUAVDescriptorTable();
664   }
665 }
666 
UpdateSRVDescriptorTable()667 bool Renderer::UpdateSRVDescriptorTable()
668 {
669   static constexpr std::array<UINT, MAX_TEXTURES> src_sizes = {1, 1, 1, 1, 1, 1, 1, 1};
670   DescriptorHandle dst_base_handle;
671   const UINT dst_handle_sizes = 8;
672   if (!g_dx_context->GetDescriptorAllocator()->Allocate(MAX_TEXTURES, &dst_base_handle))
673     return false;
674 
675   g_dx_context->GetDevice()->CopyDescriptors(
676       1, &dst_base_handle.cpu_handle, &dst_handle_sizes, MAX_TEXTURES, m_state.textures.data(),
677       src_sizes.data(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
678   m_state.srv_descriptor_base = dst_base_handle.gpu_handle;
679   m_dirty_bits = (m_dirty_bits & ~DirtyState_Textures) | DirtyState_SRV_Descriptor;
680   return true;
681 }
682 
UpdateSamplerDescriptorTable()683 bool Renderer::UpdateSamplerDescriptorTable()
684 {
685   if (!g_dx_context->GetSamplerAllocator()->GetGroupHandle(m_state.samplers,
686                                                            &m_state.sampler_descriptor_base))
687   {
688     g_dx_context->ResetSamplerAllocators();
689     return false;
690   }
691 
692   m_dirty_bits = (m_dirty_bits & ~DirtyState_Samplers) | DirtyState_Sampler_Descriptor;
693   return true;
694 }
695 
UpdateUAVDescriptorTable()696 bool Renderer::UpdateUAVDescriptorTable()
697 {
698   // We can skip writing the UAV descriptor if bbox isn't enabled, since it's not used otherwise.
699   if (!g_ActiveConfig.bBBoxEnable)
700     return true;
701 
702   DescriptorHandle handle;
703   if (!g_dx_context->GetDescriptorAllocator()->Allocate(1, &handle))
704     return false;
705 
706   g_dx_context->GetDevice()->CopyDescriptorsSimple(1, handle.cpu_handle, m_state.ps_uav,
707                                                    D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
708   m_state.uav_descriptor_base = handle.gpu_handle;
709   m_dirty_bits = (m_dirty_bits & ~DirtyState_PS_UAV) | DirtyState_UAV_Descriptor;
710   return true;
711 }
712 
UpdateComputeUAVDescriptorTable()713 bool Renderer::UpdateComputeUAVDescriptorTable()
714 {
715   DescriptorHandle handle;
716   if (!g_dx_context->GetDescriptorAllocator()->Allocate(1, &handle))
717     return false;
718 
719   if (m_state.compute_image_texture)
720   {
721     g_dx_context->GetDevice()->CopyDescriptorsSimple(
722         1, handle.cpu_handle, m_state.compute_image_texture->GetUAVDescriptor().cpu_handle,
723         D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
724   }
725   else
726   {
727     constexpr D3D12_UNORDERED_ACCESS_VIEW_DESC null_uav_desc = {};
728     g_dx_context->GetDevice()->CreateUnorderedAccessView(nullptr, nullptr, &null_uav_desc,
729                                                          handle.cpu_handle);
730   }
731 
732   m_dirty_bits &= ~DirtyState_ComputeImageTexture;
733   m_state.compute_uav_descriptor_base = handle.gpu_handle;
734   return true;
735 }
736 
737 }  // namespace DX12
738