mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #4374 from stenzek/vulkan-xfb
Vulkan: Cleanup and implement XFB support
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()) ||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user