diff options
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/Renderer.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/Renderer.cpp | 147 |
1 files changed, 71 insertions, 76 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index e1c6394b30..7bb15aacdf 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -22,6 +22,7 @@ #include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/SwapChain.h" +#include "VideoBackends/Vulkan/TextureCache.h" #include "VideoBackends/Vulkan/Util.h" #include "VideoBackends/Vulkan/VulkanContext.h" @@ -41,37 +42,39 @@ namespace Vulkan { -Renderer::Renderer() +Renderer::Renderer(std::unique_ptr<SwapChain> swap_chain) : m_swap_chain(std::move(swap_chain)) { + g_Config.bRunning = true; + UpdateActiveConfig(); + // Set to something invalid, forcing all states to be re-initialized. for (size_t i = 0; i < m_sampler_states.size(); i++) m_sampler_states[i].bits = std::numeric_limits<decltype(m_sampler_states[i].bits)>::max(); - // Set default size so a decent EFB is created initially. - s_backbuffer_width = MAX_XFB_WIDTH; - s_backbuffer_height = MAX_XFB_HEIGHT; + // These have to be initialized before FramebufferManager is created. + // If running surfaceless, assume a window size of MAX_XFB_{WIDTH,HEIGHT}. FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH); FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT); - PixelShaderManager::SetEfbScaleChanged(); + s_backbuffer_width = m_swap_chain ? m_swap_chain->GetWidth() : MAX_XFB_WIDTH; + s_backbuffer_height = m_swap_chain ? m_swap_chain->GetHeight() : MAX_XFB_HEIGHT; + s_last_efb_scale = g_ActiveConfig.iEFBScale; UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); CalculateTargetSize(s_backbuffer_width, s_backbuffer_height); + PixelShaderManager::SetEfbScaleChanged(); } Renderer::~Renderer() { g_Config.bRunning = false; UpdateActiveConfig(); + DestroyScreenshotResources(); + DestroyShaders(); DestroySemaphores(); } -bool Renderer::Initialize(FramebufferManager* framebuffer_mgr, void* window_handle, - VkSurfaceKHR surface) +bool Renderer::Initialize(FramebufferManager* framebuffer_mgr) { m_framebuffer_mgr = framebuffer_mgr; - g_Config.bRunning = true; - UpdateActiveConfig(); - - // Create state tracker, doesn't require any resources m_state_tracker = std::make_unique<StateTracker>(); BindEFBToStateTracker(); @@ -109,24 +112,6 @@ bool Renderer::Initialize(FramebufferManager* framebuffer_mgr, void* window_hand m_bounding_box->GetGPUBufferSize()); } - // Initialize annoying statics - s_last_efb_scale = g_ActiveConfig.iEFBScale; - - // Create swap chain - if (surface) - { - // Update backbuffer dimensions - m_swap_chain = SwapChain::Create(window_handle, surface); - if (!m_swap_chain) - { - PanicAlert("Failed to create swap chain."); - return false; - } - - // Update render rectangle etc. - OnSwapChainResized(); - } - // 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(); @@ -164,13 +149,13 @@ void Renderer::DestroySemaphores() if (m_image_available_semaphore) { vkDestroySemaphore(g_vulkan_context->GetDevice(), m_image_available_semaphore, nullptr); - m_image_available_semaphore = nullptr; + m_image_available_semaphore = VK_NULL_HANDLE; } if (m_rendering_finished_semaphore) { vkDestroySemaphore(g_vulkan_context->GetDevice(), m_rendering_finished_semaphore, nullptr); - m_rendering_finished_semaphore = nullptr; + m_rendering_finished_semaphore = VK_NULL_HANDLE; } } @@ -491,9 +476,6 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height // Scale the source rectangle to the selected internal resolution. TargetRectangle source_rc = Renderer::ConvertEFBRectangle(rc); - // Target rectangle can change if the VI aspect ratio changes. - UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); - // Transition the EFB render target to a shader resource. VkRect2D src_region = {{0, 0}, {m_framebuffer_mgr->GetEFBWidth(), m_framebuffer_mgr->GetEFBHeight()}}; @@ -519,6 +501,10 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height StopFrameDump(); } + // Restore the EFB color texture to color attachment ready for rendering the next frame. + m_framebuffer_mgr->GetEFBColorTexture()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + // 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). @@ -543,22 +529,28 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height g_command_buffer_mgr->SubmitCommandBuffer(true); } + // 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. + // Prep for the next frame (get command buffer ready) before doing anything else. BeginFrame(); - // Restore the EFB color texture to color attachment ready for rendering. - m_framebuffer_mgr->GetEFBColorTexture()->TransitionToLayout( - g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + // Determine what (if anything) has changed in the config. + CheckForConfigChanges(); - // Clean up stale textures - TextureCacheBase::Cleanup(frameCount); + // Handle host window resizes. + CheckForSurfaceChange(); - // Handle window resizes. + // Handle output size changes from the guest. + // There is a downside to doing this here is that if the game changes its XFB source area, + // the changes will be delayed by one frame. For the moment it has to be done here because + // this can cause a target size change, which would result in a black frame if done earlier. CheckForTargetResize(fb_width, fb_stride, fb_height); - CheckForSurfaceChange(); - // Determine what has changed in the config. - CheckForConfigChanges(); + // Clean up stale textures. + TextureCacheBase::Cleanup(frameCount); } void Renderer::DrawScreen(const TargetRectangle& src_rect, const Texture2D* src_tex) @@ -829,13 +821,23 @@ void Renderer::StopFrameDump() void Renderer::CheckForTargetResize(u32 fb_width, u32 fb_stride, u32 fb_height) { - if (FramebufferManagerBase::LastXfbWidth() != fb_stride || - FramebufferManagerBase::LastXfbHeight() != fb_height) + if (FramebufferManagerBase::LastXfbWidth() == fb_stride && + FramebufferManagerBase::LastXfbHeight() == fb_height) { - u32 last_w = (fb_stride < 1 || fb_stride > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fb_stride; - u32 last_h = (fb_height < 1 || fb_height > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fb_height; - FramebufferManagerBase::SetLastXfbWidth(last_w); - FramebufferManagerBase::SetLastXfbHeight(last_h); + return; + } + + u32 new_width = (fb_stride < 1 || fb_stride > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fb_stride; + u32 new_height = (fb_height < 1 || fb_height > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fb_height; + FramebufferManagerBase::SetLastXfbWidth(new_width); + FramebufferManagerBase::SetLastXfbHeight(new_height); + + // Changing the XFB source area will likely change the final drawing rectangle. + UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); + if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height)) + { + PixelShaderManager::SetEfbScaleChanged(); + ResizeEFBTextures(); } // This call is needed for auto-resizing to work. @@ -850,9 +852,6 @@ void Renderer::CheckForSurfaceChange() u32 old_width = m_swap_chain ? m_swap_chain->GetWidth() : 0; u32 old_height = m_swap_chain ? m_swap_chain->GetHeight() : 0; - // Wait for the GPU to catch up since we're going to destroy the swap chain. - g_command_buffer_mgr->WaitForGPUIdle(); - // Fast path, if the surface handle is the same, the window has just been resized. if (m_swap_chain && s_new_surface_handle == m_swap_chain->GetNativeHandle()) { @@ -866,6 +865,9 @@ void Renderer::CheckForSurfaceChange() } else { + // Wait for the GPU to catch up since we're going to destroy the swap chain. + g_command_buffer_mgr->WaitForGPUIdle(); + // Did we previously have a swap chain? if (m_swap_chain) { @@ -888,7 +890,7 @@ void Renderer::CheckForSurfaceChange() s_new_surface_handle); if (surface != VK_NULL_HANDLE) { - m_swap_chain = SwapChain::Create(s_new_surface_handle, surface); + m_swap_chain = SwapChain::Create(s_new_surface_handle, surface, g_ActiveConfig.IsVSync()); if (!m_swap_chain) PanicAlert("Failed to create swap chain."); } @@ -915,7 +917,6 @@ void Renderer::CheckForSurfaceChange() void Renderer::CheckForConfigChanges() { // Compare g_Config to g_ActiveConfig to determine what has changed before copying. - bool vsync_changed = (g_Config.bVSync != g_ActiveConfig.bVSync); bool msaa_changed = (g_Config.iMultisamples != g_ActiveConfig.iMultisamples); bool ssaa_changed = (g_Config.bSSAA != g_ActiveConfig.bSSAA); bool anisotropy_changed = (g_Config.iMaxAnisotropy != g_ActiveConfig.iMaxAnisotropy); @@ -926,6 +927,9 @@ void Renderer::CheckForConfigChanges() // Copy g_Config to g_ActiveConfig. UpdateActiveConfig(); + // Update texture cache settings with any changed options. + TextureCache::OnConfigChanged(g_ActiveConfig); + // MSAA samples changed, we need to recreate the EFB render pass, and all shaders. if (msaa_changed) { @@ -958,8 +962,11 @@ void Renderer::CheckForConfigChanges() } // For vsync, we need to change the present mode, which means recreating the swap chain. - if (vsync_changed) - ResizeSwapChain(); + if (m_swap_chain && g_ActiveConfig.IsVSync() != m_swap_chain->IsVSyncEnabled()) + { + g_command_buffer_mgr->WaitForGPUIdle(); + m_swap_chain->SetVSync(g_ActiveConfig.IsVSync()); + } // Wipe sampler cache if force texture filtering or anisotropy changes. if (anisotropy_changed || force_texture_filtering_changed) @@ -970,13 +977,12 @@ void Renderer::OnSwapChainResized() { s_backbuffer_width = m_swap_chain->GetWidth(); s_backbuffer_height = m_swap_chain->GetHeight(); - FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH); - FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT); UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height)) + { + PixelShaderManager::SetEfbScaleChanged(); ResizeEFBTextures(); - - PixelShaderManager::SetEfbScaleChanged(); + } } void Renderer::BindEFBToStateTracker() @@ -1361,13 +1367,9 @@ void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) } // If mipmaps are disabled, clamp min/max lod - new_state.max_lod = (SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0)) ? - static_cast<u32>(MathUtil::Clamp(tm1.max_lod / 16.0f, 0.0f, 255.0f)) : - 0; - new_state.min_lod = - std::min(new_state.max_lod.Value(), - static_cast<u32>(MathUtil::Clamp(tm1.min_lod / 16.0f, 0.0f, 255.0f))); - new_state.lod_bias = static_cast<s32>(tm0.lod_bias / 32.0f); + new_state.max_lod = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm1.max_lod : 0; + new_state.min_lod = std::min(new_state.max_lod.Value(), tm1.min_lod); + new_state.lod_bias = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm0.lod_bias : 0; // Custom textures may have a greater number of mips if (custom_tex) @@ -1381,15 +1383,7 @@ void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) new_state.wrap_v = address_modes[tm0.wrap_t]; // Only use anisotropic filtering for textures that would be linearly filtered. - if (g_vulkan_context->SupportsAnisotropicFiltering() && g_ActiveConfig.iMaxAnisotropy > 0 && - !SamplerCommon::IsBpTexMode0PointFiltering(tm0)) - { - new_state.anisotropy = g_ActiveConfig.iMaxAnisotropy; - } - else - { - new_state.anisotropy = 0; - } + new_state.enable_anisotropic_filtering = SamplerCommon::IsBpTexMode0PointFiltering(tm0) ? 0 : 1; // Skip lookup if the state hasn't changed. size_t bind_index = (texindex * 4) + stage; @@ -1411,6 +1405,7 @@ void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) void Renderer::ResetSamplerStates() { // Ensure none of the sampler objects are in use. + // This assumes that none of the samplers are in use on the command list currently being recorded. g_command_buffer_mgr->WaitForGPUIdle(); // Invalidate all sampler states, next draw will re-initialize them. |
