Remove old XFB logic

This commit is contained in:
iwubcode 2017-08-06 23:05:42 -05:00
parent 081b92b8a7
commit 33bc286baa
25 changed files with 4 additions and 1727 deletions

View File

@ -38,8 +38,6 @@ set(SRCS
VertexShaderCache.cpp
VertexShaderCache.h
VideoBackend.h
XFBEncoder.cpp
XFBEncoder.h
)
set(LIBS

View File

@ -55,7 +55,6 @@
<ClCompile Include="TextureCache.cpp" />
<ClCompile Include="VertexManager.cpp" />
<ClCompile Include="VertexShaderCache.cpp" />
<ClCompile Include="XFBEncoder.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="BoundingBox.h" />
@ -76,7 +75,6 @@
<ClInclude Include="VertexManager.h" />
<ClInclude Include="VertexShaderCache.h" />
<ClInclude Include="VideoBackend.h" />
<ClInclude Include="XFBEncoder.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CoreDir)VideoCommon\VideoCommon.vcxproj">

View File

@ -57,9 +57,6 @@
<ClCompile Include="VertexShaderCache.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="XFBEncoder.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="main.cpp" />
<ClCompile Include="BoundingBox.cpp">
<Filter>Render</Filter>
@ -114,9 +111,6 @@
<ClInclude Include="VertexShaderCache.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="XFBEncoder.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="VideoBackend.h" />
<ClInclude Include="BoundingBox.h">
<Filter>Render</Filter>

View File

@ -16,12 +16,10 @@
#include "VideoBackends/D3D/PixelShaderCache.h"
#include "VideoBackends/D3D/Render.h"
#include "VideoBackends/D3D/VertexShaderCache.h"
#include "VideoBackends/D3D/XFBEncoder.h"
#include "VideoCommon/VideoConfig.h"
namespace DX11
{
static XFBEncoder s_xfbEncoder;
static bool s_integer_efb_render_target = false;
FramebufferManager::Efb FramebufferManager::m_efb;
@ -282,15 +280,11 @@ FramebufferManager::FramebufferManager(int target_width, int target_height)
m_efb.resolved_color_tex = nullptr;
m_efb.resolved_depth_tex = nullptr;
}
s_xfbEncoder.Init();
s_integer_efb_render_target = false;
}
FramebufferManager::~FramebufferManager()
{
s_xfbEncoder.Shutdown();
SAFE_RELEASE(m_efb.color_tex);
SAFE_RELEASE(m_efb.color_int_rtv);
SAFE_RELEASE(m_efb.color_temp_tex);
@ -304,52 +298,4 @@ FramebufferManager::~FramebufferManager()
SAFE_RELEASE(m_efb.resolved_depth_tex);
}
void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight,
const EFBRectangle& sourceRc, float Gamma)
{
u8* dst = Memory::GetPointer(xfbAddr);
// The destination stride can differ from the copy region width, in which case the pixels
// outside the copy region should not be written to.
s_xfbEncoder.Encode(dst, static_cast<u32>(sourceRc.GetWidth()), fbHeight, sourceRc, Gamma);
}
std::unique_ptr<XFBSourceBase> FramebufferManager::CreateXFBSource(unsigned int target_width,
unsigned int target_height,
unsigned int layers)
{
return std::make_unique<XFBSource>(
D3DTexture2D::Create(target_width, target_height,
(D3D11_BIND_FLAG)(D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE),
D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM, 1, layers),
layers);
}
std::pair<u32, u32> FramebufferManager::GetTargetSize() const
{
return std::make_pair(m_target_width, m_target_height);
}
void XFBSource::CopyEFB(float Gamma)
{
g_renderer->ResetAPIState(); // reset any game specific settings
// Copy EFB data to XFB and restore render target again
const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)texWidth, (float)texHeight);
const D3D11_RECT rect = CD3D11_RECT(0, 0, texWidth, texHeight);
D3D::context->RSSetViewports(1, &vp);
D3D::context->OMSetRenderTargets(1, &tex->GetRTV(), nullptr);
D3D::SetPointCopySampler();
D3D::drawShadedTexQuad(
FramebufferManager::GetEFBColorTexture()->GetSRV(), &rect, g_renderer->GetTargetWidth(),
g_renderer->GetTargetHeight(), PixelShaderCache::GetColorCopyProgram(true),
VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout(),
GeometryShaderCache::GetCopyGeometryShader(), Gamma);
FramebufferManager::BindEFBRenderTarget();
g_renderer->RestoreAPIState();
}
} // namespace DX11

View File

@ -46,16 +46,6 @@ namespace DX11
// There may be multiple XFBs in GameCube RAM. This is the maximum number to
// virtualize.
struct XFBSource : public XFBSourceBase
{
XFBSource(D3DTexture2D* _tex, int slices) : tex(_tex), m_slices(slices) {}
~XFBSource() { tex->Release(); }
void CopyEFB(float Gamma) override;
D3DTexture2D* const tex;
const int m_slices;
};
class FramebufferManager : public FramebufferManagerBase
{
public:
@ -79,14 +69,6 @@ public:
static void BindEFBRenderTarget(bool bind_depth = true);
private:
std::unique_ptr<XFBSourceBase> CreateXFBSource(unsigned int target_width,
unsigned int target_height,
unsigned int layers) override;
std::pair<u32, u32> GetTargetSize() const override;
void CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,
float Gamma) override;
static struct Efb
{
D3DTexture2D* color_tex;

View File

@ -610,11 +610,6 @@ void Renderer::SetBlendingState(const BlendingState& state)
// This function has the final picture. We adjust the aspect ratio here.
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma)
{
if (!m_xfb_written)
{
Core::Callback_VideoCopiedToXFB(false);
}
ResetAPIState();
// Prepare to copy the XFBs to our backbuffer

View File

@ -1,367 +0,0 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "VideoBackends/D3D/XFBEncoder.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DBlob.h"
#include "VideoBackends/D3D/D3DShader.h"
#include "VideoBackends/D3D/D3DState.h"
#include "VideoBackends/D3D/FramebufferManager.h"
#include "VideoBackends/D3D/Render.h"
namespace DX11
{
union XFBEncodeParams
{
struct
{
FLOAT Width; // Width and height of encoded XFB in luma pixels
FLOAT Height;
FLOAT TexLeft; // Normalized tex coordinates of XFB source area in EFB texture
FLOAT TexTop;
FLOAT TexRight;
FLOAT TexBottom;
FLOAT Gamma;
};
// Constant buffers must be a multiple of 16 bytes in size
u8 pad[32]; // Pad to the next multiple of 16
};
static const char XFB_ENCODE_VS[] =
"// dolphin-emu XFB encoder vertex shader\n"
"cbuffer cbParams : register(b0)\n"
"{\n"
"struct\n" // Should match XFBEncodeParams above
"{\n"
"float Width;\n"
"float Height;\n"
"float TexLeft;\n"
"float TexTop;\n"
"float TexRight;\n"
"float TexBottom;\n"
"float Gamma;\n"
"} Params;\n"
"}\n"
"struct Output\n"
"{\n"
"float4 Pos : SV_Position;\n"
"float2 Coord : ENCODECOORD;\n"
"};\n"
"Output main(in float2 Pos : POSITION)\n"
"{\n"
"Output result;\n"
"result.Pos = float4(2*Pos.x-1, -2*Pos.y+1, 0, 1);\n"
"result.Coord = Pos * float2(floor(Params.Width/2), Params.Height);\n"
"return result;\n"
"}\n";
static const char XFB_ENCODE_PS[] =
"// dolphin-emu XFB encoder pixel shader\n"
"cbuffer cbParams : register(b0)\n"
"{\n"
"struct\n" // Should match XFBEncodeParams above
"{\n"
"float Width;\n"
"float Height;\n"
"float TexLeft;\n"
"float TexTop;\n"
"float TexRight;\n"
"float TexBottom;\n"
"float Gamma;\n"
"} Params;\n"
"}\n"
"Texture2DArray EFBTexture : register(t0);\n"
"sampler EFBSampler : register(s0);\n"
// GameCube/Wii uses the BT.601 standard algorithm for converting to YCbCr; see
// <http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion>
"static const float3x4 RGB_TO_YCBCR = float3x4(\n"
"0.257, 0.504, 0.098, 16.0/255.0,\n"
"-0.148, -0.291, 0.439, 128.0/255.0,\n"
"0.439, -0.368, -0.071, 128.0/255.0\n"
");\n"
"float3 SampleEFB(float2 coord)\n"
"{\n"
"float2 texCoord = lerp(float2(Params.TexLeft,Params.TexTop), "
"float2(Params.TexRight,Params.TexBottom), coord / float2(Params.Width,Params.Height));\n"
"return EFBTexture.Sample(EFBSampler, float3(texCoord, 0.0)).rgb;\n"
"}\n"
"void main(out float4 ocol0 : SV_Target, in float4 Pos : SV_Position, in float2 Coord : "
"ENCODECOORD)\n"
"{\n"
// Multiplying X by 2, moves pixel centers from (x+0.5) to (2x+1) instead of (2x+0.5), so
// subtract 0.5 to compensate
"float2 baseCoord = Coord * float2(2,1) - float2(0.5,0);\n"
// FIXME: Shall we apply gamma here, or apply it below to the Y components?
// Be careful if you apply it to Y! The Y components are in the range (16..235) / 255.
"float3 sampleL = pow(abs(SampleEFB(baseCoord+float2(-1,0))), Params.Gamma);\n" // Left
"float3 sampleM = pow(abs(SampleEFB(baseCoord)), Params.Gamma);\n" // Middle
"float3 sampleR = pow(abs(SampleEFB(baseCoord+float2(1,0))), Params.Gamma);\n" // Right
"float3 yuvL = mul(RGB_TO_YCBCR, float4(sampleL,1));\n"
"float3 yuvM = mul(RGB_TO_YCBCR, float4(sampleM,1));\n"
"float3 yuvR = mul(RGB_TO_YCBCR, float4(sampleR,1));\n"
// The Y components correspond to two EFB pixels, while the U and V are
// made from a blend of three EFB pixels.
"float y0 = yuvM.r;\n"
"float y1 = yuvR.r;\n"
"float u0 = 0.25*yuvL.g + 0.5*yuvM.g + 0.25*yuvR.g;\n"
"float v0 = 0.25*yuvL.b + 0.5*yuvM.b + 0.25*yuvR.b;\n"
"ocol0 = float4(y0, u0, y1, v0);\n"
"}\n";
static const D3D11_INPUT_ELEMENT_DESC QUAD_LAYOUT_DESC[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}};
static const struct QuadVertex
{
float posX;
float posY;
} QUAD_VERTS[4] = {{0, 0}, {1, 0}, {0, 1}, {1, 1}};
XFBEncoder::XFBEncoder()
: m_out(nullptr), m_outRTV(nullptr), m_outStage(nullptr), m_encodeParams(nullptr),
m_quad(nullptr), m_vShader(nullptr), m_quadLayout(nullptr), m_pShader(nullptr),
m_xfbEncodeBlendState(nullptr), m_xfbEncodeDepthState(nullptr), m_xfbEncodeRastState(nullptr),
m_efbSampler(nullptr)
{
}
void XFBEncoder::Init()
{
HRESULT hr;
// Create output texture
// The pixel shader can generate one YUYV entry per pixel. One YUYV entry
// is created for every two EFB pixels.
D3D11_TEXTURE2D_DESC t2dd = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, MAX_XFB_WIDTH / 2,
MAX_XFB_HEIGHT, 1, 1, D3D11_BIND_RENDER_TARGET);
hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_out);
CHECK(SUCCEEDED(hr), "create xfb encoder output texture");
D3D::SetDebugObjectName(m_out, "xfb encoder output texture");
// Create output render target view
D3D11_RENDER_TARGET_VIEW_DESC rtvd = CD3D11_RENDER_TARGET_VIEW_DESC(
m_out, D3D11_RTV_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM);
hr = D3D::device->CreateRenderTargetView(m_out, &rtvd, &m_outRTV);
CHECK(SUCCEEDED(hr), "create xfb encoder output texture rtv");
D3D::SetDebugObjectName(m_outRTV, "xfb encoder output rtv");
// Create output staging buffer
t2dd.Usage = D3D11_USAGE_STAGING;
t2dd.BindFlags = 0;
t2dd.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_outStage);
CHECK(SUCCEEDED(hr), "create xfb encoder output staging buffer");
D3D::SetDebugObjectName(m_outStage, "xfb encoder output staging buffer");
// Create constant buffer for uploading params to shaders
D3D11_BUFFER_DESC bd = CD3D11_BUFFER_DESC(sizeof(XFBEncodeParams), D3D11_BIND_CONSTANT_BUFFER);
hr = D3D::device->CreateBuffer(&bd, nullptr, &m_encodeParams);
CHECK(SUCCEEDED(hr), "create xfb encode params buffer");
D3D::SetDebugObjectName(m_encodeParams, "xfb encoder params buffer");
// Create vertex quad
bd = CD3D11_BUFFER_DESC(sizeof(QUAD_VERTS), D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_IMMUTABLE);
D3D11_SUBRESOURCE_DATA srd = {QUAD_VERTS, 0, 0};
hr = D3D::device->CreateBuffer(&bd, &srd, &m_quad);
CHECK(SUCCEEDED(hr), "create xfb encode quad vertex buffer");
D3D::SetDebugObjectName(m_quad, "xfb encoder quad vertex buffer");
// Create vertex shader
D3DBlob* bytecode = nullptr;
if (!D3D::CompileVertexShader(XFB_ENCODE_VS, &bytecode))
{
ERROR_LOG(VIDEO, "XFB encode vertex shader failed to compile");
return;
}
hr = D3D::device->CreateVertexShader(bytecode->Data(), bytecode->Size(), nullptr, &m_vShader);
CHECK(SUCCEEDED(hr), "create xfb encode vertex shader");
D3D::SetDebugObjectName(m_vShader, "xfb encoder vertex shader");
// Create input layout for vertex quad using bytecode from vertex shader
hr = D3D::device->CreateInputLayout(QUAD_LAYOUT_DESC,
sizeof(QUAD_LAYOUT_DESC) / sizeof(D3D11_INPUT_ELEMENT_DESC),
bytecode->Data(), bytecode->Size(), &m_quadLayout);
CHECK(SUCCEEDED(hr), "create xfb encode quad vertex layout");
D3D::SetDebugObjectName(m_quadLayout, "xfb encoder quad layout");
bytecode->Release();
// Create pixel shader
m_pShader = D3D::CompileAndCreatePixelShader(XFB_ENCODE_PS);
if (!m_pShader)
{
ERROR_LOG(VIDEO, "XFB encode pixel shader failed to compile");
return;
}
D3D::SetDebugObjectName(m_pShader, "xfb encoder pixel shader");
// Create blend state
D3D11_BLEND_DESC bld = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
hr = D3D::device->CreateBlendState(&bld, &m_xfbEncodeBlendState);
CHECK(SUCCEEDED(hr), "create xfb encode blend state");
D3D::SetDebugObjectName(m_xfbEncodeBlendState, "xfb encoder blend state");
// Create depth state
D3D11_DEPTH_STENCIL_DESC dsd = CD3D11_DEPTH_STENCIL_DESC(CD3D11_DEFAULT());
dsd.DepthEnable = FALSE;
hr = D3D::device->CreateDepthStencilState(&dsd, &m_xfbEncodeDepthState);
CHECK(SUCCEEDED(hr), "create xfb encode depth state");
D3D::SetDebugObjectName(m_xfbEncodeDepthState, "xfb encoder depth state");
// Create rasterizer state
D3D11_RASTERIZER_DESC rd = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT());
rd.CullMode = D3D11_CULL_NONE;
rd.DepthClipEnable = FALSE;
hr = D3D::device->CreateRasterizerState(&rd, &m_xfbEncodeRastState);
CHECK(SUCCEEDED(hr), "create xfb encode rasterizer state");
D3D::SetDebugObjectName(m_xfbEncodeRastState, "xfb encoder rast state");
// Create EFB texture sampler
D3D11_SAMPLER_DESC sd = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
sd.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
hr = D3D::device->CreateSamplerState(&sd, &m_efbSampler);
CHECK(SUCCEEDED(hr), "create xfb encode texture sampler");
D3D::SetDebugObjectName(m_efbSampler, "xfb encoder texture sampler");
}
void XFBEncoder::Shutdown()
{
SAFE_RELEASE(m_efbSampler);
SAFE_RELEASE(m_xfbEncodeRastState);
SAFE_RELEASE(m_xfbEncodeDepthState);
SAFE_RELEASE(m_xfbEncodeBlendState);
SAFE_RELEASE(m_pShader);
SAFE_RELEASE(m_quadLayout);
SAFE_RELEASE(m_vShader);
SAFE_RELEASE(m_quad);
SAFE_RELEASE(m_encodeParams);
SAFE_RELEASE(m_outStage);
SAFE_RELEASE(m_outRTV);
SAFE_RELEASE(m_out);
}
void XFBEncoder::Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcRect, float gamma)
{
HRESULT hr;
// Reset API
g_renderer->ResetAPIState();
// Set up all the state for XFB encoding
D3D::stateman->SetPixelShader(m_pShader);
D3D::stateman->SetVertexShader(m_vShader);
D3D::stateman->SetGeometryShader(nullptr);
D3D::stateman->PushBlendState(m_xfbEncodeBlendState);
D3D::stateman->PushDepthState(m_xfbEncodeDepthState);
D3D::stateman->PushRasterizerState(m_xfbEncodeRastState);
D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, FLOAT(width / 2), FLOAT(height));
D3D::context->RSSetViewports(1, &vp);
D3D::stateman->SetInputLayout(m_quadLayout);
D3D::stateman->SetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
UINT stride = sizeof(QuadVertex);
UINT offset = 0;
D3D::stateman->SetVertexBuffer(m_quad, stride, offset);
TargetRectangle targetRect = g_renderer->ConvertEFBRectangle(srcRect);
XFBEncodeParams params = {0};
params.Width = FLOAT(width);
params.Height = FLOAT(height);
params.TexLeft = FLOAT(targetRect.left) / g_renderer->GetTargetWidth();
params.TexTop = FLOAT(targetRect.top) / g_renderer->GetTargetHeight();
params.TexRight = FLOAT(targetRect.right) / g_renderer->GetTargetWidth();
params.TexBottom = FLOAT(targetRect.bottom) / g_renderer->GetTargetHeight();
params.Gamma = gamma;
D3D::context->UpdateSubresource(m_encodeParams, 0, nullptr, &params, 0, 0);
D3D::context->OMSetRenderTargets(1, &m_outRTV, nullptr);
ID3D11ShaderResourceView* pEFB = FramebufferManager::GetResolvedEFBColorTexture()->GetSRV();
D3D::stateman->SetVertexConstants(m_encodeParams);
D3D::stateman->SetPixelConstants(m_encodeParams);
D3D::stateman->SetTexture(0, pEFB);
D3D::stateman->SetSampler(0, m_efbSampler);
// Encode!
D3D::stateman->Apply();
D3D::context->Draw(4, 0);
// Copy to staging buffer
D3D11_BOX srcBox = CD3D11_BOX(0, 0, 0, width / 2, height, 1);
D3D::context->CopySubresourceRegion(m_outStage, 0, 0, 0, 0, m_out, 0, &srcBox);
// Clean up state
D3D::context->OMSetRenderTargets(0, nullptr, nullptr);
D3D::stateman->SetSampler(0, nullptr);
D3D::stateman->SetTexture(0, nullptr);
D3D::stateman->SetPixelConstants(nullptr);
D3D::stateman->SetVertexConstants(nullptr);
D3D::stateman->SetPixelShader(nullptr);
D3D::stateman->SetVertexShader(nullptr);
D3D::stateman->PopRasterizerState();
D3D::stateman->PopDepthState();
D3D::stateman->PopBlendState();
// Transfer staging buffer to GameCube/Wii RAM
D3D11_MAPPED_SUBRESOURCE map = {0};
hr = D3D::context->Map(m_outStage, 0, D3D11_MAP_READ, 0, &map);
CHECK(SUCCEEDED(hr), "map staging buffer");
u8* src = (u8*)map.pData;
for (unsigned int y = 0; y < height; ++y)
{
memcpy(dst, src, 2 * width);
dst += bpmem.copyMipMapStrideChannels * 32;
src += map.RowPitch;
}
D3D::context->Unmap(m_outStage, 0);
// Restore API
g_renderer->RestoreAPIState();
D3D::stateman->Apply(); // force unbind efb texture as shader resource
FramebufferManager::BindEFBRenderTarget();
}
}

