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