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