View File

@ -1,46 +0,0 @@
// Copyright 2011 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "VideoCommon/VideoCommon.h"
struct ID3D11Texture2D;
struct ID3D11RenderTargetView;
struct ID3D11Buffer;
struct ID3D11VertexShader;
struct ID3D11PixelShader;
struct ID3D11InputLayout;
struct ID3D11BlendState;
struct ID3D11DepthStencilState;
struct ID3D11RasterizerState;
struct ID3D11SamplerState;
namespace DX11
{
class XFBEncoder
{
public:
XFBEncoder();
void Init();
void Shutdown();
void Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcRect, float gamma);
private:
ID3D11Texture2D* m_out;
ID3D11RenderTargetView* m_outRTV;
ID3D11Texture2D* m_outStage;
ID3D11Buffer* m_encodeParams;
ID3D11Buffer* m_quad;
ID3D11VertexShader* m_vShader;
ID3D11InputLayout* m_quadLayout;
ID3D11PixelShader* m_pShader;
ID3D11BlendState* m_xfbEncodeBlendState;
ID3D11DepthStencilState* m_xfbEncodeDepthState;
ID3D11RasterizerState* m_xfbEncodeRastState;
ID3D11SamplerState* m_efbSampler;
};
}

View File

@ -1,33 +0,0 @@
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <utility>
#include "Common/CommonTypes.h"
#include "VideoCommon/FramebufferManagerBase.h"
class XFBSource : public XFBSourceBase
{
public:
void CopyEFB(float gamma) override {}
};
class FramebufferManager : public FramebufferManagerBase
{
public:
std::unique_ptr<XFBSourceBase> CreateXFBSource(unsigned int target_width,
unsigned int target_height,
unsigned int layers) override
{
return std::make_unique<XFBSource>();
}
std::pair<u32, u32> GetTargetSize() const override { return std::make_pair(0, 0); }
void CopyToRealXFB(u32 xfb_addr, u32 fb_stride, u32 fb_height, const EFBRectangle& source_rc,
float gamma = 1.0f) override
{
}
};

View File

@ -43,7 +43,6 @@
<ClCompile Include="VertexManager.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="FramebufferManager.h" />
<ClInclude Include="NullTexture.h" />
<ClInclude Include="PerfQuery.h" />
<ClInclude Include="Render.h" />

View File

@ -7,7 +7,6 @@
// This backend tries not to do anything in the backend,
// but everything in VideoCommon.
#include "VideoBackends/Null/FramebufferManager.h"
#include "VideoBackends/Null/PerfQuery.h"
#include "VideoBackends/Null/Render.h"
#include "VideoBackends/Null/ShaderCache.h"
@ -15,6 +14,7 @@
#include "VideoBackends/Null/VertexManager.h"
#include "VideoBackends/Null/VideoBackend.h"
#include "VideoCommon/FramebufferManagerBase.h"
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
@ -68,7 +68,7 @@ void VideoBackend::Video_Prepare()
g_renderer = std::make_unique<Renderer>();
g_vertex_manager = std::make_unique<VertexManager>();
g_perf_query = std::make_unique<PerfQuery>();
g_framebuffer_manager = std::make_unique<FramebufferManager>();
g_framebuffer_manager = std::make_unique<FramebufferManagerBase>();
g_texture_cache = std::make_unique<TextureCache>();
VertexShaderCache::s_instance = std::make_unique<VertexShaderCache>();
GeometryShaderCache::s_instance = std::make_unique<GeometryShaderCache>();

View File

