1 // Copyright 2016 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include <algorithm>
6 #include <cstddef>
7 #include <cstdio>
8 #include <limits>
9 #include <string>
10 #include <tuple>
11 
12 #include "Common/Assert.h"
13 #include "Common/CommonTypes.h"
14 #include "Common/Logging/Log.h"
15 #include "Common/MsgHandler.h"
16 
17 #include "Core/Core.h"
18 
19 #include "VideoBackends/Vulkan/BoundingBox.h"
20 #include "VideoBackends/Vulkan/CommandBufferManager.h"
21 #include "VideoBackends/Vulkan/ObjectCache.h"
22 #include "VideoBackends/Vulkan/PerfQuery.h"
23 #include "VideoBackends/Vulkan/Renderer.h"
24 #include "VideoBackends/Vulkan/StateTracker.h"
25 #include "VideoBackends/Vulkan/StreamBuffer.h"
26 #include "VideoBackends/Vulkan/SwapChain.h"
27 #include "VideoBackends/Vulkan/VKPipeline.h"
28 #include "VideoBackends/Vulkan/VKShader.h"
29 #include "VideoBackends/Vulkan/VKTexture.h"
30 #include "VideoBackends/Vulkan/VertexFormat.h"
31 #include "VideoBackends/Vulkan/VulkanContext.h"
32 
33 #include "VideoCommon/DriverDetails.h"
34 #include "VideoCommon/FramebufferManager.h"
35 #include "VideoCommon/RenderState.h"
36 #include "VideoCommon/VertexManagerBase.h"
37 #include "VideoCommon/VideoBackendBase.h"
38 #include "VideoCommon/VideoCommon.h"
39 #include "VideoCommon/VideoConfig.h"
40 #include "VideoCommon/XFMemory.h"
41 
42 namespace Vulkan
43 {
Renderer(std::unique_ptr<SwapChain> swap_chain,float backbuffer_scale)44 Renderer::Renderer(std::unique_ptr<SwapChain> swap_chain, float backbuffer_scale)
45     : ::Renderer(swap_chain ? static_cast<int>(swap_chain->GetWidth()) : 1,
46                  swap_chain ? static_cast<int>(swap_chain->GetHeight()) : 0, backbuffer_scale,
47                  swap_chain ? swap_chain->GetTextureFormat() : AbstractTextureFormat::Undefined),
48       m_swap_chain(std::move(swap_chain))
49 {
50   UpdateActiveConfig();
51   for (SamplerState& m_sampler_state : m_sampler_states)
52     m_sampler_state.hex = RenderState::GetPointSamplerState().hex;
53 }
54 
55 Renderer::~Renderer() = default;
56 
IsHeadless() const57 bool Renderer::IsHeadless() const
58 {
59   return m_swap_chain == nullptr;
60 }
61 
Initialize()62 bool Renderer::Initialize()
63 {
64   if (!::Renderer::Initialize())
65     return false;
66 
67   m_bounding_box = std::make_unique<BoundingBox>();
68   if (!m_bounding_box->Initialize())
69   {
70     PanicAlert("Failed to initialize bounding box.");
71     return false;
72   }
73 
74   // Various initialization routines will have executed commands on the command buffer.
75   // Execute what we have done before beginning the first frame.
76   ExecuteCommandBuffer(true, false);
77   return true;
78 }
79 
Shutdown()80 void Renderer::Shutdown()
81 {
82   ::Renderer::Shutdown();
83   m_swap_chain.reset();
84 }
85 
CreateTexture(const TextureConfig & config)86 std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
87 {
88   return VKTexture::Create(config);
89 }
90 
CreateStagingTexture(StagingTextureType type,const TextureConfig & config)91 std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
92                                                                        const TextureConfig& config)
93 {
94   return VKStagingTexture::Create(type, config);
95 }
96 
CreateShaderFromSource(ShaderStage stage,std::string_view source)97 std::unique_ptr<AbstractShader> Renderer::CreateShaderFromSource(ShaderStage stage,
98                                                                  std::string_view source)
99 {
100   return VKShader::CreateFromSource(stage, source);
101 }
102 
CreateShaderFromBinary(ShaderStage stage,const void * data,size_t length)103 std::unique_ptr<AbstractShader> Renderer::CreateShaderFromBinary(ShaderStage stage,
104                                                                  const void* data, size_t length)
105 {
106   return VKShader::CreateFromBinary(stage, data, length);
107 }
108 
109 std::unique_ptr<NativeVertexFormat>
CreateNativeVertexFormat(const PortableVertexDeclaration & vtx_decl)110 Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
111 {
112   return std::make_unique<VertexFormat>(vtx_decl);
113 }
114 
CreatePipeline(const AbstractPipelineConfig & config,const void * cache_data,size_t cache_data_length)115 std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelineConfig& config,
116                                                            const void* cache_data,
117                                                            size_t cache_data_length)
118 {
119   return VKPipeline::Create(config);
120 }
121 
CreateFramebuffer(AbstractTexture * color_attachment,AbstractTexture * depth_attachment)122 std::unique_ptr<AbstractFramebuffer> Renderer::CreateFramebuffer(AbstractTexture* color_attachment,
123                                                                  AbstractTexture* depth_attachment)
124 {
125   return VKFramebuffer::Create(static_cast<VKTexture*>(color_attachment),
126                                static_cast<VKTexture*>(depth_attachment));
127 }
128 
SetPipeline(const AbstractPipeline * pipeline)129 void Renderer::SetPipeline(const AbstractPipeline* pipeline)
130 {
131   StateTracker::GetInstance()->SetPipeline(static_cast<const VKPipeline*>(pipeline));
132 }
133 
BBoxRead(int index)134 u16 Renderer::BBoxRead(int index)
135 {
136   return static_cast<u16>(m_bounding_box->Get(index));
137 }
138 
BBoxWrite(int index,u16 value)139 void Renderer::BBoxWrite(int index, u16 value)
140 {
141   m_bounding_box->Set(index, value);
142 }
143 
BBoxFlush()144 void Renderer::BBoxFlush()
145 {
146   m_bounding_box->Flush();
147   m_bounding_box->Invalidate();
148 }
149 
ClearScreen(const MathUtil::Rectangle<int> & rc,bool color_enable,bool alpha_enable,bool z_enable,u32 color,u32 z)150 void Renderer::ClearScreen(const MathUtil::Rectangle<int>& rc, bool color_enable, bool alpha_enable,
151                            bool z_enable, u32 color, u32 z)
152 {
153   g_framebuffer_manager->FlushEFBPokes();
154   g_framebuffer_manager->FlagPeekCacheAsOutOfDate();
155 
156   // Native -> EFB coordinates
157   MathUtil::Rectangle<int> target_rc = Renderer::ConvertEFBRectangle(rc);
158 
159   // Size we pass this size to vkBeginRenderPass, it has to be clamped to the framebuffer
160   // dimensions. The other backends just silently ignore this case.
161   target_rc.ClampUL(0, 0, m_target_width, m_target_height);
162 
163   VkRect2D target_vk_rc = {
164       {target_rc.left, target_rc.top},
165       {static_cast<uint32_t>(target_rc.GetWidth()), static_cast<uint32_t>(target_rc.GetHeight())}};
166 
167   // Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha
168   // channel to 0xFF. This hopefully allows us to use the fast path in most cases.
169   if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16 ||
170       bpmem.zcontrol.pixel_format == PEControl::RGB8_Z24 ||
171       bpmem.zcontrol.pixel_format == PEControl::Z24)
172   {
173     // Force alpha writes, and clear the alpha channel. This is different to the other backends,
174     // where the existing values of the alpha channel are preserved.
175     alpha_enable = true;
176     color &= 0x00FFFFFF;
177   }
178 
179   // Convert RGBA8 -> floating-point values.
180   VkClearValue clear_color_value = {};
181   VkClearValue clear_depth_value = {};
182   clear_color_value.color.float32[0] = static_cast<float>((color >> 16) & 0xFF) / 255.0f;
183   clear_color_value.color.float32[1] = static_cast<float>((color >> 8) & 0xFF) / 255.0f;
184   clear_color_value.color.float32[2] = static_cast<float>((color >> 0) & 0xFF) / 255.0f;
185   clear_color_value.color.float32[3] = static_cast<float>((color >> 24) & 0xFF) / 255.0f;
186   clear_depth_value.depthStencil.depth = static_cast<float>(z & 0xFFFFFF) / 16777216.0f;
187   if (!g_ActiveConfig.backend_info.bSupportsReversedDepthRange)
188     clear_depth_value.depthStencil.depth = 1.0f - clear_depth_value.depthStencil.depth;
189 
190   // If we're not in a render pass (start of the frame), we can use a clear render pass
191   // to discard the data, rather than loading and then clearing.
192   bool use_clear_attachments = (color_enable && alpha_enable) || z_enable;
193   bool use_clear_render_pass =
194       !StateTracker::GetInstance()->InRenderPass() && color_enable && alpha_enable && z_enable;
195 
196   // The NVIDIA Vulkan driver causes the GPU to lock up, or throw exceptions if MSAA is enabled,
197   // a non-full clear rect is specified, and a clear loadop or vkCmdClearAttachments is used.
198   if (g_ActiveConfig.iMultisamples > 1 &&
199       DriverDetails::HasBug(DriverDetails::BUG_BROKEN_MSAA_CLEAR))
200   {
201     use_clear_render_pass = false;
202     use_clear_attachments = false;
203   }
204 
205   // This path cannot be used if the driver implementation doesn't guarantee pixels with no drawn
206   // geometry in "this" renderpass won't be cleared
207   if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_CLEAR_LOADOP_RENDERPASS))
208     use_clear_render_pass = false;
209 
210   // Fastest path: Use a render pass to clear the buffers.
211   if (use_clear_render_pass)
212   {
213     const std::array<VkClearValue, 2> clear_values = {{clear_color_value, clear_depth_value}};
214     StateTracker::GetInstance()->BeginClearRenderPass(target_vk_rc, clear_values.data(),
215                                                       static_cast<u32>(clear_values.size()));
216     return;
217   }
218 
219   // Fast path: Use vkCmdClearAttachments to clear the buffers within a render path
220   // We can't use this when preserving alpha but clearing color.
221   if (use_clear_attachments)
222   {
223     VkClearAttachment clear_attachments[2];
224     uint32_t num_clear_attachments = 0;
225     if (color_enable && alpha_enable)
226     {
227       clear_attachments[num_clear_attachments].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
228       clear_attachments[num_clear_attachments].colorAttachment = 0;
229       clear_attachments[num_clear_attachments].clearValue = clear_color_value;
230       num_clear_attachments++;
231       color_enable = false;
232       alpha_enable = false;
233     }
234     if (z_enable)
235     {
236       clear_attachments[num_clear_attachments].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
237       clear_attachments[num_clear_attachments].colorAttachment = 0;
238       clear_attachments[num_clear_attachments].clearValue = clear_depth_value;
239       num_clear_attachments++;
240       z_enable = false;
241     }
242     if (num_clear_attachments > 0)
243     {
244       VkClearRect vk_rect = {target_vk_rc, 0, g_framebuffer_manager->GetEFBLayers()};
245       if (!StateTracker::GetInstance()->IsWithinRenderArea(
246               target_vk_rc.offset.x, target_vk_rc.offset.y, target_vk_rc.extent.width,
247               target_vk_rc.extent.height))
248       {
249         StateTracker::GetInstance()->EndClearRenderPass();
250       }
251       StateTracker::GetInstance()->BeginRenderPass();
252 
253       vkCmdClearAttachments(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_clear_attachments,
254                             clear_attachments, 1, &vk_rect);
255     }
256   }
257 
258   // Anything left over for the slow path?
259   if (!color_enable && !alpha_enable && !z_enable)
260     return;
261 
262   g_framebuffer_manager->ClearEFB(rc, color_enable, alpha_enable, z_enable, color, z);
263 }
264 
Flush()265 void Renderer::Flush()
266 {
267   ExecuteCommandBuffer(true, false);
268 }
269 
WaitForGPUIdle()270 void Renderer::WaitForGPUIdle()
271 {
272   ExecuteCommandBuffer(false, true);
273 }
274 
BindBackbuffer(const ClearColor & clear_color)275 void Renderer::BindBackbuffer(const ClearColor& clear_color)
276 {
277   StateTracker::GetInstance()->EndRenderPass();
278 
279   // Handle host window resizes.
280   CheckForSurfaceChange();
281   CheckForSurfaceResize();
282 
283   // Check for exclusive fullscreen request.
284   if (m_swap_chain->GetCurrentFullscreenState() != m_swap_chain->GetNextFullscreenState() &&
285       !m_swap_chain->SetFullscreenState(m_swap_chain->GetNextFullscreenState()))
286   {
287     // if it fails, don't keep trying
288     m_swap_chain->SetNextFullscreenState(m_swap_chain->GetCurrentFullscreenState());
289   }
290 
291   VkResult res = g_command_buffer_mgr->CheckLastPresentFail() ?
292                      g_command_buffer_mgr->GetLastPresentResult() :
293                      m_swap_chain->AcquireNextImage();
294   if (res != VK_SUCCESS)
295   {
296     // Execute cmdbuffer before resizing, as the last frame could still be presenting.
297     ExecuteCommandBuffer(false, true);
298 
299     // Was this a lost exclusive fullscreen?
300     if (res == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
301     {
302       // The present keeps returning exclusive mode lost unless we re-create the swap chain.
303       INFO_LOG(VIDEO, "Lost exclusive fullscreen.");
304       m_swap_chain->RecreateSwapChain();
305     }
306     else if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
307     {
308       INFO_LOG(VIDEO, "Resizing swap chain due to suboptimal/out-of-date");
309       m_swap_chain->ResizeSwapChain();
310     }
311     else
312     {
313       ERROR_LOG(VIDEO, "Unknown present error 0x%08X, please report.", res);
314       m_swap_chain->RecreateSwapChain();
315     }
316 
317     res = m_swap_chain->AcquireNextImage();
318   }
319   if (res != VK_SUCCESS)
320     PanicAlert("Failed to grab image from swap chain");
321 
322   // Transition from undefined (or present src, but it can be substituted) to
323   // color attachment ready for writing. These transitions must occur outside
324   // a render pass, unless the render pass declares a self-dependency.
325   m_swap_chain->GetCurrentTexture()->OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED);
326   m_swap_chain->GetCurrentTexture()->TransitionToLayout(
327       g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
328   SetAndClearFramebuffer(m_swap_chain->GetCurrentFramebuffer(),
329                          ClearColor{{0.0f, 0.0f, 0.0f, 1.0f}});
330 }
331 
PresentBackbuffer()332 void Renderer::PresentBackbuffer()
333 {
334   // End drawing to backbuffer
335   StateTracker::GetInstance()->EndRenderPass();
336 
337   // Transition the backbuffer to PRESENT_SRC to ensure all commands drawing
338   // to it have finished before present.
339   m_swap_chain->GetCurrentTexture()->TransitionToLayout(
340       g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
341 
342   // Submit the current command buffer, signaling rendering finished semaphore when it's done
343   // Because this final command buffer is rendering to the swap chain, we need to wait for
344   // the available semaphore to be signaled before executing the buffer. This final submission
345   // can happen off-thread in the background while we're preparing the next frame.
346   g_command_buffer_mgr->SubmitCommandBuffer(true, false, m_swap_chain->GetSwapChain(),
347                                             m_swap_chain->GetCurrentImageIndex());
348 
349   // New cmdbuffer, so invalidate state.
350   StateTracker::GetInstance()->InvalidateCachedState();
351 }
352 
SetFullscreen(bool enable_fullscreen)353 void Renderer::SetFullscreen(bool enable_fullscreen)
354 {
355   if (!m_swap_chain->IsFullscreenSupported())
356     return;
357 
358   m_swap_chain->SetNextFullscreenState(enable_fullscreen);
359 }
360 
IsFullscreen() const361 bool Renderer::IsFullscreen() const
362 {
363   return m_swap_chain && m_swap_chain->GetCurrentFullscreenState();
364 }
365 
ExecuteCommandBuffer(bool submit_off_thread,bool wait_for_completion)366 void Renderer::ExecuteCommandBuffer(bool submit_off_thread, bool wait_for_completion)
367 {
368   StateTracker::GetInstance()->EndRenderPass();
369 
370   g_command_buffer_mgr->SubmitCommandBuffer(submit_off_thread, wait_for_completion);
371 
372   StateTracker::GetInstance()->InvalidateCachedState();
373 }
374 
CheckForSurfaceChange()375 void Renderer::CheckForSurfaceChange()
376 {
377   if (!m_surface_changed.TestAndClear() || !m_swap_chain)
378     return;
379 
380   // Submit the current draws up until rendering the XFB.
381   ExecuteCommandBuffer(false, true);
382 
383   // Clear the present failed flag, since we don't want to resize after recreating.
384   g_command_buffer_mgr->CheckLastPresentFail();
385 
386   // Recreate the surface. If this fails we're in trouble.
387   if (!m_swap_chain->RecreateSurface(m_new_surface_handle))
388     PanicAlert("Failed to recreate Vulkan surface. Cannot continue.");
389   m_new_surface_handle = nullptr;
390 
391   // Handle case where the dimensions are now different.
392   OnSwapChainResized();
393 }
394 
CheckForSurfaceResize()395 void Renderer::CheckForSurfaceResize()
396 {
397   if (!m_surface_resized.TestAndClear())
398     return;
399 
400   // If we don't have a surface, how can we resize the swap chain?
401   // CheckForSurfaceChange should handle this case.
402   if (!m_swap_chain)
403   {
404     WARN_LOG(VIDEO, "Surface resize event received without active surface, ignoring");
405     return;
406   }
407 
408   // Wait for the GPU to catch up since we're going to destroy the swap chain.
409   ExecuteCommandBuffer(false, true);
410 
411   // Clear the present failed flag, since we don't want to resize after recreating.
412   g_command_buffer_mgr->CheckLastPresentFail();
413 
414   // Resize the swap chain.
415   m_swap_chain->RecreateSwapChain();
416   OnSwapChainResized();
417 }
418 
OnConfigChanged(u32 bits)419 void Renderer::OnConfigChanged(u32 bits)
420 {
421   if (bits & CONFIG_CHANGE_BIT_HOST_CONFIG)
422     g_object_cache->ReloadPipelineCache();
423 
424   // For vsync, we need to change the present mode, which means recreating the swap chain.
425   if (m_swap_chain && bits & CONFIG_CHANGE_BIT_VSYNC)
426   {
427     ExecuteCommandBuffer(false, true);
428     m_swap_chain->SetVSync(g_ActiveConfig.bVSyncActive);
429   }
430 
431   // For quad-buffered stereo we need to change the layer count, so recreate the swap chain.
432   if (m_swap_chain && bits & CONFIG_CHANGE_BIT_STEREO_MODE)
433   {
434     ExecuteCommandBuffer(false, true);
435     m_swap_chain->RecreateSwapChain();
436   }
437 
438   // Wipe sampler cache if force texture filtering or anisotropy changes.
439   if (bits & (CONFIG_CHANGE_BIT_ANISOTROPY | CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING))
440   {
441     ExecuteCommandBuffer(false, true);
442     ResetSamplerStates();
443   }
444 }
445 
OnSwapChainResized()446 void Renderer::OnSwapChainResized()
447 {
448   m_backbuffer_width = m_swap_chain->GetWidth();
449   m_backbuffer_height = m_swap_chain->GetHeight();
450 }
451 
BindFramebuffer(VKFramebuffer * fb)452 void Renderer::BindFramebuffer(VKFramebuffer* fb)
453 {
454   StateTracker::GetInstance()->EndRenderPass();
455 
456   // Shouldn't be bound as a texture.
457   if (fb->GetColorAttachment())
458   {
459     StateTracker::GetInstance()->UnbindTexture(
460         static_cast<VKTexture*>(fb->GetColorAttachment())->GetView());
461   }
462   if (fb->GetDepthAttachment())
463   {
464     StateTracker::GetInstance()->UnbindTexture(
465         static_cast<VKTexture*>(fb->GetDepthAttachment())->GetView());
466   }
467 
468   fb->TransitionForRender();
469   StateTracker::GetInstance()->SetFramebuffer(fb);
470   m_current_framebuffer = fb;
471 }
472 
SetFramebuffer(AbstractFramebuffer * framebuffer)473 void Renderer::SetFramebuffer(AbstractFramebuffer* framebuffer)
474 {
475   if (m_current_framebuffer == framebuffer)
476     return;
477 
478   VKFramebuffer* vkfb = static_cast<VKFramebuffer*>(framebuffer);
479   BindFramebuffer(vkfb);
480 }
481 
SetAndDiscardFramebuffer(AbstractFramebuffer * framebuffer)482 void Renderer::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer)
483 {
484   if (m_current_framebuffer == framebuffer)
485     return;
486 
487   VKFramebuffer* vkfb = static_cast<VKFramebuffer*>(framebuffer);
488   BindFramebuffer(vkfb);
489 
490   // If we're discarding, begin the discard pass, then switch to a load pass.
491   // This way if the command buffer is flushed, we don't start another discard pass.
492   StateTracker::GetInstance()->BeginDiscardRenderPass();
493 }
494 
SetAndClearFramebuffer(AbstractFramebuffer * framebuffer,const ClearColor & color_value,float depth_value)495 void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer,
496                                       const ClearColor& color_value, float depth_value)
497 {
498   VKFramebuffer* vkfb = static_cast<VKFramebuffer*>(framebuffer);
499   BindFramebuffer(vkfb);
500 
501   std::array<VkClearValue, 2> clear_values;
502   u32 num_clear_values = 0;
503   if (vkfb->GetColorFormat() != AbstractTextureFormat::Undefined)
504   {
505     std::memcpy(clear_values[num_clear_values].color.float32, color_value.data(),
506                 sizeof(clear_values[num_clear_values].color.float32));
507     num_clear_values++;
508   }
509   if (vkfb->GetDepthFormat() != AbstractTextureFormat::Undefined)
510   {
511     clear_values[num_clear_values].depthStencil.depth = depth_value;
512     clear_values[num_clear_values].depthStencil.stencil = 0;
513     num_clear_values++;
514   }
515   StateTracker::GetInstance()->BeginClearRenderPass(vkfb->GetRect(), clear_values.data(),
516                                                     num_clear_values);
517 }
518 
SetTexture(u32 index,const AbstractTexture * texture)519 void Renderer::SetTexture(u32 index, const AbstractTexture* texture)
520 {
521   // Texture should always be in SHADER_READ_ONLY layout prior to use.
522   // This is so we don't need to transition during render passes.
523   const VKTexture* tex = static_cast<const VKTexture*>(texture);
524   if (tex)
525   {
526     if (tex->GetLayout() != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
527     {
528       if (StateTracker::GetInstance()->InRenderPass())
529       {
530         WARN_LOG(VIDEO, "Transitioning image in render pass in Renderer::SetTexture()");
531         StateTracker::GetInstance()->EndRenderPass();
532       }
533 
534       tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
535                               VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
536     }
537 
538     StateTracker::GetInstance()->SetTexture(index, tex->GetView());
539   }
540   else
541   {
542     StateTracker::GetInstance()->SetTexture(0, VK_NULL_HANDLE);
543   }
544 }
545 
SetSamplerState(u32 index,const SamplerState & state)546 void Renderer::SetSamplerState(u32 index, const SamplerState& state)
547 {
548   // Skip lookup if the state hasn't changed.
549   if (m_sampler_states[index].hex == state.hex)
550     return;
551 
552   // Look up new state and replace in state tracker.
553   VkSampler sampler = g_object_cache->GetSampler(state);
554   if (sampler == VK_NULL_HANDLE)
555   {
556     ERROR_LOG(VIDEO, "Failed to create sampler");
557     sampler = g_object_cache->GetPointSampler();
558   }
559 
560   StateTracker::GetInstance()->SetSampler(index, sampler);
561   m_sampler_states[index].hex = state.hex;
562 }
563 
SetComputeImageTexture(AbstractTexture * texture,bool read,bool write)564 void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write)
565 {
566   VKTexture* vk_texture = static_cast<VKTexture*>(texture);
567   if (vk_texture)
568   {
569     StateTracker::GetInstance()->EndRenderPass();
570     StateTracker::GetInstance()->SetImageTexture(vk_texture->GetView());
571     vk_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
572                                    read ? (write ? VKTexture::ComputeImageLayout::ReadWrite :
573                                                    VKTexture::ComputeImageLayout::ReadOnly) :
574                                           VKTexture::ComputeImageLayout::WriteOnly);
575   }
576   else
577   {
578     StateTracker::GetInstance()->SetImageTexture(VK_NULL_HANDLE);
579   }
580 }
581 
UnbindTexture(const AbstractTexture * texture)582 void Renderer::UnbindTexture(const AbstractTexture* texture)
583 {
584   StateTracker::GetInstance()->UnbindTexture(static_cast<const VKTexture*>(texture)->GetView());
585 }
586 
ResetSamplerStates()587 void Renderer::ResetSamplerStates()
588 {
589   // Invalidate all sampler states, next draw will re-initialize them.
590   for (u32 i = 0; i < m_sampler_states.size(); i++)
591   {
592     m_sampler_states[i].hex = RenderState::GetPointSamplerState().hex;
593     StateTracker::GetInstance()->SetSampler(i, g_object_cache->GetPointSampler());
594   }
595 
596   // Invalidate all sampler objects (some will be unused now).
597   g_object_cache->ClearSamplerCache();
598 }
599 
SetScissorRect(const MathUtil::Rectangle<int> & rc)600 void Renderer::SetScissorRect(const MathUtil::Rectangle<int>& rc)
601 {
602   VkRect2D scissor = {{rc.left, rc.top},
603                       {static_cast<u32>(rc.GetWidth()), static_cast<u32>(rc.GetHeight())}};
604 
605   // See Vulkan spec for vkCmdSetScissor:
606   // The x and y members of offset must be greater than or equal to 0.
607   if (scissor.offset.x < 0)
608   {
609     scissor.extent.width -= -scissor.offset.x;
610     scissor.offset.x = 0;
611   }
612   if (scissor.offset.y < 0)
613   {
614     scissor.extent.height -= -scissor.offset.y;
615     scissor.offset.y = 0;
616   }
617   StateTracker::GetInstance()->SetScissor(scissor);
618 }
619 
SetViewport(float x,float y,float width,float height,float near_depth,float far_depth)620 void Renderer::SetViewport(float x, float y, float width, float height, float near_depth,
621                            float far_depth)
622 {
623   VkViewport viewport = {x, y, width, height, near_depth, far_depth};
624   StateTracker::GetInstance()->SetViewport(viewport);
625 }
626 
Draw(u32 base_vertex,u32 num_vertices)627 void Renderer::Draw(u32 base_vertex, u32 num_vertices)
628 {
629   if (!StateTracker::GetInstance()->Bind())
630     return;
631 
632   vkCmdDraw(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_vertices, 1, base_vertex, 0);
633 }
634 
DrawIndexed(u32 base_index,u32 num_indices,u32 base_vertex)635 void Renderer::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
636 {
637   if (!StateTracker::GetInstance()->Bind())
638     return;
639 
640   vkCmdDrawIndexed(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_indices, 1, base_index,
641                    base_vertex, 0);
642 }
643 
DispatchComputeShader(const AbstractShader * shader,u32 groups_x,u32 groups_y,u32 groups_z)644 void Renderer::DispatchComputeShader(const AbstractShader* shader, u32 groups_x, u32 groups_y,
645                                      u32 groups_z)
646 {
647   StateTracker::GetInstance()->SetComputeShader(static_cast<const VKShader*>(shader));
648   if (StateTracker::GetInstance()->BindCompute())
649     vkCmdDispatch(g_command_buffer_mgr->GetCurrentCommandBuffer(), groups_x, groups_y, groups_z);
650 }
651 
652 }  // namespace Vulkan
653