summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorStenzek <stenzek@users.noreply.github.com>2016-11-04 23:11:10 +1000
committerGitHub <noreply@github.com>2016-11-04 23:11:10 +1000
commitac2971b30e015a17c8aac937092a0dfd7c4beaf5 (patch)
tree5bb0b8686a3b2e3b01155a000077a2226b70a26f /Source
parentd2fdafa1556aaa2b71017d9e2ac2dffc2e551008 (diff)
parentc880c37244d4c9d63ce3e3560383825097818d85 (diff)
Merge pull request #4374 from stenzek/vulkan-xfb
Vulkan: Cleanup and implement XFB support
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/VideoBackends/Vulkan/BoundingBox.cpp19
-rw-r--r--Source/Core/VideoBackends/Vulkan/BoundingBox.h11
-rw-r--r--Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp170
-rw-r--r--Source/Core/VideoBackends/Vulkan/FramebufferManager.h62
-rw-r--r--Source/Core/VideoBackends/Vulkan/ObjectCache.cpp48
-rw-r--r--Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp5
-rw-r--r--Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h8
-rw-r--r--Source/Core/VideoBackends/Vulkan/PerfQuery.cpp19
-rw-r--r--Source/Core/VideoBackends/Vulkan/PerfQuery.h7
-rw-r--r--Source/Core/VideoBackends/Vulkan/Renderer.cpp447
-rw-r--r--Source/Core/VideoBackends/Vulkan/Renderer.h48
-rw-r--r--Source/Core/VideoBackends/Vulkan/StateTracker.cpp37
-rw-r--r--Source/Core/VideoBackends/Vulkan/StateTracker.h10
-rw-r--r--Source/Core/VideoBackends/Vulkan/TextureCache.cpp429
-rw-r--r--Source/Core/VideoBackends/Vulkan/TextureCache.h62
-rw-r--r--Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp19
-rw-r--r--Source/Core/VideoBackends/Vulkan/TextureEncoder.h13
-rw-r--r--Source/Core/VideoBackends/Vulkan/Util.cpp9
-rw-r--r--Source/Core/VideoBackends/Vulkan/Util.h2
-rw-r--r--Source/Core/VideoBackends/Vulkan/VertexManager.cpp58
-rw-r--r--Source/Core/VideoBackends/Vulkan/VertexManager.h7
-rw-r--r--Source/Core/VideoBackends/Vulkan/main.cpp26
22 files changed, 1000 insertions, 516 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/BoundingBox.cpp b/Source/Core/VideoBackends/Vulkan/BoundingBox.cpp
index e9dad9f9d6..5cfc174bf8 100644
--- a/Source/Core/VideoBackends/Vulkan/BoundingBox.cpp
+++ b/Source/Core/VideoBackends/Vulkan/BoundingBox.cpp
@@ -10,6 +10,7 @@
#include "VideoBackends/Vulkan/BoundingBox.h"
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/ObjectCache.h"
+#include "VideoBackends/Vulkan/Renderer.h"
#include "VideoBackends/Vulkan/StagingBuffer.h"
#include "VideoBackends/Vulkan/StateTracker.h"
#include "VideoBackends/Vulkan/Util.h"
@@ -47,7 +48,7 @@ bool BoundingBox::Initialize()
return true;
}
-void BoundingBox::Flush(StateTracker* state_tracker)
+void BoundingBox::Flush()
{
if (m_gpu_buffer == VK_NULL_HANDLE)
return;
@@ -75,7 +76,7 @@ void BoundingBox::Flush(StateTracker* state_tracker)
// However, the writes must be serialized, so we can't put it in the init buffer.
if (!updated_buffer)
{
- state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
// Ensure GPU buffer is in a state where it can be transferred to.
Util::BufferMemoryBarrier(
@@ -104,7 +105,7 @@ void BoundingBox::Flush(StateTracker* state_tracker)
m_valid = true;
}
-void BoundingBox::Invalidate(StateTracker* state_tracker)
+void BoundingBox::Invalidate()
{
if (m_gpu_buffer == VK_NULL_HANDLE)
return;
@@ -112,19 +113,19 @@ void BoundingBox::Invalidate(StateTracker* state_tracker)
m_valid = false;
}
-s32 BoundingBox::Get(StateTracker* state_tracker, size_t index)
+s32 BoundingBox::Get(size_t index)
{
_assert_(index < NUM_VALUES);
if (!m_valid)
- Readback(state_tracker);
+ Readback();
s32 value;
m_readback_buffer->Read(index * sizeof(s32), &value, sizeof(value), false);
return value;
}
-void BoundingBox::Set(StateTracker* state_tracker, size_t index, s32 value)
+void BoundingBox::Set(size_t index, s32 value)
{
_assert_(index < NUM_VALUES);
@@ -212,10 +213,10 @@ bool BoundingBox::CreateReadbackBuffer()
return true;
}
-void BoundingBox::Readback(StateTracker* state_tracker)
+void BoundingBox::Readback()
{
// Can't be done within a render pass.
- state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
// Ensure all writes are completed to the GPU buffer prior to the transfer.
Util::BufferMemoryBarrier(
@@ -240,7 +241,7 @@ void BoundingBox::Readback(StateTracker* state_tracker)
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
// Wait until these commands complete.
- Util::ExecuteCurrentCommandsAndRestoreState(state_tracker, false, true);
+ Util::ExecuteCurrentCommandsAndRestoreState(false, true);
// Cache is now valid.
m_readback_buffer->InvalidateCPUCache();
diff --git a/Source/Core/VideoBackends/Vulkan/BoundingBox.h b/Source/Core/VideoBackends/Vulkan/BoundingBox.h
index 0647c08607..872f49aa31 100644
--- a/Source/Core/VideoBackends/Vulkan/BoundingBox.h
+++ b/Source/Core/VideoBackends/Vulkan/BoundingBox.h
@@ -15,7 +15,6 @@
namespace Vulkan
{
class StagingBuffer;
-class StateTracker;
class BoundingBox
{
@@ -28,16 +27,16 @@ public:
VkBuffer GetGPUBuffer() const { return m_gpu_buffer; }
VkDeviceSize GetGPUBufferOffset() const { return 0; }
VkDeviceSize GetGPUBufferSize() const { return BUFFER_SIZE; }
- s32 Get(StateTracker* state_tracker, size_t index);
- void Set(StateTracker* state_tracker, size_t index, s32 value);
+ s32 Get(size_t index);
+ void Set(size_t index, s32 value);
- void Invalidate(StateTracker* state_tracker);
- void Flush(StateTracker* state_tracker);
+ void Invalidate();
+ void Flush();
private:
bool CreateGPUBuffer();
bool CreateReadbackBuffer();
- void Readback(StateTracker* state_tracker);
+ void Readback();
VkBuffer m_gpu_buffer = VK_NULL_HANDLE;
VkDeviceMemory m_gpu_memory = VK_NULL_HANDLE;
diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp
index f78cf9953f..c3fc47cb5e 100644
--- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp
+++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp
@@ -10,6 +10,8 @@
#include "Common/CommonFuncs.h"
#include "Common/Logging/Log.h"
+#include "Core/HW/Memmap.h"
+
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/StagingTexture2D.h"
@@ -49,6 +51,11 @@ FramebufferManager::~FramebufferManager()
DestroyPokeShaders();
}
+FramebufferManager* FramebufferManager::GetInstance()
+{
+ return static_cast<FramebufferManager*>(g_framebuffer_manager.get());
+}
+
bool FramebufferManager::Initialize()
{
if (!CreateEFBRenderPass())
@@ -450,15 +457,14 @@ void FramebufferManager::ReinterpretPixelData(int convtype)
std::swap(m_efb_framebuffer, m_efb_convert_framebuffer);
}
-Texture2D* FramebufferManager::ResolveEFBColorTexture(StateTracker* state_tracker,
- const VkRect2D& region)
+Texture2D* FramebufferManager::ResolveEFBColorTexture(const VkRect2D& region)
{
// Return the normal EFB texture if multisampling is off.
if (m_efb_samples == VK_SAMPLE_COUNT_1_BIT)
return m_efb_color_texture.get();
// Can't resolve within a render pass.
- state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
// Resolving is considered to be a transfer operation.
m_efb_color_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
@@ -485,15 +491,14 @@ Texture2D* FramebufferManager::ResolveEFBColorTexture(StateTracker* state_tracke
return m_efb_resolve_color_texture.get();
}
-Texture2D* FramebufferManager::ResolveEFBDepthTexture(StateTracker* state_tracker,
- const VkRect2D& region)
+Texture2D* FramebufferManager::ResolveEFBDepthTexture(const VkRect2D& region)
{
// Return the normal EFB texture if multisampling is off.
if (m_efb_samples == VK_SAMPLE_COUNT_1_BIT)
return m_efb_depth_texture.get();
// Can't resolve within a render pass.
- state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
m_efb_depth_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
@@ -659,9 +664,9 @@ void FramebufferManager::DestroyConversionShaders()
DestroyShader(m_ps_depth_resolve);
}
-u32 FramebufferManager::PeekEFBColor(StateTracker* state_tracker, u32 x, u32 y)
+u32 FramebufferManager::PeekEFBColor(u32 x, u32 y)
{
- if (!m_color_readback_texture_valid && !PopulateColorReadbackTexture(state_tracker))
+ if (!m_color_readback_texture_valid && !PopulateColorReadbackTexture())
return 0;
u32 value;
@@ -669,18 +674,18 @@ u32 FramebufferManager::PeekEFBColor(StateTracker* state_tracker, u32 x, u32 y)
return value;
}
-bool FramebufferManager::PopulateColorReadbackTexture(StateTracker* state_tracker)
+bool FramebufferManager::PopulateColorReadbackTexture()
{
// Can't be in our normal render pass.
- state_tracker->EndRenderPass();
- state_tracker->OnReadback();
+ StateTracker::GetInstance()->EndRenderPass();
+ StateTracker::GetInstance()->OnReadback();
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
VkRect2D src_region = {{0, 0}, {m_efb_width, m_efb_height}};
Texture2D* src_texture = m_efb_color_texture.get();
VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
if (m_efb_samples > 1)
- src_texture = ResolveEFBColorTexture(state_tracker, src_region);
+ src_texture = ResolveEFBColorTexture(src_region);
if (m_efb_width != EFB_WIDTH || m_efb_height != EFB_HEIGHT)
{
@@ -728,8 +733,8 @@ bool FramebufferManager::PopulateColorReadbackTexture(StateTracker* state_tracke
// Wait until the copy is complete.
g_command_buffer_mgr->ExecuteCommandBuffer(false, true);
- state_tracker->InvalidateDescriptorSets();
- state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->InvalidateDescriptorSets();
+ StateTracker::GetInstance()->SetPendingRebind();
// Map to host memory.
if (!m_color_readback_texture->IsMapped() && !m_color_readback_texture->Map())
@@ -739,9 +744,9 @@ bool FramebufferManager::PopulateColorReadbackTexture(StateTracker* state_tracke
return true;
}
-float FramebufferManager::PeekEFBDepth(StateTracker* state_tracker, u32 x, u32 y)
+float FramebufferManager::PeekEFBDepth(u32 x, u32 y)
{
- if (!m_depth_readback_texture_valid && !PopulateDepthReadbackTexture(state_tracker))
+ if (!m_depth_readback_texture_valid && !PopulateDepthReadbackTexture())
return 0.0f;
float value;
@@ -749,11 +754,11 @@ float FramebufferManager::PeekEFBDepth(StateTracker* state_tracker, u32 x, u32 y
return value;
}
-bool FramebufferManager::PopulateDepthReadbackTexture(StateTracker* state_tracker)
+bool FramebufferManager::PopulateDepthReadbackTexture()
{
// Can't be in our normal render pass.
- state_tracker->EndRenderPass();
- state_tracker->OnReadback();
+ StateTracker::GetInstance()->EndRenderPass();
+ StateTracker::GetInstance()->OnReadback();
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
VkRect2D src_region = {{0, 0}, {m_efb_width, m_efb_height}};
@@ -762,7 +767,7 @@ bool FramebufferManager::PopulateDepthReadbackTexture(StateTracker* state_tracke
if (m_efb_samples > 1)
{
// EFB depth resolves are written out as color textures
- src_texture = ResolveEFBDepthTexture(state_tracker, src_region);
+ src_texture = ResolveEFBDepthTexture(src_region);
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
}
if (m_efb_width != EFB_WIDTH || m_efb_height != EFB_HEIGHT)
@@ -812,8 +817,8 @@ bool FramebufferManager::PopulateDepthReadbackTexture(StateTracker* state_tracke
// Wait until the copy is complete.
g_command_buffer_mgr->ExecuteCommandBuffer(false, true);
- state_tracker->InvalidateDescriptorSets();
- state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->InvalidateDescriptorSets();
+ StateTracker::GetInstance()->SetPendingRebind();
// Map to host memory.
if (!m_depth_readback_texture->IsMapped() && !m_depth_readback_texture->Map())
@@ -1086,11 +1091,11 @@ void FramebufferManager::DestroyReadbackFramebuffer()
}
}
-void FramebufferManager::PokeEFBColor(StateTracker* state_tracker, u32 x, u32 y, u32 color)
+void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color)
{
// Flush if we exceeded the number of vertices per batch.
if ((m_color_poke_vertices.size() + 6) > MAX_POKE_VERTICES)
- FlushEFBPokes(state_tracker);
+ FlushEFBPokes();
CreatePokeVertices(&m_color_poke_vertices, x, y, 0.0f, color);
@@ -1099,11 +1104,11 @@ void FramebufferManager::PokeEFBColor(StateTracker* state_tracker, u32 x, u32 y,
m_color_readback_texture->WriteTexel(x, y, &color, sizeof(color));
}
-void FramebufferManager::PokeEFBDepth(StateTracker* state_tracker, u32 x, u32 y, float depth)
+void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth)
{
// Flush if we exceeded the number of vertices per batch.
if ((m_color_poke_vertices.size() + 6) > MAX_POKE_VERTICES)
- FlushEFBPokes(state_tracker);
+ FlushEFBPokes();
CreatePokeVertices(&m_depth_poke_vertices, x, y, depth, 0);
@@ -1140,27 +1145,22 @@ void FramebufferManager::CreatePokeVertices(std::vector<EFBPokeVertex>* destinat
}
}
-void FramebufferManager::FlushEFBPokes(StateTracker* state_tracker)
+void FramebufferManager::FlushEFBPokes()
{
if (!m_color_poke_vertices.empty())
{
- DrawPokeVertices(state_tracker, m_color_poke_vertices.data(), m_color_poke_vertices.size(),
- true, false);
-
+ DrawPokeVertices(m_color_poke_vertices.data(), m_color_poke_vertices.size(), true, false);
m_color_poke_vertices.clear();
}
if (!m_depth_poke_vertices.empty())
{
- DrawPokeVertices(state_tracker, m_depth_poke_vertices.data(), m_depth_poke_vertices.size(),
- false, true);
-
+ DrawPokeVertices(m_depth_poke_vertices.data(), m_depth_poke_vertices.size(), false, true);
m_depth_poke_vertices.clear();
}
}
-void FramebufferManager::DrawPokeVertices(StateTracker* state_tracker,
- const EFBPokeVertex* vertices, size_t vertex_count,
+void FramebufferManager::DrawPokeVertices(const EFBPokeVertex* vertices, size_t vertex_count,
bool write_color, bool write_depth)
{
// Relatively simple since we don't have any bindings.
@@ -1205,7 +1205,7 @@ void FramebufferManager::DrawPokeVertices(StateTracker* state_tracker,
{
// Kick a command buffer first.
WARN_LOG(VIDEO, "Kicking command buffer due to no EFB poke space.");
- Util::ExecuteCurrentCommandsAndRestoreState(state_tracker, true);
+ Util::ExecuteCurrentCommandsAndRestoreState(true);
command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
if (!m_poke_vertex_stream_buffer->ReserveMemory(vertices_size, sizeof(EfbPokeData), true, true,
@@ -1221,9 +1221,9 @@ void FramebufferManager::DrawPokeVertices(StateTracker* state_tracker,
m_poke_vertex_stream_buffer->CommitMemory(vertices_size);
// Set up state.
- state_tracker->EndClearRenderPass();
- state_tracker->BeginRenderPass();
- state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->EndClearRenderPass();
+ StateTracker::GetInstance()->BeginRenderPass();
+ StateTracker::GetInstance()->SetPendingRebind();
Util::SetViewportAndScissor(command_buffer, 0, 0, m_efb_width, m_efb_height);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdBindVertexBuffers(command_buffer, 0, 1, &vb_buffer, &vb_offset);
@@ -1367,4 +1367,96 @@ void FramebufferManager::DestroyPokeShaders()
}
}
+std::unique_ptr<XFBSourceBase> FramebufferManager::CreateXFBSource(unsigned int target_width,
+ unsigned int target_height,
+ unsigned int layers)
+{
+ TextureCacheBase::TCacheEntryConfig config;
+ config.width = target_width;
+ config.height = target_height;
+ config.layers = layers;
+ config.rendertarget = true;
+ auto* base_texture = TextureCache::GetInstance()->CreateTexture(config);
+ auto* texture = static_cast<TextureCache::TCacheEntry*>(base_texture);
+ if (!texture)
+ {
+ PanicAlert("Failed to create texture for XFB source");
+ return nullptr;
+ }
+
+ return std::make_unique<XFBSource>(std::unique_ptr<TextureCache::TCacheEntry>(texture));
+}
+
+void FramebufferManager::CopyToRealXFB(u32 xfb_addr, u32 fb_stride, u32 fb_height,
+ const EFBRectangle& source_rc, float gamma)
+{
+ // Pending/batched EFB pokes should be included in the copied image.
+ FlushEFBPokes();
+
+ // Schedule early command-buffer execution.
+ StateTracker::GetInstance()->EndRenderPass();
+ StateTracker::GetInstance()->OnReadback();
+
+ // GPU EFB textures -> Guest memory
+ u8* xfb_ptr = Memory::GetPointer(xfb_addr);
+ _assert_(xfb_ptr);
+
+ // source_rc is in native coordinates, so scale it to the internal resolution.
+ TargetRectangle scaled_rc = g_renderer->ConvertEFBRectangle(source_rc);
+ VkRect2D scaled_rc_vk = {
+ {scaled_rc.left, scaled_rc.top},
+ {static_cast<u32>(scaled_rc.GetWidth()), static_cast<u32>(scaled_rc.GetHeight())}};
+ Texture2D* src_texture = ResolveEFBColorTexture(scaled_rc_vk);
+
+ // 2 bytes per pixel, so divide fb_stride by 2 to get the width.
+ TextureCache::GetInstance()->EncodeYUYVTextureToMemory(xfb_ptr, fb_stride / 2, fb_stride,
+ fb_height, src_texture, scaled_rc);
+
+ // If we sourced directly from the EFB framebuffer, restore it to a color attachment.
+ if (src_texture == m_efb_color_texture.get())
+ {
+ src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ }
+}
+
+XFBSource::XFBSource(std::unique_ptr<TextureCache::TCacheEntry> texture)
+ : XFBSourceBase(), m_texture(std::move(texture))
+{
+}
+
+XFBSource::~XFBSource()
+{
+}
+
+void XFBSource::DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height)
+{
+ // Guest memory -> GPU EFB Textures
+ const u8* src_ptr = Memory::GetPointer(xfb_addr);
+ _assert_(src_ptr);
+ TextureCache::GetInstance()->DecodeYUYVTextureFromMemory(m_texture.get(), src_ptr, fb_width,
+ fb_width * 2, fb_height);
+}
+
+void XFBSource::CopyEFB(float gamma)
+{
+ // Pending/batched EFB pokes should be included in the copied image.
+ FramebufferManager::GetInstance()->FlushEFBPokes();
+
+ // Virtual XFB, copy EFB at native resolution to m_texture
+ MathUtil::Rectangle<int> rect(0, 0, static_cast<int>(texWidth), static_cast<int>(texHeight));
+ VkRect2D vk_rect = {{rect.left, rect.top},
+ {static_cast<u32>(rect.GetWidth()), static_cast<u32>(rect.GetHeight())}};
+
+ Texture2D* src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(vk_rect);
+ TextureCache::GetInstance()->CopyRectangleFromTexture(m_texture.get(), rect, src_texture, rect);
+
+ // If we sourced directly from the EFB framebuffer, restore it to a color attachment.
+ if (src_texture == FramebufferManager::GetInstance()->GetEFBColorTexture())
+ {
+ src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ }
+}
+
} // namespace Vulkan
diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.h b/Source/Core/VideoBackends/Vulkan/FramebufferManager.h
index a8c5f0fc64..92a689fb41 100644
--- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.h
+++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.h
@@ -8,6 +8,7 @@
#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/Constants.h"
+#include "VideoBackends/Vulkan/TextureCache.h"
#include "VideoCommon/FramebufferManagerBase.h"
namespace Vulkan
@@ -17,12 +18,7 @@ class StateTracker;
class StreamBuffer;
class Texture2D;
class VertexFormat;
-
-class XFBSource : public XFBSourceBase
-{
- void DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height) override {}
- void CopyEFB(float gamma) override {}
-};
+class XFBSource;
class FramebufferManager : public FramebufferManagerBase
{
@@ -30,6 +26,8 @@ public:
FramebufferManager();
~FramebufferManager();
+ static FramebufferManager* GetInstance();
+
bool Initialize();
VkRenderPass GetEFBLoadRenderPass() const { return m_efb_load_render_pass; }
@@ -45,15 +43,11 @@ public:
std::unique_ptr<XFBSourceBase> CreateXFBSource(unsigned int target_width,
unsigned int target_height,
- unsigned int layers) override
- {
- return std::make_unique<XFBSource>();
- }
+ unsigned int layers) override;
+ // GPU EFB textures -> Guest
void CopyToRealXFB(u32 xfb_addr, u32 fb_stride, u32 fb_height, const EFBRectangle& source_rc,
- float gamma = 1.0f) override
- {
- }
+ float gamma = 1.0f) override;
void ResizeEFBTextures();
@@ -69,18 +63,18 @@ public:
// This render pass can be used for other readback operations.
VkRenderPass GetColorCopyForReadbackRenderPass() const { return m_copy_color_render_pass; }
// Resolve color/depth textures to a non-msaa texture, and return it.
- Texture2D* ResolveEFBColorTexture(StateTracker* state_tracker, const VkRect2D& region);
- Texture2D* ResolveEFBDepthTexture(StateTracker* state_tracker, const VkRect2D& region);
+ Texture2D* ResolveEFBColorTexture(const VkRect2D& region);
+ Texture2D* ResolveEFBDepthTexture(const VkRect2D& region);
// Reads a framebuffer value back from the GPU. This may block if the cache is not current.
- u32 PeekEFBColor(StateTracker* state_tracker, u32 x, u32 y);
- float PeekEFBDepth(StateTracker* state_tracker, u32 x, u32 y);
+ u32 PeekEFBColor(u32 x, u32 y);
+ float PeekEFBDepth(u32 x, u32 y);
void InvalidatePeekCache();
// Writes a value to the framebuffer. This will never block, and writes will be batched.
- void PokeEFBColor(StateTracker* state_tracker, u32 x, u32 y, u32 color);
- void PokeEFBDepth(StateTracker* state_tracker, u32 x, u32 y, float depth);
- void FlushEFBPokes(StateTracker* state_tracker);
+ void PokeEFBColor(u32 x, u32 y, u32 color);
+ void PokeEFBDepth(u32 x, u32 y, float depth);
+ void FlushEFBPokes();
private:
struct EFBPokeVertex
@@ -112,14 +106,14 @@ private:
bool CompilePokeShaders();
void DestroyPokeShaders();
- bool PopulateColorReadbackTexture(StateTracker* state_tracker);
- bool PopulateDepthReadbackTexture(StateTracker* state_tracker);
+ bool PopulateColorReadbackTexture();
+ bool PopulateDepthReadbackTexture();
void CreatePokeVertices(std::vector<EFBPokeVertex>* destination_list, u32 x, u32 y, float z,
u32 color);
- void DrawPokeVertices(StateTracker* state_tracker, const EFBPokeVertex* vertices,
- size_t vertex_count, bool write_color, bool write_depth);
+ void DrawPokeVertices(const EFBPokeVertex* vertices, size_t vertex_count, bool write_color,
+ bool write_depth);
VkRenderPass m_efb_load_render_pass = VK_NULL_HANDLE;
VkRenderPass m_efb_clear_render_pass = VK_NULL_HANDLE;
@@ -173,4 +167,24 @@ private:
VkShaderModule m_poke_fragment_shader = VK_NULL_HANDLE;
};
+// The XFB source class simply wraps a texture cache entry.
+// All the required functionality is provided by TextureCache.
+class XFBSource final : public XFBSourceBase
+{
+public:
+ explicit XFBSource(std::unique_ptr<TextureCache::TCacheEntry> texture);
+ ~XFBSource();
+
+ TextureCache::TCacheEntry* GetTexture() const { return m_texture.get(); }
+ // Guest -> GPU EFB Textures
+ void DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height) override;
+
+ // Used for virtual XFB
+ void CopyEFB(float gamma) override;
+
+private:
+ std::unique_ptr<TextureCache::TCacheEntry> m_texture;
+ VkFramebuffer m_framebuffer;
+};
+
} // namespace Vulkan
diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
index e5e65c1a73..61e685874a 100644
--- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
+++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp
@@ -899,12 +899,12 @@ bool operator<(const SamplerState& lhs, const SamplerState& rhs)
bool ObjectCache::CompileSharedShaders()
{
static const char PASSTHROUGH_VERTEX_SHADER_SOURCE[] = R"(
- layout(location = 0) in float4 ipos;
- layout(location = 5) in float4 icol0;
- layout(location = 8) in float3 itex0;
+ layout(location = 0) in vec4 ipos;
+ layout(location = 5) in vec4 icol0;
+ layout(location = 8) in vec3 itex0;
- layout(location = 0) out float3 uv0;
- layout(location = 1) out float4 col0;
+ layout(location = 0) out vec3 uv0;
+ layout(location = 1) out vec4 col0;
void main()
{
@@ -918,17 +918,11 @@ bool ObjectCache::CompileSharedShaders()
layout(triangles) in;
layout(triangle_strip, max_vertices = EFB_LAYERS * 3) out;
- in VertexData
- {
- float3 uv0;
- float4 col0;
- } in_data[];
+ layout(location = 0) in vec3 in_uv0[];
+ layout(location = 1) in vec4 in_col0[];
- out VertexData
- {
- float3 uv0;
- float4 col0;
- } out_data;
+ layout(location = 0) out vec3 out_uv0;
+ layout(location = 1) out vec4 out_col0;
void main()
{
@@ -938,8 +932,8 @@ bool ObjectCache::CompileSharedShaders()
{
gl_Layer = j;
gl_Position = gl_in[i].gl_Position;
- out_data.uv0 = float3(in_data[i].uv0.xy, float(j));
- out_data.col0 = in_data[i].col0;
+ out_uv0 = vec3(in_uv0[i].xy, float(j));
+ out_col0 = in_col0[i];
EmitVertex();
}
EndPrimitive();
@@ -948,7 +942,7 @@ bool ObjectCache::CompileSharedShaders()
)";
static const char SCREEN_QUAD_VERTEX_SHADER_SOURCE[] = R"(
- layout(location = 0) out float3 uv0;
+ layout(location = 0) out vec3 uv0;
void main()
{
@@ -959,9 +953,9 @@ bool ObjectCache::CompileSharedShaders()
* 2 0,2 0,1 -1,1 BL
* 3 1,2 1,1 1,1 BR
*/
- vec2 rawpos = float2(float(gl_VertexID & 1), clamp(float(gl_VertexID & 2), 0.0f, 1.0f));
- gl_Position = float4(rawpos * 2.0f - 1.0f, 0.0f, 1.0f);
- uv0 = float3(rawpos, 0.0f);
+ vec2 rawpos = vec2(float(gl_VertexID & 1), clamp(float(gl_VertexID & 2), 0.0f, 1.0f));
+ gl_Position = vec4(rawpos * 2.0f - 1.0f, 0.0f, 1.0f);
+ uv0 = vec3(rawpos, 0.0f);
}
)";
@@ -969,15 +963,9 @@ bool ObjectCache::CompileSharedShaders()
layout(triangles) in;
layout(triangle_strip, max_vertices = EFB_LAYERS * 3) out;
- in VertexData
- {
- float3 uv0;
- } in_data[];
+ layout(location = 0) in vec3 in_uv0[];
- out VertexData
- {
- float3 uv0;
- } out_data;
+ layout(location = 0) out vec3 out_uv0;
void main()
{
@@ -987,7 +975,7 @@ bool ObjectCache::CompileSharedShaders()
{
gl_Layer = j;
gl_Position = gl_in[i].gl_Position;
- out_data.uv0 = float3(in_data[i].uv0.xy, float(j));
+ out_uv0 = vec3(in_uv0[i].xy, float(j));
EmitVertex();
}
EndPrimitive();
diff --git a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp
index 07100eb7bb..3e992d4944 100644
--- a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp
+++ b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp
@@ -62,8 +62,7 @@ bool PaletteTextureConverter::Initialize()
return true;
}
-void PaletteTextureConverter::ConvertTexture(StateTracker* state_tracker,
- VkCommandBuffer command_buffer,
+void PaletteTextureConverter::ConvertTexture(VkCommandBuffer command_buffer,
VkRenderPass render_pass,
VkFramebuffer dst_framebuffer, Texture2D* src_texture,
u32 width, u32 height, void* palette,
@@ -89,7 +88,7 @@ void PaletteTextureConverter::ConvertTexture(StateTracker* state_tracker,
g_command_buffer_mgr->AllocateDescriptorSet(m_palette_set_layout)) == VK_NULL_HANDLE)
{
WARN_LOG(VIDEO, "Executing command list while waiting for space in palette buffer");
- Util::ExecuteCurrentCommandsAndRestoreState(state_tracker, false);
+ Util::ExecuteCurrentCommandsAndRestoreState(false);
if (!m_palette_stream_buffer->ReserveMemory(palette_size,
g_vulkan_context->GetTexelBufferAlignment()) ||
diff --git a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h
index 39b8cccd8c..2540b5affa 100644
--- a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h
+++ b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.h
@@ -13,7 +13,6 @@
namespace Vulkan
{
-class StateTracker;
class Texture2D;
// Since this converter uses a uniform texel buffer, we can't use the general pipeline generators.
@@ -26,10 +25,9 @@ public:
bool Initialize();
- void ConvertTexture(StateTracker* state_tracker, VkCommandBuffer command_buffer,
- VkRenderPass render_pass, VkFramebuffer dst_framebuffer,
- Texture2D* src_texture, u32 width, u32 height, void* palette,
- TlutFormat format, u32 src_format);
+ void ConvertTexture(VkCommandBuffer command_buffer, VkRenderPass render_pass,
+ VkFramebuffer dst_framebuffer, Texture2D* src_texture, u32 width, u32 height,
+ void* palette, TlutFormat format, u32 src_format);
private:
static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3;
diff --git a/Source/Core/VideoBackends/Vulkan/PerfQuery.cpp b/Source/Core/VideoBackends/Vulkan/PerfQuery.cpp
index 2a64babb32..11ba898bf6 100644
--- a/Source/Core/VideoBackends/Vulkan/PerfQuery.cpp
+++ b/Source/Core/VideoBackends/Vulkan/PerfQuery.cpp
@@ -32,10 +32,13 @@ PerfQuery::~PerfQuery()
vkDestroyQueryPool(g_vulkan_context->GetDevice(), m_query_pool, nullptr);
}
-bool PerfQuery::Initialize(StateTracker* state_tracker)
+Vulkan::PerfQuery* PerfQuery::GetInstance()
{
- m_state_tracker = state_tracker;
+ return static_cast<PerfQuery*>(g_perf_query.get());
+}
+bool PerfQuery::Initialize()
+{
if (!CreateQueryPool())
{
PanicAlert("Failed to create query pool");
@@ -86,11 +89,11 @@ void PerfQuery::EnableQuery(PerfQueryGroup type)
// Ensure the query starts within a render pass.
// TODO: Is this needed?
- m_state_tracker->BeginRenderPass();
+ StateTracker::GetInstance()->BeginRenderPass();
vkCmdBeginQuery(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_query_pool, index, flags);
// Prevent background command buffer submission while the query is active.
- m_state_tracker->SetBackgroundCommandBufferExecution(false);
+ StateTracker::GetInstance()->SetBackgroundCommandBufferExecution(false);
}
}
@@ -101,7 +104,7 @@ void PerfQuery::DisableQuery(PerfQueryGroup type)
// DisableQuery should be called for each EnableQuery, so subtract one to get the previous one.
u32 index = (m_query_read_pos + m_query_count - 1) % PERF_QUERY_BUFFER_SIZE;
vkCmdEndQuery(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_query_pool, index);
- m_state_tracker->SetBackgroundCommandBufferExecution(true);
+ StateTracker::GetInstance()->SetBackgroundCommandBufferExecution(true);
DEBUG_LOG(VIDEO, "end query %u", index);
}
}
@@ -113,7 +116,7 @@ void PerfQuery::ResetQuery()
std::fill_n(m_results, ArraySize(m_results), 0);
// Reset entire query pool, ensuring all queries are ready to write to.
- m_state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
vkCmdResetQueryPool(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_query_pool, 0,
PERF_QUERY_BUFFER_SIZE);
@@ -346,7 +349,7 @@ void PerfQuery::NonBlockingPartialFlush()
// Submit a command buffer in the background if the front query is not bound to one.
// Ideally this will complete before the buffer fills.
if (m_query_buffer[m_query_read_pos].pending_fence == VK_NULL_HANDLE)
- Util::ExecuteCurrentCommandsAndRestoreState(m_state_tracker, true, false);
+ Util::ExecuteCurrentCommandsAndRestoreState(true, false);
}
void PerfQuery::BlockingPartialFlush()
@@ -360,7 +363,7 @@ void PerfQuery::BlockingPartialFlush()
{
// This will callback OnCommandBufferQueued which will set the fence on the entry.
// We wait for completion, which will also call OnCommandBufferExecuted, and clear the fence.
- Util::ExecuteCurrentCommandsAndRestoreState(m_state_tracker, false, true);
+ Util::ExecuteCurrentCommandsAndRestoreState(false, true);
}
else
{
diff --git a/Source/Core/VideoBackends/Vulkan/PerfQuery.h b/Source/Core/VideoBackends/Vulkan/PerfQuery.h
index 3d385299f1..c5f5d13e90 100644
--- a/Source/Core/VideoBackends/Vulkan/PerfQuery.h
+++ b/Source/Core/VideoBackends/Vulkan/PerfQuery.h
@@ -14,7 +14,6 @@
namespace Vulkan
{
class StagingBuffer;
-class StateTracker;
class PerfQuery : public PerfQueryBase
{
@@ -22,7 +21,9 @@ public:
PerfQuery();
~PerfQuery();
- bool Initialize(StateTracker* state_tracker);
+ static PerfQuery* GetInstance();
+
+ bool Initialize();
void EnableQuery(PerfQueryGroup type) override;
void DisableQuery(PerfQueryGroup type) override;
@@ -52,8 +53,6 @@ private:
void NonBlockingPartialFlush();
void BlockingPartialFlush();
- StateTracker* m_state_tracker = nullptr;
-
// when testing in SMS: 64 was too small, 128 was ok
// TODO: This should be size_t, but the base class uses u32s
using PerfQueryDataType = u32;
diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp
index 3fac85e340..066f3f512a 100644
--- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp
+++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp
@@ -13,6 +13,7 @@
#include "Common/MsgHandler.h"
#include "Core/ConfigManager.h"
+#include "Core/Core.h"
#include "VideoBackends/Vulkan/BoundingBox.h"
#include "VideoBackends/Vulkan/CommandBufferManager.h"
@@ -63,15 +64,18 @@ Renderer::~Renderer()
{
g_Config.bRunning = false;
UpdateActiveConfig();
- DestroyScreenshotResources();
+ DestroyFrameDumpResources();
DestroyShaders();
DestroySemaphores();
}
-bool Renderer::Initialize(FramebufferManager* framebuffer_mgr)
+Renderer* Renderer::GetInstance()
+{
+ return static_cast<Renderer*>(g_renderer.get());
+}
+
+bool Renderer::Initialize()
{
- m_framebuffer_mgr = framebuffer_mgr;
- m_state_tracker = std::make_unique<StateTracker>();
BindEFBToStateTracker();
if (!CreateSemaphores())
@@ -103,9 +107,9 @@ bool Renderer::Initialize(FramebufferManager* framebuffer_mgr)
if (g_vulkan_context->SupportsBoundingBox())
{
// Bind bounding box to state tracker
- m_state_tracker->SetBBoxBuffer(m_bounding_box->GetGPUBuffer(),
- m_bounding_box->GetGPUBufferOffset(),
- m_bounding_box->GetGPUBufferSize());
+ StateTracker::GetInstance()->SetBBoxBuffer(m_bounding_box->GetGPUBuffer(),
+ m_bounding_box->GetGPUBufferOffset(),
+ m_bounding_box->GetGPUBufferSize());
}
// Various initialization routines will have executed commands on the command buffer.
@@ -170,7 +174,7 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
{
if (type == PEEK_COLOR)
{
- u32 color = m_framebuffer_mgr->PeekEFBColor(m_state_tracker.get(), x, y);
+ u32 color = FramebufferManager::GetInstance()->PeekEFBColor(x, y);
// a little-endian value is expected to be returned
color = ((color & 0xFF00FF00) | ((color >> 16) & 0xFF) | ((color << 16) & 0xFF0000));
@@ -207,7 +211,7 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
else // if (type == PEEK_Z)
{
// Depth buffer is inverted for improved precision near far plane
- float depth = 1.0f - m_framebuffer_mgr->PeekEFBDepth(m_state_tracker.get(), x, y);
+ float depth = 1.0f - FramebufferManager::GetInstance()->PeekEFBDepth(x, y);
u32 ret = 0;
if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
@@ -235,7 +239,7 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num
const EfbPokeData& point = points[i];
u32 color = ((point.data & 0xFF00FF00) | ((point.data >> 16) & 0xFF) |
((point.data << 16) & 0xFF0000));
- m_framebuffer_mgr->PokeEFBColor(m_state_tracker.get(), point.x, point.y, color);
+ FramebufferManager::GetInstance()->PokeEFBColor(point.x, point.y, color);
}
}
else // if (type == POKE_Z)
@@ -245,14 +249,14 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num
// Convert to floating-point depth.
const EfbPokeData& point = points[i];
float depth = (1.0f - float(point.data & 0xFFFFFF) / 16777216.0f);
- m_framebuffer_mgr->PokeEFBDepth(m_state_tracker.get(), point.x, point.y, depth);
+ FramebufferManager::GetInstance()->PokeEFBDepth(point.x, point.y, depth);
}
}
}
u16 Renderer::BBoxRead(int index)
{
- s32 value = m_bounding_box->Get(m_state_tracker.get(), static_cast<size_t>(index));
+ s32 value = m_bounding_box->Get(static_cast<size_t>(index));
// 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.
@@ -294,7 +298,7 @@ void Renderer::BBoxWrite(int index, u16 value)
scaled_value = scaled_value * s_target_height / EFB_HEIGHT;
}
- m_bounding_box->Set(m_state_tracker.get(), static_cast<size_t>(index), scaled_value);
+ m_bounding_box->Set(static_cast<size_t>(index), scaled_value);
}
TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
@@ -314,8 +318,8 @@ void Renderer::BeginFrame()
// Ensure that the state tracker rebinds everything, and allocates a new set
// of descriptors out of the next pool.
- m_state_tracker->InvalidateDescriptorSets();
- m_state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->InvalidateDescriptorSets();
+ StateTracker::GetInstance()->SetPendingRebind();
}
void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable,
@@ -327,15 +331,6 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha
{target_rc.left, target_rc.top},
{static_cast<uint32_t>(target_rc.GetWidth()), static_cast<uint32_t>(target_rc.GetHeight())}};
- // 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));
-
// 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 ||
@@ -347,10 +342,19 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha
color |= 0xFF000000;
}
+ // 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));
+
// 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.
bool use_clear_render_pass = (color_enable && alpha_enable && z_enable);
- if (m_state_tracker->InRenderPass())
+ if (StateTracker::GetInstance()->InRenderPass())
{
// Prefer not to end a render pass just to do a clear.
use_clear_render_pass = false;
@@ -360,7 +364,7 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha
if (use_clear_render_pass)
{
VkClearValue clear_values[2] = {clear_color_value, clear_depth_value};
- m_state_tracker->BeginClearRenderPass(target_vk_rc, clear_values);
+ StateTracker::GetInstance()->BeginClearRenderPass(target_vk_rc, clear_values);
return;
}
@@ -388,17 +392,17 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha
}
if (num_clear_attachments > 0)
{
- VkClearRect clear_rect = {target_vk_rc, 0, m_framebuffer_mgr->GetEFBLayers()};
- if (!m_state_tracker->IsWithinRenderArea(target_vk_rc.offset.x, target_vk_rc.offset.y,
- target_vk_rc.extent.width,
- target_vk_rc.extent.height))
+ 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))
{
- m_state_tracker->EndClearRenderPass();
+ StateTracker::GetInstance()->EndClearRenderPass();
}
- m_state_tracker->BeginRenderPass();
+ StateTracker::GetInstance()->BeginRenderPass();
vkCmdClearAttachments(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_clear_attachments,
- clear_attachments, 1, &clear_rect);
+ clear_attachments, 1, &vk_rect);
}
}
@@ -407,13 +411,14 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha
return;
// Clearing must occur within a render pass.
- if (!m_state_tracker->IsWithinRenderArea(target_vk_rc.offset.x, target_vk_rc.offset.y,
- target_vk_rc.extent.width, target_vk_rc.extent.height))
+ if (!StateTracker::GetInstance()->IsWithinRenderArea(target_vk_rc.offset.x, target_vk_rc.offset.y,
+ target_vk_rc.extent.width,
+ target_vk_rc.extent.height))
{
- m_state_tracker->EndClearRenderPass();
+ StateTracker::GetInstance()->EndClearRenderPass();
}
- m_state_tracker->BeginRenderPass();
- m_state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->BeginRenderPass();
+ StateTracker::GetInstance()->SetPendingRebind();
// Mask away the appropriate colors and use a shader
BlendState blend_state = Util::GetNoBlendingBlendState();
@@ -431,13 +436,14 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha
RasterizationState rs_state = Util::GetNoCullRasterizationState();
rs_state.per_sample_shading = g_ActiveConfig.bSSAA ? VK_TRUE : VK_FALSE;
- rs_state.samples = m_framebuffer_mgr->GetEFBSamples();
+ rs_state.samples = FramebufferManager::GetInstance()->GetEFBSamples();
// No need to start a new render pass, but we do need to restore viewport state
- UtilityShaderDraw draw(
- g_command_buffer_mgr->GetCurrentCommandBuffer(), g_object_cache->GetStandardPipelineLayout(),
- m_framebuffer_mgr->GetEFBLoadRenderPass(), g_object_cache->GetPassthroughVertexShader(),
- g_object_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader);
+ UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ g_object_cache->GetStandardPipelineLayout(),
+ FramebufferManager::GetInstance()->GetEFBLoadRenderPass(),
+ g_object_cache->GetPassthroughVertexShader(),
+ g_object_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader);
draw.SetRasterizationState(rs_state);
draw.SetDepthStencilState(depth_state);
@@ -451,9 +457,9 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha
void Renderer::ReinterpretPixelData(unsigned int convtype)
{
- m_state_tracker->EndRenderPass();
- m_state_tracker->SetPendingRebind();
- m_framebuffer_mgr->ReinterpretPixelData(convtype);
+ StateTracker::GetInstance()->EndRenderPass();
+ StateTracker::GetInstance()->SetPendingRebind();
+ FramebufferManager::GetInstance()->ReinterpretPixelData(convtype);
// EFB framebuffer has now changed, so update accordingly.
BindEFBToStateTracker();
@@ -462,37 +468,38 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height,
const EFBRectangle& rc, u64 ticks, float gamma)
{
- // Flush any pending EFB pokes.
- m_framebuffer_mgr->FlushEFBPokes(m_state_tracker.get());
-
- // End the current render pass.
- m_state_tracker->EndRenderPass();
- m_state_tracker->OnEndFrame();
+ // Pending/batched EFB pokes should be included in the final image.
+ FramebufferManager::GetInstance()->FlushEFBPokes();
- // Scale the source rectangle to the selected internal resolution.
- TargetRectangle source_rc = Renderer::ConvertEFBRectangle(rc);
+ // Check that we actually have an image to render in XFB-on modes.
+ if ((!XFBWrited && !g_ActiveConfig.RealXFBEnabled()) || !fb_width || !fb_height)
+ {
+ Core::Callback_VideoCopiedToXFB(false);
+ return;
+ }
+ u32 xfb_count = 0;
+ const XFBSourceBase* const* xfb_sources =
+ FramebufferManager::GetXFBSource(xfb_addr, fb_stride, fb_height, &xfb_count);
+ if (g_ActiveConfig.VirtualXFBEnabled() && (!xfb_sources || xfb_count == 0))
+ {
+ Core::Callback_VideoCopiedToXFB(false);
+ return;
+ }
- // Transition the EFB render target to a shader resource.
- VkRect2D src_region = {{0, 0},
- {m_framebuffer_mgr->GetEFBWidth(), m_framebuffer_mgr->GetEFBHeight()}};
- Texture2D* efb_color_texture =
- m_framebuffer_mgr->ResolveEFBColorTexture(m_state_tracker.get(), src_region);
- efb_color_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
- VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+ // End the current render pass.
+ StateTracker::GetInstance()->EndRenderPass();
+ StateTracker::GetInstance()->OnEndFrame();
// Draw to the screenshot buffer if needed.
- if (IsFrameDumping() && DrawScreenshot(source_rc, efb_color_texture))
- {
- DumpFrameData(reinterpret_cast<const u8*>(m_screenshot_readback_texture->GetMapPointer()),
- static_cast<int>(m_screenshot_render_texture->GetWidth()),
- static_cast<int>(m_screenshot_render_texture->GetHeight()),
- static_cast<int>(m_screenshot_readback_texture->GetRowStride()), ticks);
- FinishFrameData();
- }
+ // We don't actually copy it to the frame dump here, instead we submit the present to the screen
+ // and the readback in one command buffer, wait for it, and then dump the frame. This allows us
+ // to render to the screen and dump the frame in one command buffer instead of two.
+ VkFence frame_dump_fence = g_command_buffer_mgr->GetCurrentCommandBufferFence();
+ bool dump_this_frame = IsFrameDumping() && DrawFrameDump(rc, xfb_addr, xfb_sources, xfb_count,
+ fb_width, fb_stride, fb_height);
- // 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);
+ // If we're dumping frames, don't bother waking the worker thread, since we have to wait anyway.
+ bool submit_on_background_thread = !dump_this_frame;
// 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
@@ -502,20 +509,30 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
// Draw to the screen if we have a swap chain.
if (m_swap_chain)
{
- DrawScreen(source_rc, efb_color_texture);
+ DrawScreen(rc, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
// 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(
- true, m_image_available_semaphore, m_rendering_finished_semaphore,
+ submit_on_background_thread, m_image_available_semaphore, m_rendering_finished_semaphore,
m_swap_chain->GetSwapChain(), m_swap_chain->GetCurrentImageIndex());
}
else
{
// No swap chain, just execute command buffer.
- g_command_buffer_mgr->SubmitCommandBuffer(true);
+ g_command_buffer_mgr->SubmitCommandBuffer(submit_on_background_thread);
+ }
+
+ // If we're dumping frames, wait for the GPU to complete these commands, and then copy the image.
+ // NOTE: This call must come immediately after submitting the command buffer. Placing any other
+ // function calls between the submit and wait could cause another command buffer to be submitted,
+ // making frame_dump_fence refer to an incorrect fence.
+ if (dump_this_frame)
+ {
+ g_command_buffer_mgr->WaitForFence(frame_dump_fence);
+ DumpFrame(ticks);
}
// NOTE: It is important that no rendering calls are made to the EFB between submitting the
@@ -542,7 +559,97 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
TextureCacheBase::Cleanup(frameCount);
}
-void Renderer::DrawScreen(const TargetRectangle& src_rect, const Texture2D* src_tex)
+void Renderer::DrawFrame(VkRenderPass render_pass, const EFBRectangle& rc, u32 xfb_addr,
+ const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
+ u32 fb_stride, u32 fb_height)
+{
+ if (!g_ActiveConfig.bUseXFB)
+ DrawEFB(render_pass, rc);
+ else if (!g_ActiveConfig.bUseRealXFB)
+ DrawVirtualXFB(render_pass, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
+ else
+ DrawRealXFB(render_pass, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
+}
+
+void Renderer::DrawEFB(VkRenderPass render_pass, const EFBRectangle& rc)
+{
+ // Scale the source rectangle to the selected internal resolution.
+ TargetRectangle scaled_rc = Renderer::ConvertEFBRectangle(rc);
+ scaled_rc.left = std::max(scaled_rc.left, 0);
+ scaled_rc.right = std::max(scaled_rc.right, 0);
+ scaled_rc.top = std::max(scaled_rc.top, 0);
+ scaled_rc.bottom = std::max(scaled_rc.bottom, 0);
+
+ // Transition the EFB render target to a shader resource.
+ VkRect2D src_region = {
+ {0, 0}, {static_cast<u32>(scaled_rc.GetWidth()), static_cast<u32>(scaled_rc.GetHeight())}};
+ Texture2D* efb_color_texture =
+ FramebufferManager::GetInstance()->ResolveEFBColorTexture(src_region);
+ efb_color_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ // Copy EFB -> backbuffer
+ BlitScreen(render_pass, GetTargetRectangle(), scaled_rc, efb_color_texture, true);
+
+ // Restore the EFB color texture to color attachment ready for rendering the next frame.
+ if (efb_color_texture == FramebufferManager::GetInstance()->GetEFBColorTexture())
+ {
+ FramebufferManager::GetInstance()->GetEFBColorTexture()->TransitionToLayout(
+ g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ }
+}
+
+void Renderer::DrawVirtualXFB(VkRenderPass render_pass, u32 xfb_addr,
+ const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
+ u32 fb_stride, u32 fb_height)
+{
+ const TargetRectangle& target_rect = GetTargetRectangle();
+ for (u32 i = 0; i < xfb_count; ++i)
+ {
+ const XFBSource* xfb_source = static_cast<const XFBSource*>(xfb_sources[i]);
+ TargetRectangle source_rect = xfb_source->sourceRc;
+ TargetRectangle draw_rect;
+
+ int xfb_width = static_cast<int>(xfb_source->srcWidth);
+ int xfb_height = static_cast<int>(xfb_source->srcHeight);
+ int h_offset = (static_cast<s32>(xfb_source->srcAddr) - static_cast<s32>(xfb_addr)) /
+ (static_cast<s32>(fb_stride) * 2);
+ draw_rect.top =
+ target_rect.top + h_offset * target_rect.GetHeight() / static_cast<s32>(fb_height);
+ draw_rect.bottom =
+ target_rect.top +
+ (h_offset + xfb_height) * target_rect.GetHeight() / static_cast<s32>(fb_height);
+ draw_rect.left = target_rect.left +
+ (target_rect.GetWidth() -
+ xfb_width * target_rect.GetWidth() / static_cast<s32>(fb_stride)) /
+ 2;
+ draw_rect.right = target_rect.left +
+ (target_rect.GetWidth() +
+ xfb_width * target_rect.GetWidth() / static_cast<s32>(fb_stride)) /
+ 2;
+
+ source_rect.right -= Renderer::EFBToScaledX(fb_stride - fb_width);
+ BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture(), true);
+ }
+}
+
+void Renderer::DrawRealXFB(VkRenderPass render_pass, const XFBSourceBase* const* xfb_sources,
+ u32 xfb_count, u32 fb_width, u32 fb_stride, u32 fb_height)
+{
+ const TargetRectangle& target_rect = GetTargetRectangle();
+ for (u32 i = 0; i < xfb_count; ++i)
+ {
+ const XFBSource* xfb_source = static_cast<const XFBSource*>(xfb_sources[i]);
+ TargetRectangle source_rect = xfb_source->sourceRc;
+ TargetRectangle draw_rect = target_rect;
+ source_rect.right -= fb_stride - fb_width;
+ BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture(), true);
+ }
+}
+
+void Renderer::DrawScreen(const EFBRectangle& rc, u32 xfb_addr,
+ const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
+ u32 fb_stride, u32 fb_height)
{
// Grab the next image from the swap chain in preparation for drawing the window.
VkResult res = m_swap_chain->AcquireNextImage(m_image_available_semaphore);
@@ -563,32 +670,31 @@ void Renderer::DrawScreen(const TargetRectangle& src_rect, const Texture2D* src_
backbuffer->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
- // Blit the EFB to the back buffer (Swap chain)
- UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
- g_object_cache->GetStandardPipelineLayout(), m_swap_chain->GetRenderPass(),
- g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE,
- m_blit_fragment_shader);
-
- // Begin the present render pass
+ // Begin render pass for rendering to the swap chain.
VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
- VkRect2D target_region = {{0, 0}, {backbuffer->GetWidth(), backbuffer->GetHeight()}};
- draw.BeginRenderPass(m_swap_chain->GetCurrentFramebuffer(), target_region, &clear_value);
+ 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);
- // Copy EFB -> backbuffer
- const TargetRectangle& dst_rect = GetTargetRectangle();
- BlitScreen(m_swap_chain->GetRenderPass(), dst_rect, src_rect, src_tex, true);
+ // Draw guest buffers (EFB or XFB)
+ DrawFrame(m_swap_chain->GetRenderPass(), rc, xfb_addr, xfb_sources, xfb_count, fb_width,
+ fb_stride, fb_height);
- // OSD stuff
+ // Draw OSD
Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0,
backbuffer->GetWidth(), backbuffer->GetHeight());
DrawDebugText();
-
- // Do our OSD callbacks
OSD::DoCallbacks(OSD::CallbackType::OnFrame);
OSD::DrawMessages();
// End drawing to backbuffer
- draw.EndRenderPass();
+ vkCmdEndRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer());
// Transition the backbuffer to PRESENT_SRC to ensure all commands drawing
// to it have finished before present.
@@ -596,7 +702,9 @@ void Renderer::DrawScreen(const TargetRectangle& src_rect, const Texture2D* src_
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
-bool Renderer::DrawScreenshot(const TargetRectangle& src_rect, const Texture2D* src_tex)
+bool Renderer::DrawFrameDump(const EFBRectangle& rc, u32 xfb_addr,
+ const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
+ u32 fb_stride, u32 fb_height)
{
// Draw the screenshot to an image containing only the active screen area, removing any
// borders as a result of the game rendering in a different aspect ratio.
@@ -607,37 +715,45 @@ bool Renderer::DrawScreenshot(const TargetRectangle& src_rect, const Texture2D*
target_rect.top = 0;
u32 width = std::max(1u, static_cast<u32>(target_rect.GetWidth()));
u32 height = std::max(1u, static_cast<u32>(target_rect.GetHeight()));
- if (!ResizeScreenshotBuffer(width, height))
+ if (!ResizeFrameDumpBuffer(width, height))
return false;
VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkClearRect clear_rect = {{{0, 0}, {width, height}}, 0, 1};
VkClearAttachment clear_attachment = {VK_IMAGE_ASPECT_COLOR_BIT, 0, clear_value};
- VkRenderPassBeginInfo info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
- nullptr,
- m_framebuffer_mgr->GetColorCopyForReadbackRenderPass(),
- m_screenshot_framebuffer,
- {{0, 0}, {width, height}},
- 1,
- &clear_value};
+ VkRenderPassBeginInfo info = {
+ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ nullptr,
+ FramebufferManager::GetInstance()->GetColorCopyForReadbackRenderPass(),
+ m_frame_dump_framebuffer,
+ {{0, 0}, {width, height}},
+ 1,
+ &clear_value};
vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &info,
VK_SUBPASS_CONTENTS_INLINE);
vkCmdClearAttachments(g_command_buffer_mgr->GetCurrentCommandBuffer(), 1, &clear_attachment, 1,
&clear_rect);
- BlitScreen(m_framebuffer_mgr->GetColorCopyForReadbackRenderPass(), target_rect, src_rect, src_tex,
- true);
+ DrawFrame(FramebufferManager::GetInstance()->GetColorCopyForReadbackRenderPass(), rc, xfb_addr,
+ xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
vkCmdEndRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer());
// Copy to the readback texture.
- m_screenshot_readback_texture->CopyFromImage(
- g_command_buffer_mgr->GetCurrentCommandBuffer(), m_screenshot_render_texture->GetImage(),
+ m_frame_dump_readback_texture->CopyFromImage(
+ g_command_buffer_mgr->GetCurrentCommandBuffer(), m_frame_dump_render_texture->GetImage(),
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, width, height, 0, 0);
- // Wait for the command buffer to complete.
- g_command_buffer_mgr->ExecuteCommandBuffer(false, true);
return true;
}
+void Renderer::DumpFrame(u64 ticks)
+{
+ DumpFrameData(reinterpret_cast<const u8*>(m_frame_dump_readback_texture->GetMapPointer()),
+ static_cast<int>(m_frame_dump_render_texture->GetWidth()),
+ static_cast<int>(m_frame_dump_render_texture->GetHeight()),
+ static_cast<int>(m_frame_dump_readback_texture->GetRowStride()), ticks);
+ FinishFrameData();
+}
+
void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect,
const TargetRectangle& src_rect, const Texture2D* src_tex,
bool linear_filter)
@@ -658,10 +774,7 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r
{
TargetRectangle left_rect;
TargetRectangle right_rect;
- if (g_ActiveConfig.iStereoMode == STEREO_TAB)
- ConvertStereoRectangle(dst_rect, right_rect, left_rect);
- else
- ConvertStereoRectangle(dst_rect, left_rect, right_rect);
+ ConvertStereoRectangle(dst_rect, left_rect, right_rect);
draw.DrawQuad(left_rect.left, left_rect.top, left_rect.GetWidth(), left_rect.GetHeight(),
src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(),
@@ -679,40 +792,40 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r
}
}
-bool Renderer::ResizeScreenshotBuffer(u32 new_width, u32 new_height)
+bool Renderer::ResizeFrameDumpBuffer(u32 new_width, u32 new_height)
{
- if (m_screenshot_render_texture && m_screenshot_render_texture->GetWidth() == new_width &&
- m_screenshot_render_texture->GetHeight() == new_height)
+ if (m_frame_dump_render_texture && m_frame_dump_render_texture->GetWidth() == new_width &&
+ m_frame_dump_render_texture->GetHeight() == new_height)
{
return true;
}
- if (m_screenshot_framebuffer != VK_NULL_HANDLE)
+ if (m_frame_dump_framebuffer != VK_NULL_HANDLE)
{
- vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_screenshot_framebuffer, nullptr);
- m_screenshot_framebuffer = VK_NULL_HANDLE;
+ vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_frame_dump_framebuffer, nullptr);
+ m_frame_dump_framebuffer = VK_NULL_HANDLE;
}
- m_screenshot_render_texture =
+ m_frame_dump_render_texture =
Texture2D::Create(new_width, new_height, 1, 1, EFB_COLOR_TEXTURE_FORMAT,
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
- m_screenshot_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, new_width,
+ m_frame_dump_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, new_width,
new_height, EFB_COLOR_TEXTURE_FORMAT);
- if (!m_screenshot_render_texture || !m_screenshot_readback_texture ||
- !m_screenshot_readback_texture->Map())
+ if (!m_frame_dump_render_texture || !m_frame_dump_readback_texture ||
+ !m_frame_dump_readback_texture->Map())
{
WARN_LOG(VIDEO, "Failed to resize screenshot render texture");
- m_screenshot_render_texture.reset();
- m_screenshot_readback_texture.reset();
+ m_frame_dump_render_texture.reset();
+ m_frame_dump_readback_texture.reset();
return false;
}
- VkImageView attachment = m_screenshot_render_texture->GetView();
+ VkImageView attachment = m_frame_dump_render_texture->GetView();
VkFramebufferCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
- info.renderPass = m_framebuffer_mgr->GetColorCopyForReadbackRenderPass();
+ info.renderPass = FramebufferManager::GetInstance()->GetColorCopyForReadbackRenderPass();
info.attachmentCount = 1;
info.pAttachments = &attachment;
info.width = new_width;
@@ -720,32 +833,32 @@ bool Renderer::ResizeScreenshotBuffer(u32 new_width, u32 new_height)
info.layers = 1;
VkResult res =
- vkCreateFramebuffer(g_vulkan_context->GetDevice(), &info, nullptr, &m_screenshot_framebuffer);
+ vkCreateFramebuffer(g_vulkan_context->GetDevice(), &info, nullptr, &m_frame_dump_framebuffer);
if (res != VK_SUCCESS)
{
WARN_LOG(VIDEO, "Failed to resize screenshot framebuffer");
- m_screenshot_render_texture.reset();
- m_screenshot_readback_texture.reset();
+ m_frame_dump_render_texture.reset();
+ m_frame_dump_readback_texture.reset();
return false;
}
// Render pass expects texture is in transfer src to start with.
- m_screenshot_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ m_frame_dump_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
return true;
}
-void Renderer::DestroyScreenshotResources()
+void Renderer::DestroyFrameDumpResources()
{
- if (m_screenshot_framebuffer != VK_NULL_HANDLE)
+ if (m_frame_dump_framebuffer != VK_NULL_HANDLE)
{
- vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_screenshot_framebuffer, nullptr);
- m_screenshot_framebuffer = VK_NULL_HANDLE;
+ vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_frame_dump_framebuffer, nullptr);
+ m_frame_dump_framebuffer = VK_NULL_HANDLE;
}
- m_screenshot_render_texture.reset();
- m_screenshot_readback_texture.reset();
+ m_frame_dump_render_texture.reset();
+ m_frame_dump_readback_texture.reset();
}
void Renderer::CheckForTargetResize(u32 fb_width, u32 fb_stride, u32 fb_height)
@@ -889,8 +1002,8 @@ void Renderer::CheckForConfigChanges()
if (msaa_changed || stereo_changed)
{
g_command_buffer_mgr->WaitForGPUIdle();
- m_framebuffer_mgr->RecreateRenderPass();
- m_framebuffer_mgr->ResizeEFBTextures();
+ FramebufferManager::GetInstance()->RecreateRenderPass();
+ FramebufferManager::GetInstance()->ResizeEFBTextures();
BindEFBToStateTracker();
}
@@ -900,8 +1013,9 @@ void Renderer::CheckForConfigChanges()
{
g_command_buffer_mgr->WaitForGPUIdle();
RecompileShaders();
- m_framebuffer_mgr->RecompileShaders();
+ FramebufferManager::GetInstance()->RecompileShaders();
g_object_cache->ClearPipelineCache();
+ g_object_cache->RecompileSharedShaders();
}
// For vsync, we need to change the present mode, which means recreating the swap chain.
@@ -931,25 +1045,28 @@ void Renderer::OnSwapChainResized()
void Renderer::BindEFBToStateTracker()
{
// Update framebuffer in state tracker
- VkRect2D framebuffer_size = {
- {0, 0}, {m_framebuffer_mgr->GetEFBWidth(), m_framebuffer_mgr->GetEFBHeight()}};
- m_state_tracker->SetRenderPass(m_framebuffer_mgr->GetEFBLoadRenderPass(),
- m_framebuffer_mgr->GetEFBClearRenderPass());
- m_state_tracker->SetFramebuffer(m_framebuffer_mgr->GetEFBFramebuffer(), framebuffer_size);
+ 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);
// Update rasterization state with MSAA info
RasterizationState rs_state = {};
- rs_state.bits = m_state_tracker->GetRasterizationState().bits;
- rs_state.samples = m_framebuffer_mgr->GetEFBSamples();
+ rs_state.bits = StateTracker::GetInstance()->GetRasterizationState().bits;
+ rs_state.samples = FramebufferManager::GetInstance()->GetEFBSamples();
rs_state.per_sample_shading = g_ActiveConfig.bSSAA ? VK_TRUE : VK_FALSE;
- m_state_tracker->SetRasterizationState(rs_state);
+ StateTracker::GetInstance()->SetRasterizationState(rs_state);
}
void Renderer::ResizeEFBTextures()
{
// Ensure the GPU is finished with the current EFB textures.
g_command_buffer_mgr->WaitForGPUIdle();
- m_framebuffer_mgr->ResizeEFBTextures();
+ FramebufferManager::GetInstance()->ResizeEFBTextures();
BindEFBToStateTracker();
// Viewport and scissor rect have to be reset since they will be scaled differently.
@@ -976,19 +1093,19 @@ void Renderer::ApplyState(bool bUseDstAlpha)
void Renderer::ResetAPIState()
{
// End the EFB render pass if active
- m_state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
}
void Renderer::RestoreAPIState()
{
// Instruct the state tracker to re-bind everything before the next draw
- m_state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->SetPendingRebind();
}
void Renderer::SetGenerationMode()
{
RasterizationState new_rs_state = {};
- new_rs_state.bits = m_state_tracker->GetRasterizationState().bits;
+ new_rs_state.bits = StateTracker::GetInstance()->GetRasterizationState().bits;
switch (bpmem.genMode.cullmode)
{
@@ -1009,7 +1126,7 @@ void Renderer::SetGenerationMode()
break;
}
- m_state_tracker->SetRasterizationState(new_rs_state);
+ StateTracker::GetInstance()->SetRasterizationState(new_rs_state);
}
void Renderer::SetDepthMode()
@@ -1050,7 +1167,7 @@ void Renderer::SetDepthMode()
break;
}
- m_state_tracker->SetDepthStencilState(new_ds_state);
+ StateTracker::GetInstance()->SetDepthStencilState(new_ds_state);
}
void Renderer::SetColorMask()
@@ -1066,16 +1183,16 @@ void Renderer::SetColorMask()
}
BlendState new_blend_state = {};
- new_blend_state.bits = m_state_tracker->GetBlendState().bits;
+ new_blend_state.bits = StateTracker::GetInstance()->GetBlendState().bits;
new_blend_state.write_mask = color_mask;
- m_state_tracker->SetBlendState(new_blend_state);
+ StateTracker::GetInstance()->SetBlendState(new_blend_state);
}
void Renderer::SetBlendMode(bool force_update)
{
BlendState new_blend_state = {};
- new_blend_state.bits = m_state_tracker->GetBlendState().bits;
+ new_blend_state.bits = StateTracker::GetInstance()->GetBlendState().bits;
// Fast path for blending disabled
if (!bpmem.blendmode.blendenable)
@@ -1087,7 +1204,7 @@ void Renderer::SetBlendMode(bool force_update)
new_blend_state.alpha_blend_op = VK_BLEND_OP_ADD;
new_blend_state.src_alpha_blend = VK_BLEND_FACTOR_ONE;
new_blend_state.dst_alpha_blend = VK_BLEND_FACTOR_ZERO;
- m_state_tracker->SetBlendState(new_blend_state);
+ StateTracker::GetInstance()->SetBlendState(new_blend_state);
return;
}
// Fast path for subtract blending
@@ -1100,7 +1217,7 @@ void Renderer::SetBlendMode(bool force_update)
new_blend_state.alpha_blend_op = VK_BLEND_OP_REVERSE_SUBTRACT;
new_blend_state.src_alpha_blend = VK_BLEND_FACTOR_ONE;
new_blend_state.dst_alpha_blend = VK_BLEND_FACTOR_ONE;
- m_state_tracker->SetBlendState(new_blend_state);
+ StateTracker::GetInstance()->SetBlendState(new_blend_state);
return;
}
@@ -1195,13 +1312,13 @@ void Renderer::SetBlendMode(bool force_update)
new_blend_state.dst_alpha_blend = Util::GetAlphaBlendFactor(new_blend_state.dst_blend);
}
- m_state_tracker->SetBlendState(new_blend_state);
+ StateTracker::GetInstance()->SetBlendState(new_blend_state);
}
void Renderer::SetLogicOpMode()
{
BlendState new_blend_state = {};
- new_blend_state.bits = m_state_tracker->GetBlendState().bits;
+ new_blend_state.bits = StateTracker::GetInstance()->GetBlendState().bits;
// Does our device support logic ops?
bool logic_op_enable = bpmem.blendmode.logicopenable && !bpmem.blendmode.blendenable;
@@ -1224,7 +1341,7 @@ void Renderer::SetLogicOpMode()
new_blend_state.logic_op = VK_LOGIC_OP_CLEAR;
}
- m_state_tracker->SetBlendState(new_blend_state);
+ StateTracker::GetInstance()->SetBlendState(new_blend_state);
}
else
{
@@ -1269,7 +1386,7 @@ void Renderer::SetLogicOpMode()
new_blend_state.src_alpha_blend = Util::GetAlphaBlendFactor(new_blend_state.src_blend);
new_blend_state.dst_alpha_blend = Util::GetAlphaBlendFactor(new_blend_state.dst_blend);
- m_state_tracker->SetBlendState(new_blend_state);
+ StateTracker::GetInstance()->SetBlendState(new_blend_state);
}
else
{
@@ -1338,7 +1455,7 @@ void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex)
sampler = g_object_cache->GetPointSampler();
}
- m_state_tracker->SetSampler(bind_index, sampler);
+ StateTracker::GetInstance()->SetSampler(bind_index, sampler);
m_sampler_states[bind_index].bits = new_state.bits;
}
@@ -1352,7 +1469,7 @@ void Renderer::ResetSamplerStates()
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();
- m_state_tracker->SetSampler(i, g_object_cache->GetPointSampler());
+ StateTracker::GetInstance()->SetSampler(i, g_object_cache->GetPointSampler());
}
// Invalidate all sampler objects (some will be unused now).
@@ -1375,7 +1492,7 @@ void Renderer::SetScissorRect(const EFBRectangle& rc)
{target_rc.left, target_rc.top},
{static_cast<uint32_t>(target_rc.GetWidth()), static_cast<uint32_t>(target_rc.GetHeight())}};
- m_state_tracker->SetScissor(scissor);
+ StateTracker::GetInstance()->SetScissor(scissor);
}
void Renderer::SetViewport()
@@ -1420,7 +1537,7 @@ void Renderer::SetViewport()
}
VkViewport viewport = {x, y, width, height, min_depth, max_depth};
- m_state_tracker->SetViewport(viewport);
+ StateTracker::GetInstance()->SetViewport(viewport);
}
void Renderer::ChangeSurface(void* new_surface_handle)
diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h
index d3a5d42926..dd2b342ad9 100644
--- a/Source/Core/VideoBackends/Vulkan/Renderer.h
+++ b/Source/Core/VideoBackends/Vulkan/Renderer.h
@@ -12,13 +12,14 @@
#include "VideoBackends/Vulkan/Constants.h"
#include "VideoCommon/RenderBase.h"
+struct XFBSourceBase;
+
namespace Vulkan
{
class BoundingBox;
class FramebufferManager;
class SwapChain;
class StagingTexture2D;
-class StateTracker;
class Texture2D;
class RasterFont;
@@ -28,10 +29,11 @@ public:
Renderer(std::unique_ptr<SwapChain> swap_chain);
~Renderer();
+ static Renderer* GetInstance();
+
SwapChain* GetSwapChain() const { return m_swap_chain.get(); }
- StateTracker* GetStateTracker() const { return m_state_tracker.get(); }
BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); }
- bool Initialize(FramebufferManager* framebuffer_mgr);
+ bool Initialize();
void RenderText(const std::string& pstr, int left, int top, u32 color) override;
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
@@ -88,19 +90,41 @@ private:
bool CompileShaders();
void DestroyShaders();
- void DrawScreen(const TargetRectangle& src_rect, const Texture2D* src_tex);
- bool DrawScreenshot(const TargetRectangle& src_rect, const Texture2D* src_tex);
+ // Draw either the EFB, or specified XFB sources to the currently-bound framebuffer.
+ void DrawFrame(VkRenderPass render_pass, const EFBRectangle& rc, u32 xfb_addr,
+ const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
+ u32 fb_stride, u32 fb_height);
+ void DrawEFB(VkRenderPass render_pass, const EFBRectangle& rc);
+ void DrawVirtualXFB(VkRenderPass render_pass, u32 xfb_addr,
+ const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
+ u32 fb_stride, u32 fb_height);
+ void DrawRealXFB(VkRenderPass render_pass, const XFBSourceBase* const* xfb_sources, u32 xfb_count,
+ u32 fb_width, u32 fb_stride, u32 fb_height);
+
+ // Draw the frame, as well as the OSD to the swap chain.
+ void DrawScreen(const EFBRectangle& rc, u32 xfb_addr, const XFBSourceBase* const* xfb_sources,
+ u32 xfb_count, u32 fb_width, u32 fb_stride, u32 fb_height);
+
+ // Draw the frame only to the screenshot buffer.
+ bool DrawFrameDump(const EFBRectangle& rc, u32 xfb_addr, const XFBSourceBase* const* xfb_sources,
+ u32 xfb_count, u32 fb_width, u32 fb_stride, u32 fb_height);
+
+ // Copies the screenshot readback texture to the frame dumping buffer.
+ // NOTE: This assumes that DrawScreenshot has been called prior, and the fence associated
+ // with the command buffer where the readback buffer was populated has been reached.
+ void DumpFrame(u64 ticks);
+
+ // Copies/scales an image to the currently-bound framebuffer.
void BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect,
const TargetRectangle& src_rect, const Texture2D* src_tex, bool linear_filter);
- bool ResizeScreenshotBuffer(u32 new_width, u32 new_height);
- void DestroyScreenshotResources();
- FramebufferManager* m_framebuffer_mgr = nullptr;
+
+ bool ResizeFrameDumpBuffer(u32 new_width, u32 new_height);
+ void DestroyFrameDumpResources();
VkSemaphore m_image_available_semaphore = VK_NULL_HANDLE;
VkSemaphore m_rendering_finished_semaphore = VK_NULL_HANDLE;
std::unique_ptr<SwapChain> m_swap_chain;
- std::unique_ptr<StateTracker> m_state_tracker;
std::unique_ptr<BoundingBox> m_bounding_box;
std::unique_ptr<RasterFont> m_raster_font;
@@ -116,8 +140,8 @@ private:
VkShaderModule m_blit_fragment_shader = VK_NULL_HANDLE;
// Texture used for screenshot/frame dumping
- std::unique_ptr<Texture2D> m_screenshot_render_texture;
- std::unique_ptr<StagingTexture2D> m_screenshot_readback_texture;
- VkFramebuffer m_screenshot_framebuffer = VK_NULL_HANDLE;
+ std::unique_ptr<Texture2D> m_frame_dump_render_texture;
+ std::unique_ptr<StagingTexture2D> m_frame_dump_readback_texture;
+ VkFramebuffer m_frame_dump_framebuffer = VK_NULL_HANDLE;
};
}
diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp
index 84b1099862..bfd198886b 100644
--- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp
+++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp
@@ -24,10 +24,33 @@
namespace Vulkan
{
-StateTracker::StateTracker()
+static std::unique_ptr<StateTracker> s_state_tracker;
+
+StateTracker* StateTracker::GetInstance()
+{
+ return s_state_tracker.get();
+}
+
+bool StateTracker::CreateInstance()
+{
+ _assert_(!s_state_tracker);
+ s_state_tracker = std::make_unique<StateTracker>();
+ if (!s_state_tracker->Initialize())
+ {
+ s_state_tracker.reset();
+ return false;
+ }
+ return true;
+}
+
+void StateTracker::DestroyInstance()
+{
+ s_state_tracker.reset();
+}
+
+bool StateTracker::Initialize()
{
// Set some sensible defaults
- m_pipeline_state.pipeline_layout = g_object_cache->GetStandardPipelineLayout();
m_pipeline_state.rasterization_state.cull_mode = VK_CULL_MODE_NONE;
m_pipeline_state.rasterization_state.per_sample_shading = VK_FALSE;
m_pipeline_state.rasterization_state.depth_clamp = VK_FALSE;
@@ -68,7 +91,10 @@ StateTracker::StateTracker()
StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, INITIAL_UNIFORM_STREAM_BUFFER_SIZE,
MAXIMUM_UNIFORM_STREAM_BUFFER_SIZE);
if (!m_uniform_stream_buffer)
+ {
PanicAlert("Failed to create uniform stream buffer");
+ return false;
+ }
// The validation layer complains if max(offsets) + max(ubo_ranges) >= ubo_size.
// To work around this we reserve the maximum buffer size at all times, but only commit
@@ -87,10 +113,7 @@ StateTracker::StateTracker()
// Set default constants
UploadAllConstants();
-}
-
-StateTracker::~StateTracker()
-{
+ return true;
}
void StateTracker::SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset)
@@ -372,7 +395,7 @@ void StateTracker::UploadAllConstants()
// If this fails, wait until the GPU has caught up.
// The only places that call constant updates are safe to have state restored.
WARN_LOG(VIDEO, "Executing command list while waiting for space in uniform buffer");
- Util::ExecuteCurrentCommandsAndRestoreState(this, false);
+ Util::ExecuteCurrentCommandsAndRestoreState(false);
if (!m_uniform_stream_buffer->ReserveMemory(total_allocation_size,
g_vulkan_context->GetUniformBufferAlignment(), true,
true, false))
diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.h b/Source/Core/VideoBackends/Vulkan/StateTracker.h
index a763c72b7f..b31adec6b5 100644
--- a/Source/Core/VideoBackends/Vulkan/StateTracker.h
+++ b/Source/Core/VideoBackends/Vulkan/StateTracker.h
@@ -24,8 +24,12 @@ class VertexFormat;
class StateTracker
{
public:
- StateTracker();
- ~StateTracker();
+ StateTracker() = default;
+ ~StateTracker() = default;
+
+ static StateTracker* GetInstance();
+ static bool CreateInstance();
+ static void DestroyInstance();
const RasterizationState& GetRasterizationState() const
{
@@ -108,6 +112,8 @@ public:
bool IsWithinRenderArea(s32 x, s32 y, u32 width, u32 height) const;
private:
+ bool Initialize();
+
// Check that the specified viewport is within the render area.
// If not, ends the render pass if it is a clear render pass.
bool IsViewportWithinRenderArea() const;
diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp
index 60790c687f..d99200b203 100644
--- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp
+++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp
@@ -44,9 +44,13 @@ TextureCache::~TextureCache()
TextureCache::DeleteShaders();
}
-bool TextureCache::Initialize(StateTracker* state_tracker)
+TextureCache* TextureCache::GetInstance()
+{
+ return static_cast<TextureCache*>(g_texture_cache.get());
+}
+
+bool TextureCache::Initialize()
{
- m_state_tracker = state_tracker;
m_texture_upload_buffer =
StreamBuffer::Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, INITIAL_TEXTURE_UPLOAD_BUFFER_SIZE,
MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE);
@@ -99,8 +103,8 @@ void TextureCache::ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase*
if (unconverted->IsEfbCopy())
{
command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
- m_state_tracker->EndRenderPass();
- m_state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->EndRenderPass();
+ StateTracker::GetInstance()->SetPendingRebind();
}
else
{
@@ -109,24 +113,25 @@ void TextureCache::ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase*
}
m_palette_texture_converter->ConvertTexture(
- m_state_tracker, command_buffer, GetRenderPassForTextureUpdate(entry->GetTexture()),
- entry->GetFramebuffer(), unconverted->GetTexture(), entry->config.width, entry->config.height,
- palette, format, unconverted->format);
+ command_buffer, GetRenderPassForTextureUpdate(entry->GetTexture()), entry->GetFramebuffer(),
+ unconverted->GetTexture(), entry->config.width, entry->config.height, palette, format,
+ unconverted->format);
// Render pass transitions to SHADER_READ_ONLY.
entry->GetTexture()->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
+static bool IsDepthCopyFormat(PEControl::PixelFormat format)
+{
+ return format == PEControl::Z24;
+}
+
void TextureCache::CopyEFB(u8* dst, u32 format, u32 native_width, u32 bytes_per_row,
u32 num_blocks_y, u32 memory_stride, PEControl::PixelFormat src_format,
const EFBRectangle& src_rect, bool is_intensity, bool scale_by_half)
{
- // A better way of doing this would be nice.
- FramebufferManager* framebuffer_mgr =
- static_cast<FramebufferManager*>(g_framebuffer_manager.get());
-
// Flush EFB pokes first, as they're expected to be included.
- framebuffer_mgr->FlushEFBPokes(m_state_tracker);
+ FramebufferManager::GetInstance()->FlushEFBPokes();
// MSAA case where we need to resolve first.
// TODO: Do in one pass.
@@ -134,29 +139,120 @@ void TextureCache::CopyEFB(u8* dst, u32 format, u32 native_width, u32 bytes_per_
VkRect2D region = {{scaled_src_rect.left, scaled_src_rect.top},
{static_cast<u32>(scaled_src_rect.GetWidth()),
static_cast<u32>(scaled_src_rect.GetHeight())}};
- Texture2D* src_texture = (src_format == PEControl::Z24) ?
- framebuffer_mgr->ResolveEFBDepthTexture(m_state_tracker, region) :
- framebuffer_mgr->ResolveEFBColorTexture(m_state_tracker, region);
+ Texture2D* src_texture;
+ if (IsDepthCopyFormat(src_format))
+ src_texture = FramebufferManager::GetInstance()->ResolveEFBDepthTexture(region);
+ else
+ src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(region);
// End render pass before barrier (since we have no self-dependencies)
- m_state_tracker->EndRenderPass();
- m_state_tracker->SetPendingRebind();
- m_state_tracker->InvalidateDescriptorSets();
- m_state_tracker->OnReadback();
+ StateTracker::GetInstance()->EndRenderPass();
+ StateTracker::GetInstance()->SetPendingRebind();
+ StateTracker::GetInstance()->InvalidateDescriptorSets();
+ StateTracker::GetInstance()->OnReadback();
// Transition to shader resource before reading.
VkImageLayout original_layout = src_texture->GetLayout();
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
- m_texture_encoder->EncodeTextureToRam(m_state_tracker, src_texture->GetView(), dst, format,
- native_width, bytes_per_row, num_blocks_y, memory_stride,
- src_format, is_intensity, scale_by_half, src_rect);
+ m_texture_encoder->EncodeTextureToRam(src_texture->GetView(), dst, format, native_width,
+ bytes_per_row, num_blocks_y, memory_stride, src_format,
+ is_intensity, scale_by_half, src_rect);
// Transition back to original state
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), original_layout);
}
+void TextureCache::CopyRectangleFromTexture(TCacheEntry* dst_texture,
+ const MathUtil::Rectangle<int>& dst_rect,
+ Texture2D* src_texture,
+ const MathUtil::Rectangle<int>& src_rect)
+{
+ // Fast path when not scaling the image.
+ if (src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight())
+ CopyTextureRectangle(dst_texture, dst_rect, src_texture, src_rect);
+ else
+ ScaleTextureRectangle(dst_texture, dst_rect, src_texture, src_rect);
+}
+
+void TextureCache::CopyTextureRectangle(TCacheEntry* dst_texture,
+ const MathUtil::Rectangle<int>& dst_rect,
+ Texture2D* src_texture,
+ const MathUtil::Rectangle<int>& src_rect)
+{
+ _assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() &&
+ static_cast<u32>(src_rect.GetHeight()) <= src_texture->GetHeight(),
+ "Source rect is too large for CopyRectangleFromTexture");
+
+ _assert_msg_(VIDEO, static_cast<u32>(dst_rect.GetWidth()) <= dst_texture->config.width &&
+ static_cast<u32>(dst_rect.GetHeight()) <= dst_texture->config.height,
+ "Dest rect is too large for CopyRectangleFromTexture");
+
+ VkImageCopy image_copy = {
+ {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
+ src_texture->GetLayers()}, // VkImageSubresourceLayers srcSubresource
+ {src_rect.left, src_rect.top, 0}, // VkOffset3D srcOffset
+ {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, // VkImageSubresourceLayers dstSubresource
+ dst_texture->config.layers},
+ {dst_rect.left, dst_rect.top, 0}, // VkOffset3D dstOffset
+ {static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()),
+ 1} // VkExtent3D extent
+ };
+
+ // Must be called outside of a render pass.
+ StateTracker::GetInstance()->EndRenderPass();
+
+ src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+ dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+
+ vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(),
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_texture->GetTexture()->GetImage(),
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
+}
+
+void TextureCache::ScaleTextureRectangle(TCacheEntry* dst_texture,
+ const MathUtil::Rectangle<int>& dst_rect,
+ Texture2D* src_texture,
+ const MathUtil::Rectangle<int>& src_rect)
+{
+ // Can't do this within a game render pass.
+ StateTracker::GetInstance()->EndRenderPass();
+ StateTracker::GetInstance()->SetPendingRebind();
+
+ // Can't render to a non-rendertarget (no framebuffer).
+ _assert_msg_(VIDEO, dst_texture->config.rendertarget,
+ "Destination texture for partial copy is not a rendertarget");
+
+ // Render pass expects dst_texture to be in SHADER_READ_ONLY state.
+ src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+ dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ g_object_cache->GetStandardPipelineLayout(),
+ GetRenderPassForTextureUpdate(dst_texture->GetTexture()),
+ g_object_cache->GetPassthroughVertexShader(),
+ g_object_cache->GetPassthroughGeometryShader(), m_copy_shader);
+
+ VkRect2D region = {
+ {dst_rect.left, dst_rect.top},
+ {static_cast<u32>(dst_rect.GetWidth()), static_cast<u32>(dst_rect.GetHeight())}};
+ draw.BeginRenderPass(dst_texture->GetFramebuffer(), region);
+ draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler());
+ draw.DrawQuad(dst_rect.left, dst_rect.top, dst_rect.GetWidth(), dst_rect.GetHeight(),
+ src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(),
+ static_cast<int>(src_texture->GetWidth()),
+ static_cast<int>(src_texture->GetHeight()));
+ draw.EndRenderPass();
+
+ // Render pass transitions destination texture to SHADER_READ_ONLY.
+ dst_texture->GetTexture()->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+}
+
TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config)
{
// Determine image usage, we need to flag as an attachment if it can be used as a rendertarget.
@@ -207,7 +303,7 @@ TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntry
texture->GetLayout(), &clear_value, 1, &clear_range);
}
- return new TCacheEntry(config, this, std::move(texture), framebuffer);
+ return new TCacheEntry(config, std::move(texture), framebuffer);
}
bool TextureCache::CreateRenderPasses()
@@ -319,20 +415,17 @@ VkRenderPass TextureCache::GetRenderPassForTextureUpdate(const Texture2D* textur
return m_update_render_pass;
}
-TextureCache::TCacheEntry::TCacheEntry(const TCacheEntryConfig& config_, TextureCache* parent,
+TextureCache::TCacheEntry::TCacheEntry(const TCacheEntryConfig& config_,
std::unique_ptr<Texture2D> texture,
VkFramebuffer framebuffer)
- : TCacheEntryBase(config_), m_parent(parent), m_texture(std::move(texture)),
- m_framebuffer(framebuffer)
+ : TCacheEntryBase(config_), m_texture(std::move(texture)), m_framebuffer(framebuffer)
{
}
TextureCache::TCacheEntry::~TCacheEntry()
{
- // Texture is automatically cleaned up, however, we don't want to leave it bound to the state
- // tracker.
- m_parent->m_state_tracker->UnbindTexture(m_texture->GetView());
-
+ // Texture is automatically cleaned up, however, we don't want to leave it bound.
+ StateTracker::GetInstance()->UnbindTexture(m_texture->GetView());
if (m_framebuffer != VK_NULL_HANDLE)
g_command_buffer_mgr->DeferFramebufferDestruction(m_framebuffer);
}
@@ -376,14 +469,14 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
(upload_size + upload_alignment) <= MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE)
{
// Assume tightly packed rows, with no padding as the buffer source.
- StreamBuffer* upload_buffer = m_parent->m_texture_upload_buffer.get();
+ StreamBuffer* upload_buffer = TextureCache::GetInstance()->m_texture_upload_buffer.get();
// Allocate memory from the streaming buffer for the texture data.
if (!upload_buffer->ReserveMemory(upload_size, g_vulkan_context->GetBufferImageGranularity()))
{
// Execute the command buffer first.
WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer");
- Util::ExecuteCurrentCommandsAndRestoreState(m_parent->m_state_tracker, false);
+ Util::ExecuteCurrentCommandsAndRestoreState(false);
// Try allocating again. This may cause a fence wait.
if (!upload_buffer->ReserveMemory(upload_size, g_vulkan_context->GetBufferImageGranularity()))
@@ -467,26 +560,28 @@ void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat
FramebufferManager* framebuffer_mgr =
static_cast<FramebufferManager*>(g_framebuffer_manager.get());
TargetRectangle scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect);
- bool is_depth_copy = (src_format == PEControl::Z24);
+ bool is_depth_copy = IsDepthCopyFormat(src_format);
// Flush EFB pokes first, as they're expected to be included.
- framebuffer_mgr->FlushEFBPokes(m_parent->m_state_tracker);
+ framebuffer_mgr->FlushEFBPokes();
// Has to be flagged as a render target.
_assert_(m_framebuffer != VK_NULL_HANDLE);
// Can't be done in a render pass, since we're doing our own render pass!
- StateTracker* state_tracker = m_parent->m_state_tracker;
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
- state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
// Transition EFB to shader resource before binding
VkRect2D region = {{scaled_src_rect.left, scaled_src_rect.top},
{static_cast<u32>(scaled_src_rect.GetWidth()),
static_cast<u32>(scaled_src_rect.GetHeight())}};
- Texture2D* src_texture = is_depth_copy ?
- framebuffer_mgr->ResolveEFBDepthTexture(state_tracker, region) :
- framebuffer_mgr->ResolveEFBColorTexture(state_tracker, region);
+ Texture2D* src_texture;
+ if (is_depth_copy)
+ src_texture = FramebufferManager::GetInstance()->ResolveEFBDepthTexture(region);
+ else
+ src_texture = FramebufferManager::GetInstance()->ResolveEFBColorTexture(region);
+
VkSampler src_sampler =
scale_by_half ? g_object_cache->GetLinearSampler() : g_object_cache->GetPointSampler();
VkImageLayout original_layout = src_texture->GetLayout();
@@ -494,9 +589,10 @@ void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat
UtilityShaderDraw draw(
command_buffer, g_object_cache->GetPushConstantPipelineLayout(),
- m_parent->GetRenderPassForTextureUpdate(m_texture.get()),
+ TextureCache::GetInstance()->GetRenderPassForTextureUpdate(m_texture.get()),
g_object_cache->GetPassthroughVertexShader(), g_object_cache->GetPassthroughGeometryShader(),
- is_depth_copy ? m_parent->m_efb_depth_to_tex_shader : m_parent->m_efb_color_to_tex_shader);
+ is_depth_copy ? TextureCache::GetInstance()->m_efb_depth_to_tex_shader :
+ TextureCache::GetInstance()->m_efb_color_to_tex_shader);
draw.SetPushConstants(colmat, (is_depth_copy ? sizeof(float) * 20 : sizeof(float) * 28));
draw.SetPSSampler(0, src_texture->GetView(), src_sampler);
@@ -512,7 +608,7 @@ void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat
draw.EndRenderPass();
// We touched everything, so put it back.
- state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->SetPendingRebind();
// Transition the EFB back to its original layout.
src_texture->TransitionToLayout(command_buffer, original_layout);
@@ -526,78 +622,19 @@ void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase*
const MathUtil::Rectangle<int>& dst_rect)
{
const TCacheEntry* source_vk = static_cast<const TCacheEntry*>(source);
- VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
-
- // Fast path when not scaling the image.
- if (src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight())
- {
- // These assertions should hold true unless the base code is passing us sizes too large, in
- // which case it should be fixed instead.
- _assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= source->config.width &&
- static_cast<u32>(src_rect.GetHeight()) <= source->config.height,
- "Source rect is too large for CopyRectangleFromTexture");
-
- _assert_msg_(VIDEO, static_cast<u32>(dst_rect.GetWidth()) <= config.width &&
- static_cast<u32>(dst_rect.GetHeight()) <= config.height,
- "Dest rect is too large for CopyRectangleFromTexture");
-
- VkImageCopy image_copy = {
- {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
- source->config.layers}, // VkImageSubresourceLayers srcSubresource
- {src_rect.left, src_rect.top, 0}, // VkOffset3D srcOffset
- {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
- config.layers}, // VkImageSubresourceLayers dstSubresource
- {dst_rect.left, dst_rect.top, 0}, // VkOffset3D dstOffset
- {static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()),
- 1} // VkExtent3D extent
- };
-
- // Must be called outside of a render pass.
- m_parent->m_state_tracker->EndRenderPass();
-
- source_vk->m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
- m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
-
- vkCmdCopyImage(command_buffer, source_vk->m_texture->GetImage(),
- VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture->GetImage(),
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
-
- m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
- source_vk->m_texture->TransitionToLayout(command_buffer,
- VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
- return;
- }
-
- // Can't do this within a game render pass.
- m_parent->m_state_tracker->EndRenderPass();
- m_parent->m_state_tracker->SetPendingRebind();
+ TextureCache::GetInstance()->CopyRectangleFromTexture(this, dst_rect, source_vk->GetTexture(),
+ src_rect);
- // Can't render to a non-rendertarget (no framebuffer).
- _assert_msg_(VIDEO, config.rendertarget,
- "Destination texture for partial copy is not a rendertarget");
-
- UtilityShaderDraw draw(
- g_command_buffer_mgr->GetCurrentCommandBuffer(), g_object_cache->GetStandardPipelineLayout(),
- m_parent->GetRenderPassForTextureUpdate(m_texture.get()),
- g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE, m_parent->m_copy_shader);
-
- VkRect2D region = {
- {dst_rect.left, dst_rect.top},
- {static_cast<u32>(dst_rect.GetWidth()), static_cast<u32>(dst_rect.GetHeight())}};
- draw.BeginRenderPass(m_framebuffer, region);
- draw.SetPSSampler(0, source_vk->GetTexture()->GetView(), g_object_cache->GetLinearSampler());
- draw.DrawQuad(dst_rect.left, dst_rect.top, dst_rect.GetWidth(), dst_rect.GetHeight(),
- src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(),
- source->config.width, source->config.height);
- draw.EndRenderPass();
-
- // Render pass transitions texture to SHADER_READ_ONLY.
- m_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+ // Ensure textures are ready for use again.
+ m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+ source_vk->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
void TextureCache::TCacheEntry::Bind(unsigned int stage)
{
- m_parent->m_state_tracker->SetTexture(stage, m_texture->GetView());
+ StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView());
}
bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int level)
@@ -617,7 +654,7 @@ bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int l
// since we'll be executing the command buffer.
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
- m_parent->m_state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
// Copy to download buffer.
staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
@@ -630,8 +667,8 @@ bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int l
// Block until the GPU has finished copying to the staging texture.
g_command_buffer_mgr->ExecuteCommandBuffer(false, true);
- m_parent->m_state_tracker->InvalidateDescriptorSets();
- m_parent->m_state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->InvalidateDescriptorSets();
+ StateTracker::GetInstance()->SetPendingRebind();
// Map the staging texture so we can copy the contents out.
if (staging_texture->Map())
@@ -722,6 +759,52 @@ bool TextureCache::CompileShaders()
}
)";
+ static const char RGB_TO_YUYV_SHADER_SOURCE[] = R"(
+ SAMPLER_BINDING(0) uniform sampler2DArray source;
+ layout(location = 0) in vec3 uv0;
+ layout(location = 0) out vec4 ocol0;
+
+ const vec3 y_const = vec3(0.257,0.504,0.098);
+ const vec3 u_const = vec3(-0.148,-0.291,0.439);
+ const vec3 v_const = vec3(0.439,-0.368,-0.071);
+ const vec4 const3 = vec4(0.0625,0.5,0.0625,0.5);
+
+ void main()
+ {
+ vec3 c0 = texture(source, vec3(uv0.xy - dFdx(uv0.xy) * 0.25, 0.0)).rgb;
+ vec3 c1 = texture(source, vec3(uv0.xy + dFdx(uv0.xy) * 0.25, 0.0)).rgb;
+ vec3 c01 = (c0 + c1) * 0.5;
+ ocol0 = vec4(dot(c1, y_const),
+ dot(c01,u_const),
+ dot(c0,y_const),
+ dot(c01, v_const)) + const3;
+ }
+ )";
+
+ static const char YUYV_TO_RGB_SHADER_SOURCE[] = R"(
+ SAMPLER_BINDING(0) uniform sampler2D source;
+ layout(location = 0) in vec3 uv0;
+ layout(location = 0) out vec4 ocol0;
+
+ void main()
+ {
+ ivec2 uv = ivec2(gl_FragCoord.xy);
+ vec4 c0 = texelFetch(source, ivec2(uv.x / 2, uv.y), 0);
+
+ // The texture used to stage the upload is in BGRA order.
+ c0 = c0.zyxw;
+
+ float y = mix(c0.r, c0.b, (uv.x & 1) == 1);
+ float yComp = 1.164 * (y - 0.0625);
+ float uComp = c0.g - 0.5;
+ float vComp = c0.a - 0.5;
+ ocol0 = vec4(yComp + (1.596 * vComp),
+ yComp - (0.813 * vComp) - (0.391 * uComp),
+ yComp + (2.018 * uComp),
+ 1.0);
+ }
+ )";
+
std::string header = g_object_cache->GetUtilityShaderHeader();
std::string source;
@@ -737,8 +820,14 @@ bool TextureCache::CompileShaders()
source = header + EFB_DEPTH_TO_TEX_SOURCE;
m_efb_depth_to_tex_shader = Util::CompileAndCreateFragmentShader(source);
+ source = header + RGB_TO_YUYV_SHADER_SOURCE;
+ m_rgb_to_yuyv_shader = Util::CompileAndCreateFragmentShader(source);
+ source = header + YUYV_TO_RGB_SHADER_SOURCE;
+ m_yuyv_to_rgb_shader = Util::CompileAndCreateFragmentShader(source);
+
return (m_copy_shader != VK_NULL_HANDLE && m_efb_color_to_tex_shader != VK_NULL_HANDLE &&
- m_efb_depth_to_tex_shader != VK_NULL_HANDLE);
+ m_efb_depth_to_tex_shader != VK_NULL_HANDLE && m_rgb_to_yuyv_shader != VK_NULL_HANDLE &&
+ m_yuyv_to_rgb_shader != VK_NULL_HANDLE);
}
void TextureCache::DeleteShaders()
@@ -762,6 +851,120 @@ void TextureCache::DeleteShaders()
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_depth_to_tex_shader, nullptr);
m_efb_depth_to_tex_shader = VK_NULL_HANDLE;
}
+ if (m_rgb_to_yuyv_shader != VK_NULL_HANDLE)
+ {
+ vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_rgb_to_yuyv_shader, nullptr);
+ m_rgb_to_yuyv_shader = VK_NULL_HANDLE;
+ }
+ if (m_yuyv_to_rgb_shader != VK_NULL_HANDLE)
+ {
+ vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_yuyv_to_rgb_shader, nullptr);
+ m_yuyv_to_rgb_shader = VK_NULL_HANDLE;
+ }
+}
+
+void TextureCache::EncodeYUYVTextureToMemory(void* dst_ptr, u32 dst_width, u32 dst_stride,
+ u32 dst_height, Texture2D* src_texture,
+ const MathUtil::Rectangle<int>& src_rect)
+{
+ StateTracker::GetInstance()->EndRenderPass();
+
+ VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
+ src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ // Borrow framebuffer from EFB2RAM encoder.
+ Texture2D* encoding_texture = m_texture_encoder->GetEncodingTexture();
+ StagingTexture2D* download_texture = m_texture_encoder->GetDownloadTexture();
+ encoding_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+
+ // Use fragment shader to convert RGBA to YUYV.
+ // Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in
+ // the order the guest is expecting and we don't have to swap it at readback time. The width
+ // is halved because we're using an RGBA8 texture, but the YUYV data is two bytes per pixel.
+ u32 output_width = dst_width / 2;
+ UtilityShaderDraw draw(command_buffer, g_object_cache->GetStandardPipelineLayout(),
+ m_texture_encoder->GetEncodingRenderPass(),
+ g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE,
+ m_rgb_to_yuyv_shader);
+ VkRect2D region = {{0, 0}, {output_width, dst_height}};
+ draw.BeginRenderPass(m_texture_encoder->GetEncodingTextureFramebuffer(), region);
+ draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetPointSampler());
+ draw.DrawQuad(0, 0, static_cast<int>(output_width), static_cast<int>(dst_height), src_rect.left,
+ src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(),
+ static_cast<int>(src_texture->GetWidth()),
+ static_cast<int>(src_texture->GetHeight()));
+ draw.EndRenderPass();
+
+ // Render pass transitions to TRANSFER_SRC.
+ encoding_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+
+ // Copy from encoding texture to download buffer.
+ download_texture->CopyFromImage(command_buffer, encoding_texture->GetImage(),
+ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width, dst_height, 0, 0);
+ Util::ExecuteCurrentCommandsAndRestoreState(false, true);
+
+ // Finally, copy to guest memory. This may have a different stride.
+ download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride);
+}
+
+void TextureCache::DecodeYUYVTextureFromMemory(TCacheEntry* dst_texture, const void* src_ptr,
+ u32 src_width, u32 src_stride, u32 src_height)
+{
+ // Copies (and our decoding step) cannot be done inside a render pass.
+ StateTracker::GetInstance()->EndRenderPass();
+
+ // We share the upload buffer with normal textures here, since the XFB buffers aren't very large.
+ u32 upload_size = src_stride * src_height;
+ if (!m_texture_upload_buffer->ReserveMemory(upload_size,
+ g_vulkan_context->GetBufferImageGranularity()))
+ {
+ // Execute the command buffer first.
+ WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer");
+ Util::ExecuteCurrentCommandsAndRestoreState(false);
+ if (!m_texture_upload_buffer->ReserveMemory(upload_size,
+ g_vulkan_context->GetBufferImageGranularity()))
+ PanicAlert("Failed to allocate space in texture upload buffer");
+ }
+
+ // Assume that each source row is not padded.
+ _assert_(src_stride == (src_width * sizeof(u16)));
+ VkDeviceSize image_upload_buffer_offset = m_texture_upload_buffer->GetCurrentOffset();
+ std::memcpy(m_texture_upload_buffer->GetCurrentHostPointer(), src_ptr, upload_size);
+ m_texture_upload_buffer->CommitMemory(upload_size);
+
+ // Copy from the upload buffer to the intermediate texture. We borrow this from the encoder.
+ // The width is specified as half here because we have two pixels packed in each RGBA texel.
+ // In the future this could be skipped by reading the upload buffer as a uniform texel buffer.
+ VkBufferImageCopy image_copy = {
+ image_upload_buffer_offset, // VkDeviceSize bufferOffset
+ 0, // uint32_t bufferRowLength
+ 0, // uint32_t bufferImageHeight
+ {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers imageSubresource
+ {0, 0, 0}, // VkOffset3D imageOffset
+ {src_width / 2, src_height, 1} // VkExtent3D imageExtent
+ };
+ VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
+ Texture2D* intermediate_texture = m_texture_encoder->GetEncodingTexture();
+ intermediate_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+ vkCmdCopyBufferToImage(command_buffer, m_texture_upload_buffer->GetBuffer(),
+ intermediate_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
+ &image_copy);
+ intermediate_texture->TransitionToLayout(command_buffer,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+ dst_texture->GetTexture()->TransitionToLayout(command_buffer,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+
+ // Convert from the YUYV data now in the intermediate texture to RGBA in the destination.
+ UtilityShaderDraw draw(command_buffer, g_object_cache->GetStandardPipelineLayout(),
+ m_texture_encoder->GetEncodingRenderPass(),
+ g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
+ m_yuyv_to_rgb_shader);
+ VkRect2D region = {{0, 0}, {src_width, src_height}};
+ draw.BeginRenderPass(dst_texture->GetFramebuffer(), region);
+ draw.SetViewportAndScissor(0, 0, static_cast<int>(src_width), static_cast<int>(src_height));
+ draw.SetPSSampler(0, intermediate_texture->GetView(), g_object_cache->GetPointSampler());
+ draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4);
+ draw.EndRenderPass();
}
} // namespace Vulkan
diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.h b/Source/Core/VideoBackends/Vulkan/TextureCache.h
index 18c8bd6020..5da9515991 100644
--- a/Source/Core/VideoBackends/Vulkan/TextureCache.h
+++ b/Source/Core/VideoBackends/Vulkan/TextureCache.h
@@ -20,25 +20,10 @@ class TextureEncoder;
class TextureCache : public TextureCacheBase
{
public:
- TextureCache();
- ~TextureCache();
-
- bool Initialize(StateTracker* state_tracker);
-
- bool CompileShaders() override;
- void DeleteShaders() override;
- void ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase* base_unconverted, void* palette,
- TlutFormat format) override;
-
- void CopyEFB(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y,
- u32 memory_stride, PEControl::PixelFormat src_format, const EFBRectangle& src_rect,
- bool is_intensity, bool scale_by_half) override;
-
-private:
struct TCacheEntry : TCacheEntryBase
{
- TCacheEntry(const TCacheEntryConfig& config_, TextureCache* parent,
- std::unique_ptr<Texture2D> texture, VkFramebuffer framebuffer);
+ TCacheEntry(const TCacheEntryConfig& config_, std::unique_ptr<Texture2D> texture,
+ VkFramebuffer framebuffer);
~TCacheEntry();
Texture2D* GetTexture() const { return m_texture.get(); }
@@ -55,20 +40,51 @@ private:
bool Save(const std::string& filename, unsigned int level) override;
private:
- TextureCache* m_parent;
std::unique_ptr<Texture2D> m_texture;
-
- // If we're an EFB copy, framebuffer for drawing into.
VkFramebuffer m_framebuffer;
};
+ TextureCache();
+ ~TextureCache();
+
+ static TextureCache* GetInstance();
+
+ bool Initialize();
+
+ bool CompileShaders() override;
+ void DeleteShaders() override;
+
TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override;
- bool CreateRenderPasses();
+ void ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase* base_unconverted, void* palette,
+ TlutFormat format) override;
+
+ void CopyEFB(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y,
+ u32 memory_stride, PEControl::PixelFormat src_format, const EFBRectangle& src_rect,
+ bool is_intensity, bool scale_by_half) override;
+
+ void CopyRectangleFromTexture(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
+ Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
+
+ // Encodes texture to guest memory in XFB (YUYV) format.
+ void EncodeYUYVTextureToMemory(void* dst_ptr, u32 dst_width, u32 dst_stride, u32 dst_height,
+ Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
+ // Decodes data from guest memory in XFB (YUYV) format to a RGBA format texture on the GPU.
+ void DecodeYUYVTextureFromMemory(TCacheEntry* dst_texture, const void* src_ptr, u32 src_width,
+ u32 src_stride, u32 src_height);
+
+private:
+ bool CreateRenderPasses();
VkRenderPass GetRenderPassForTextureUpdate(const Texture2D* texture) const;
- StateTracker* m_state_tracker = nullptr;
+ // Copies the contents of a texture using vkCmdCopyImage
+ void CopyTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
+ Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
+
+ // Copies (and optionally scales) the contents of a texture using a framgent shader.
+ void ScaleTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
+ Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
VkRenderPass m_initialize_render_pass = VK_NULL_HANDLE;
VkRenderPass m_update_render_pass = VK_NULL_HANDLE;
@@ -82,6 +98,8 @@ private:
VkShaderModule m_copy_shader = VK_NULL_HANDLE;
VkShaderModule m_efb_color_to_tex_shader = VK_NULL_HANDLE;
VkShaderModule m_efb_depth_to_tex_shader = VK_NULL_HANDLE;
+ VkShaderModule m_rgb_to_yuyv_shader = VK_NULL_HANDLE;
+ VkShaderModule m_yuyv_to_rgb_shader = VK_NULL_HANDLE;
};
} // namespace Vulkan
diff --git a/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp b/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp
index 0771d37193..404b30b09c 100644
--- a/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp
+++ b/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp
@@ -73,11 +73,11 @@ bool TextureEncoder::Initialize()
return true;
}
-void TextureEncoder::EncodeTextureToRam(StateTracker* state_tracker, VkImageView src_texture,
- u8* dest_ptr, u32 format, u32 native_width,
- u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
- PEControl::PixelFormat src_format, bool is_intensity,
- int scale_by_half, const EFBRectangle& src_rect)
+void TextureEncoder::EncodeTextureToRam(VkImageView src_texture, u8* dest_ptr, u32 format,
+ u32 native_width, u32 bytes_per_row, u32 num_blocks_y,
+ u32 memory_stride, PEControl::PixelFormat src_format,
+ bool is_intensity, int scale_by_half,
+ const EFBRectangle& src_rect)
{
if (m_texture_encoding_shaders[format] == VK_NULL_HANDLE)
{
@@ -86,7 +86,7 @@ void TextureEncoder::EncodeTextureToRam(StateTracker* state_tracker, VkImageView
}
// Can't do our own draw within a render pass.
- state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetPushConstantPipelineLayout(), m_encoding_render_pass,
@@ -122,8 +122,8 @@ void TextureEncoder::EncodeTextureToRam(StateTracker* state_tracker, VkImageView
// Block until the GPU has finished copying to the staging texture.
g_command_buffer_mgr->ExecuteCommandBuffer(false, true);
- state_tracker->InvalidateDescriptorSets();
- state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->InvalidateDescriptorSets();
+ StateTracker::GetInstance()->SetPendingRebind();
// Copy from staging texture to the final destination, adjusting pitch if necessary.
m_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr, memory_stride);
@@ -197,7 +197,8 @@ bool TextureEncoder::CreateEncodingTexture()
m_encoding_texture = Texture2D::Create(
ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT,
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
- VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
if (!m_encoding_texture)
return false;
diff --git a/Source/Core/VideoBackends/Vulkan/TextureEncoder.h b/Source/Core/VideoBackends/Vulkan/TextureEncoder.h
index daea8b59ff..c3fa68c752 100644
--- a/Source/Core/VideoBackends/Vulkan/TextureEncoder.h
+++ b/Source/Core/VideoBackends/Vulkan/TextureEncoder.h
@@ -15,7 +15,6 @@
namespace Vulkan
{
class StagingTexture2D;
-class StateTracker;
class Texture2D;
class TextureEncoder
@@ -24,15 +23,19 @@ public:
TextureEncoder();
~TextureEncoder();
+ VkRenderPass GetEncodingRenderPass() const { return m_encoding_render_pass; }
+ Texture2D* GetEncodingTexture() const { return m_encoding_texture.get(); }
+ VkFramebuffer GetEncodingTextureFramebuffer() const { return m_encoding_texture_framebuffer; }
+ StagingTexture2D* GetDownloadTexture() const { return m_download_texture.get(); }
bool Initialize();
// Uses an encoding shader to copy src_texture to dest_ptr.
// Assumes that no render pass is currently in progress.
// WARNING: Executes the current command buffer.
- void EncodeTextureToRam(StateTracker* state_tracker, VkImageView src_texture, u8* dest_ptr,
- u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y,
- u32 memory_stride, PEControl::PixelFormat src_format, bool is_intensity,
- int scale_by_half, const EFBRectangle& source);
+ void EncodeTextureToRam(VkImageView src_texture, u8* dest_ptr, u32 format, u32 native_width,
+ u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
+ PEControl::PixelFormat src_format, bool is_intensity, int scale_by_half,
+ const EFBRectangle& source);
private:
// From OGL.
diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp
index 9ef432d779..2e8025dbc1 100644
--- a/Source/Core/VideoBackends/Vulkan/Util.cpp
+++ b/Source/Core/VideoBackends/Vulkan/Util.cpp
@@ -195,13 +195,12 @@ void BufferMemoryBarrier(VkCommandBuffer command_buffer, VkBuffer buffer,
&buffer_info, 0, nullptr);
}
-void ExecuteCurrentCommandsAndRestoreState(StateTracker* state_tracker, bool execute_off_thread,
- bool wait_for_completion)
+void ExecuteCurrentCommandsAndRestoreState(bool execute_off_thread, bool wait_for_completion)
{
- state_tracker->EndRenderPass();
+ StateTracker::GetInstance()->EndRenderPass();
g_command_buffer_mgr->ExecuteCommandBuffer(execute_off_thread, wait_for_completion);
- state_tracker->InvalidateDescriptorSets();
- state_tracker->SetPendingRebind();
+ StateTracker::GetInstance()->InvalidateDescriptorSets();
+ StateTracker::GetInstance()->SetPendingRebind();
}
VkShaderModule CreateShaderModule(const u32* spv, size_t spv_word_count)
diff --git a/Source/Core/VideoBackends/Vulkan/Util.h b/Source/Core/VideoBackends/Vulkan/Util.h
index e9dec51700..6900cce08b 100644
--- a/Source/Core/VideoBackends/Vulkan/Util.h
+++ b/Source/Core/VideoBackends/Vulkan/Util.h
@@ -47,7 +47,7 @@ void BufferMemoryBarrier(VkCommandBuffer command_buffer, VkBuffer buffer,
// Completes the current render pass, executes the command buffer, and restores state ready for next
// render. Use when you want to kick the current buffer to make room for new data.
-void ExecuteCurrentCommandsAndRestoreState(StateTracker* state_tracker, bool execute_off_thread,
+void ExecuteCurrentCommandsAndRestoreState(bool execute_off_thread,
bool wait_for_completion = false);
// Create a shader module from the specified SPIR-V.
diff --git a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp
index 02be335aea..8300fb5fdd 100644
--- a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp
+++ b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp
@@ -36,10 +36,13 @@ VertexManager::~VertexManager()
{
}
-bool VertexManager::Initialize(StateTracker* state_tracker)
+VertexManager* VertexManager::GetInstance()
{
- m_state_tracker = state_tracker;
+ return static_cast<VertexManager*>(g_vertex_manager.get());
+}
+bool VertexManager::Initialize()
+{
m_vertex_stream_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
INITIAL_VERTEX_BUFFER_SIZE, MAX_VERTEX_BUFFER_SIZE);
@@ -72,8 +75,9 @@ void VertexManager::PrepareDrawBuffers(u32 stride)
ADDSTAT(stats.thisFrame.bytesVertexStreamed, static_cast<int>(vertex_data_size));
ADDSTAT(stats.thisFrame.bytesIndexStreamed, static_cast<int>(index_data_size));
- m_state_tracker->SetVertexBuffer(m_vertex_stream_buffer->GetBuffer(), 0);
- m_state_tracker->SetIndexBuffer(m_index_stream_buffer->GetBuffer(), 0, VK_INDEX_TYPE_UINT16);
+ StateTracker::GetInstance()->SetVertexBuffer(m_vertex_stream_buffer->GetBuffer(), 0);
+ StateTracker::GetInstance()->SetIndexBuffer(m_index_stream_buffer->GetBuffer(), 0,
+ VK_INDEX_TYPE_UINT16);
}
void VertexManager::ResetBuffer(u32 stride)
@@ -94,7 +98,7 @@ void VertexManager::ResetBuffer(u32 stride)
{
// Flush any pending commands first, so that we can wait on the fences
WARN_LOG(VIDEO, "Executing command list while waiting for space in vertex/index buffer");
- Util::ExecuteCurrentCommandsAndRestoreState(m_state_tracker, false);
+ Util::ExecuteCurrentCommandsAndRestoreState(false);
// Attempt to allocate again, this may cause a fence wait
if (!has_vbuffer_allocation)
@@ -133,21 +137,21 @@ void VertexManager::vFlush(bool use_dst_alpha)
u32 index_count = IndexGenerator::GetIndexLen();
// Update assembly state
- m_state_tracker->SetVertexFormat(vertex_format);
+ StateTracker::GetInstance()->SetVertexFormat(vertex_format);
switch (m_current_primitive_type)
{
case PRIMITIVE_POINTS:
- m_state_tracker->SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
- m_state_tracker->DisableBackFaceCulling();
+ StateTracker::GetInstance()->SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
+ StateTracker::GetInstance()->DisableBackFaceCulling();
break;
case PRIMITIVE_LINES:
- m_state_tracker->SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_LINE_LIST);
- m_state_tracker->DisableBackFaceCulling();
+ StateTracker::GetInstance()->SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_LINE_LIST);
+ StateTracker::GetInstance()->DisableBackFaceCulling();
break;
case PRIMITIVE_TRIANGLES:
- m_state_tracker->SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
+ StateTracker::GetInstance()->SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
g_renderer->SetGenerationMode();
break;
}
@@ -158,37 +162,34 @@ void VertexManager::vFlush(bool use_dst_alpha)
dstalpha_mode = DSTALPHA_DUAL_SOURCE_BLEND;
// Check for any shader stage changes
- m_state_tracker->CheckForShaderChanges(m_current_primitive_type, dstalpha_mode);
+ StateTracker::GetInstance()->CheckForShaderChanges(m_current_primitive_type, dstalpha_mode);
// Update any changed constants
- m_state_tracker->UpdateVertexShaderConstants();
- m_state_tracker->UpdateGeometryShaderConstants();
- m_state_tracker->UpdatePixelShaderConstants();
+ StateTracker::GetInstance()->UpdateVertexShaderConstants();
+ StateTracker::GetInstance()->UpdateGeometryShaderConstants();
+ StateTracker::GetInstance()->UpdatePixelShaderConstants();
// Flush all EFB pokes and invalidate the peek cache.
- // TODO: Cleaner way without the cast.
- FramebufferManager* framebuffer_mgr =
- static_cast<FramebufferManager*>(g_framebuffer_manager.get());
- framebuffer_mgr->InvalidatePeekCache();
- framebuffer_mgr->FlushEFBPokes(m_state_tracker);
+ FramebufferManager::GetInstance()->InvalidatePeekCache();
+ FramebufferManager::GetInstance()->FlushEFBPokes();
// If bounding box is enabled, we need to flush any changes first, then invalidate what we have.
if (g_vulkan_context->SupportsBoundingBox())
{
- BoundingBox* bounding_box = static_cast<Renderer*>(g_renderer.get())->GetBoundingBox();
+ BoundingBox* bounding_box = Renderer::GetInstance()->GetBoundingBox();
bool bounding_box_enabled = (::BoundingBox::active && g_ActiveConfig.bBBoxEnable);
if (bounding_box_enabled)
{
- bounding_box->Flush(m_state_tracker);
- bounding_box->Invalidate(m_state_tracker);
+ bounding_box->Flush();
+ bounding_box->Invalidate();
}
// Update which descriptor set/pipeline layout to use.
- m_state_tracker->SetBBoxEnable(bounding_box_enabled);
+ StateTracker::GetInstance()->SetBBoxEnable(bounding_box_enabled);
}
// Bind all pending state to the command buffer
- if (!m_state_tracker->Bind())
+ if (!StateTracker::GetInstance()->Bind())
{
WARN_LOG(VIDEO, "Skipped draw of %u indices", index_count);
return;
@@ -207,8 +208,9 @@ void VertexManager::vFlush(bool use_dst_alpha)
bool logic_op_enabled = bpmem.blendmode.logicopenable && !bpmem.blendmode.blendenable;
if (use_dst_alpha && (!g_vulkan_context->SupportsDualSourceBlend() || logic_op_enabled))
{
- m_state_tracker->CheckForShaderChanges(m_current_primitive_type, DSTALPHA_ALPHA_PASS);
- if (!m_state_tracker->Bind())
+ StateTracker::GetInstance()->CheckForShaderChanges(m_current_primitive_type,
+ DSTALPHA_ALPHA_PASS);
+ if (!StateTracker::GetInstance()->Bind())
{
WARN_LOG(VIDEO, "Skipped draw of %u indices (alpha pass)", index_count);
return;
@@ -218,7 +220,7 @@ void VertexManager::vFlush(bool use_dst_alpha)
m_current_draw_base_index, m_current_draw_base_vertex, 0);
}
- m_state_tracker->OnDraw();
+ StateTracker::GetInstance()->OnDraw();
}
} // namespace Vulkan
diff --git a/Source/Core/VideoBackends/Vulkan/VertexManager.h b/Source/Core/VideoBackends/Vulkan/VertexManager.h
index 0de4faae85..b79e671c6c 100644
--- a/Source/Core/VideoBackends/Vulkan/VertexManager.h
+++ b/Source/Core/VideoBackends/Vulkan/VertexManager.h
@@ -12,7 +12,6 @@
namespace Vulkan
{
-class StateTracker;
class StreamBuffer;
class VertexManager : public VertexManagerBase
@@ -21,7 +20,9 @@ public:
VertexManager();
~VertexManager();
- bool Initialize(StateTracker* state_tracker);
+ static VertexManager* GetInstance();
+
+ bool Initialize();
NativeVertexFormat* CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override;
@@ -32,8 +33,6 @@ protected:
private:
void vFlush(bool use_dst_alpha) override;
- StateTracker* m_state_tracker = nullptr;
-
std::vector<u8> m_cpu_vertex_buffer;
std::vector<u16> m_cpu_index_buffer;
diff --git a/Source/Core/VideoBackends/Vulkan/main.cpp b/Source/Core/VideoBackends/Vulkan/main.cpp
index 1fb8f6c6ab..4a70a6e509 100644
--- a/Source/Core/VideoBackends/Vulkan/main.cpp
+++ b/Source/Core/VideoBackends/Vulkan/main.cpp
@@ -194,19 +194,16 @@ bool VideoBackend::Initialize(void* window_handle)
g_framebuffer_manager = std::make_unique<FramebufferManager>();
g_renderer = std::make_unique<Renderer>(std::move(swap_chain));
- // Cast to our wrapper classes, so we can call the init methods.
- Renderer* renderer = static_cast<Renderer*>(g_renderer.get());
- FramebufferManager* framebuffer_mgr =
- static_cast<FramebufferManager*>(g_framebuffer_manager.get());
-
// Invoke init methods on main wrapper classes.
// These have to be done before the others because the destructors
// for the remaining classes may call methods on these.
- if (!g_object_cache->Initialize() || !framebuffer_mgr->Initialize() ||
- !renderer->Initialize(framebuffer_mgr))
+ if (!g_object_cache->Initialize() || !FramebufferManager::GetInstance()->Initialize() ||
+ !StateTracker::CreateInstance() || !Renderer::GetInstance()->Initialize())
{
PanicAlert("Failed to initialize Vulkan classes.");
g_renderer.reset();
+ StateTracker::DestroyInstance();
+ g_framebuffer_manager.reset();
g_object_cache.reset();
g_command_buffer_mgr.reset();
g_vulkan_context.reset();
@@ -219,18 +216,16 @@ bool VideoBackend::Initialize(void* window_handle)
g_vertex_manager = std::make_unique<VertexManager>();
g_texture_cache = std::make_unique<TextureCache>();
g_perf_query = std::make_unique<PerfQuery>();
- VertexManager* vertex_manager = static_cast<VertexManager*>(g_vertex_manager.get());
- TextureCache* texture_cache = static_cast<TextureCache*>(g_texture_cache.get());
- PerfQuery* perf_query = static_cast<PerfQuery*>(g_perf_query.get());
- if (!vertex_manager->Initialize(renderer->GetStateTracker()) ||
- !texture_cache->Initialize(renderer->GetStateTracker()) ||
- !perf_query->Initialize(renderer->GetStateTracker()))
+ if (!VertexManager::GetInstance()->Initialize() || !TextureCache::GetInstance()->Initialize() ||
+ !PerfQuery::GetInstance()->Initialize())
{
PanicAlert("Failed to initialize Vulkan classes.");
g_perf_query.reset();
g_texture_cache.reset();
g_vertex_manager.reset();
g_renderer.reset();
+ StateTracker::DestroyInstance();
+ g_framebuffer_manager.reset();
g_object_cache.reset();
g_command_buffer_mgr.reset();
g_vulkan_context.reset();
@@ -273,11 +268,12 @@ void VideoBackend::Video_Cleanup()
// Save all cached pipelines out to disk for next time.
g_object_cache->SavePipelineCache();
- g_texture_cache.reset();
g_perf_query.reset();
+ g_texture_cache.reset();
g_vertex_manager.reset();
- g_renderer.reset();
g_framebuffer_manager.reset();
+ StateTracker::DestroyInstance();
+ g_renderer.reset();
CleanupShared();
}