@ -33,7 +33,6 @@ bool FramebufferManager::m_enable_stencil_buffer;
GLenum FramebufferManager::m_textureType;
std::vector<GLuint> FramebufferManager::m_efbFramebuffer;
GLuint FramebufferManager::m_xfbFramebuffer;
GLuint FramebufferManager::m_efbColor;
GLuint FramebufferManager::m_efbDepth;
GLuint FramebufferManager::m_efbColorSwap; // for hot swap when reinterpreting EFB pixel formats
@ -110,7 +109,6 @@ bool FramebufferManager::HasStencilBuffer()
FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int msaaSamples,
bool enable_stencil_buffer)
{
m_xfbFramebuffer = 0;
m_efbColor = 0;
m_efbDepth = 0;
m_efbColorSwap = 0;
@ -189,9 +187,6 @@ FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int ms
CreateTexture(m_textureType, depth_internal_format, depth_pixel_format, depth_data_type);
m_efbColorSwap = CreateTexture(m_textureType, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
// Create XFB framebuffer; targets will be created elsewhere.
glGenFramebuffers(1, &m_xfbFramebuffer);
// Bind target textures to EFB framebuffer.
glGenFramebuffers(m_EFBLayers, m_efbFramebuffer.data());
BindLayeredTexture(m_efbColor, m_efbFramebuffer, GL_COLOR_ATTACHMENT0, m_textureType);
@ -419,9 +414,6 @@ FramebufferManager::~FramebufferManager()
m_efbFramebuffer.clear();
m_resolvedFramebuffer.clear();
glDeleteFramebuffers(1, &m_xfbFramebuffer);
m_xfbFramebuffer = 0;
glObj[0] = m_resolvedColorTexture;
glObj[1] = m_resolvedDepthTexture;
glDeleteTextures(2, glObj);
@ -527,21 +519,6 @@ void FramebufferManager::ResolveEFBStencilTexture()
glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer[0]);
}
void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight,
const EFBRectangle& sourceRc, float Gamma)
{
/* u8* xfb_in_ram = Memory::GetPointer(xfbAddr);
if (!xfb_in_ram)
{
WARN_LOG(VIDEO, "Tried to copy to invalid XFB address");
return;
}
TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc);
TextureConverter::EncodeToRamYUYV(ResolveAndGetRenderTarget(sourceRc), targetRc, xfb_in_ram,
sourceRc.GetWidth(), fbStride, fbHeight);*/
}
GLuint FramebufferManager::GetResolvedFramebuffer()
{
if (m_msaaSamples <= 1)
@ -610,56 +587,6 @@ void FramebufferManager::ReinterpretPixelData(unsigned int convtype)
g_renderer->RestoreAPIState();
}
XFBSource::~XFBSource()
{
glDeleteTextures(1, &texture);
}
void XFBSource::CopyEFB(float Gamma)
{
g_renderer->ResetAPIState();
// Copy EFB data to XFB and restore render target again
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FramebufferManager::GetXFBFramebuffer());
for (int i = 0; i < m_layers; i++)
{
// Bind EFB and texture layer
glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetEFBFramebuffer(i));
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0, i);
glBlitFramebuffer(0, 0, texWidth, texHeight, 0, 0, texWidth, texHeight, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
}
// Return to EFB.
FramebufferManager::SetFramebuffer(0);
g_renderer->RestoreAPIState();
}
std::unique_ptr<XFBSourceBase> FramebufferManager::CreateXFBSource(unsigned int target_width,
unsigned int target_height,
unsigned int layers)
{
GLuint texture;
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, target_width, target_height, layers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
return std::make_unique<XFBSource>(texture, layers);
}
std::pair<u32, u32> FramebufferManager::GetTargetSize() const
{
return std::make_pair(m_targetWidth, m_targetHeight);
}
void FramebufferManager::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points)
{
g_renderer->ResetAPIState();

View File

@ -48,17 +48,6 @@
namespace OGL
{
struct XFBSource : public XFBSourceBase
{
XFBSource(GLuint tex, int layers) : texture(tex), m_layers(layers) {}
~XFBSource();
void CopyEFB(float Gamma) override;
const GLuint texture;
const int m_layers;
};
class FramebufferManager : public FramebufferManagerBase
{
public:
@ -76,7 +65,6 @@ public:
{
return (layer < m_EFBLayers) ? m_efbFramebuffer[layer] : m_efbFramebuffer.back();
}
static GLuint GetXFBFramebuffer() { return m_xfbFramebuffer; }
// Resolved framebuffer is only used in MSAA mode.
static GLuint GetResolvedFramebuffer();
static void SetFramebuffer(GLuint fb);
@ -108,13 +96,6 @@ private:
GLenum data_type);
void BindLayeredTexture(GLuint texture, const std::vector<GLuint>& framebuffers,
GLenum attachment, GLenum texture_type);
std::unique_ptr<XFBSourceBase> CreateXFBSource(unsigned int target_width,
unsigned int target_height,
unsigned int layers) override;
std::pair<u32, u32> GetTargetSize() const override;
void CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,
float Gamma) override;
static int m_targetWidth;
static int m_targetHeight;
@ -122,7 +103,6 @@ private:
static GLenum m_textureType;
static std::vector<GLuint> m_efbFramebuffer;
static GLuint m_xfbFramebuffer;
static GLuint m_efbColor;
static GLuint m_efbDepth;
static GLuint

View File

@ -790,12 +790,7 @@ Renderer::Renderer()
ClearEFBCache();
}
Renderer::~Renderer()
{
FlushFrameDump();
//FinishFrameData();
DestroyFrameDumpResources();
}
Renderer::~Renderer() = default;
void Renderer::Shutdown()
{
@ -1360,27 +1355,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ti
glBindFramebuffer(GL_FRAMEBUFFER, 0);
BlitScreen(sourceRc, flipped_trc, xfb_texture->GetRawTexIdentifier(), xfb_texture->GetConfig().width, xfb_texture->GetConfig().height);
// The FlushFrameDump call here is necessary even after frame dumping is stopped.
// If left out, screenshots are "one frame" behind, as an extra frame is dumped and buffered.
/*FlushFrameDump();
if (IsFrameDumping())
{
// Currently, we only use the off-screen buffer as a frame dump source if full-resolution
// frame dumping is enabled, saving the need for an extra copy. In the future, this could
// be extended to be used for surfaceless contexts as well.
bool use_offscreen_buffer = g_ActiveConfig.bInternalResolutionFrameDumps;
if (use_offscreen_buffer)
{
// DumpFrameUsingFBO resets GL_FRAMEBUFFER, so change back to the window for drawing OSD.
DumpFrameUsingFBO(sourceRc, ticks);
}
else
{
// GL_READ_FRAMEBUFFER is set by GL_FRAMEBUFFER in DrawFrame -> Draw{EFB,VirtualXFB,RealXFB}.
DumpFrame(flipped_trc, ticks);
}
}*/
// Finish up the current frame, print some stats
SetWindowSize(xfb_texture->GetConfig().width, xfb_texture->GetConfig().height);
@ -1509,134 +1483,6 @@ void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc,
BlitScreen(source_rc, target_rc, tex, m_target_width, m_target_height);
}
void Renderer::FlushFrameDump()
{
/*if (!m_last_frame_exported)
return;
FinishFrameData();
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_frame_dumping_pbo[0]);
m_frame_pbo_is_mapped[0] = true;
void* data = glMapBufferRange(
GL_PIXEL_PACK_BUFFER, 0, m_last_frame_width[0] * m_last_frame_height[0] * 4, GL_MAP_READ_BIT);
DumpFrameData(reinterpret_cast<u8*>(data), m_last_frame_width[0], m_last_frame_height[0],
m_last_frame_width[0] * 4, m_last_frame_state, true);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
m_last_frame_exported = false;*/
}
void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
{
if (!m_frame_dumping_pbo[0])
{
glGenBuffers(2, m_frame_dumping_pbo.data());
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_frame_dumping_pbo[0]);
}
else
{
FlushFrameDump();
std::swap(m_frame_dumping_pbo[0], m_frame_dumping_pbo[1]);
std::swap(m_frame_pbo_is_mapped[0], m_frame_pbo_is_mapped[1]);
std::swap(m_last_frame_width[0], m_last_frame_width[1]);
std::swap(m_last_frame_height[0], m_last_frame_height[1]);
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_frame_dumping_pbo[0]);
if (m_frame_pbo_is_mapped[0])
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
m_frame_pbo_is_mapped[0] = false;
}
if (flipped_trc.GetWidth() != m_last_frame_width[0] ||
flipped_trc.GetHeight() != m_last_frame_height[0])
{
m_last_frame_width[0] = flipped_trc.GetWidth();
m_last_frame_height[0] = flipped_trc.GetHeight();
glBufferData(GL_PIXEL_PACK_BUFFER, m_last_frame_width[0] * m_last_frame_height[0] * 4, nullptr,
GL_STREAM_READ);
}
m_last_frame_state = AVIDump::FetchState(ticks);
m_last_frame_exported = true;
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(flipped_trc.left, flipped_trc.bottom, m_last_frame_width[0], m_last_frame_height[0],
GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
void Renderer::DumpFrameUsingFBO(const TargetRectangle& source_rc, u64 ticks)
{
// This needs to be converted to the GL bottom-up window coordinate system.
TargetRectangle render_rc = CalculateFrameDumpDrawRectangle();
std::swap(render_rc.top, render_rc.bottom);
// Ensure the render texture meets the size requirements of the draw area.
u32 render_width = static_cast<u32>(render_rc.GetWidth());
u32 render_height = static_cast<u32>(render_rc.GetHeight());
PrepareFrameDumpRenderTexture(render_width, render_height);
// Ensure the alpha channel of the render texture is blank. The frame dump backend expects
// that the alpha is set to 1.0 for all pixels.
glBindFramebuffer(GL_FRAMEBUFFER, m_frame_dump_render_framebuffer);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Render the frame into the frame dump render texture. Disable alpha writes in case the
// post-processing shader writes a non-1.0 value.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
DrawEFB(m_frame_dump_render_framebuffer, render_rc, source_rc);
// Copy frame to output buffer. This assumes that GL_FRAMEBUFFER has been set.
DumpFrame(render_rc, ticks);
// Restore state after drawing. This isn't the game state, it's the state set by ResetAPIState.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Renderer::PrepareFrameDumpRenderTexture(u32 width, u32 height)
{
// Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used).
// Or, resize texture if it isn't large enough to accommodate the current frame.
if (m_frame_dump_render_texture != 0 && m_frame_dump_render_framebuffer != 0 &&
m_frame_dump_render_texture_width >= width && m_frame_dump_render_texture_height >= height)
{
return;
}
// Recreate texture objects.
if (m_frame_dump_render_texture != 0)
glDeleteTextures(1, &m_frame_dump_render_texture);
if (m_frame_dump_render_framebuffer != 0)
glDeleteFramebuffers(1, &m_frame_dump_render_framebuffer);
glGenTextures(1, &m_frame_dump_render_texture);
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D, m_frame_dump_render_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glGenFramebuffers(1, &m_frame_dump_render_framebuffer);
FramebufferManager::SetFramebuffer(m_frame_dump_render_framebuffer);
FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
m_frame_dump_render_texture, 0);
m_frame_dump_render_texture_width = width;
m_frame_dump_render_texture_height = height;
OGLTexture::SetStage();
}
void Renderer::DestroyFrameDumpResources()
{
if (m_frame_dump_render_framebuffer)
glDeleteFramebuffers(1, &m_frame_dump_render_framebuffer);
if (m_frame_dump_render_texture)
glDeleteTextures(1, &m_frame_dump_render_texture);
if (m_frame_dumping_pbo[0])
glDeleteBuffers(2, m_frame_dumping_pbo.data());
}
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
void Renderer::ResetAPIState()
{

View File

@ -4,7 +4,6 @@
#pragma once
#include <array>
#include <string>
#include "Common/GL/GLUtil.h"
@ -116,25 +115,5 @@ private:
void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width,
int src_height);
void FlushFrameDump();
void DumpFrame(const TargetRectangle& flipped_trc, u64 ticks);
void DumpFrameUsingFBO(const TargetRectangle& source_rc, u64 ticks);
// Frame dumping framebuffer, we render to this, then read it back
void PrepareFrameDumpRenderTexture(u32 width, u32 height);
void DestroyFrameDumpResources();
GLuint m_frame_dump_render_texture = 0;
GLuint m_frame_dump_render_framebuffer = 0;
u32 m_frame_dump_render_texture_width = 0;
u32 m_frame_dump_render_texture_height = 0;
// avi dumping state to delay one frame
std::array<u32, 2> m_frame_dumping_pbo = {};
std::array<bool, 2> m_frame_pbo_is_mapped = {};
std::array<int, 2> m_last_frame_width = {};
std::array<int, 2> m_last_frame_height = {};
bool m_last_frame_exported = false;
AVIDump::Frame m_last_frame_state;
};
}

View File

@ -41,11 +41,6 @@ static GLuint s_dstTexture = 0; // for encoding to RAM
const int renderBufferWidth = EFB_WIDTH * 4;
const int renderBufferHeight = 1024;
static SHADER s_rgbToYuyvProgram;
static int s_rgbToYuyvUniform_loc;
static SHADER s_yuyvToRgbProgram;
struct EncodingProgram
{
SHADER program;
@ -56,87 +51,6 @@ static std::map<EFBCopyParams, EncodingProgram> s_encoding_programs;
static GLuint s_PBO = 0; // for readback with different strides
static void CreatePrograms()
{
/* TODO: Accuracy Improvements
*
* This shader doesn't really match what the GameCube does internally in the
* copy pipeline.
* 1. It uses OpenGL's built in filtering when yscaling, someone could work
* out how the copypipeline does it's filtering and implement it correctly
* in this shader.
* 2. Deflickering isn't implemented, a futher filtering over 3 lines.
* Isn't really needed on non-interlaced monitors (and would lower quality;
* But hey, accuracy!)
* 3. Flipper's YUYV conversion implements a 3 pixel horizontal blur on the
* UV channels, centering the U channel on the Left pixel and the V channel
* on the Right pixel.
* The current implementation Centers both UV channels at the same place
* inbetween the two Pixels, and only blurs over these two pixels.
*/
// Output is BGRA because that is slightly faster than RGBA.
const char* VProgramRgbToYuyv =
"out vec2 uv0;\n"
"uniform vec4 copy_position;\n" // left, top, right, bottom
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"void main()\n"
"{\n"
" vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
" gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
" uv0 = mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy);\n"
"}\n";
const char* FProgramRgbToYuyv =
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"in vec2 uv0;\n"
"out vec4 ocol0;\n"
"void main()\n"
"{\n"
" vec3 c0 = texture(samp9, vec3(uv0 - dFdx(uv0) * 0.25, 0.0)).rgb;\n"
" vec3 c1 = texture(samp9, vec3(uv0 + dFdx(uv0) * 0.25, 0.0)).rgb;\n"
" vec3 c01 = (c0 + c1) * 0.5;\n"
" vec3 y_const = vec3(0.257,0.504,0.098);\n"
" vec3 u_const = vec3(-0.148,-0.291,0.439);\n"
" vec3 v_const = vec3(0.439,-0.368,-0.071);\n"
" vec4 const3 = vec4(0.0625,0.5,0.0625,0.5);\n"
" ocol0 = vec4(dot(c1,y_const),dot(c01,u_const),dot(c0,y_const),dot(c01, v_const)) + "
"const3;\n"
"}\n";
ProgramShaderCache::CompileShader(s_rgbToYuyvProgram, VProgramRgbToYuyv, FProgramRgbToYuyv);
s_rgbToYuyvUniform_loc = glGetUniformLocation(s_rgbToYuyvProgram.glprogid, "copy_position");
/* TODO: Accuracy Improvements
*
* The YVYU to RGB conversion here matches the RGB to YUYV done above, but
* if a game modifies or adds images to the XFB then it should be using the
* same algorithm as the flipper, and could result in slight color inaccuracies
* when run back through this shader.
*/
const char* VProgramYuyvToRgb = "void main()\n"
"{\n"
" vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
" gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
"}\n";
const char* FProgramYuyvToRgb = "SAMPLER_BINDING(9) uniform sampler2D samp9;\n"
"in vec2 uv0;\n"
"out vec4 ocol0;\n"
"void main()\n"
"{\n"
" ivec2 uv = ivec2(gl_FragCoord.xy);\n"
// We switch top/bottom here. TODO: move this to screen blit.
" ivec2 ts = textureSize(samp9, 0);\n"
" vec4 c0 = texelFetch(samp9, ivec2(uv.x>>1, ts.y-uv.y-1), 0);\n"
" float y = mix(c0.r, c0.b, (uv.x & 1) == 1);\n"
" float yComp = 1.164 * (y - 0.0625);\n"
" float uComp = c0.g - 0.5;\n"
" float vComp = c0.a - 0.5;\n"
" ocol0 = vec4(yComp + (1.596 * vComp),\n"
" yComp - (0.813 * vComp) - (0.391 * uComp),\n"
" yComp + (2.018 * uComp),\n"
" 1.0);\n"
"}\n";
ProgramShaderCache::CompileShader(s_yuyvToRgbProgram, VProgramYuyvToRgb, FProgramYuyvToRgb);
}
static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params)
{
auto iter = s_encoding_programs.find(params);
@ -191,8 +105,6 @@ void Init()
FramebufferManager::SetFramebuffer(0);
glGenBuffers(1, &s_PBO);
CreatePrograms();
}
void Shutdown()
@ -202,9 +114,6 @@ void Shutdown()
glDeleteBuffers(1, &s_PBO);
glDeleteFramebuffers(2, s_texConvFrameBuffer);
s_rgbToYuyvProgram.Destroy();
s_yuyvToRgbProgram.Destroy();
for (auto& program : s_encoding_programs)
program.second.program.Destroy();
s_encoding_programs.clear();
@ -297,64 +206,6 @@ void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 nativ
g_renderer->RestoreAPIState();
}
void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, u8* destAddr, u32 dstWidth,
u32 dstStride, u32 dstHeight)
{
g_renderer->ResetAPIState();
s_rgbToYuyvProgram.Bind();
glUniform4f(s_rgbToYuyvUniform_loc, static_cast<float>(sourceRc.left),
static_cast<float>(sourceRc.top), static_cast<float>(sourceRc.right),
static_cast<float>(sourceRc.bottom));
// We enable linear filtering, because the GameCube does filtering in the vertical direction when
// yscale is enabled.
// Otherwise we get jaggies when a game uses yscaling (most PAL games)
EncodeToRamUsingShader(srcTexture, destAddr, dstWidth * 2, dstHeight, dstStride, true, 1.0f);
FramebufferManager::SetFramebuffer(0);
OGLTexture::DisableStage(0);
g_renderer->RestoreAPIState();
}
// Should be scale free.
void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture)
{
u8* srcAddr = Memory::GetPointer(xfbAddr);
if (!srcAddr)
{
WARN_LOG(VIDEO, "Tried to decode from invalid memory address");
return;
}
g_renderer->ResetAPIState(); // reset any game specific settings
OpenGL_BindAttributelessVAO();
// switch to texture converter frame buffer
// attach destTexture as color destination
FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[1]);
FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_ARRAY,
destTexture, 0);
// activate source texture
// set srcAddr as data for source texture
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D, s_srcTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcWidth / 2, srcHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
srcAddr);
g_sampler_cache->BindNearestSampler(9);
glViewport(0, 0, srcWidth, srcHeight);
s_yuyvToRgbProgram.Bind();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
FramebufferManager::SetFramebuffer(0);
g_renderer->RestoreAPIState();
}
} // namespace
} // namespace OGL

View File

@ -20,11 +20,6 @@ namespace TextureConverter
void Init();
void Shutdown();
void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, u8* destAddr, u32 dstWidth,
u32 dstStride, u32 dstHeight);
void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture);
// returns size of the encoded data (in bytes)
void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 native_width,
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,

View File

@ -1377,93 +1377,4 @@ void FramebufferManager::DestroyPokeShaders()
}
}
std::unique_ptr<XFBSourceBase> FramebufferManager::CreateXFBSource(unsigned int target_width,
unsigned int target_height,
unsigned int layers)
{
TextureConfig config;
config.width = target_width;
config.height = target_height;
config.layers = layers;
config.rendertarget = true;
auto texture = TextureCache::GetInstance()->CreateTexture(config);
if (!texture)
{
PanicAlert("Failed to create texture for XFB source");
return nullptr;
}
return std::make_unique<XFBSource>(std::move(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);
// The destination stride can differ from the copy region width, in which case the pixels
// outside the copy region should not be written to.
TextureCache::GetInstance()->GetTextureConverter()->EncodeTextureToMemoryYUYV(
xfb_ptr, static_cast<u32>(source_rc.GetWidth()), 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<AbstractTexture> texture)
: XFBSourceBase(), m_texture(std::move(texture))
{
}
XFBSource::~XFBSource()
{
}
VKTexture* XFBSource::GetTexture() const
{
return static_cast<VKTexture*>(m_texture.get());
}
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);
static_cast<VKTexture*>(m_texture.get())->CopyRectangleFromTexture(src_texture, rect, 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

View File

@ -44,14 +44,6 @@ public:
VkSampleCountFlagBits GetEFBSamples() const;
MultisamplingState GetEFBMultisamplingState() const;
std::unique_ptr<XFBSourceBase> CreateXFBSource(unsigned int target_width,
unsigned int target_height,
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;
void ResizeEFBTextures();
// Recompile shaders, use when MSAA mode changes.
@ -168,21 +160,4 @@ 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<AbstractTexture> texture);
~XFBSource();
VKTexture* GetTexture() const;
// Used for virtual XFB
void CopyEFB(float gamma) override;
private:
std::unique_ptr<AbstractTexture> m_texture;
};
} // namespace Vulkan

View File

