Remove old XFB logic
This commit is contained in:
parent
081b92b8a7
commit
33bc286baa
|
@ -38,8 +38,6 @@ set(SRCS
|
|||
VertexShaderCache.cpp
|
||||
VertexShaderCache.h
|
||||
VideoBackend.h
|
||||
XFBEncoder.cpp
|
||||
XFBEncoder.h
|
||||
)
|
||||
|
||||
set(LIBS
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ¶ms, 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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
||||
};
|
|
@ -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" />
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue