| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | // Copyright 2016 Dolphin Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2+
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-30 23:07:50 -04:00
										 |  |  | #include <cstddef>
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include <cstdio>
 | 
					
						
							|  |  |  | #include <limits>
 | 
					
						
							| 
									
										
										
										
											2016-09-30 23:07:50 -04:00
										 |  |  | #include <string>
 | 
					
						
							| 
									
										
										
										
											2017-04-09 15:05:24 -04:00
										 |  |  | #include <tuple>
 | 
					
						
							| 
									
										
										
										
											2016-09-30 23:07:50 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-01 10:56:13 -05:00
										 |  |  | #include "Common/CommonTypes.h"
 | 
					
						
							| 
									
										
										
										
											2016-09-30 23:07:50 -04:00
										 |  |  | #include "Common/Logging/Log.h"
 | 
					
						
							|  |  |  | #include "Common/MsgHandler.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 22:41:42 +10:00
										 |  |  | #include "Core/Core.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-14 00:40:04 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include "VideoBackends/Vulkan/BoundingBox.h"
 | 
					
						
							|  |  |  | #include "VideoBackends/Vulkan/CommandBufferManager.h"
 | 
					
						
							|  |  |  | #include "VideoBackends/Vulkan/FramebufferManager.h"
 | 
					
						
							|  |  |  | #include "VideoBackends/Vulkan/ObjectCache.h"
 | 
					
						
							| 
									
										
										
										
											2017-04-21 23:33:58 +10:00
										 |  |  | #include "VideoBackends/Vulkan/PostProcessing.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include "VideoBackends/Vulkan/RasterFont.h"
 | 
					
						
							| 
									
										
										
										
											2017-04-22 23:44:34 -05:00
										 |  |  | #include "VideoBackends/Vulkan/Renderer.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include "VideoBackends/Vulkan/StateTracker.h"
 | 
					
						
							|  |  |  | #include "VideoBackends/Vulkan/SwapChain.h"
 | 
					
						
							| 
									
										
										
										
											2016-10-01 22:54:02 +10:00
										 |  |  | #include "VideoBackends/Vulkan/TextureCache.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include "VideoBackends/Vulkan/Util.h"
 | 
					
						
							| 
									
										
										
										
											2017-04-22 23:44:34 -05:00
										 |  |  | #include "VideoBackends/Vulkan/VKTexture.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include "VideoBackends/Vulkan/VulkanContext.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "VideoCommon/BPFunctions.h"
 | 
					
						
							|  |  |  | #include "VideoCommon/BPMemory.h"
 | 
					
						
							| 
									
										
										
										
											2017-05-25 23:59:23 +10:00
										 |  |  | #include "VideoCommon/DriverDetails.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include "VideoCommon/OnScreenDisplay.h"
 | 
					
						
							|  |  |  | #include "VideoCommon/PixelEngine.h"
 | 
					
						
							| 
									
										
										
										
											2017-04-17 23:14:17 +10:00
										 |  |  | #include "VideoCommon/RenderState.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include "VideoCommon/TextureCacheBase.h"
 | 
					
						
							| 
									
										
										
										
											2017-01-23 11:20:20 -05:00
										 |  |  | #include "VideoCommon/VideoBackendBase.h"
 | 
					
						
							| 
									
										
										
										
											2017-05-29 17:02:09 -05:00
										 |  |  | #include "VideoCommon/VideoCommon.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | #include "VideoCommon/VideoConfig.h"
 | 
					
						
							| 
									
										
										
										
											2017-02-01 10:56:13 -05:00
										 |  |  | #include "VideoCommon/XFMemory.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Vulkan | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-03-04 16:40:08 +10:00
										 |  |  | Renderer::Renderer(std::unique_ptr<SwapChain> swap_chain) | 
					
						
							|  |  |  |     : ::Renderer(swap_chain ? static_cast<int>(swap_chain->GetWidth()) : 1, | 
					
						
							|  |  |  |                  swap_chain ? static_cast<int>(swap_chain->GetHeight()) : 0), | 
					
						
							|  |  |  |       m_swap_chain(std::move(swap_chain)) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-02 21:37:24 +10:00
										 |  |  |   UpdateActiveConfig(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   for (size_t i = 0; i < m_sampler_states.size(); i++) | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  |     m_sampler_states[i].hex = RenderState::GetPointSamplerState().hex; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Renderer::~Renderer() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   UpdateActiveConfig(); | 
					
						
							| 
									
										
										
										
											2016-11-09 23:15:44 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-01 22:22:14 +10:00
										 |  |  |   DestroyShaders(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   DestroySemaphores(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  | Renderer* Renderer::GetInstance() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return static_cast<Renderer*>(g_renderer.get()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool Renderer::Initialize() | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							|  |  |  |   BindEFBToStateTracker(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!CreateSemaphores()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("Failed to create semaphores."); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!CompileShaders()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("Failed to compile shaders."); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   m_raster_font = std::make_unique<RasterFont>(); | 
					
						
							|  |  |  |   if (!m_raster_font->Initialize()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("Failed to initialize raster font."); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   m_bounding_box = std::make_unique<BoundingBox>(); | 
					
						
							|  |  |  |   if (!m_bounding_box->Initialize()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("Failed to initialize bounding box."); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (g_vulkan_context->SupportsBoundingBox()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // Bind bounding box to state tracker
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     StateTracker::GetInstance()->SetBBoxBuffer(m_bounding_box->GetGPUBuffer(), | 
					
						
							|  |  |  |                                                m_bounding_box->GetGPUBufferOffset(), | 
					
						
							|  |  |  |                                                m_bounding_box->GetGPUBufferSize()); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-21 23:33:58 +10:00
										 |  |  |   // Initialize post processing.
 | 
					
						
							|  |  |  |   m_post_processor = std::make_unique<VulkanPostProcessing>(); | 
					
						
							|  |  |  |   if (!static_cast<VulkanPostProcessing*>(m_post_processor.get()) | 
					
						
							|  |  |  |            ->Initialize(m_raster_font->GetTexture())) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     PanicAlert("failed to initialize post processor."); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   // Various initialization routines will have executed commands on the command buffer.
 | 
					
						
							|  |  |  |   // Execute what we have done before beginning the first frame.
 | 
					
						
							|  |  |  |   g_command_buffer_mgr->PrepareToSubmitCommandBuffer(); | 
					
						
							|  |  |  |   g_command_buffer_mgr->SubmitCommandBuffer(false); | 
					
						
							|  |  |  |   BeginFrame(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool Renderer::CreateSemaphores() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Create two semaphores, one that is triggered when the swapchain buffer is ready, another after
 | 
					
						
							|  |  |  |   // submit and before present
 | 
					
						
							|  |  |  |   VkSemaphoreCreateInfo semaphore_info = { | 
					
						
							|  |  |  |       VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,  // VkStructureType          sType
 | 
					
						
							|  |  |  |       nullptr,                                  // const void*              pNext
 | 
					
						
							|  |  |  |       0                                         // VkSemaphoreCreateFlags   flags
 | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   VkResult res; | 
					
						
							|  |  |  |   if ((res = vkCreateSemaphore(g_vulkan_context->GetDevice(), &semaphore_info, nullptr, | 
					
						
							|  |  |  |                                &m_image_available_semaphore)) != VK_SUCCESS || | 
					
						
							|  |  |  |       (res = vkCreateSemaphore(g_vulkan_context->GetDevice(), &semaphore_info, nullptr, | 
					
						
							|  |  |  |                                &m_rendering_finished_semaphore)) != VK_SUCCESS) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: "); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::DestroySemaphores() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (m_image_available_semaphore) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     vkDestroySemaphore(g_vulkan_context->GetDevice(), m_image_available_semaphore, nullptr); | 
					
						
							| 
									
										
										
										
											2016-10-01 10:40:44 +10:00
										 |  |  |     m_image_available_semaphore = VK_NULL_HANDLE; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (m_rendering_finished_semaphore) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     vkDestroySemaphore(g_vulkan_context->GetDevice(), m_rendering_finished_semaphore, nullptr); | 
					
						
							| 
									
										
										
										
											2016-10-01 10:40:44 +10:00
										 |  |  |     m_rendering_finished_semaphore = VK_NULL_HANDLE; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-30 16:25:36 +10:00
										 |  |  | std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return VKTexture::Create(config); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-22 00:49:40 +10:00
										 |  |  | std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type, | 
					
						
							|  |  |  |                                                                        const TextureConfig& config) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return VKStagingTexture::Create(type, config); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | void Renderer::RenderText(const std::string& text, int left, int top, u32 color) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   u32 backbuffer_width = m_swap_chain->GetWidth(); | 
					
						
							|  |  |  |   u32 backbuffer_height = m_swap_chain->GetHeight(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   m_raster_font->PrintMultiLineText(m_swap_chain->GetRenderPass(), text, | 
					
						
							|  |  |  |                                     left * 2.0f / static_cast<float>(backbuffer_width) - 1, | 
					
						
							|  |  |  |                                     1 - top * 2.0f / static_cast<float>(backbuffer_height), | 
					
						
							|  |  |  |                                     backbuffer_width, backbuffer_height, color); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-01-23 02:51:46 -05:00
										 |  |  |   if (type == EFBAccessType::PeekColor) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     u32 color = FramebufferManager::GetInstance()->PeekEFBColor(x, y); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // a little-endian value is expected to be returned
 | 
					
						
							|  |  |  |     color = ((color & 0xFF00FF00) | ((color >> 16) & 0xFF) | ((color << 16) & 0xFF0000)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check what to do with the alpha channel (GX_PokeAlphaRead)
 | 
					
						
							|  |  |  |     PixelEngine::UPEAlphaReadReg alpha_read_mode = PixelEngine::GetAlphaReadMode(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       color = RGBA8ToRGBA6ToRGBA8(color); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       color = RGBA8ToRGB565ToRGBA8(color); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (bpmem.zcontrol.pixel_format != PEControl::RGBA6_Z24) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       color |= 0xFF000000; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (alpha_read_mode.ReadMode == 2) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return color;  // GX_READ_NONE
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (alpha_read_mode.ReadMode == 1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return color | 0xFF000000;  // GX_READ_FF
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else /*if(alpha_read_mode.ReadMode == 0)*/ | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return color & 0x00FFFFFF;  // GX_READ_00
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-01-23 02:51:46 -05:00
										 |  |  |   else  // if (type == EFBAccessType::PeekZ)
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							|  |  |  |     // Depth buffer is inverted for improved precision near far plane
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     float depth = 1.0f - FramebufferManager::GetInstance()->PeekEFBDepth(x, y); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     u32 ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // if Z is in 16 bit format you must return a 16 bit integer
 | 
					
						
							|  |  |  |       ret = MathUtil::Clamp<u32>(static_cast<u32>(depth * 65536.0f), 0, 0xFFFF); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       ret = MathUtil::Clamp<u32>(static_cast<u32>(depth * 16777216.0f), 0, 0xFFFFFF); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-01-23 02:51:46 -05:00
										 |  |  |   if (type == EFBAccessType::PokeColor) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							|  |  |  |     for (size_t i = 0; i < num_points; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // Convert to expected format (BGRA->RGBA)
 | 
					
						
							|  |  |  |       // TODO: Check alpha, depending on mode?
 | 
					
						
							|  |  |  |       const EfbPokeData& point = points[i]; | 
					
						
							|  |  |  |       u32 color = ((point.data & 0xFF00FF00) | ((point.data >> 16) & 0xFF) | | 
					
						
							|  |  |  |                    ((point.data << 16) & 0xFF0000)); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |       FramebufferManager::GetInstance()->PokeEFBColor(point.x, point.y, color); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-01-23 02:51:46 -05:00
										 |  |  |   else  // if (type == EFBAccessType::PokeZ)
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							|  |  |  |     for (size_t i = 0; i < num_points; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // Convert to floating-point depth.
 | 
					
						
							|  |  |  |       const EfbPokeData& point = points[i]; | 
					
						
							|  |  |  |       float depth = (1.0f - float(point.data & 0xFFFFFF) / 16777216.0f); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |       FramebufferManager::GetInstance()->PokeEFBDepth(point.x, point.y, depth); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u16 Renderer::BBoxRead(int index) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   s32 value = m_bounding_box->Get(static_cast<size_t>(index)); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Here we get the min/max value of the truncated position of the upscaled framebuffer.
 | 
					
						
							|  |  |  |   // So we have to correct them to the unscaled EFB sizes.
 | 
					
						
							|  |  |  |   if (index < 2) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // left/right
 | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |     value = value * EFB_WIDTH / m_target_width; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // up/down
 | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |     value = value * EFB_HEIGHT / m_target_height; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // fix max values to describe the outer border
 | 
					
						
							|  |  |  |   if (index & 1) | 
					
						
							|  |  |  |     value++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return static_cast<u16>(value); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::BBoxWrite(int index, u16 value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   s32 scaled_value = static_cast<s32>(value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // fix max values to describe the outer border
 | 
					
						
							|  |  |  |   if (index & 1) | 
					
						
							|  |  |  |     scaled_value--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // scale to internal resolution
 | 
					
						
							|  |  |  |   if (index < 2) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // left/right
 | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |     scaled_value = scaled_value * m_target_width / EFB_WIDTH; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // up/down
 | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |     scaled_value = scaled_value * m_target_height / EFB_HEIGHT; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   m_bounding_box->Set(static_cast<size_t>(index), scaled_value); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   TargetRectangle result; | 
					
						
							|  |  |  |   result.left = EFBToScaledX(rc.left); | 
					
						
							|  |  |  |   result.top = EFBToScaledY(rc.top); | 
					
						
							|  |  |  |   result.right = EFBToScaledX(rc.right); | 
					
						
							|  |  |  |   result.bottom = EFBToScaledY(rc.bottom); | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::BeginFrame() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Activate a new command list, and restore state ready for the next draw
 | 
					
						
							|  |  |  |   g_command_buffer_mgr->ActivateCommandBuffer(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Ensure that the state tracker rebinds everything, and allocates a new set
 | 
					
						
							|  |  |  |   // of descriptors out of the next pool.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->InvalidateDescriptorSets(); | 
					
						
							| 
									
										
										
										
											2016-11-30 22:34:36 +10:00
										 |  |  |   StateTracker::GetInstance()->InvalidateConstants(); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->SetPendingRebind(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable, | 
					
						
							|  |  |  |                            bool z_enable, u32 color, u32 z) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Native -> EFB coordinates
 | 
					
						
							|  |  |  |   TargetRectangle target_rc = Renderer::ConvertEFBRectangle(rc); | 
					
						
							| 
									
										
										
										
											2017-04-14 18:10:53 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Size we pass this size to vkBeginRenderPass, it has to be clamped to the framebuffer
 | 
					
						
							|  |  |  |   // dimensions. The other backends just silently ignore this case.
 | 
					
						
							|  |  |  |   target_rc.ClampUL(0, 0, m_target_width, m_target_height); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |   VkRect2D target_vk_rc = { | 
					
						
							|  |  |  |       {target_rc.left, target_rc.top}, | 
					
						
							|  |  |  |       {static_cast<uint32_t>(target_rc.GetWidth()), static_cast<uint32_t>(target_rc.GetHeight())}}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 15:18:23 +10:00
										 |  |  |   // Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha
 | 
					
						
							|  |  |  |   // channel to 0xFF. This hopefully allows us to use the fast path in most cases.
 | 
					
						
							|  |  |  |   if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16 || | 
					
						
							|  |  |  |       bpmem.zcontrol.pixel_format == PEControl::RGB8_Z24 || | 
					
						
							|  |  |  |       bpmem.zcontrol.pixel_format == PEControl::Z24) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-12-27 12:59:52 +10:00
										 |  |  |     // Force alpha writes, and clear the alpha channel. This is different to the other backends,
 | 
					
						
							|  |  |  |     // where the existing values of the alpha channel are preserved.
 | 
					
						
							| 
									
										
										
										
											2016-09-11 15:18:23 +10:00
										 |  |  |     alpha_enable = true; | 
					
						
							| 
									
										
										
										
											2016-12-27 12:59:52 +10:00
										 |  |  |     color &= 0x00FFFFFF; | 
					
						
							| 
									
										
										
										
											2016-09-11 15:18:23 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-23 21:00:20 +10:00
										 |  |  |   // Convert RGBA8 -> floating-point values.
 | 
					
						
							|  |  |  |   VkClearValue clear_color_value = {}; | 
					
						
							|  |  |  |   VkClearValue clear_depth_value = {}; | 
					
						
							|  |  |  |   clear_color_value.color.float32[0] = static_cast<float>((color >> 16) & 0xFF) / 255.0f; | 
					
						
							|  |  |  |   clear_color_value.color.float32[1] = static_cast<float>((color >> 8) & 0xFF) / 255.0f; | 
					
						
							|  |  |  |   clear_color_value.color.float32[2] = static_cast<float>((color >> 0) & 0xFF) / 255.0f; | 
					
						
							|  |  |  |   clear_color_value.color.float32[3] = static_cast<float>((color >> 24) & 0xFF) / 255.0f; | 
					
						
							|  |  |  |   clear_depth_value.depthStencil.depth = (1.0f - (static_cast<float>(z & 0xFFFFFF) / 16777216.0f)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |   // If we're not in a render pass (start of the frame), we can use a clear render pass
 | 
					
						
							|  |  |  |   // to discard the data, rather than loading and then clearing.
 | 
					
						
							| 
									
										
										
										
											2017-09-03 14:04:14 +10:00
										 |  |  |   bool use_clear_attachments = (color_enable && alpha_enable) || z_enable; | 
					
						
							|  |  |  |   bool use_clear_render_pass = | 
					
						
							|  |  |  |       !StateTracker::GetInstance()->InRenderPass() && color_enable && alpha_enable && z_enable; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // The NVIDIA Vulkan driver causes the GPU to lock up, or throw exceptions if MSAA is enabled,
 | 
					
						
							|  |  |  |   // a non-full clear rect is specified, and a clear loadop or vkCmdClearAttachments is used.
 | 
					
						
							|  |  |  |   if (g_ActiveConfig.iMultisamples > 1 && | 
					
						
							|  |  |  |       DriverDetails::HasBug(DriverDetails::BUG_BROKEN_MSAA_CLEAR)) | 
					
						
							| 
									
										
										
										
											2017-06-19 18:46:17 -07:00
										 |  |  |   { | 
					
						
							|  |  |  |     use_clear_render_pass = false; | 
					
						
							| 
									
										
										
										
											2017-09-03 14:04:14 +10:00
										 |  |  |     use_clear_attachments = false; | 
					
						
							| 
									
										
										
										
											2017-06-19 18:46:17 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-09-03 14:04:14 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // This path cannot be used if the driver implementation doesn't guarantee pixels with no drawn
 | 
					
						
							|  |  |  |   // geometry in "this" renderpass won't be cleared
 | 
					
						
							|  |  |  |   if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_CLEAR_LOADOP_RENDERPASS)) | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |     use_clear_render_pass = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Fastest path: Use a render pass to clear the buffers.
 | 
					
						
							|  |  |  |   if (use_clear_render_pass) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |     VkClearValue clear_values[2] = {clear_color_value, clear_depth_value}; | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     StateTracker::GetInstance()->BeginClearRenderPass(target_vk_rc, clear_values); | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |   // Fast path: Use vkCmdClearAttachments to clear the buffers within a render path
 | 
					
						
							|  |  |  |   // We can't use this when preserving alpha but clearing color.
 | 
					
						
							| 
									
										
										
										
											2017-09-03 14:04:14 +10:00
										 |  |  |   if (use_clear_attachments) | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |   { | 
					
						
							|  |  |  |     VkClearAttachment clear_attachments[2]; | 
					
						
							|  |  |  |     uint32_t num_clear_attachments = 0; | 
					
						
							|  |  |  |     if (color_enable && alpha_enable) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       clear_attachments[num_clear_attachments].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | 
					
						
							|  |  |  |       clear_attachments[num_clear_attachments].colorAttachment = 0; | 
					
						
							|  |  |  |       clear_attachments[num_clear_attachments].clearValue = clear_color_value; | 
					
						
							|  |  |  |       num_clear_attachments++; | 
					
						
							|  |  |  |       color_enable = false; | 
					
						
							|  |  |  |       alpha_enable = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (z_enable) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       clear_attachments[num_clear_attachments].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; | 
					
						
							|  |  |  |       clear_attachments[num_clear_attachments].colorAttachment = 0; | 
					
						
							|  |  |  |       clear_attachments[num_clear_attachments].clearValue = clear_depth_value; | 
					
						
							|  |  |  |       num_clear_attachments++; | 
					
						
							|  |  |  |       z_enable = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (num_clear_attachments > 0) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |       VkClearRect vk_rect = {target_vk_rc, 0, FramebufferManager::GetInstance()->GetEFBLayers()}; | 
					
						
							|  |  |  |       if (!StateTracker::GetInstance()->IsWithinRenderArea( | 
					
						
							|  |  |  |               target_vk_rc.offset.x, target_vk_rc.offset.y, target_vk_rc.extent.width, | 
					
						
							|  |  |  |               target_vk_rc.extent.height)) | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |       { | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |         StateTracker::GetInstance()->EndClearRenderPass(); | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |       StateTracker::GetInstance()->BeginRenderPass(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |       vkCmdClearAttachments(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_clear_attachments, | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |                             clear_attachments, 1, &vk_rect); | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |   // Anything left over for the slow path?
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   if (!color_enable && !alpha_enable && !z_enable) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Clearing must occur within a render pass.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   if (!StateTracker::GetInstance()->IsWithinRenderArea(target_vk_rc.offset.x, target_vk_rc.offset.y, | 
					
						
							|  |  |  |                                                        target_vk_rc.extent.width, | 
					
						
							|  |  |  |                                                        target_vk_rc.extent.height)) | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     StateTracker::GetInstance()->EndClearRenderPass(); | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->BeginRenderPass(); | 
					
						
							|  |  |  |   StateTracker::GetInstance()->SetPendingRebind(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Mask away the appropriate colors and use a shader
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:10:40 +10:00
										 |  |  |   BlendingState blend_state = RenderState::GetNoBlendingBlendState(); | 
					
						
							| 
									
										
										
										
											2017-04-17 23:14:17 +10:00
										 |  |  |   blend_state.colorupdate = color_enable; | 
					
						
							|  |  |  |   blend_state.alphaupdate = alpha_enable; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:10:40 +10:00
										 |  |  |   DepthState depth_state = RenderState::GetNoDepthTestingDepthStencilState(); | 
					
						
							| 
									
										
										
										
											2017-04-30 15:54:45 +10:00
										 |  |  |   depth_state.testenable = z_enable; | 
					
						
							|  |  |  |   depth_state.updateenable = z_enable; | 
					
						
							|  |  |  |   depth_state.func = ZMode::ALWAYS; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // No need to start a new render pass, but we do need to restore viewport state
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), | 
					
						
							| 
									
										
										
										
											2016-11-13 15:24:55 +10:00
										 |  |  |                          g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |                          FramebufferManager::GetInstance()->GetEFBLoadRenderPass(), | 
					
						
							| 
									
										
										
										
											2017-07-20 15:25:33 +10:00
										 |  |  |                          g_shader_cache->GetPassthroughVertexShader(), | 
					
						
							|  |  |  |                          g_shader_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 18:07:57 +10:00
										 |  |  |   draw.SetMultisamplingState(FramebufferManager::GetInstance()->GetEFBMultisamplingState()); | 
					
						
							| 
									
										
										
										
											2017-04-30 15:54:45 +10:00
										 |  |  |   draw.SetDepthState(depth_state); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   draw.SetBlendState(blend_state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   draw.DrawColoredQuad(target_rc.left, target_rc.top, target_rc.GetWidth(), target_rc.GetHeight(), | 
					
						
							| 
									
										
										
										
											2016-09-11 16:37:41 +10:00
										 |  |  |                        clear_color_value.color.float32[0], clear_color_value.color.float32[1], | 
					
						
							|  |  |  |                        clear_color_value.color.float32[2], clear_color_value.color.float32[3], | 
					
						
							|  |  |  |                        clear_depth_value.depthStencil.depth); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::ReinterpretPixelData(unsigned int convtype) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->EndRenderPass(); | 
					
						
							|  |  |  |   StateTracker::GetInstance()->SetPendingRebind(); | 
					
						
							|  |  |  |   FramebufferManager::GetInstance()->ReinterpretPixelData(convtype); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // EFB framebuffer has now changed, so update accordingly.
 | 
					
						
							|  |  |  |   BindEFBToStateTracker(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-01 11:19:29 -05:00
										 |  |  | void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks, | 
					
						
							|  |  |  |                         float Gamma) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-22 22:41:42 +10:00
										 |  |  |   // Pending/batched EFB pokes should be included in the final image.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   FramebufferManager::GetInstance()->FlushEFBPokes(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-29 17:02:09 -05:00
										 |  |  |   auto* xfb_texture = static_cast<VKTexture*>(texture); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   // End the current render pass.
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->EndRenderPass(); | 
					
						
							|  |  |  |   StateTracker::GetInstance()->OnEndFrame(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-02 21:50:38 +10:00
										 |  |  |   // There are a few variables which can alter the final window draw rectangle, and some of them
 | 
					
						
							|  |  |  |   // are determined by guest state. Currently, the only way to catch these is to update every frame.
 | 
					
						
							|  |  |  |   UpdateDrawRectangle(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   // Ensure the worker thread is not still submitting a previous command buffer.
 | 
					
						
							|  |  |  |   // In other words, the last frame has been submitted (otherwise the next call would
 | 
					
						
							|  |  |  |   // be a race, as the image may not have been consumed yet).
 | 
					
						
							|  |  |  |   g_command_buffer_mgr->PrepareToSubmitCommandBuffer(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Draw to the screen if we have a swap chain.
 | 
					
						
							|  |  |  |   if (m_swap_chain) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2017-09-29 00:31:08 -05:00
										 |  |  |     DrawScreen(xfb_texture, xfb_region); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Submit the current command buffer, signaling rendering finished semaphore when it's done
 | 
					
						
							|  |  |  |     // Because this final command buffer is rendering to the swap chain, we need to wait for
 | 
					
						
							|  |  |  |     // the available semaphore to be signaled before executing the buffer. This final submission
 | 
					
						
							|  |  |  |     // can happen off-thread in the background while we're preparing the next frame.
 | 
					
						
							|  |  |  |     g_command_buffer_mgr->SubmitCommandBuffer( | 
					
						
							| 
									
										
										
										
											2016-11-09 23:15:44 +10:00
										 |  |  |         true, m_image_available_semaphore, m_rendering_finished_semaphore, | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |         m_swap_chain->GetSwapChain(), m_swap_chain->GetCurrentImageIndex()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // No swap chain, just execute command buffer.
 | 
					
						
							| 
									
										
										
										
											2016-11-09 23:15:44 +10:00
										 |  |  |     g_command_buffer_mgr->SubmitCommandBuffer(true); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-02 21:37:24 +10:00
										 |  |  |   // NOTE: It is important that no rendering calls are made to the EFB between submitting the
 | 
					
						
							|  |  |  |   // (now-previous) frame and after the below config checks are completed. If the target size
 | 
					
						
							|  |  |  |   // changes, as the resize methods to not defer the destruction of the framebuffer, the current
 | 
					
						
							|  |  |  |   // command buffer will contain references to a now non-existent framebuffer.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   // Prep for the next frame (get command buffer ready) before doing anything else.
 | 
					
						
							|  |  |  |   BeginFrame(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 15:52:12 +10:00
										 |  |  |   // Restore the EFB color texture to color attachment ready for rendering the next frame.
 | 
					
						
							|  |  |  |   FramebufferManager::GetInstance()->GetEFBColorTexture()->TransitionToLayout( | 
					
						
							|  |  |  |       g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-02 21:37:24 +10:00
										 |  |  |   // Determine what (if anything) has changed in the config.
 | 
					
						
							|  |  |  |   CheckForConfigChanges(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-02 21:37:24 +10:00
										 |  |  |   // Handle host window resizes.
 | 
					
						
							|  |  |  |   CheckForSurfaceChange(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-29 17:02:09 -05:00
										 |  |  |   if (CalculateTargetSize()) | 
					
						
							|  |  |  |     ResizeEFBTextures(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-02 21:50:38 +10:00
										 |  |  |   // Update the window size based on the frame that was just rendered.
 | 
					
						
							|  |  |  |   // Due to depending on guest state, we need to call this every frame.
 | 
					
						
							| 
									
										
										
										
											2017-06-02 20:33:26 -05:00
										 |  |  |   SetWindowSize(xfb_texture->GetConfig().width, xfb_texture->GetConfig().height); | 
					
						
							| 
									
										
										
										
											2017-03-02 21:50:38 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-02 21:37:24 +10:00
										 |  |  |   // Clean up stale textures.
 | 
					
						
							| 
									
										
										
										
											2016-09-06 18:57:58 -04:00
										 |  |  |   TextureCache::GetInstance()->Cleanup(frameCount); | 
					
						
							| 
									
										
										
										
											2017-07-20 15:25:35 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Pull in now-ready async shaders.
 | 
					
						
							|  |  |  |   g_shader_cache->RetrieveAsyncShaders(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-29 00:31:08 -05:00
										 |  |  | void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2017-09-16 15:53:08 +10:00
										 |  |  |   VkResult res; | 
					
						
							| 
									
										
										
										
											2017-09-16 16:15:20 +10:00
										 |  |  |   if (!g_command_buffer_mgr->CheckLastPresentFail()) | 
					
						
							| 
									
										
										
										
											2017-09-16 15:53:08 +10:00
										 |  |  |   { | 
					
						
							|  |  |  |     // Grab the next image from the swap chain in preparation for drawing the window.
 | 
					
						
							|  |  |  |     res = m_swap_chain->AcquireNextImage(m_image_available_semaphore); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // If the last present failed, we need to recreate the swap chain.
 | 
					
						
							|  |  |  |     res = VK_ERROR_OUT_OF_DATE_KHR; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-12-04 00:23:37 +10:00
										 |  |  |     // There's an issue here. We can't resize the swap chain while the GPU is still busy with it,
 | 
					
						
							|  |  |  |     // but calling WaitForGPUIdle would create a deadlock as PrepareToSubmitCommandBuffer has been
 | 
					
						
							|  |  |  |     // called by SwapImpl. WaitForGPUIdle waits on the semaphore, which PrepareToSubmitCommandBuffer
 | 
					
						
							|  |  |  |     // has already done, so it blocks indefinitely. To work around this, we submit the current
 | 
					
						
							|  |  |  |     // command buffer, resize the swap chain (which calls WaitForGPUIdle), and then finally call
 | 
					
						
							|  |  |  |     // PrepareToSubmitCommandBuffer to return to the state that the caller expects.
 | 
					
						
							|  |  |  |     g_command_buffer_mgr->SubmitCommandBuffer(false); | 
					
						
							| 
									
										
										
										
											2017-09-16 16:15:20 +10:00
										 |  |  |     m_swap_chain->ResizeSwapChain(); | 
					
						
							| 
									
										
										
										
											2017-09-16 15:52:12 +10:00
										 |  |  |     BeginFrame(); | 
					
						
							| 
									
										
										
										
											2016-12-04 00:23:37 +10:00
										 |  |  |     g_command_buffer_mgr->PrepareToSubmitCommandBuffer(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     res = m_swap_chain->AcquireNextImage(m_image_available_semaphore); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (res != VK_SUCCESS) | 
					
						
							|  |  |  |     PanicAlert("Failed to grab image from swap chain"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Transition from undefined (or present src, but it can be substituted) to
 | 
					
						
							|  |  |  |   // color attachment ready for writing. These transitions must occur outside
 | 
					
						
							|  |  |  |   // a render pass, unless the render pass declares a self-dependency.
 | 
					
						
							|  |  |  |   Texture2D* backbuffer = m_swap_chain->GetCurrentTexture(); | 
					
						
							|  |  |  |   backbuffer->OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED); | 
					
						
							|  |  |  |   backbuffer->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), | 
					
						
							|  |  |  |                                  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 22:41:42 +10:00
										 |  |  |   // Begin render pass for rendering to the swap chain.
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; | 
					
						
							| 
									
										
										
										
											2016-10-22 22:41:42 +10:00
										 |  |  |   VkRenderPassBeginInfo info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | 
					
						
							|  |  |  |                                 nullptr, | 
					
						
							|  |  |  |                                 m_swap_chain->GetRenderPass(), | 
					
						
							|  |  |  |                                 m_swap_chain->GetCurrentFramebuffer(), | 
					
						
							|  |  |  |                                 {{0, 0}, {backbuffer->GetWidth(), backbuffer->GetHeight()}}, | 
					
						
							|  |  |  |                                 1, | 
					
						
							|  |  |  |                                 &clear_value}; | 
					
						
							|  |  |  |   vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &info, | 
					
						
							|  |  |  |                        VK_SUBPASS_CONTENTS_INLINE); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-29 17:02:09 -05:00
										 |  |  |   // Draw
 | 
					
						
							| 
									
										
										
										
											2017-09-29 00:31:08 -05:00
										 |  |  |   BlitScreen(m_swap_chain->GetRenderPass(), GetTargetRectangle(), xfb_region, | 
					
						
							|  |  |  |              xfb_texture->GetRawTexIdentifier()); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 22:41:42 +10:00
										 |  |  |   // Draw OSD
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0, | 
					
						
							|  |  |  |                               backbuffer->GetWidth(), backbuffer->GetHeight()); | 
					
						
							|  |  |  |   DrawDebugText(); | 
					
						
							|  |  |  |   OSD::DoCallbacks(OSD::CallbackType::OnFrame); | 
					
						
							|  |  |  |   OSD::DrawMessages(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // End drawing to backbuffer
 | 
					
						
							| 
									
										
										
										
											2016-10-22 22:41:42 +10:00
										 |  |  |   vkCmdEndRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer()); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Transition the backbuffer to PRESENT_SRC to ensure all commands drawing
 | 
					
						
							|  |  |  |   // to it have finished before present.
 | 
					
						
							|  |  |  |   backbuffer->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), | 
					
						
							|  |  |  |                                  VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect, | 
					
						
							| 
									
										
										
										
											2017-04-21 23:33:58 +10:00
										 |  |  |                           const TargetRectangle& src_rect, const Texture2D* src_tex) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2017-04-21 23:33:58 +10:00
										 |  |  |   VulkanPostProcessing* post_processor = static_cast<VulkanPostProcessing*>(m_post_processor.get()); | 
					
						
							| 
									
										
										
										
											2017-11-10 22:55:00 -05:00
										 |  |  |   if (g_ActiveConfig.stereo_mode == StereoMode::SBS || | 
					
						
							|  |  |  |       g_ActiveConfig.stereo_mode == StereoMode::TAB) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							|  |  |  |     TargetRectangle left_rect; | 
					
						
							|  |  |  |     TargetRectangle right_rect; | 
					
						
							| 
									
										
										
										
											2017-04-09 15:05:24 -04:00
										 |  |  |     std::tie(left_rect, right_rect) = ConvertStereoRectangle(dst_rect); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-21 23:33:58 +10:00
										 |  |  |     post_processor->BlitFromTexture(left_rect, src_rect, src_tex, 0, render_pass); | 
					
						
							|  |  |  |     post_processor->BlitFromTexture(right_rect, src_rect, src_tex, 1, render_pass); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-11-10 22:55:00 -05:00
										 |  |  |   else if (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer) | 
					
						
							| 
									
										
										
										
											2017-06-26 21:08:21 +02:00
										 |  |  |   { | 
					
						
							|  |  |  |     post_processor->BlitFromTexture(dst_rect, src_rect, src_tex, -1, render_pass); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2017-04-21 23:33:58 +10:00
										 |  |  |     post_processor->BlitFromTexture(dst_rect, src_rect, src_tex, 0, render_pass); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::CheckForSurfaceChange() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |   if (!m_surface_needs_change.IsSet()) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 16:15:20 +10:00
										 |  |  |   // Wait for the GPU to catch up since we're going to destroy the swap chain.
 | 
					
						
							|  |  |  |   g_command_buffer_mgr->WaitForGPUIdle(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Clear the present failed flag, since we don't want to resize after recreating.
 | 
					
						
							|  |  |  |   g_command_buffer_mgr->CheckLastPresentFail(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Fast path, if the surface handle is the same, the window has just been resized.
 | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |   if (m_swap_chain && m_new_surface_handle == m_swap_chain->GetNativeHandle()) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							|  |  |  |     INFO_LOG(VIDEO, "Detected window resize."); | 
					
						
							| 
									
										
										
										
											2017-09-16 16:15:20 +10:00
										 |  |  |     m_swap_chain->RecreateSwapChain(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Notify the main thread we are done.
 | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |     m_surface_needs_change.Clear(); | 
					
						
							|  |  |  |     m_new_surface_handle = nullptr; | 
					
						
							|  |  |  |     m_surface_changed.Set(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // Did we previously have a swap chain?
 | 
					
						
							|  |  |  |     if (m_swap_chain) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |       if (!m_new_surface_handle) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |       { | 
					
						
							|  |  |  |         // If there is no surface now, destroy the swap chain.
 | 
					
						
							|  |  |  |         m_swap_chain.reset(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         // Recreate the surface. If this fails we're in trouble.
 | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |         if (!m_swap_chain->RecreateSurface(m_new_surface_handle)) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |           PanicAlert("Failed to recreate Vulkan surface. Cannot continue."); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // Previously had no swap chain. So create one.
 | 
					
						
							|  |  |  |       VkSurfaceKHR surface = SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |                                                             m_new_surface_handle); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |       if (surface != VK_NULL_HANDLE) | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |         m_swap_chain = SwapChain::Create(m_new_surface_handle, surface, g_ActiveConfig.IsVSync()); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |         if (!m_swap_chain) | 
					
						
							|  |  |  |           PanicAlert("Failed to create swap chain."); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         PanicAlert("Failed to create surface."); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Notify calling thread.
 | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |     m_surface_needs_change.Clear(); | 
					
						
							| 
									
										
										
										
											2017-11-23 16:53:44 +10:00
										 |  |  |     m_surface_handle = m_new_surface_handle; | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |     m_new_surface_handle = nullptr; | 
					
						
							|  |  |  |     m_surface_changed.Set(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 16:15:20 +10:00
										 |  |  |   // Handle case where the dimensions are now different.
 | 
					
						
							|  |  |  |   OnSwapChainResized(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::CheckForConfigChanges() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-08 00:10:38 +10:00
										 |  |  |   // Save the video config so we can compare against to determine which settings have changed.
 | 
					
						
							| 
									
										
										
										
											2017-11-10 22:45:32 -05:00
										 |  |  |   const int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; | 
					
						
							|  |  |  |   const AspectMode old_aspect_mode = g_ActiveConfig.aspect_mode; | 
					
						
							|  |  |  |   const int old_efb_scale = g_ActiveConfig.iEFBScale; | 
					
						
							|  |  |  |   const bool old_force_filtering = g_ActiveConfig.bForceFiltering; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Copy g_Config to g_ActiveConfig.
 | 
					
						
							| 
									
										
										
										
											2016-10-08 00:10:38 +10:00
										 |  |  |   // NOTE: This can potentially race with the UI thread, however if it does, the changes will be
 | 
					
						
							|  |  |  |   // delayed until the next time CheckForConfigChanges is called.
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   UpdateActiveConfig(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-08 00:10:38 +10:00
										 |  |  |   // Determine which (if any) settings have changed.
 | 
					
						
							| 
									
										
										
										
											2017-11-10 22:45:32 -05:00
										 |  |  |   const bool anisotropy_changed = old_anisotropy != g_ActiveConfig.iMaxAnisotropy; | 
					
						
							|  |  |  |   const bool force_texture_filtering_changed = | 
					
						
							|  |  |  |       old_force_filtering != g_ActiveConfig.bForceFiltering; | 
					
						
							|  |  |  |   const bool efb_scale_changed = old_efb_scale != g_ActiveConfig.iEFBScale; | 
					
						
							|  |  |  |   const bool aspect_changed = old_aspect_mode != g_ActiveConfig.aspect_mode; | 
					
						
							| 
									
										
										
										
											2016-10-08 00:10:38 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-01 22:54:02 +10:00
										 |  |  |   // Update texture cache settings with any changed options.
 | 
					
						
							| 
									
										
										
										
											2016-09-06 18:57:58 -04:00
										 |  |  |   TextureCache::GetInstance()->OnConfigChanged(g_ActiveConfig); | 
					
						
							| 
									
										
										
										
											2016-10-01 22:54:02 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-02 21:50:38 +10:00
										 |  |  |   // Handle settings that can cause the target rectangle to change.
 | 
					
						
							| 
									
										
										
										
											2017-06-25 22:23:47 -05:00
										 |  |  |   if (efb_scale_changed || aspect_changed) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-11-10 23:31:44 +10:00
										 |  |  |     if (CalculateTargetSize()) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |       ResizeEFBTextures(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-08 00:10:38 +10:00
										 |  |  |   // MSAA samples changed, we need to recreate the EFB render pass.
 | 
					
						
							|  |  |  |   // If the stereoscopy mode changed, we need to recreate the buffers as well.
 | 
					
						
							| 
									
										
										
										
											2017-06-24 19:58:14 +10:00
										 |  |  |   // SSAA changed on/off, we have to recompile shaders.
 | 
					
						
							|  |  |  |   // Changing stereoscopy from off<->on also requires shaders to be recompiled.
 | 
					
						
							| 
									
										
										
										
											2017-07-20 17:10:02 +10:00
										 |  |  |   if (CheckForHostConfigChanges()) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-10-08 00:10:38 +10:00
										 |  |  |     g_command_buffer_mgr->WaitForGPUIdle(); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     FramebufferManager::GetInstance()->RecreateRenderPass(); | 
					
						
							|  |  |  |     FramebufferManager::GetInstance()->ResizeEFBTextures(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     BindEFBToStateTracker(); | 
					
						
							|  |  |  |     RecompileShaders(); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     FramebufferManager::GetInstance()->RecompileShaders(); | 
					
						
							| 
									
										
										
										
											2017-07-20 15:25:33 +10:00
										 |  |  |     g_shader_cache->ReloadShaderAndPipelineCaches(); | 
					
						
							|  |  |  |     g_shader_cache->RecompileSharedShaders(); | 
					
						
							| 
									
										
										
										
											2017-06-24 19:58:14 +10:00
										 |  |  |     StateTracker::GetInstance()->InvalidateShaderPointers(); | 
					
						
							|  |  |  |     StateTracker::GetInstance()->ReloadPipelineUIDCache(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // For vsync, we need to change the present mode, which means recreating the swap chain.
 | 
					
						
							| 
									
										
										
										
											2016-10-02 22:09:19 +10:00
										 |  |  |   if (m_swap_chain && g_ActiveConfig.IsVSync() != m_swap_chain->IsVSyncEnabled()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     g_command_buffer_mgr->WaitForGPUIdle(); | 
					
						
							|  |  |  |     m_swap_chain->SetVSync(g_ActiveConfig.IsVSync()); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-26 21:08:21 +02:00
										 |  |  |   // For quad-buffered stereo we need to change the layer count, so recreate the swap chain.
 | 
					
						
							|  |  |  |   if (m_swap_chain && | 
					
						
							| 
									
										
										
										
											2017-11-10 22:55:00 -05:00
										 |  |  |       (g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer) != m_swap_chain->IsStereoEnabled()) | 
					
						
							| 
									
										
										
										
											2017-09-16 16:15:20 +10:00
										 |  |  |   { | 
					
						
							|  |  |  |     g_command_buffer_mgr->WaitForGPUIdle(); | 
					
						
							|  |  |  |     m_swap_chain->RecreateSwapChain(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-06-26 21:08:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   // Wipe sampler cache if force texture filtering or anisotropy changes.
 | 
					
						
							|  |  |  |   if (anisotropy_changed || force_texture_filtering_changed) | 
					
						
							|  |  |  |     ResetSamplerStates(); | 
					
						
							| 
									
										
										
										
											2017-04-21 23:33:58 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Check for a changed post-processing shader and recompile if needed.
 | 
					
						
							|  |  |  |   static_cast<VulkanPostProcessing*>(m_post_processor.get())->UpdateConfig(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::OnSwapChainResized() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |   m_backbuffer_width = m_swap_chain->GetWidth(); | 
					
						
							|  |  |  |   m_backbuffer_height = m_swap_chain->GetHeight(); | 
					
						
							| 
									
										
										
										
											2016-11-10 23:31:44 +10:00
										 |  |  |   UpdateDrawRectangle(); | 
					
						
							|  |  |  |   if (CalculateTargetSize()) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     ResizeEFBTextures(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::BindEFBToStateTracker() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Update framebuffer in state tracker
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   VkRect2D framebuffer_size = {{0, 0}, | 
					
						
							|  |  |  |                                {FramebufferManager::GetInstance()->GetEFBWidth(), | 
					
						
							|  |  |  |                                 FramebufferManager::GetInstance()->GetEFBHeight()}}; | 
					
						
							|  |  |  |   StateTracker::GetInstance()->SetRenderPass( | 
					
						
							|  |  |  |       FramebufferManager::GetInstance()->GetEFBLoadRenderPass(), | 
					
						
							|  |  |  |       FramebufferManager::GetInstance()->GetEFBClearRenderPass()); | 
					
						
							|  |  |  |   StateTracker::GetInstance()->SetFramebuffer( | 
					
						
							|  |  |  |       FramebufferManager::GetInstance()->GetEFBFramebuffer(), framebuffer_size); | 
					
						
							| 
									
										
										
										
											2017-04-30 18:07:57 +10:00
										 |  |  |   StateTracker::GetInstance()->SetMultisamplingstate( | 
					
						
							|  |  |  |       FramebufferManager::GetInstance()->GetEFBMultisamplingState()); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::ResizeEFBTextures() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Ensure the GPU is finished with the current EFB textures.
 | 
					
						
							|  |  |  |   g_command_buffer_mgr->WaitForGPUIdle(); | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   FramebufferManager::GetInstance()->ResizeEFBTextures(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   BindEFBToStateTracker(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Viewport and scissor rect have to be reset since they will be scaled differently.
 | 
					
						
							|  |  |  |   SetViewport(); | 
					
						
							|  |  |  |   BPFunctions::SetScissor(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-28 01:37:41 +01:00
										 |  |  | void Renderer::ApplyState() | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::ResetAPIState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // End the EFB render pass if active
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->EndRenderPass(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::RestoreAPIState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Instruct the state tracker to re-bind everything before the next draw
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->SetPendingRebind(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 18:07:57 +10:00
										 |  |  | void Renderer::SetRasterizationState(const RasterizationState& state) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2017-04-30 18:07:57 +10:00
										 |  |  |   StateTracker::GetInstance()->SetRasterizationState(state); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 15:54:45 +10:00
										 |  |  | void Renderer::SetDepthState(const DepthState& state) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2017-04-30 15:54:45 +10:00
										 |  |  |   StateTracker::GetInstance()->SetDepthState(state); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 01:00:45 +10:00
										 |  |  | void Renderer::SetBlendingState(const BlendingState& state) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2017-04-17 23:14:17 +10:00
										 |  |  |   StateTracker::GetInstance()->SetBlendState(state); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  | void Renderer::SetSamplerState(u32 index, const SamplerState& state) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | { | 
					
						
							|  |  |  |   // Skip lookup if the state hasn't changed.
 | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  |   if (m_sampler_states[index].hex == state.hex) | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Look up new state and replace in state tracker.
 | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  |   VkSampler sampler = g_object_cache->GetSampler(state); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   if (sampler == VK_NULL_HANDLE) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     ERROR_LOG(VIDEO, "Failed to create sampler"); | 
					
						
							|  |  |  |     sampler = g_object_cache->GetPointSampler(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  |   StateTracker::GetInstance()->SetSampler(index, sampler); | 
					
						
							|  |  |  |   m_sampler_states[index].hex = state.hex; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::ResetSamplerStates() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Ensure none of the sampler objects are in use.
 | 
					
						
							| 
									
										
										
										
											2016-10-02 23:53:09 +10:00
										 |  |  |   // This assumes that none of the samplers are in use on the command list currently being recorded.
 | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   g_command_buffer_mgr->WaitForGPUIdle(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Invalidate all sampler states, next draw will re-initialize them.
 | 
					
						
							|  |  |  |   for (size_t i = 0; i < m_sampler_states.size(); i++) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  |     m_sampler_states[i].hex = RenderState::GetPointSamplerState().hex; | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |     StateTracker::GetInstance()->SetSampler(i, g_object_cache->GetPointSampler()); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Invalidate all sampler objects (some will be unused now).
 | 
					
						
							|  |  |  |   g_object_cache->ClearSamplerCache(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::SetInterlacingMode() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::SetScissorRect(const EFBRectangle& rc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   TargetRectangle target_rc = ConvertEFBRectangle(rc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   VkRect2D scissor = { | 
					
						
							|  |  |  |       {target_rc.left, target_rc.top}, | 
					
						
							|  |  |  |       {static_cast<uint32_t>(target_rc.GetWidth()), static_cast<uint32_t>(target_rc.GetHeight())}}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->SetScissor(scissor); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::SetViewport() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   int scissor_x_offset = bpmem.scissorOffset.x * 2; | 
					
						
							|  |  |  |   int scissor_y_offset = bpmem.scissorOffset.y * 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   float x = Renderer::EFBToScaledXf(xfmem.viewport.xOrig - xfmem.viewport.wd - scissor_x_offset); | 
					
						
							|  |  |  |   float y = Renderer::EFBToScaledYf(xfmem.viewport.yOrig + xfmem.viewport.ht - scissor_y_offset); | 
					
						
							|  |  |  |   float width = Renderer::EFBToScaledXf(2.0f * xfmem.viewport.wd); | 
					
						
							|  |  |  |   float height = Renderer::EFBToScaledYf(-2.0f * xfmem.viewport.ht); | 
					
						
							| 
									
										
										
										
											2017-02-21 16:15:22 +01:00
										 |  |  |   float min_depth = (xfmem.viewport.farZ - xfmem.viewport.zRange) / 16777216.0f; | 
					
						
							|  |  |  |   float max_depth = xfmem.viewport.farZ / 16777216.0f; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   if (width < 0.0f) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     x += width; | 
					
						
							|  |  |  |     width = -width; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (height < 0.0f) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     y += height; | 
					
						
							|  |  |  |     height = -height; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-12-27 14:26:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-19 16:41:04 +01:00
										 |  |  |   // If an oversized or inverted depth range is used, we need to calculate the depth range in the
 | 
					
						
							|  |  |  |   // vertex shader.
 | 
					
						
							|  |  |  |   // TODO: Inverted depth ranges are bugged in all drivers, which should be added to DriverDetails.
 | 
					
						
							| 
									
										
										
										
											2017-02-24 15:16:28 +01:00
										 |  |  |   if (UseVertexDepthRange()) | 
					
						
							| 
									
										
										
										
											2016-11-23 23:25:44 +01:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2017-02-19 16:41:04 +01:00
										 |  |  |     // We need to ensure depth values are clamped the maximum value supported by the console GPU.
 | 
					
						
							| 
									
										
										
										
											2016-12-27 14:26:11 +01:00
										 |  |  |     min_depth = 0.0f; | 
					
						
							|  |  |  |     max_depth = GX_MAX_DEPTH; | 
					
						
							| 
									
										
										
										
											2016-11-23 23:25:44 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 14:26:11 +01:00
										 |  |  |   // We use an inverted depth range here to apply the Reverse Z trick.
 | 
					
						
							|  |  |  |   // This trick makes sure we match the precision provided by the 1:0
 | 
					
						
							|  |  |  |   // clipping depth range on the hardware.
 | 
					
						
							| 
									
										
										
										
											2016-11-23 23:25:44 +01:00
										 |  |  |   VkViewport viewport = {x, y, width, height, 1.0f - max_depth, 1.0f - min_depth}; | 
					
						
							| 
									
										
										
										
											2016-10-22 20:50:36 +10:00
										 |  |  |   StateTracker::GetInstance()->SetViewport(viewport); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::ChangeSurface(void* new_surface_handle) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Called by the main thread when the window is resized.
 | 
					
						
							| 
									
										
										
										
											2017-03-04 16:42:21 +10:00
										 |  |  |   m_new_surface_handle = new_surface_handle; | 
					
						
							|  |  |  |   m_surface_needs_change.Set(); | 
					
						
							|  |  |  |   m_surface_changed.Set(); | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::RecompileShaders() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   DestroyShaders(); | 
					
						
							|  |  |  |   if (!CompileShaders()) | 
					
						
							|  |  |  |     PanicAlert("Failed to recompile shaders."); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool Renderer::CompileShaders() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   static const char CLEAR_FRAGMENT_SHADER_SOURCE[] = R"( | 
					
						
							|  |  |  |     layout(location = 0) in float3 uv0; | 
					
						
							|  |  |  |     layout(location = 1) in float4 col0; | 
					
						
							|  |  |  |     layout(location = 0) out float4 ocol0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void main() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       ocol0 = col0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   )"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-20 15:25:33 +10:00
										 |  |  |   std::string source = g_shader_cache->GetUtilityShaderHeader() + CLEAR_FRAGMENT_SHADER_SOURCE; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  |   m_clear_fragment_shader = Util::CompileAndCreateFragmentShader(source); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-21 23:33:58 +10:00
										 |  |  |   return m_clear_fragment_shader != VK_NULL_HANDLE; | 
					
						
							| 
									
										
										
										
											2016-08-13 22:57:50 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Renderer::DestroyShaders() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   auto DestroyShader = [this](VkShaderModule& shader) { | 
					
						
							|  |  |  |     if (shader != VK_NULL_HANDLE) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       vkDestroyShaderModule(g_vulkan_context->GetDevice(), shader, nullptr); | 
					
						
							|  |  |  |       shader = VK_NULL_HANDLE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   DestroyShader(m_clear_fragment_shader); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace Vulkan
 |