@ -61,11 +61,6 @@ Renderer::~Renderer()
{
UpdateActiveConfig();
// Ensure all frames are written to frame dump at shutdown.
if (m_frame_dumping_active)
EndFrameDumping();
DestroyFrameDumpResources();
DestroyShaders();
DestroySemaphores();
}
@ -491,12 +486,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ti
// Pending/batched EFB pokes should be included in the final image.
FramebufferManager::GetInstance()->FlushEFBPokes();
// Check that we actually have an image to render in XFB-on modes.
if (!m_xfb_written)
{
Core::Callback_VideoCopiedToXFB(false);
}
auto* xfb_texture = static_cast<VKTexture*>(texture);
// End the current render pass.
@ -563,90 +552,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ti
g_shader_cache->RetrieveAsyncShaders();
}
void Renderer::TransitionBuffersForSwap(const TargetRectangle& scaled_rect,
const XFBSourceBase* const* xfb_sources, u32 xfb_count)
{
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
// Drawing XFB sources, so transition all of them.
// Don't need the EFB, so leave it as-is.
for (u32 i = 0; i < xfb_count; i++)
{
const XFBSource* xfb_source = static_cast<const XFBSource*>(xfb_sources[i]);
xfb_source->GetTexture()->GetRawTexIdentifier()->TransitionToLayout(
command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
}
void Renderer::DrawFrame(VkRenderPass render_pass, const TargetRectangle& target_rect,
const TargetRectangle& scaled_efb_rect, u32 xfb_addr,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height)
{
}
void Renderer::DrawEFB(VkRenderPass render_pass, const TargetRectangle& target_rect,
const TargetRectangle& scaled_efb_rect)
{
// Transition the EFB render target to a shader resource.
Texture2D* efb_color_texture =
g_ActiveConfig.iMultisamples > 1 ?
FramebufferManager::GetInstance()->GetResolvedEFBColorTexture() :
FramebufferManager::GetInstance()->GetEFBColorTexture();
// Copy EFB -> backbuffer
BlitScreen(render_pass, target_rect, scaled_efb_rect, efb_color_texture);
}
void Renderer::DrawVirtualXFB(VkRenderPass render_pass, const TargetRectangle& target_rect,
u32 xfb_addr, const XFBSourceBase* const* xfb_sources, u32 xfb_count,
u32 fb_width, u32 fb_stride, u32 fb_height)
{
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()->GetRawTexIdentifier());
}
}
void Renderer::DrawRealXFB(VkRenderPass render_pass, const TargetRectangle& target_rect,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height)
{
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()->GetRawTexIdentifier());
}
}
void Renderer::DrawScreen(VKTexture* xfb_texture)
{
VkResult res;
@ -718,173 +623,6 @@ void Renderer::DrawScreen(VKTexture* xfb_texture)
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
bool Renderer::DrawFrameDump(const TargetRectangle& scaled_efb_rect, u32 xfb_addr,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height, u64 ticks)
{
TargetRectangle target_rect = CalculateFrameDumpDrawRectangle();
u32 width = std::max(1u, static_cast<u32>(target_rect.GetWidth()));
u32 height = std::max(1u, static_cast<u32>(target_rect.GetHeight()));
if (!ResizeFrameDumpBuffer(width, height))
return false;
// If there was a previous frame dumped, we'll still be in TRANSFER_SRC layout.
m_frame_dump_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
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,
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);
DrawFrame(FramebufferManager::GetInstance()->GetColorCopyForReadbackRenderPass(), target_rect,
scaled_efb_rect, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
vkCmdEndRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer());
// Prepare the readback texture for copying.
StagingTexture2D* readback_texture = PrepareFrameDumpImage(width, height, ticks);
if (!readback_texture)
return false;
// Queue a copy to the current frame dump buffer. It will be written to the frame dump later.
m_frame_dump_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
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);
return true;
}
void Renderer::StartFrameDumping()
{
_assert_(!m_frame_dumping_active);
// Register fence callback so that we know when frames are ready to be written to the dump.
// This is done by clearing the fence pointer, so WriteFrameDumpFrame doesn't have to wait.
auto queued_callback = [](VkCommandBuffer, VkFence) {};
auto signaled_callback = std::bind(&Renderer::OnFrameDumpImageReady, this, std::placeholders::_1);
// We use the array pointer as a key here, that way if Renderer needed fence callbacks in
// the future it could be used without conflicting.
// We're not interested in when fences are submitted, so the first callback is a no-op.
g_command_buffer_mgr->AddFencePointCallback(
m_frame_dump_images.data(), std::move(queued_callback), std::move(signaled_callback));
m_frame_dumping_active = true;
}
void Renderer::EndFrameDumping()
{
_assert_(m_frame_dumping_active);
// Write any pending frames to the frame dump.
FlushFrameDump();
// Remove the fence callback that we registered earlier, one less function that needs to be
// called when preparing a command buffer.
g_command_buffer_mgr->RemoveFencePointCallback(m_frame_dump_images.data());
m_frame_dumping_active = false;
}
void Renderer::OnFrameDumpImageReady(VkFence fence)
{
for (FrameDumpImage& frame : m_frame_dump_images)
{
// fence being a null handle means that we don't have to wait to re-use this image.
if (frame.fence == fence)
frame.fence = VK_NULL_HANDLE;
}
}
void Renderer::WriteFrameDumpImage(size_t index)
{
/*FrameDumpImage& frame = m_frame_dump_images[index];
_assert_(frame.pending);
// Check fence has been signaled.
// The callback here should set fence to null.
if (frame.fence != VK_NULL_HANDLE)
{
g_command_buffer_mgr->WaitForFence(frame.fence);
_assert_(frame.fence == VK_NULL_HANDLE);
}
// Copy the now-populated image data to the output file.
DumpFrameData(reinterpret_cast<const u8*>(frame.readback_texture->GetMapPointer()),
static_cast<int>(frame.readback_texture->GetWidth()),
static_cast<int>(frame.readback_texture->GetHeight()),
static_cast<int>(frame.readback_texture->GetRowStride()), frame.dump_state);
frame.pending = false;*/
}
StagingTexture2D* Renderer::PrepareFrameDumpImage(u32 width, u32 height, u64 ticks)
{
// Ensure the last frame that was sent to the frame dump has completed encoding before we send
// the next image to it.
//FinishFrameData();
// If the last image hasn't been written to the frame dump yet, write it now.
// This is necessary so that the worker thread is no more than one frame behind, and the pointer
// (which is actually the buffer) is safe for us to re-use next time.
if (m_frame_dump_images[m_current_frame_dump_image].pending)
WriteFrameDumpImage(m_current_frame_dump_image);
// Move to the next image buffer
m_current_frame_dump_image = (m_current_frame_dump_image + 1) % FRAME_DUMP_BUFFERED_FRAMES;
FrameDumpImage& image = m_frame_dump_images[m_current_frame_dump_image];
// Ensure the dimensions of the readback texture are sufficient.
if (!image.readback_texture || width != image.readback_texture->GetWidth() ||
height != image.readback_texture->GetHeight())
{
// Allocate a new readback texture.
// The reset() call is here so that the memory is released before allocating the new texture.
image.readback_texture.reset();
image.readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, width, height,
EFB_COLOR_TEXTURE_FORMAT);
if (!image.readback_texture || !image.readback_texture->Map())
{
// Not actually fatal, just means we can't dump this frame.
PanicAlert("Failed to allocate frame dump readback texture.");
image.readback_texture.reset();
return nullptr;
}
}
// The copy happens immediately after this function returns, so flag this frame as pending.
image.fence = g_command_buffer_mgr->GetCurrentCommandBufferFence();
image.dump_state = AVIDump::FetchState(ticks);
image.pending = true;
return image.readback_texture.get();
}
void Renderer::FlushFrameDump()
{
// We must write frames in order, so this is why we use a counter rather than a range.
for (size_t i = 0; i < FRAME_DUMP_BUFFERED_FRAMES; i++)
{
if (m_frame_dump_images[m_current_frame_dump_image].pending)
WriteFrameDumpImage(m_current_frame_dump_image);
m_current_frame_dump_image = (m_current_frame_dump_image + 1) % FRAME_DUMP_BUFFERED_FRAMES;
}
// Since everything has been written now, may as well start at index zero.
// count-1 here because the index is incremented before usage.
m_current_frame_dump_image = FRAME_DUMP_BUFFERED_FRAMES - 1;
}
void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect,
const TargetRectangle& src_rect, const Texture2D* src_tex)
{
@ -908,100 +646,6 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r
}
}
bool Renderer::ResizeFrameDumpBuffer(u32 new_width, u32 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;
}
// Ensure all previous frames have been dumped, since we are destroying a framebuffer
// that may still be in use.
FlushFrameDump();
if (m_frame_dump_framebuffer != VK_NULL_HANDLE)
{
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_frame_dump_framebuffer, nullptr);
m_frame_dump_framebuffer = VK_NULL_HANDLE;
}
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);
if (!m_frame_dump_render_texture)
{
WARN_LOG(VIDEO, "Failed to resize frame dump render texture");
m_frame_dump_render_texture.reset();
return false;
}
VkImageView attachment = m_frame_dump_render_texture->GetView();
VkFramebufferCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
info.renderPass = FramebufferManager::GetInstance()->GetColorCopyForReadbackRenderPass();
info.attachmentCount = 1;
info.pAttachments = &attachment;
info.width = new_width;
info.height = new_height;
info.layers = 1;
VkResult res =
vkCreateFramebuffer(g_vulkan_context->GetDevice(), &info, nullptr, &m_frame_dump_framebuffer);
if (res != VK_SUCCESS)
{
WARN_LOG(VIDEO, "Failed to create frame dump framebuffer");
m_frame_dump_render_texture.reset();
return false;
}
// Render pass expects texture is in transfer src to start with.
m_frame_dump_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
return true;
}
void Renderer::DestroyFrameDumpResources()
{
if (m_frame_dump_framebuffer != VK_NULL_HANDLE)
{
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_frame_dump_framebuffer, nullptr);
m_frame_dump_framebuffer = VK_NULL_HANDLE;
}
m_frame_dump_render_texture.reset();
for (FrameDumpImage& image : m_frame_dump_images)
{
image.readback_texture.reset();
image.fence = VK_NULL_HANDLE;
image.dump_state = {};
image.pending = false;
}
m_current_frame_dump_image = FRAME_DUMP_BUFFERED_FRAMES - 1;
}
void Renderer::CheckForTargetResize(u32 fb_width, u32 fb_stride, u32 fb_height)
{
/*if (FramebufferManagerBase::LastXfbWidth() == fb_stride &&
FramebufferManagerBase::LastXfbHeight() == fb_height)
{
return;
}
u32 new_width = (fb_stride < 1 || fb_stride > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fb_stride;
u32 new_height = (fb_height < 1 || fb_height > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fb_height;
FramebufferManagerBase::SetLastXfbWidth(new_width);
FramebufferManagerBase::SetLastXfbHeight(new_height);
// Changing the XFB source area may alter the target size.
if (CalculateTargetSize())
ResizeEFBTextures();*/
}
void Renderer::CheckForSurfaceChange()
{
if (!m_surface_needs_change.IsSet())

View File

@ -72,7 +72,6 @@ private:
void BeginFrame();
void CheckForTargetResize(u32 fb_width, u32 fb_stride, u32 fb_height);
void CheckForSurfaceChange();
void CheckForConfigChanges();
@ -86,61 +85,13 @@ private:
bool CompileShaders();
void DestroyShaders();
// Transitions EFB/XFB buffers to SHADER_READ_ONLY, ready for presenting/dumping.
// If MSAA is enabled, and XFB is disabled, also resolves the EFB buffer.
void TransitionBuffersForSwap(const TargetRectangle& scaled_rect,
const XFBSourceBase* const* xfb_sources, u32 xfb_count);
// Draw either the EFB, or specified XFB sources to the currently-bound framebuffer.
void DrawFrame(VkRenderPass render_pass, const TargetRectangle& target_rect,
const TargetRectangle& scaled_efb_rect, 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 TargetRectangle& target_rect,
const TargetRectangle& scaled_efb_rect);
void DrawVirtualXFB(VkRenderPass render_pass, const TargetRectangle& target_rect, 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 TargetRectangle& target_rect,
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(VKTexture* xfb_texture);
// Draw the frame only to the screenshot buffer.
bool DrawFrameDump(const TargetRectangle& scaled_efb_rect, u32 xfb_addr,
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height, u64 ticks);
// Sets up renderer state to permit framedumping.
// Ideally we would have EndFrameDumping be a virtual method of Renderer, but due to various
// design issues it would have to end up being called in the destructor, which won't work.
void StartFrameDumping();
void EndFrameDumping();
// Fence callback so that we know when frames are ready to be written to the dump.
// This is done by clearing the fence pointer, so WriteFrameDumpFrame doesn't have to wait.
void OnFrameDumpImageReady(VkFence fence);
// Writes the specified buffered frame to the frame dump.
// NOTE: Assumes that frame.ticks and frame.pending are valid.
void WriteFrameDumpImage(size_t index);
// If there is a pending frame in this buffer, writes it to the frame dump.
// Ensures that the specified readback buffer meets the size requirements of the current frame.
StagingTexture2D* PrepareFrameDumpImage(u32 width, u32 height, u64 ticks);
// Ensures all buffered frames are written to frame dump.
void FlushFrameDump();
// 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 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;
@ -153,22 +104,5 @@ private:
// Shaders used for clear/blit.
VkShaderModule m_clear_fragment_shader = VK_NULL_HANDLE;
// Texture used for screenshot/frame dumping
std::unique_ptr<Texture2D> m_frame_dump_render_texture;
VkFramebuffer m_frame_dump_framebuffer = VK_NULL_HANDLE;
// Readback resources for frame dumping
static const size_t FRAME_DUMP_BUFFERED_FRAMES = 2;
struct FrameDumpImage
{
std::unique_ptr<StagingTexture2D> readback_texture;
VkFence fence = VK_NULL_HANDLE;
AVIDump::Frame dump_state = {};
bool pending = false;
};
std::array<FrameDumpImage, FRAME_DUMP_BUFFERED_FRAMES> m_frame_dump_images;
size_t m_current_frame_dump_image = FRAME_DUMP_BUFFERED_FRAMES - 1;
bool m_frame_dumping_active = false;
};
}

View File

@ -14,145 +14,7 @@
std::unique_ptr<FramebufferManagerBase> g_framebuffer_manager;
std::unique_ptr<XFBSourceBase>
FramebufferManagerBase::m_realXFBSource; // Only used in Real XFB mode
FramebufferManagerBase::VirtualXFBListType
FramebufferManagerBase::m_virtualXFBList; // Only used in Virtual XFB mode
std::array<const XFBSourceBase*, FramebufferManagerBase::MAX_VIRTUAL_XFB>
FramebufferManagerBase::m_overlappingXFBArray;
unsigned int FramebufferManagerBase::m_EFBLayers = 1;
FramebufferManagerBase::FramebufferManagerBase()
{
// Can't hurt
m_overlappingXFBArray.fill(nullptr);
}
FramebufferManagerBase::~FramebufferManagerBase() = default;
FramebufferManagerBase::~FramebufferManagerBase()
{
// Necessary, as these are static members
// (they really shouldn't be and should be refactored at some point).
m_virtualXFBList.clear();
m_realXFBSource.reset();
}
void FramebufferManagerBase::CopyToXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight,
const EFBRectangle& sourceRc, float Gamma)
{
}
void FramebufferManagerBase::CopyToVirtualXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight,
const EFBRectangle& sourceRc, float Gamma)
{
if (!g_framebuffer_manager)
return;
VirtualXFBListType::iterator vxfb = FindVirtualXFB(xfbAddr, sourceRc.GetWidth(), fbHeight);
if (m_virtualXFBList.end() == vxfb)
{
if (m_virtualXFBList.size() < MAX_VIRTUAL_XFB)
{
// create a new Virtual XFB and place it at the front of the list
m_virtualXFBList.emplace_front();
vxfb = m_virtualXFBList.begin();
}
else
{
// Replace the last virtual XFB
--vxfb;
}
}
// else // replace existing virtual XFB
// move this Virtual XFB to the front of the list.
if (m_virtualXFBList.begin() != vxfb)
m_virtualXFBList.splice(m_virtualXFBList.begin(), m_virtualXFBList, vxfb);
u32 target_width, target_height;
std::tie(target_width, target_height) = g_framebuffer_manager->GetTargetSize();
// recreate if needed
if (vxfb->xfbSource &&
(vxfb->xfbSource->texWidth != target_width || vxfb->xfbSource->texHeight != target_height))
vxfb->xfbSource.reset();
if (!vxfb->xfbSource)
{
vxfb->xfbSource =
g_framebuffer_manager->CreateXFBSource(target_width, target_height, m_EFBLayers);
if (!vxfb->xfbSource)
return;
vxfb->xfbSource->texWidth = target_width;
vxfb->xfbSource->texHeight = target_height;
}
vxfb->xfbSource->srcAddr = vxfb->xfbAddr = xfbAddr;
vxfb->xfbSource->srcWidth = vxfb->xfbWidth = sourceRc.GetWidth();
vxfb->xfbSource->srcHeight = vxfb->xfbHeight = fbHeight;
vxfb->xfbSource->sourceRc = g_renderer->ConvertEFBRectangle(sourceRc);
// keep stale XFB data from being used
ReplaceVirtualXFB();
// Copy EFB data to XFB and restore render target again
vxfb->xfbSource->CopyEFB(Gamma);
}
FramebufferManagerBase::VirtualXFBListType::iterator
FramebufferManagerBase::FindVirtualXFB(u32 xfbAddr, u32 width, u32 height)
{
const u32 srcLower = xfbAddr;
const u32 srcUpper = xfbAddr + 2 * width * height;
return std::find_if(m_virtualXFBList.begin(), m_virtualXFBList.end(),
[srcLower, srcUpper](const VirtualXFB& xfb) {
const u32 dstLower = xfb.xfbAddr;
const u32 dstUpper = xfb.xfbAddr + 2 * xfb.xfbWidth * xfb.xfbHeight;
return dstLower >= srcLower && dstUpper <= srcUpper;
});
}
void FramebufferManagerBase::ReplaceVirtualXFB()
{
VirtualXFBListType::iterator it = m_virtualXFBList.begin();
const s32 srcLower = it->xfbAddr;
const s32 srcUpper = it->xfbAddr + 2 * it->xfbWidth * it->xfbHeight;
const s32 lineSize = 2 * it->xfbWidth;
++it;
for (; it != m_virtualXFBList.end(); ++it)
{
s32 dstLower = it->xfbAddr;
s32 dstUpper = it->xfbAddr + 2 * it->xfbWidth * it->xfbHeight;
if (dstLower >= srcLower && dstUpper <= srcUpper)
{
// Invalidate the data
it->xfbAddr = 0;
it->xfbHeight = 0;
it->xfbWidth = 0;
}
else if (AddressRangesOverlap(srcLower, srcUpper, dstLower, dstUpper))
{
s32 upperOverlap = (srcUpper - dstLower) / lineSize;
s32 lowerOverlap = (dstUpper - srcLower) / lineSize;
if (upperOverlap > 0 && lowerOverlap < 0)
{
it->xfbAddr += lineSize * upperOverlap;
it->xfbHeight -= upperOverlap;
}
else if (lowerOverlap > 0)
{
it->xfbHeight -= lowerOverlap;
}
}
}
}

View File

@ -17,76 +17,16 @@ inline bool AddressRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper)
return !((aLower >= bUpper) || (bLower >= aUpper));
}
struct XFBSourceBase
{
virtual ~XFBSourceBase() {}
virtual void CopyEFB(float Gamma) = 0;
u32 srcAddr;
u32 srcWidth;
u32 srcHeight;
unsigned int texWidth;
unsigned int texHeight;
// TODO: only used by OGL
TargetRectangle sourceRc;
};
class FramebufferManagerBase
{
public:
enum
{
// There may be multiple XFBs in GameCube RAM. This is the maximum number to
// virtualize.
MAX_VIRTUAL_XFB = 8
};
FramebufferManagerBase();
virtual ~FramebufferManagerBase();
static void CopyToXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,
float Gamma);
static unsigned int GetEFBLayers() { return m_EFBLayers; }
virtual std::pair<u32, u32> GetTargetSize() const = 0;
protected:
struct VirtualXFB
{
VirtualXFB() {}
// Address and size in GameCube RAM
u32 xfbAddr = 0;
u32 xfbWidth = 0;
u32 xfbHeight = 0;
std::unique_ptr<XFBSourceBase> xfbSource;
};
typedef std::list<VirtualXFB> VirtualXFBListType;
static unsigned int m_EFBLayers;
private:
virtual std::unique_ptr<XFBSourceBase>
CreateXFBSource(unsigned int target_width, unsigned int target_height, unsigned int layers) = 0;
static VirtualXFBListType::iterator FindVirtualXFB(u32 xfbAddr, u32 width, u32 height);
static void ReplaceVirtualXFB();
// TODO: merge these virtual funcs, they are nearly all the same
virtual void CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,
float Gamma = 1.0f) = 0;
static void CopyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,
float Gamma = 1.0f);
static std::unique_ptr<XFBSourceBase> m_realXFBSource; // Only used in Real XFB mode
static VirtualXFBListType m_virtualXFBList; // Only used in Virtual XFB mode
static std::array<const XFBSourceBase*, MAX_VIRTUAL_XFB> m_overlappingXFBArray;
};
extern std::unique_ptr<FramebufferManagerBase> g_framebuffer_manager;

View File

@ -112,8 +112,6 @@ void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStri
if (!fbStride || !fbHeight)
return;
m_xfb_written = true;
}
unsigned int Renderer::GetEFBScale() const
@ -412,34 +410,6 @@ std::tuple<float, float> Renderer::ScaleToDisplayAspectRatio(const int width,
return std::make_tuple(scaled_width, scaled_height);
}
TargetRectangle Renderer::CalculateFrameDumpDrawRectangle() const
{
// No point including any borders in the frame dump image, since they'd have to be cropped anyway.
TargetRectangle rc;
rc.left = 0;
rc.top = 0;
// If full-resolution frame dumping is disabled, just use the window draw rectangle.
if (!g_ActiveConfig.bInternalResolutionFrameDumps)
{
// But still remove the borders, since the caller expects this.
rc.right = m_target_rectangle.GetWidth();
rc.bottom = m_target_rectangle.GetHeight();
return rc;
}
// Grab the dimensions of the EFB textures, we scale either of these depending on the ratio.
u32 efb_width, efb_height;
std::tie(efb_width, efb_height) = g_framebuffer_manager->GetTargetSize();
float draw_width, draw_height;
std::tie(draw_width, draw_height) = ScaleToDisplayAspectRatio(efb_width, efb_height);
rc.right = static_cast<int>(std::ceil(draw_width));
rc.bottom = static_cast<int>(std::ceil(draw_height));
return rc;
}
void Renderer::UpdateDrawRectangle()
{
// The rendering window size
@ -693,7 +663,6 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
stats.ResetFrame();
Core::Callback_VideoCopiedToXFB(update_frame_count);
m_xfb_written = false;
}
bool Renderer::IsFrameDumping()

View File

@ -96,7 +96,6 @@ public:
float CalculateDrawAspectRatio() const;
std::tuple<float, float> ScaleToDisplayAspectRatio(int width, int height) const;
TargetRectangle CalculateFrameDumpDrawRectangle() const;
void UpdateDrawRectangle();
// Use this to convert a single target rectangle to two stereo rectangles
@ -167,7 +166,6 @@ protected:
int m_backbuffer_width = 0;
int m_backbuffer_height = 0;
TargetRectangle m_target_rectangle = {};
bool m_xfb_written = false;
FPSCounter m_fps_counter;