Add support for hybrid XFB
This commit is contained in:
parent
84ca9a4aec
commit
79387dddb2
|
@ -52,7 +52,6 @@
|
|||
<ClCompile Include="PixelShaderCache.cpp" />
|
||||
<ClCompile Include="PSTextureEncoder.cpp" />
|
||||
<ClCompile Include="Render.cpp" />
|
||||
<ClCompile Include="Television.cpp" />
|
||||
<ClCompile Include="TextureCache.cpp" />
|
||||
<ClCompile Include="VertexManager.cpp" />
|
||||
<ClCompile Include="VertexShaderCache.cpp" />
|
||||
|
@ -73,7 +72,6 @@
|
|||
<ClInclude Include="PixelShaderCache.h" />
|
||||
<ClInclude Include="PSTextureEncoder.h" />
|
||||
<ClInclude Include="Render.h" />
|
||||
<ClInclude Include="Television.h" />
|
||||
<ClInclude Include="TextureCache.h" />
|
||||
<ClInclude Include="VertexManager.h" />
|
||||
<ClInclude Include="VertexShaderCache.h" />
|
||||
|
|
|
@ -48,9 +48,6 @@
|
|||
<ClCompile Include="Render.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Television.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TextureCache.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
|
@ -108,9 +105,6 @@
|
|||
<ClInclude Include="Render.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Television.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TextureCache.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -330,12 +330,6 @@ std::pair<u32, u32> FramebufferManager::GetTargetSize() const
|
|||
return std::make_pair(m_target_width, m_target_height);
|
||||
}
|
||||
|
||||
void XFBSource::DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
|
||||
{
|
||||
// DX11's XFB decoder does not use this function.
|
||||
// YUYV data is decoded in Render::Swap.
|
||||
}
|
||||
|
||||
void XFBSource::CopyEFB(float Gamma)
|
||||
{
|
||||
g_renderer->ResetAPIState(); // reset any game specific settings
|
||||
|
|
|
@ -50,7 +50,6 @@ struct XFBSource : public XFBSourceBase
|
|||
{
|
||||
XFBSource(D3DTexture2D* _tex, int slices) : tex(_tex), m_slices(slices) {}
|
||||
~XFBSource() { tex->Release(); }
|
||||
void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) override;
|
||||
void CopyEFB(float Gamma) override;
|
||||
|
||||
D3DTexture2D* const tex;
|
||||
|
|
|
@ -41,8 +41,11 @@ void PSTextureEncoder::Init()
|
|||
HRESULT hr;
|
||||
|
||||
// Create output texture RGBA format
|
||||
// TODO: This Texture is overly large and parts of it are unused
|
||||
// EFB2RAM copies use max (EFB_WIDTH * 4) by (EFB_HEIGHT / 4)
|
||||
// XFB2RAM copies use max (EFB_WIDTH / 2) by (EFB_HEIGHT)
|
||||
D3D11_TEXTURE2D_DESC t2dd = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, EFB_WIDTH * 4,
|
||||
EFB_HEIGHT / 4, 1, 1, D3D11_BIND_RENDER_TARGET);
|
||||
EFB_HEIGHT, 1, 1, D3D11_BIND_RENDER_TARGET);
|
||||
hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_out);
|
||||
CHECK(SUCCEEDED(hr), "create efb encode output texture");
|
||||
D3D::SetDebugObjectName(m_out, "efb encoder output texture");
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
#include "VideoBackends/D3D/D3DBase.h"
|
||||
#include "VideoBackends/D3D/D3DState.h"
|
||||
#include "VideoBackends/D3D/D3DUtil.h"
|
||||
#include "VideoBackends/D3D/DXTexture.h"
|
||||
#include "VideoBackends/D3D/FramebufferManager.h"
|
||||
#include "VideoBackends/D3D/GeometryShaderCache.h"
|
||||
#include "VideoBackends/D3D/PixelShaderCache.h"
|
||||
#include "VideoBackends/D3D/Television.h"
|
||||
#include "VideoBackends/D3D/TextureCache.h"
|
||||
#include "VideoBackends/D3D/VertexShaderCache.h"
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
|||
#include "VideoCommon/SamplerCommon.h"
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
|
||||
namespace DX11
|
||||
|
@ -66,11 +67,8 @@ struct GXPipelineState
|
|||
|
||||
static u32 s_last_multisamples = 1;
|
||||
static bool s_last_stereo_mode = false;
|
||||
static bool s_last_xfb_mode = false;
|
||||
static bool s_last_fullscreen_mode = false;
|
||||
|
||||
static Television s_television;
|
||||
|
||||
static std::array<ID3D11BlendState*, 4> s_clear_blend_states{};
|
||||
static std::array<ID3D11DepthStencilState*, 3> s_clear_depth_states{};
|
||||
static ID3D11BlendState* s_reset_blend_state = nullptr;
|
||||
|
@ -85,8 +83,6 @@ static StateCache s_gx_state_cache;
|
|||
|
||||
static void SetupDeviceObjects()
|
||||
{
|
||||
s_television.Init();
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
D3D11_DEPTH_STENCIL_DESC ddesc;
|
||||
|
@ -182,8 +178,6 @@ static void TeardownDeviceObjects()
|
|||
SAFE_RELEASE(s_screenshot_texture);
|
||||
SAFE_RELEASE(s_3d_vision_texture);
|
||||
|
||||
s_television.Shutdown();
|
||||
|
||||
s_gx_state_cache.Clear();
|
||||
}
|
||||
|
||||
|
@ -241,7 +235,6 @@ Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferH
|
|||
{
|
||||
s_last_multisamples = g_ActiveConfig.iMultisamples;
|
||||
s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;
|
||||
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
|
||||
s_last_fullscreen_mode = D3D::GetFullscreenState();
|
||||
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
|
||||
|
@ -640,22 +633,11 @@ void Renderer::SetBlendingState(const BlendingState& state)
|
|||
}
|
||||
|
||||
// This function has the final picture. We adjust the aspect ratio here.
|
||||
void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
||||
const EFBRectangle& rc, u64 ticks, float Gamma)
|
||||
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma)
|
||||
{
|
||||
if ((!m_xfb_written && !g_ActiveConfig.RealXFBEnabled()) || !fbWidth || !fbHeight)
|
||||
if (!m_xfb_written)
|
||||
{
|
||||
Core::Callback_VideoCopiedToXFB(false);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 xfbCount = 0;
|
||||
const XFBSourceBase* const* xfbSourceList =
|
||||
FramebufferManager::GetXFBSource(xfbAddr, fbStride, fbHeight, &xfbCount);
|
||||
if ((!xfbSourceList || xfbCount == 0) && g_ActiveConfig.bUseXFB && !g_ActiveConfig.bUseRealXFB)
|
||||
{
|
||||
Core::Callback_VideoCopiedToXFB(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ResetAPIState();
|
||||
|
@ -671,67 +653,11 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
|||
|
||||
// activate linear filtering for the buffer copies
|
||||
D3D::SetLinearCopySampler();
|
||||
auto* xfb_texture = static_cast<DXTexture*>(texture);
|
||||
TargetRectangle source_rc = xfb_texture->config.Rect();
|
||||
|
||||
if (g_ActiveConfig.bUseXFB && g_ActiveConfig.bUseRealXFB)
|
||||
{
|
||||
// TODO: Television should be used to render Virtual XFB mode as well.
|
||||
D3D11_VIEWPORT vp = CD3D11_VIEWPORT((float)targetRc.left, (float)targetRc.top,
|
||||
(float)targetRc.GetWidth(), (float)targetRc.GetHeight());
|
||||
D3D::context->RSSetViewports(1, &vp);
|
||||
|
||||
s_television.Submit(xfbAddr, fbStride, fbWidth, fbHeight);
|
||||
s_television.Render();
|
||||
}
|
||||
else if (g_ActiveConfig.bUseXFB)
|
||||
{
|
||||
// draw each xfb source
|
||||
for (u32 i = 0; i < xfbCount; ++i)
|
||||
{
|
||||
const auto* const xfbSource = static_cast<const XFBSource*>(xfbSourceList[i]);
|
||||
|
||||
// use virtual xfb with offset
|
||||
int xfbHeight = xfbSource->srcHeight;
|
||||
int xfbWidth = xfbSource->srcWidth;
|
||||
int hOffset = ((s32)xfbSource->srcAddr - (s32)xfbAddr) / ((s32)fbStride * 2);
|
||||
|
||||
TargetRectangle drawRc;
|
||||
drawRc.top = targetRc.top + hOffset * targetRc.GetHeight() / (s32)fbHeight;
|
||||
drawRc.bottom = targetRc.top + (hOffset + xfbHeight) * targetRc.GetHeight() / (s32)fbHeight;
|
||||
drawRc.left = targetRc.left +
|
||||
(targetRc.GetWidth() - xfbWidth * targetRc.GetWidth() / (s32)fbStride) / 2;
|
||||
drawRc.right = targetRc.left +
|
||||
(targetRc.GetWidth() + xfbWidth * targetRc.GetWidth() / (s32)fbStride) / 2;
|
||||
|
||||
// The following code disables auto stretch. Kept for reference.
|
||||
// scale draw area for a 1 to 1 pixel mapping with the draw target
|
||||
// float vScale = (float)fbHeight / (float)s_backbuffer_height;
|
||||
// float hScale = (float)fbWidth / (float)s_backbuffer_width;
|
||||
// drawRc.top *= vScale;
|
||||
// drawRc.bottom *= vScale;
|
||||
// drawRc.left *= hScale;
|
||||
// drawRc.right *= hScale;
|
||||
|
||||
TargetRectangle sourceRc;
|
||||
sourceRc.left = xfbSource->sourceRc.left;
|
||||
sourceRc.top = xfbSource->sourceRc.top;
|
||||
sourceRc.right = xfbSource->sourceRc.right;
|
||||
sourceRc.bottom = xfbSource->sourceRc.bottom;
|
||||
|
||||
sourceRc.right -= Renderer::EFBToScaledX(fbStride - fbWidth);
|
||||
|
||||
BlitScreen(sourceRc, drawRc, xfbSource->tex, xfbSource->texWidth, xfbSource->texHeight,
|
||||
Gamma);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TargetRectangle sourceRc = Renderer::ConvertEFBRectangle(rc);
|
||||
|
||||
// TODO: Improve sampling algorithm for the pixel shader so that we can use the multisampled EFB
|
||||
// texture as source
|
||||
D3DTexture2D* read_texture = FramebufferManager::GetResolvedEFBColorTexture();
|
||||
BlitScreen(sourceRc, targetRc, read_texture, GetTargetWidth(), GetTargetHeight(), Gamma);
|
||||
}
|
||||
BlitScreen(source_rc, targetRc, xfb_texture->GetRawTexIdentifier(), xfb_texture->config.width,
|
||||
xfb_texture->config.height, Gamma);
|
||||
|
||||
// Dump frames
|
||||
if (IsFrameDumping())
|
||||
|
@ -773,33 +699,20 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
|||
g_texture_cache->OnConfigChanged(g_ActiveConfig);
|
||||
VertexShaderCache::RetreiveAsyncShaders();
|
||||
|
||||
SetWindowSize(fbStride, fbHeight);
|
||||
SetWindowSize(xfb_texture->config.width, xfb_texture->config.height);
|
||||
|
||||
const bool window_resized = CheckForResize();
|
||||
const bool fullscreen = D3D::GetFullscreenState();
|
||||
const bool fs_changed = s_last_fullscreen_mode != fullscreen;
|
||||
|
||||
bool xfbchanged = s_last_xfb_mode != g_ActiveConfig.bUseRealXFB;
|
||||
|
||||
if (FramebufferManagerBase::LastXfbWidth() != fbStride ||
|
||||
FramebufferManagerBase::LastXfbHeight() != fbHeight)
|
||||
{
|
||||
xfbchanged = true;
|
||||
unsigned int xfb_w = (fbStride < 1 || fbStride > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fbStride;
|
||||
unsigned int xfb_h = (fbHeight < 1 || fbHeight > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fbHeight;
|
||||
FramebufferManagerBase::SetLastXfbWidth(xfb_w);
|
||||
FramebufferManagerBase::SetLastXfbHeight(xfb_h);
|
||||
}
|
||||
|
||||
// Flip/present backbuffer to frontbuffer here
|
||||
D3D::Present();
|
||||
|
||||
// Resize the back buffers NOW to avoid flickering
|
||||
if (CalculateTargetSize() || xfbchanged || window_resized || fs_changed ||
|
||||
if (CalculateTargetSize() || window_resized || fs_changed ||
|
||||
s_last_multisamples != g_ActiveConfig.iMultisamples ||
|
||||
s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0))
|
||||
{
|
||||
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
|
||||
s_last_multisamples = g_ActiveConfig.iMultisamples;
|
||||
s_last_fullscreen_mode = fullscreen;
|
||||
PixelShaderCache::InvalidateMSAAShaders();
|
||||
|
|
|
@ -46,8 +46,7 @@ public:
|
|||
|
||||
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
|
||||
|
||||
void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,
|
||||
u64 ticks, float Gamma) override;
|
||||
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
|
||||
|
||||
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
|
||||
u32 color, u32 z) override;
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
// Copyright 2011 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "VideoBackends/D3D/Television.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "VideoBackends/D3D/D3DBase.h"
|
||||
#include "VideoBackends/D3D/D3DShader.h"
|
||||
#include "VideoBackends/D3D/D3DState.h"
|
||||
#include "VideoBackends/D3D/D3DUtil.h"
|
||||
#include "VideoBackends/D3D/VertexShaderCache.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
namespace DX11
|
||||
{
|
||||
static const char YUYV_DECODER_PS[] =
|
||||
"// dolphin-emu YUYV decoder pixel shader\n"
|
||||
|
||||
"Texture2D Tex0 : register(t0);\n"
|
||||
"sampler Samp0 : register(s0);\n"
|
||||
|
||||
"static const float3x3 YCBCR_TO_RGB = float3x3(\n"
|
||||
"1.164, 0.000, 1.596,\n"
|
||||
"1.164, -0.392, -0.813,\n"
|
||||
"1.164, 2.017, 0.000\n"
|
||||
");\n"
|
||||
|
||||
"void main(out float4 ocol0 : SV_Target, in float4 pos : SV_Position, in float2 uv0 : "
|
||||
"TEXCOORD0)\n"
|
||||
"{\n"
|
||||
"float3 sample = Tex0.Sample(Samp0, uv0).rgb;\n"
|
||||
|
||||
// GameCube/Wii XFB data is in YUYV format with ITU-R Rec. BT.601 color
|
||||
// primaries, compressed to the range Y in 16..235, U and V in 16..240.
|
||||
// We want to convert it to RGB format with sRGB color primaries, with
|
||||
// range 0..255.
|
||||
|
||||
// Recover RGB components
|
||||
"float3 yuv_601_sub = sample.grb - float3(16.0/255.0, 128.0/255.0, 128.0/255.0);\n"
|
||||
"float3 rgb_601 = mul(YCBCR_TO_RGB, yuv_601_sub);\n"
|
||||
|
||||
// If we were really obsessed with accuracy, we would correct for the
|
||||
// differing color primaries between BT.601 and sRGB. However, this may not
|
||||
// be worth the trouble because:
|
||||
// - BT.601 defines two sets of primaries: one for NTSC and one for PAL.
|
||||
// - sRGB's color primaries are actually an intermediate between BT.601's
|
||||
// NTSC and PAL primaries.
|
||||
// - If users even noticed any difference at all, they would be confused by
|
||||
// the slightly-different colors in the NTSC and PAL versions of the same
|
||||
// game.
|
||||
// - Even the game designers probably don't pay close attention to this
|
||||
// stuff.
|
||||
// Still, instructions on how to do it can be found at
|
||||
// <http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC20>
|
||||
|
||||
"ocol0 = float4(rgb_601, 1);\n"
|
||||
"}\n";
|
||||
|
||||
Television::Television() : m_yuyvTexture(nullptr), m_yuyvTextureSRV(nullptr), m_pShader(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void Television::Init()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
// Create YUYV texture for real XFB mode
|
||||
|
||||
// Initialize the texture with YCbCr black
|
||||
//
|
||||
// Some games use narrower XFB widths (Nintendo titles are fond of 608),
|
||||
// so the sampler's BorderColor won't cover the right side
|
||||
// (see sampler state below)
|
||||
const unsigned int MAX_XFB_SIZE = 2 * (MAX_XFB_WIDTH)*MAX_XFB_HEIGHT;
|
||||
std::vector<u8> fill(MAX_XFB_SIZE);
|
||||
for (size_t i = 0; i < MAX_XFB_SIZE / sizeof(u32); ++i)
|
||||
reinterpret_cast<u32*>(fill.data())[i] = 0x80108010;
|
||||
D3D11_SUBRESOURCE_DATA srd = {fill.data(), 2 * (MAX_XFB_WIDTH), 0};
|
||||
|
||||
// This texture format is designed for YUYV data.
|
||||
D3D11_TEXTURE2D_DESC t2dd =
|
||||
CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_G8R8_G8B8_UNORM, MAX_XFB_WIDTH, MAX_XFB_HEIGHT, 1, 1);
|
||||
hr = D3D::device->CreateTexture2D(&t2dd, &srd, &m_yuyvTexture);
|
||||
CHECK(SUCCEEDED(hr), "create tv yuyv texture");
|
||||
D3D::SetDebugObjectName(m_yuyvTexture, "tv yuyv texture");
|
||||
|
||||
// Create shader resource view for YUYV texture
|
||||
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvd = CD3D11_SHADER_RESOURCE_VIEW_DESC(
|
||||
m_yuyvTexture, D3D11_SRV_DIMENSION_TEXTURE2D, DXGI_FORMAT_G8R8_G8B8_UNORM);
|
||||
hr = D3D::device->CreateShaderResourceView(m_yuyvTexture, &srvd, &m_yuyvTextureSRV);
|
||||
CHECK(SUCCEEDED(hr), "create tv yuyv texture srv");
|
||||
D3D::SetDebugObjectName(m_yuyvTextureSRV, "tv yuyv texture srv");
|
||||
|
||||
// Create YUYV-decoding pixel shader
|
||||
|
||||
m_pShader = D3D::CompileAndCreatePixelShader(YUYV_DECODER_PS);
|
||||
CHECK(m_pShader != nullptr, "compile and create yuyv decoder pixel shader");
|
||||
D3D::SetDebugObjectName(m_pShader, "yuyv decoder pixel shader");
|
||||
|
||||
// Create sampler state and set border color
|
||||
//
|
||||
// The default sampler border color of { 0.f, 0.f, 0.f, 0.f }
|
||||
// creates a green border around the image - see issue 6483
|
||||
// (remember, the XFB is being interpreted as YUYV, and 0,0,0,0
|
||||
// is actually two green pixels in YUYV - black should be 16,128,16,128,
|
||||
// but we reverse the order to match DXGI_FORMAT_G8R8_G8B8_UNORM's ordering)
|
||||
float border[4] = {128.0f / 255.0f, 16.0f / 255.0f, 128.0f / 255.0f, 16.0f / 255.0f};
|
||||
D3D11_SAMPLER_DESC samDesc = CD3D11_SAMPLER_DESC(
|
||||
D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_BORDER, D3D11_TEXTURE_ADDRESS_BORDER,
|
||||
D3D11_TEXTURE_ADDRESS_BORDER, 0.f, 1, D3D11_COMPARISON_ALWAYS, border, 0.f, 0.f);
|
||||
hr = D3D::device->CreateSamplerState(&samDesc, &m_samplerState);
|
||||
CHECK(SUCCEEDED(hr), "create yuyv decoder sampler state");
|
||||
D3D::SetDebugObjectName(m_samplerState, "yuyv decoder sampler state");
|
||||
}
|
||||
|
||||
void Television::Shutdown()
|
||||
{
|
||||
SAFE_RELEASE(m_pShader);
|
||||
SAFE_RELEASE(m_yuyvTextureSRV);
|
||||
SAFE_RELEASE(m_yuyvTexture);
|
||||
SAFE_RELEASE(m_samplerState);
|
||||
}
|
||||
|
||||
void Television::Submit(u32 xfbAddr, u32 stride, u32 width, u32 height)
|
||||
{
|
||||
m_curAddr = xfbAddr;
|
||||
m_curWidth = width;
|
||||
m_curHeight = height;
|
||||
|
||||
// Load data from GameCube RAM to YUYV texture
|
||||
u8* yuyvSrc = Memory::GetPointer(xfbAddr);
|
||||
D3D11_BOX box = CD3D11_BOX(0, 0, 0, stride, height, 1);
|
||||
D3D::context->UpdateSubresource(m_yuyvTexture, 0, &box, yuyvSrc, 2 * stride, 2 * stride * height);
|
||||
}
|
||||
|
||||
void Television::Render()
|
||||
{
|
||||
if (g_ActiveConfig.bUseRealXFB && g_ActiveConfig.bUseXFB)
|
||||
{
|
||||
// Use real XFB mode
|
||||
// TODO: If this is the lower field, render at a vertical offset of 1
|
||||
// line down. We could even consider implementing a deinterlacing
|
||||
// algorithm.
|
||||
|
||||
D3D11_RECT sourceRc = CD3D11_RECT(0, 0, int(m_curWidth), int(m_curHeight));
|
||||
|
||||
D3D::stateman->SetSampler(0, m_samplerState);
|
||||
|
||||
D3D::drawShadedTexQuad(m_yuyvTextureSRV, &sourceRc, MAX_XFB_WIDTH, MAX_XFB_HEIGHT, m_pShader,
|
||||
VertexShaderCache::GetSimpleVertexShader(),
|
||||
VertexShaderCache::GetSimpleInputLayout());
|
||||
}
|
||||
else if (g_ActiveConfig.bUseXFB)
|
||||
{
|
||||
// Use virtual XFB mode
|
||||
|
||||
// TODO: Eventually, Television should render the Virtual XFB mode
|
||||
// display as well.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2011 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
struct ID3D11Texture2D;
|
||||
struct ID3D11ShaderResourceView;
|
||||
struct ID3D11PixelShader;
|
||||
struct ID3D11SamplerState;
|
||||
|
||||
namespace DX11
|
||||
{
|
||||
class Television
|
||||
{
|
||||
public:
|
||||
Television();
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
// Submit video data to be drawn. This will change the current state of the
|
||||
// TV. xfbAddr points to YUYV data stored in GameCube/Wii RAM, but the XFB
|
||||
// may be virtualized when rendering so the RAM may not actually be read.
|
||||
void Submit(u32 xfbAddr, u32 stride, u32 width, u32 height);
|
||||
|
||||
// Render the current state of the TV.
|
||||
void Render();
|
||||
|
||||
private:
|
||||
// Properties of last Submit call
|
||||
u32 m_curAddr;
|
||||
u32 m_curWidth;
|
||||
u32 m_curHeight;
|
||||
|
||||
// Used for real XFB mode
|
||||
|
||||
ID3D11Texture2D* m_yuyvTexture;
|
||||
ID3D11ShaderResourceView* m_yuyvTextureSRV;
|
||||
ID3D11PixelShader* m_pShader;
|
||||
ID3D11SamplerState* m_samplerState;
|
||||
};
|
||||
}
|
|
@ -78,6 +78,7 @@ void VideoBackend::InitBackendInfo()
|
|||
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = false;
|
||||
g_Config.backend_info.bSupportsGPUTextureDecoding = false;
|
||||
g_Config.backend_info.bSupportsST3CTextures = false;
|
||||
g_Config.backend_info.bSupportsCopyToVram = true;
|
||||
g_Config.backend_info.bSupportsBitfield = false;
|
||||
g_Config.backend_info.bSupportsDynamicSamplerIndexing = false;
|
||||
g_Config.backend_info.bSupportsBPTCTextures = false;
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
class XFBSource : public XFBSourceBase
|
||||
{
|
||||
public:
|
||||
void DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height) override {}
|
||||
void CopyEFB(float gamma) override {}
|
||||
};
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
|
|||
return result;
|
||||
}
|
||||
|
||||
void Renderer::SwapImpl(u32, u32, u32, u32, const EFBRectangle&, u64, float)
|
||||
void Renderer::SwapImpl(AbstractTexture*, const EFBRectangle&, u64, float)
|
||||
{
|
||||
UpdateActiveConfig();
|
||||
}
|
||||
|
|
|
@ -21,8 +21,7 @@ public:
|
|||
void BBoxWrite(int index, u16 value) override {}
|
||||
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
|
||||
|
||||
void SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, const EFBRectangle& rc,
|
||||
u64 ticks, float gamma) override;
|
||||
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
|
||||
|
||||
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
|
||||
u32 color, u32 z) override
|
||||
|
|
|
@ -530,7 +530,7 @@ void FramebufferManager::ResolveEFBStencilTexture()
|
|||
void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight,
|
||||
const EFBRectangle& sourceRc, float Gamma)
|
||||
{
|
||||
u8* xfb_in_ram = Memory::GetPointer(xfbAddr);
|
||||
/* u8* xfb_in_ram = Memory::GetPointer(xfbAddr);
|
||||
if (!xfb_in_ram)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Tried to copy to invalid XFB address");
|
||||
|
@ -539,7 +539,7 @@ void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight,
|
|||
|
||||
TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc);
|
||||
TextureConverter::EncodeToRamYUYV(ResolveAndGetRenderTarget(sourceRc), targetRc, xfb_in_ram,
|
||||
sourceRc.GetWidth(), fbStride, fbHeight);
|
||||
sourceRc.GetWidth(), fbStride, fbHeight);*/
|
||||
}
|
||||
|
||||
GLuint FramebufferManager::GetResolvedFramebuffer()
|
||||
|
@ -615,11 +615,6 @@ XFBSource::~XFBSource()
|
|||
glDeleteTextures(1, &texture);
|
||||
}
|
||||
|
||||
void XFBSource::DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
|
||||
{
|
||||
TextureConverter::DecodeToTexture(xfbAddr, fbWidth, fbHeight, texture);
|
||||
}
|
||||
|
||||
void XFBSource::CopyEFB(float Gamma)
|
||||
{
|
||||
g_renderer->ResetAPIState();
|
||||
|
|
|
@ -54,7 +54,6 @@ struct XFBSource : public XFBSourceBase
|
|||
~XFBSource();
|
||||
|
||||
void CopyEFB(float Gamma) override;
|
||||
void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) override;
|
||||
|
||||
const GLuint texture;
|
||||
const int m_layers;
|
||||
|
|
|
@ -25,7 +25,7 @@ static const char s_vertex_shader[] = "out vec2 uv0;\n"
|
|||
"void main(void) {\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 = rawpos * src_rect.zw + src_rect.xy;\n"
|
||||
" uv0 = vec2(mix(src_rect.xy, src_rect.zw, rawpos));\n"
|
||||
"}\n";
|
||||
|
||||
OpenGLPostProcessing::OpenGLPostProcessing() : m_initialized(false)
|
||||
|
@ -52,8 +52,8 @@ void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle
|
|||
|
||||
glUniform4f(m_uniform_resolution, (float)src_width, (float)src_height, 1.0f / (float)src_width,
|
||||
1.0f / (float)src_height);
|
||||
glUniform4f(m_uniform_src_rect, src.left / (float)src_width, src.bottom / (float)src_height,
|
||||
src.GetWidth() / (float)src_width, src.GetHeight() / (float)src_height);
|
||||
glUniform4f(m_uniform_src_rect, src.left / (float)src_width, src.top / (float)src_height,
|
||||
src.right / (float)src_width, src.bottom / (float)src_height);
|
||||
glUniform1ui(m_uniform_time, (GLuint)m_timer.GetTimeElapsed());
|
||||
glUniform1i(m_uniform_layer, layer);
|
||||
|
||||
|
|
|
@ -66,7 +66,6 @@ static std::unique_ptr<RasterFont> s_raster_font;
|
|||
static int s_MSAASamples = 1;
|
||||
static u32 s_last_multisamples = 1;
|
||||
static bool s_last_stereo_mode = false;
|
||||
static bool s_last_xfb_mode = false;
|
||||
|
||||
static bool s_vsync;
|
||||
|
||||
|
@ -726,7 +725,6 @@ Renderer::Renderer()
|
|||
s_MSAASamples = s_last_multisamples;
|
||||
|
||||
s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;
|
||||
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
|
||||
|
||||
// Handle VSync on/off
|
||||
s_vsync = g_ActiveConfig.IsVSync();
|
||||
|
@ -1331,8 +1329,7 @@ void Renderer::SetBlendingState(const BlendingState& state)
|
|||
}
|
||||
|
||||
// This function has the final picture. We adjust the aspect ratio here.
|
||||
void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
||||
const EFBRectangle& rc, u64 ticks, float Gamma)
|
||||
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma)
|
||||
{
|
||||
if (g_ogl_config.bSupportsDebug)
|
||||
{
|
||||
|
@ -1342,20 +1339,13 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
|||
glDisable(GL_DEBUG_OUTPUT);
|
||||
}
|
||||
|
||||
if ((!m_xfb_written && !g_ActiveConfig.RealXFBEnabled()) || !fbWidth || !fbHeight)
|
||||
{
|
||||
Core::Callback_VideoCopiedToXFB(false);
|
||||
return;
|
||||
}
|
||||
auto* xfb_texture = static_cast<OGLTexture*>(texture);
|
||||
|
||||
u32 xfbCount = 0;
|
||||
const XFBSourceBase* const* xfbSourceList =
|
||||
FramebufferManager::GetXFBSource(xfbAddr, fbStride, fbHeight, &xfbCount);
|
||||
if (g_ActiveConfig.VirtualXFBEnabled() && (!xfbSourceList || xfbCount == 0))
|
||||
{
|
||||
Core::Callback_VideoCopiedToXFB(false);
|
||||
return;
|
||||
}
|
||||
TargetRectangle sourceRc = ConvertEFBRectangle(rc);
|
||||
sourceRc.left = 0;
|
||||
sourceRc.right = xfb_texture->config.width;
|
||||
sourceRc.top = xfb_texture->config.height;
|
||||
sourceRc.bottom = 0;
|
||||
|
||||
ResetAPIState();
|
||||
|
||||
|
@ -1366,7 +1356,8 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
|||
std::swap(flipped_trc.top, flipped_trc.bottom);
|
||||
|
||||
// Copy the framebuffer to screen.
|
||||
DrawFrame(0, flipped_trc, rc, xfbAddr, xfbSourceList, xfbCount, fbWidth, fbStride, fbHeight);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
BlitScreen(sourceRc, flipped_trc, xfb_texture->GetRawTexIdentifier(), xfb_texture->config.width, xfb_texture->config.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.
|
||||
|
@ -1380,7 +1371,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
|||
if (use_offscreen_buffer)
|
||||
{
|
||||
// DumpFrameUsingFBO resets GL_FRAMEBUFFER, so change back to the window for drawing OSD.
|
||||
DumpFrameUsingFBO(rc, xfbAddr, xfbSourceList, xfbCount, fbWidth, fbStride, fbHeight, ticks);
|
||||
DumpFrameUsingFBO(sourceRc, ticks);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1391,24 +1382,10 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
|||
|
||||
// Finish up the current frame, print some stats
|
||||
|
||||
SetWindowSize(fbStride, fbHeight);
|
||||
SetWindowSize(xfb_texture->config.width, xfb_texture->config.height);
|
||||
|
||||
GLInterface->Update(); // just updates the render window position and the backbuffer size
|
||||
|
||||
bool xfbchanged = s_last_xfb_mode != g_ActiveConfig.bUseRealXFB;
|
||||
|
||||
if (FramebufferManagerBase::LastXfbWidth() != fbStride ||
|
||||
FramebufferManagerBase::LastXfbHeight() != fbHeight)
|
||||
{
|
||||
xfbchanged = true;
|
||||
unsigned int const last_w =
|
||||
(fbStride < 1 || fbStride > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fbStride;
|
||||
unsigned int const last_h =
|
||||
(fbHeight < 1 || fbHeight > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fbHeight;
|
||||
FramebufferManagerBase::SetLastXfbWidth(last_w);
|
||||
FramebufferManagerBase::SetLastXfbHeight(last_h);
|
||||
}
|
||||
|
||||
bool window_resized = false;
|
||||
int window_width = static_cast<int>(std::max(GLInterface->GetBackBufferWidth(), 1u));
|
||||
int window_height = static_cast<int>(std::max(GLInterface->GetBackBufferHeight(), 1u));
|
||||
|
@ -1428,9 +1405,8 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
|||
stencil_buffer_enabled != BoundingBox::NeedsStencilBuffer() ||
|
||||
s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0);
|
||||
|
||||
if (xfbchanged || window_resized || fb_needs_update)
|
||||
if (window_resized || fb_needs_update)
|
||||
{
|
||||
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
|
||||
UpdateDrawRectangle();
|
||||
}
|
||||
if (fb_needs_update)
|
||||
|
@ -1523,110 +1499,13 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
|||
ClearEFBCache();
|
||||
}
|
||||
|
||||
void Renderer::DrawFrame(GLuint framebuffer, const TargetRectangle& target_rc,
|
||||
const EFBRectangle& source_rc, u32 xfb_addr,
|
||||
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
|
||||
u32 fb_stride, u32 fb_height)
|
||||
{
|
||||
if (g_ActiveConfig.bUseXFB)
|
||||
{
|
||||
if (g_ActiveConfig.bUseRealXFB)
|
||||
DrawRealXFB(framebuffer, target_rc, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
|
||||
else
|
||||
DrawVirtualXFB(framebuffer, target_rc, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride,
|
||||
fb_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawEFB(framebuffer, target_rc, source_rc);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc,
|
||||
const EFBRectangle& source_rc)
|
||||
const TargetRectangle& source_rc)
|
||||
{
|
||||
TargetRectangle scaled_source_rc = ConvertEFBRectangle(source_rc);
|
||||
|
||||
// for msaa mode, we must resolve the efb content to non-msaa
|
||||
GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(source_rc);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
BlitScreen(scaled_source_rc, target_rc, tex, m_target_width, m_target_height);
|
||||
}
|
||||
|
||||
void Renderer::DrawVirtualXFB(GLuint framebuffer, const TargetRectangle& target_rc, u32 xfb_addr,
|
||||
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
|
||||
u32 fb_stride, u32 fb_height)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
|
||||
for (u32 i = 0; i < xfb_count; ++i)
|
||||
{
|
||||
const XFBSource* xfbSource = static_cast<const XFBSource*>(xfb_sources[i]);
|
||||
|
||||
TargetRectangle draw_rc;
|
||||
TargetRectangle source_rc;
|
||||
source_rc.left = xfbSource->sourceRc.left;
|
||||
source_rc.right = xfbSource->sourceRc.right;
|
||||
source_rc.top = xfbSource->sourceRc.top;
|
||||
source_rc.bottom = xfbSource->sourceRc.bottom;
|
||||
|
||||
// use virtual xfb with offset
|
||||
int xfbHeight = xfbSource->srcHeight;
|
||||
int xfbWidth = xfbSource->srcWidth;
|
||||
int hOffset = (static_cast<s32>(xfbSource->srcAddr) - static_cast<s32>(xfb_addr)) /
|
||||
(static_cast<s32>(fb_stride) * 2);
|
||||
|
||||
draw_rc.top = target_rc.top - hOffset * target_rc.GetHeight() / static_cast<s32>(fb_height);
|
||||
draw_rc.bottom =
|
||||
target_rc.top - (hOffset + xfbHeight) * target_rc.GetHeight() / static_cast<s32>(fb_height);
|
||||
draw_rc.left =
|
||||
target_rc.left +
|
||||
(target_rc.GetWidth() - xfbWidth * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
|
||||
draw_rc.right =
|
||||
target_rc.left +
|
||||
(target_rc.GetWidth() + xfbWidth * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
|
||||
|
||||
// The following code disables auto stretch. Kept for reference.
|
||||
// scale draw area for a 1 to 1 pixel mapping with the draw target
|
||||
// float h_scale = static_cast<float>(fb_width) / static_cast<float>(target_rc.GetWidth());
|
||||
// float v_scale = static_cast<float>(fb_height) / static_cast<float>(target_rc.GetHeight());
|
||||
// draw_rc.top *= v_scale;
|
||||
// draw_rc.bottom *= v_scale;
|
||||
// draw_rc.left *= h_scale;
|
||||
// draw_rc.right *= h_scale;
|
||||
|
||||
source_rc.right -= Renderer::EFBToScaledX(fb_stride - fb_width);
|
||||
|
||||
BlitScreen(source_rc, draw_rc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::DrawRealXFB(GLuint framebuffer, const TargetRectangle& target_rc,
|
||||
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
|
||||
u32 fb_stride, u32 fb_height)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
|
||||
for (u32 i = 0; i < xfb_count; ++i)
|
||||
{
|
||||
const XFBSource* xfbSource = static_cast<const XFBSource*>(xfb_sources[i]);
|
||||
|
||||
TargetRectangle source_rc;
|
||||
source_rc.left = xfbSource->sourceRc.left;
|
||||
source_rc.right = xfbSource->sourceRc.right;
|
||||
source_rc.top = xfbSource->sourceRc.top;
|
||||
source_rc.bottom = xfbSource->sourceRc.bottom;
|
||||
|
||||
source_rc.right -= fb_stride - fb_width;
|
||||
|
||||
// RealXFB doesn't call ConvertEFBRectangle for sourceRc, therefore it is still assuming a top-
|
||||
// left origin. The top offset is always zero (see FramebufferManagerBase::GetRealXFBSource).
|
||||
source_rc.top = source_rc.bottom;
|
||||
source_rc.bottom = 0;
|
||||
|
||||
TargetRectangle draw_rc = target_rc;
|
||||
BlitScreen(source_rc, draw_rc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
|
||||
}
|
||||
BlitScreen(source_rc, target_rc, tex, m_target_width, m_target_height);
|
||||
}
|
||||
|
||||
void Renderer::FlushFrameDump()
|
||||
|
@ -1683,9 +1562,7 @@ void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
|
|||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
void Renderer::DumpFrameUsingFBO(const EFBRectangle& source_rc, u32 xfb_addr,
|
||||
const XFBSourceBase* const* xfb_sources, u32 xfb_count,
|
||||
u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks)
|
||||
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();
|
||||
|
@ -1705,8 +1582,7 @@ void Renderer::DumpFrameUsingFBO(const EFBRectangle& source_rc, u32 xfb_addr,
|
|||
// 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);
|
||||
DrawFrame(m_frame_dump_render_framebuffer, render_rc, source_rc, xfb_addr, xfb_sources, xfb_count,
|
||||
fb_width, fb_stride, fb_height);
|
||||
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);
|
||||
|
|
|
@ -98,8 +98,7 @@ public:
|
|||
|
||||
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
|
||||
|
||||
void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,
|
||||
u64 ticks, float Gamma) override;
|
||||
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
|
||||
|
||||
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
|
||||
u32 color, u32 z) override;
|
||||
|
@ -112,27 +111,14 @@ private:
|
|||
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
|
||||
const TargetRectangle& targetPixelRc, const void* data);
|
||||
|
||||
// Draw either the EFB, or specified XFB sources to the currently-bound framebuffer.
|
||||
void DrawFrame(GLuint framebuffer, const TargetRectangle& target_rc,
|
||||
const EFBRectangle& source_rc, u32 xfb_addr,
|
||||
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
|
||||
u32 fb_stride, u32 fb_height);
|
||||
void DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc, const EFBRectangle& source_rc);
|
||||
void DrawVirtualXFB(GLuint framebuffer, const TargetRectangle& target_rc, u32 xfb_addr,
|
||||
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
|
||||
u32 fb_stride, u32 fb_height);
|
||||
void DrawRealXFB(GLuint framebuffer, const TargetRectangle& target_rc,
|
||||
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
|
||||
u32 fb_stride, u32 fb_height);
|
||||
void DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc, const TargetRectangle& source_rc);
|
||||
|
||||
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 EFBRectangle& source_rc, u32 xfb_addr,
|
||||
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
|
||||
u32 fb_stride, u32 fb_height, 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);
|
||||
|
|
|
@ -91,6 +91,7 @@ void VideoBackend::InitBackendInfo()
|
|||
g_Config.backend_info.bSupportsReversedDepthRange = true;
|
||||
g_Config.backend_info.bSupportsMultithreading = false;
|
||||
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = true;
|
||||
g_Config.backend_info.bSupportsCopyToVram = true;
|
||||
|
||||
// TODO: There is a bug here, if texel buffers are not supported the graphics options
|
||||
// will show the option when it is not supported. The only way around this would be
|
||||
|
|
|
@ -520,15 +520,6 @@ void CopyToXFB(yuv422_packed* xfb_in_ram, u32 fbWidth, u32 fbHeight, const EFBRe
|
|||
// Scanline buffer, leave room for borders
|
||||
yuv444 scanline[EFB_WIDTH + 2];
|
||||
|
||||
// our internal yuv444 type is not normalized, so black is {0, 0, 0} instead of {16, 128, 128}
|
||||
yuv444 black;
|
||||
black.Y = 0;
|
||||
black.U = 0;
|
||||
black.V = 0;
|
||||
|
||||
scanline[0] = black; // black border at start
|
||||
scanline[right + 1] = black; // black border at end
|
||||
|
||||
for (u16 y = sourceRc.top; y < sourceRc.bottom; y++)
|
||||
{
|
||||
// Get a scanline of YUV pixels in 4:4:4 format
|
||||
|
@ -538,6 +529,10 @@ void CopyToXFB(yuv422_packed* xfb_in_ram, u32 fbWidth, u32 fbHeight, const EFBRe
|
|||
scanline[i] = GetColorYUV(x, y);
|
||||
}
|
||||
|
||||
// Flipper clamps the border colors
|
||||
scanline[0] = scanline[1];
|
||||
scanline[right + 1] = scanline[right];
|
||||
|
||||
// And Downsample them to 4:2:2
|
||||
for (int i = 1, x = left; x < right; i += 2, x += 2)
|
||||
{
|
||||
|
@ -562,26 +557,7 @@ void CopyToXFB(yuv422_packed* xfb_in_ram, u32 fbWidth, u32 fbHeight, const EFBRe
|
|||
// main memory or doing a yuyv conversion
|
||||
void BypassXFB(u8* texture, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma)
|
||||
{
|
||||
if (fbWidth * fbHeight > MAX_XFB_WIDTH * MAX_XFB_HEIGHT)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Framebuffer is too large: %ix%i", fbWidth, fbHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t textureAddress = 0;
|
||||
const int left = sourceRc.left;
|
||||
const int right = sourceRc.right;
|
||||
|
||||
for (u16 y = sourceRc.top; y < sourceRc.bottom; y++)
|
||||
{
|
||||
for (u16 x = left; x < right; x++)
|
||||
{
|
||||
const u32 color = Common::swap32(GetColor(x, y) | 0xFF);
|
||||
|
||||
std::memcpy(&texture[textureAddress], &color, sizeof(u32));
|
||||
textureAddress += sizeof(u32);
|
||||
}
|
||||
}
|
||||
// TODO: Upload directly to texture cache.
|
||||
}
|
||||
|
||||
bool ZCompare(u16 x, u16 y, u32 z)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "Common/Logging/Log.h"
|
||||
|
||||
#include "VideoBackends/Software/SWOGLWindow.h"
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
|
||||
std::unique_ptr<SWOGLWindow> SWOGLWindow::s_instance;
|
||||
|
||||
|
@ -53,9 +54,9 @@ void SWOGLWindow::Prepare()
|
|||
|
||||
std::string frag_shader = "in vec2 TexCoord;\n"
|
||||
"out vec4 ColorOut;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"uniform sampler2DArray samp;\n"
|
||||
"void main() {\n"
|
||||
" ColorOut = texture(Texture, TexCoord);\n"
|
||||
" ColorOut = texture(samp, vec3(TexCoord, 0.0));\n"
|
||||
"}\n";
|
||||
|
||||
std::string vertex_shader = "out vec2 TexCoord;\n"
|
||||
|
@ -74,12 +75,9 @@ void SWOGLWindow::Prepare()
|
|||
|
||||
glUseProgram(m_image_program);
|
||||
|
||||
glUniform1i(glGetUniformLocation(m_image_program, "Texture"), 0);
|
||||
glUniform1i(glGetUniformLocation(m_image_program, "samp"), 0);
|
||||
|
||||
glGenTextures(1, &m_image_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, m_image_texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
|
||||
|
||||
glGenVertexArrays(1, &m_image_vao);
|
||||
}
|
||||
|
@ -89,23 +87,19 @@ void SWOGLWindow::PrintText(const std::string& text, int x, int y, u32 color)
|
|||
m_text.push_back({text, x, y, color});
|
||||
}
|
||||
|
||||
void SWOGLWindow::ShowImage(const u8* data, int stride, int width, int height, float aspect)
|
||||
void SWOGLWindow::ShowImage(AbstractTexture* image, float aspect)
|
||||
{
|
||||
GLInterface->MakeCurrent();
|
||||
GLInterface->Update();
|
||||
Prepare();
|
||||
GLInterface->Update(); // just updates the render window position and the backbuffer size
|
||||
|
||||
GLsizei glWidth = (GLsizei)GLInterface->GetBackBufferWidth();
|
||||
GLsizei glHeight = (GLsizei)GLInterface->GetBackBufferHeight();
|
||||
|
||||
glViewport(0, 0, glWidth, glHeight);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, m_image_texture);
|
||||
image->Bind(0);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride / 4);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, data);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glUseProgram(m_image_program);
|
||||
|
||||
|
@ -119,7 +113,6 @@ void SWOGLWindow::ShowImage(const u8* data, int stride, int width, int height, f
|
|||
m_text.clear();
|
||||
|
||||
GLInterface->Swap();
|
||||
GLInterface->ClearCurrent();
|
||||
}
|
||||
|
||||
int SWOGLWindow::PeekMessages()
|
||||
|
|
|
@ -10,17 +10,20 @@
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class AbstractTexture;
|
||||
|
||||
class SWOGLWindow
|
||||
{
|
||||
public:
|
||||
static void Init(void* window_handle);
|
||||
static void Shutdown();
|
||||
void Prepare();
|
||||
|
||||
// Will be printed on the *next* image
|
||||
void PrintText(const std::string& text, int x, int y, u32 color);
|
||||
|
||||
// Image to show, will be swapped immediately
|
||||
void ShowImage(const u8* data, int stride, int width, int height, float aspect);
|
||||
void ShowImage(AbstractTexture* image, float aspect);
|
||||
|
||||
int PeekMessages();
|
||||
|
||||
|
@ -28,7 +31,6 @@ public:
|
|||
|
||||
private:
|
||||
SWOGLWindow() {}
|
||||
void Prepare();
|
||||
|
||||
struct TextData
|
||||
{
|
||||
|
@ -40,5 +42,5 @@ private:
|
|||
|
||||
bool m_init{false};
|
||||
|
||||
u32 m_image_program, m_image_texture, m_image_vao;
|
||||
u32 m_image_program, m_image_vao;
|
||||
};
|
||||
|
|
|
@ -23,26 +23,13 @@
|
|||
#include "VideoCommon/VideoBackendBase.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
static u8* s_xfbColorTexture[2];
|
||||
static int s_currentColorTexture = 0;
|
||||
|
||||
SWRenderer::SWRenderer()
|
||||
: ::Renderer(static_cast<int>(MAX_XFB_WIDTH), static_cast<int>(MAX_XFB_HEIGHT))
|
||||
{
|
||||
}
|
||||
|
||||
SWRenderer::~SWRenderer()
|
||||
{
|
||||
delete[] s_xfbColorTexture[0];
|
||||
delete[] s_xfbColorTexture[1];
|
||||
}
|
||||
|
||||
void SWRenderer::Init()
|
||||
{
|
||||
s_xfbColorTexture[0] = new u8[MAX_XFB_WIDTH * MAX_XFB_HEIGHT * 4];
|
||||
s_xfbColorTexture[1] = new u8[MAX_XFB_WIDTH * MAX_XFB_HEIGHT * 4];
|
||||
|
||||
s_currentColorTexture = 0;
|
||||
}
|
||||
|
||||
void SWRenderer::Shutdown()
|
||||
|
@ -55,80 +42,16 @@ void SWRenderer::RenderText(const std::string& pstr, int left, int top, u32 colo
|
|||
SWOGLWindow::s_instance->PrintText(pstr, left, top, color);
|
||||
}
|
||||
|
||||
u8* SWRenderer::GetNextColorTexture()
|
||||
{
|
||||
return s_xfbColorTexture[!s_currentColorTexture];
|
||||
}
|
||||
|
||||
u8* SWRenderer::GetCurrentColorTexture()
|
||||
{
|
||||
return s_xfbColorTexture[s_currentColorTexture];
|
||||
}
|
||||
|
||||
void SWRenderer::SwapColorTexture()
|
||||
{
|
||||
s_currentColorTexture = !s_currentColorTexture;
|
||||
}
|
||||
|
||||
void SWRenderer::UpdateColorTexture(EfbInterface::yuv422_packed* xfb, u32 fbWidth, u32 fbHeight)
|
||||
{
|
||||
if (fbWidth * fbHeight > MAX_XFB_WIDTH * MAX_XFB_HEIGHT)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Framebuffer is too large: %ix%i", fbWidth, fbHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 offset = 0;
|
||||
u8* TexturePointer = GetNextColorTexture();
|
||||
|
||||
for (u16 y = 0; y < fbHeight; y++)
|
||||
{
|
||||
for (u16 x = 0; x < fbWidth; x += 2)
|
||||
{
|
||||
// We do this one color sample (aka 2 RGB pixles) at a time
|
||||
int Y1 = xfb[x].Y - 16;
|
||||
int Y2 = xfb[x + 1].Y - 16;
|
||||
int U = int(xfb[x].UV) - 128;
|
||||
int V = int(xfb[x + 1].UV) - 128;
|
||||
|
||||
// We do the inverse BT.601 conversion for YCbCr to RGB
|
||||
// http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
|
||||
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y1 + 1.596f * V), 0, 255);
|
||||
TexturePointer[offset++] =
|
||||
MathUtil::Clamp(int(1.164f * Y1 - 0.392f * U - 0.813f * V), 0, 255);
|
||||
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y1 + 2.017f * U), 0, 255);
|
||||
TexturePointer[offset++] = 255;
|
||||
|
||||
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y2 + 1.596f * V), 0, 255);
|
||||
TexturePointer[offset++] =
|
||||
MathUtil::Clamp(int(1.164f * Y2 - 0.392f * U - 0.813f * V), 0, 255);
|
||||
TexturePointer[offset++] = MathUtil::Clamp(int(1.164f * Y2 + 2.017f * U), 0, 255);
|
||||
TexturePointer[offset++] = 255;
|
||||
}
|
||||
xfb += fbWidth;
|
||||
}
|
||||
SwapColorTexture();
|
||||
}
|
||||
|
||||
// Called on the GPU thread
|
||||
void SWRenderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
||||
const EFBRectangle& rc, u64 ticks, float Gamma)
|
||||
void SWRenderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma)
|
||||
{
|
||||
if (g_ActiveConfig.bUseXFB)
|
||||
{
|
||||
EfbInterface::yuv422_packed* xfb = (EfbInterface::yuv422_packed*)Memory::GetPointer(xfbAddr);
|
||||
UpdateColorTexture(xfb, fbWidth, fbHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
EfbInterface::BypassXFB(GetCurrentColorTexture(), fbWidth, fbHeight, rc, Gamma);
|
||||
}
|
||||
SWOGLWindow::s_instance->ShowImage(texture, 1.0);
|
||||
|
||||
// Save screenshot
|
||||
if (IsFrameDumping())
|
||||
{
|
||||
AVIDump::Frame state = AVIDump::FetchState(ticks);
|
||||
DumpFrameData(GetCurrentColorTexture(), fbWidth, fbHeight, fbWidth * 4, state);
|
||||
//DumpFrameData(GetCurrentColorTexture(), fbWidth, fbHeight, fbWidth * 4, state);
|
||||
FinishFrameData();
|
||||
}
|
||||
|
||||
|
@ -136,15 +59,9 @@ void SWRenderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
|||
|
||||
DrawDebugText();
|
||||
|
||||
SWOGLWindow::s_instance->ShowImage(GetCurrentColorTexture(), fbWidth * 4, fbWidth, fbHeight, 1.0);
|
||||
SWOGLWindow::s_instance->ShowImage(texture, 1.0);
|
||||
|
||||
UpdateActiveConfig();
|
||||
|
||||
// virtual XFB is not supported
|
||||
if (g_ActiveConfig.bUseXFB)
|
||||
{
|
||||
Config::SetCurrent(Config::GFX_USE_REAL_XFB, true);
|
||||
}
|
||||
}
|
||||
|
||||
u32 SWRenderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData)
|
||||
|
|
|
@ -14,16 +14,10 @@ class SWRenderer : public Renderer
|
|||
{
|
||||
public:
|
||||
SWRenderer();
|
||||
~SWRenderer() override;
|
||||
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
|
||||
static u8* GetNextColorTexture();
|
||||
static u8* GetCurrentColorTexture();
|
||||
void SwapColorTexture();
|
||||
void UpdateColorTexture(EfbInterface::yuv422_packed* xfb, u32 fbWidth, u32 fbHeight);
|
||||
|
||||
void RenderText(const std::string& pstr, int left, int top, u32 color) override;
|
||||
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
|
||||
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {}
|
||||
|
@ -32,8 +26,7 @@ public:
|
|||
|
||||
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
|
||||
|
||||
void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc,
|
||||
u64 ticks, float Gamma) override;
|
||||
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
|
||||
|
||||
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
|
||||
u32 color, u32 z) override;
|
||||
|
|
|
@ -8,16 +8,17 @@
|
|||
#include <utility>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/GL/GLInterfaceBase.h"
|
||||
|
||||
#include "VideoBackends/Software/Clipper.h"
|
||||
#include "VideoBackends/Software/DebugUtil.h"
|
||||
#include "VideoBackends/Software/EfbCopy.h"
|
||||
#include "VideoBackends/Software/EfbInterface.h"
|
||||
#include "VideoBackends/Software/Rasterizer.h"
|
||||
#include "VideoBackends/Software/SWOGLWindow.h"
|
||||
#include "VideoBackends/Software/SWRenderer.h"
|
||||
#include "VideoBackends/Software/SWTexture.h"
|
||||
#include "VideoBackends/Software/SWVertexLoader.h"
|
||||
#include "VideoBackends/Software/TextureCache.h"
|
||||
#include "VideoBackends/Software/VideoBackend.h"
|
||||
|
||||
#include "VideoCommon/FramebufferManagerBase.h"
|
||||
|
@ -46,58 +47,6 @@ public:
|
|||
bool IsFlushed() const override { return true; }
|
||||
};
|
||||
|
||||
class TextureCache : public TextureCacheBase
|
||||
{
|
||||
public:
|
||||
bool CompileShaders() override { return true; }
|
||||
void DeleteShaders() override {}
|
||||
void ConvertTexture(TCacheEntry* entry, TCacheEntry* unconverted, const void* palette,
|
||||
TLUTFormat format) override
|
||||
{
|
||||
}
|
||||
void CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width, u32 bytes_per_row,
|
||||
u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect,
|
||||
bool scale_by_half) override
|
||||
{
|
||||
EfbCopy::CopyEfb();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override
|
||||
{
|
||||
return std::make_unique<SWTexture>(config);
|
||||
}
|
||||
|
||||
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
|
||||
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override
|
||||
{
|
||||
EfbCopy::CopyEfb();
|
||||
}
|
||||
};
|
||||
|
||||
class XFBSource : public XFBSourceBase
|
||||
{
|
||||
void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) override {}
|
||||
void CopyEFB(float Gamma) override {}
|
||||
};
|
||||
|
||||
class FramebufferManager : public FramebufferManagerBase
|
||||
{
|
||||
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 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,
|
||||
float Gamma = 1.0f) override
|
||||
{
|
||||
EfbCopy::CopyEfb();
|
||||
}
|
||||
};
|
||||
|
||||
std::string VideoSoftware::GetName() const
|
||||
{
|
||||
return "Software Renderer";
|
||||
|
@ -123,6 +72,7 @@ void VideoSoftware::InitBackendInfo()
|
|||
g_Config.backend_info.bSupportsGPUTextureDecoding = false;
|
||||
g_Config.backend_info.bSupportsST3CTextures = false;
|
||||
g_Config.backend_info.bSupportsBPTCTextures = false;
|
||||
g_Config.backend_info.bSupportsCopyToVram = false;
|
||||
|
||||
// aamodes
|
||||
g_Config.backend_info.AAModes = {1};
|
||||
|
@ -169,12 +119,14 @@ void VideoSoftware::Video_Cleanup()
|
|||
// This is called after Video_Initialize() from the Core
|
||||
void VideoSoftware::Video_Prepare()
|
||||
{
|
||||
GLInterface->MakeCurrent();
|
||||
SWOGLWindow::s_instance->Prepare();
|
||||
|
||||
g_renderer = std::make_unique<SWRenderer>();
|
||||
g_vertex_manager = std::make_unique<SWVertexLoader>();
|
||||
g_perf_query = std::make_unique<PerfQuery>();
|
||||
g_texture_cache = std::make_unique<TextureCache>();
|
||||
SWRenderer::Init();
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManager>();
|
||||
}
|
||||
|
||||
unsigned int VideoSoftware::PeekMessages()
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
<ClInclude Include="SWTexture.h" />
|
||||
<ClInclude Include="SWVertexLoader.h" />
|
||||
<ClInclude Include="Tev.h" />
|
||||
<ClInclude Include="TextureCache.h" />
|
||||
<ClInclude Include="TextureEncoder.h" />
|
||||
<ClInclude Include="TextureSampler.h" />
|
||||
<ClInclude Include="TransformUnit.h" />
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "VideoBackends/Software/EfbCopy.h"
|
||||
#include "VideoBackends/Software/SWTexture.h"
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
|
||||
namespace SW
|
||||
{
|
||||
|
||||
class TextureCache : public TextureCacheBase
|
||||
{
|
||||
public:
|
||||
bool CompileShaders() override { return true; }
|
||||
void DeleteShaders() override {}
|
||||
void ConvertTexture(TCacheEntry* entry, TCacheEntry* unconverted, const void* palette,
|
||||
TLUTFormat format) override
|
||||
{
|
||||
}
|
||||
void CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width, u32 bytes_per_row,
|
||||
u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect,
|
||||
bool scale_by_half) override
|
||||
{
|
||||
EfbCopy::CopyEfb();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override
|
||||
{
|
||||
return std::make_unique<SWTexture>(config);
|
||||
}
|
||||
|
||||
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
|
||||
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override
|
||||
{
|
||||
EfbCopy::CopyEfb();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace SW
|
|
@ -89,11 +89,6 @@ MultisamplingState FramebufferManager::GetEFBMultisamplingState() const
|
|||
return ms;
|
||||
}
|
||||
|
||||
std::pair<u32, u32> FramebufferManager::GetTargetSize() const
|
||||
{
|
||||
return std::make_pair(GetEFBWidth(), GetEFBHeight());
|
||||
}
|
||||
|
||||
bool FramebufferManager::Initialize()
|
||||
{
|
||||
if (!CreateEFBRenderPass())
|
||||
|
@ -1450,15 +1445,6 @@ VKTexture* XFBSource::GetTexture() const
|
|||
return static_cast<VKTexture*>(m_texture.get());
|
||||
}
|
||||
|
||||
void XFBSource::DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height)
|
||||
{
|
||||
// Guest memory -> GPU EFB Textures
|
||||
const u8* src_ptr = Memory::GetPointer(xfb_addr);
|
||||
_assert_(src_ptr);
|
||||
TextureCache::GetInstance()->GetTextureConverter()->DecodeYUYVTextureFromMemory(
|
||||
static_cast<VKTexture*>(m_texture.get()), src_ptr, fb_width, fb_width * 2, fb_height);
|
||||
}
|
||||
|
||||
void XFBSource::CopyEFB(float gamma)
|
||||
{
|
||||
// Pending/batched EFB pokes should be included in the copied image.
|
||||
|
|
|
@ -43,7 +43,6 @@ public:
|
|||
u32 GetEFBLayers() const;
|
||||
VkSampleCountFlagBits GetEFBSamples() const;
|
||||
MultisamplingState GetEFBMultisamplingState() const;
|
||||
std::pair<u32, u32> GetTargetSize() const override;
|
||||
|
||||
std::unique_ptr<XFBSourceBase> CreateXFBSource(unsigned int target_width,
|
||||
unsigned int target_height,
|
||||
|
@ -178,8 +177,6 @@ public:
|
|||
~XFBSource();
|
||||
|
||||
VKTexture* GetTexture() const;
|
||||
// Guest -> GPU EFB Textures
|
||||
void DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height) override;
|
||||
|
||||
// Used for virtual XFB
|
||||
void CopyEFB(float gamma) override;
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "VideoCommon/SamplerCommon.h"
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
|
||||
|
@ -485,27 +486,19 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
|
|||
BindEFBToStateTracker();
|
||||
}
|
||||
|
||||
void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height,
|
||||
const EFBRectangle& rc, u64 ticks, float gamma)
|
||||
void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma)
|
||||
{
|
||||
// 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 && !g_ActiveConfig.RealXFBEnabled()) || !fb_width || !fb_height)
|
||||
if (!m_xfb_written)
|
||||
{
|
||||
Core::Callback_VideoCopiedToXFB(false);
|
||||
return;
|
||||
}
|
||||
u32 xfb_count = 0;
|
||||
const XFBSourceBase* const* xfb_sources =
|
||||
FramebufferManager::GetXFBSource(xfb_addr, fb_stride, fb_height, &xfb_count);
|
||||
if (g_ActiveConfig.VirtualXFBEnabled() && (!xfb_sources || xfb_count == 0))
|
||||
{
|
||||
Core::Callback_VideoCopiedToXFB(false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* xfb_texture = static_cast<VKTexture*>(texture);
|
||||
|
||||
// End the current render pass.
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
StateTracker::GetInstance()->OnEndFrame();
|
||||
|
@ -514,14 +507,6 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
|
|||
// are determined by guest state. Currently, the only way to catch these is to update every frame.
|
||||
UpdateDrawRectangle();
|
||||
|
||||
// Scale the source rectangle to the internal resolution when XFB is disabled.
|
||||
TargetRectangle scaled_efb_rect = Renderer::ConvertEFBRectangle(rc);
|
||||
|
||||
// If MSAA is enabled, and we're not using XFB, we need to resolve the EFB framebuffer before
|
||||
// rendering the final image to the screen, or dumping the frame. This is because we can't resolve
|
||||
// an image within a render pass, which will have already started by the time it is used.
|
||||
TransitionBuffersForSwap(scaled_efb_rect, xfb_sources, xfb_count);
|
||||
|
||||
// Render the frame dump image if enabled.
|
||||
if (IsFrameDumping())
|
||||
{
|
||||
|
@ -529,8 +514,8 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
|
|||
if (!m_frame_dumping_active)
|
||||
StartFrameDumping();
|
||||
|
||||
DrawFrameDump(scaled_efb_rect, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height,
|
||||
ticks);
|
||||
/* DrawFrameDump(scaled_efb_rect, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height,
|
||||
ticks);*/
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -547,7 +532,7 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
|
|||
// Draw to the screen if we have a swap chain.
|
||||
if (m_swap_chain)
|
||||
{
|
||||
DrawScreen(scaled_efb_rect, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
|
||||
DrawScreen(xfb_texture);
|
||||
|
||||
// Submit the current command buffer, signaling rendering finished semaphore when it's done
|
||||
// Because this final command buffer is rendering to the swap chain, we need to wait for
|
||||
|
@ -581,15 +566,12 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
|
|||
// Handle host window resizes.
|
||||
CheckForSurfaceChange();
|
||||
|
||||
// Handle output size changes from the guest.
|
||||
// There is a downside to doing this here is that if the game changes its XFB source area,
|
||||
// the changes will be delayed by one frame. For the moment it has to be done here because
|
||||
// this can cause a target size change, which would result in a black frame if done earlier.
|
||||
CheckForTargetResize(fb_width, fb_stride, fb_height);
|
||||
if (CalculateTargetSize())
|
||||
ResizeEFBTextures();
|
||||
|
||||
// Update the window size based on the frame that was just rendered.
|
||||
// Due to depending on guest state, we need to call this every frame.
|
||||
SetWindowSize(static_cast<int>(fb_stride), static_cast<int>(fb_height));
|
||||
SetWindowSize(xfb_texture->config.width, xfb_texture->config.height);
|
||||
|
||||
// Clean up stale textures.
|
||||
TextureCache::GetInstance()->Cleanup(frameCount);
|
||||
|
@ -714,9 +696,7 @@ void Renderer::DrawRealXFB(VkRenderPass render_pass, const TargetRectangle& targ
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::DrawScreen(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::DrawScreen(VKTexture* xfb_texture)
|
||||
{
|
||||
VkResult res;
|
||||
if (!g_command_buffer_mgr->CheckLastPresentFail())
|
||||
|
@ -767,9 +747,9 @@ void Renderer::DrawScreen(const TargetRectangle& scaled_efb_rect, u32 xfb_addr,
|
|||
vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &info,
|
||||
VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
// Draw guest buffers (EFB or XFB)
|
||||
DrawFrame(m_swap_chain->GetRenderPass(), GetTargetRectangle(), scaled_efb_rect, xfb_addr,
|
||||
xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
|
||||
// Draw
|
||||
TargetRectangle source_rc = xfb_texture->config.Rect();
|
||||
BlitScreen(m_swap_chain->GetRenderPass(), GetTargetRectangle(), source_rc, xfb_texture->GetRawTexIdentifier());
|
||||
|
||||
// Draw OSD
|
||||
Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0,
|
||||
|
@ -1055,7 +1035,7 @@ void Renderer::DestroyFrameDumpResources()
|
|||
|
||||
void Renderer::CheckForTargetResize(u32 fb_width, u32 fb_stride, u32 fb_height)
|
||||
{
|
||||
if (FramebufferManagerBase::LastXfbWidth() == fb_stride &&
|
||||
/*if (FramebufferManagerBase::LastXfbWidth() == fb_stride &&
|
||||
FramebufferManagerBase::LastXfbHeight() == fb_height)
|
||||
{
|
||||
return;
|
||||
|
@ -1068,7 +1048,7 @@ void Renderer::CheckForTargetResize(u32 fb_width, u32 fb_stride, u32 fb_height)
|
|||
|
||||
// Changing the XFB source area may alter the target size.
|
||||
if (CalculateTargetSize())
|
||||
ResizeEFBTextures();
|
||||
ResizeEFBTextures();*/
|
||||
}
|
||||
|
||||
void Renderer::CheckForSurfaceChange()
|
||||
|
|
|
@ -23,6 +23,7 @@ class SwapChain;
|
|||
class StagingTexture2D;
|
||||
class Texture2D;
|
||||
class RasterFont;
|
||||
class VKTexture;
|
||||
|
||||
class Renderer : public ::Renderer
|
||||
{
|
||||
|
@ -43,8 +44,7 @@ public:
|
|||
void BBoxWrite(int index, u16 value) override;
|
||||
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
|
||||
|
||||
void SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, const EFBRectangle& rc,
|
||||
u64 ticks, float gamma) override;
|
||||
void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
|
||||
|
||||
void ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable, bool z_enable,
|
||||
u32 color, u32 z) override;
|
||||
|
@ -106,9 +106,7 @@ private:
|
|||
u32 fb_stride, u32 fb_height);
|
||||
|
||||
// Draw the frame, as well as the OSD to the swap chain.
|
||||
void DrawScreen(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 DrawScreen(VKTexture* xfb_texture);
|
||||
|
||||
// Draw the frame only to the screenshot buffer.
|
||||
bool DrawFrameDump(const TargetRectangle& scaled_efb_rect, u32 xfb_addr,
|
||||
|
|
|
@ -246,6 +246,7 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config)
|
|||
config->backend_info.bSupportsST3CTextures = false; // Dependent on features.
|
||||
config->backend_info.bSupportsBPTCTextures = false; // Dependent on features.
|
||||
config->backend_info.bSupportsReversedDepthRange = false; // No support yet due to driver bugs.
|
||||
config->backend_info.bSupportsCopyToVram = true; // Assumed support.
|
||||
}
|
||||
|
||||
void VulkanContext::PopulateBackendInfoAdapters(VideoConfig* config, const GPUList& gpu_list)
|
||||
|
|
|
@ -250,16 +250,18 @@ static void BPWritten(const BPCmd& bp)
|
|||
float num_xfb_lines = 1.0f + bpmem.copyTexSrcWH.y * yScale;
|
||||
|
||||
u32 height = static_cast<u32>(num_xfb_lines);
|
||||
if (height > MAX_XFB_HEIGHT)
|
||||
{
|
||||
INFO_LOG(VIDEO, "Tried to scale EFB to too many XFB lines: %d (%f)", height, num_xfb_lines);
|
||||
height = MAX_XFB_HEIGHT;
|
||||
}
|
||||
|
||||
DEBUG_LOG(VIDEO, "RenderToXFB: destAddr: %08x | srcRect {%d %d %d %d} | fbWidth: %u | "
|
||||
"fbStride: %u | fbHeight: %u",
|
||||
destAddr, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom,
|
||||
bpmem.copyTexSrcWH.x + 1, destStride, height);
|
||||
|
||||
bool is_depth_copy = bpmem.zcontrol.pixel_format == PEControl::Z24;
|
||||
g_texture_cache->CopyRenderTargetToTexture(destAddr, EFBCopyFormat::XFB, destStride,
|
||||
is_depth_copy, srcRect, false,
|
||||
false);
|
||||
|
||||
// This stays in to signal end of a "frame"
|
||||
g_renderer->RenderToXFB(destAddr, srcRect, destStride, height, s_gammaLUT[PE_copy.gamma]);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,6 @@ FramebufferManagerBase::VirtualXFBListType
|
|||
std::array<const XFBSourceBase*, FramebufferManagerBase::MAX_VIRTUAL_XFB>
|
||||
FramebufferManagerBase::m_overlappingXFBArray;
|
||||
|
||||
unsigned int FramebufferManagerBase::s_last_xfb_width = 1;
|
||||
unsigned int FramebufferManagerBase::s_last_xfb_height = 1;
|
||||
|
||||
unsigned int FramebufferManagerBase::m_EFBLayers = 1;
|
||||
|
||||
FramebufferManagerBase::FramebufferManagerBase()
|
||||
|
@ -40,85 +37,6 @@ FramebufferManagerBase::~FramebufferManagerBase()
|
|||
m_realXFBSource.reset();
|
||||
}
|
||||
|
||||
const XFBSourceBase* const* FramebufferManagerBase::GetXFBSource(u32 xfbAddr, u32 fbWidth,
|
||||
u32 fbHeight, u32* xfbCountP)
|
||||
{
|
||||
if (!g_ActiveConfig.bUseXFB)
|
||||
return nullptr;
|
||||
|
||||
if (g_ActiveConfig.bUseRealXFB)
|
||||
return GetRealXFBSource(xfbAddr, fbWidth, fbHeight, xfbCountP);
|
||||
else
|
||||
return GetVirtualXFBSource(xfbAddr, fbWidth, fbHeight, xfbCountP);
|
||||
}
|
||||
|
||||
const XFBSourceBase* const* FramebufferManagerBase::GetRealXFBSource(u32 xfbAddr, u32 fbWidth,
|
||||
u32 fbHeight, u32* xfbCountP)
|
||||
{
|
||||
*xfbCountP = 1;
|
||||
|
||||
// recreate if needed
|
||||
if (m_realXFBSource &&
|
||||
(m_realXFBSource->texWidth != fbWidth || m_realXFBSource->texHeight != fbHeight))
|
||||
m_realXFBSource.reset();
|
||||
|
||||
if (!m_realXFBSource && g_framebuffer_manager)
|
||||
m_realXFBSource = g_framebuffer_manager->CreateXFBSource(fbWidth, fbHeight, 1);
|
||||
|
||||
if (!m_realXFBSource)
|
||||
return nullptr;
|
||||
|
||||
m_realXFBSource->srcAddr = xfbAddr;
|
||||
|
||||
m_realXFBSource->srcWidth = MAX_XFB_WIDTH;
|
||||
m_realXFBSource->srcHeight = MAX_XFB_HEIGHT;
|
||||
|
||||
m_realXFBSource->texWidth = fbWidth;
|
||||
m_realXFBSource->texHeight = fbHeight;
|
||||
|
||||
m_realXFBSource->sourceRc.left = 0;
|
||||
m_realXFBSource->sourceRc.top = 0;
|
||||
m_realXFBSource->sourceRc.right = fbWidth;
|
||||
m_realXFBSource->sourceRc.bottom = fbHeight;
|
||||
|
||||
// Decode YUYV data from GameCube RAM
|
||||
m_realXFBSource->DecodeToTexture(xfbAddr, fbWidth, fbHeight);
|
||||
|
||||
m_overlappingXFBArray[0] = m_realXFBSource.get();
|
||||
return &m_overlappingXFBArray[0];
|
||||
}
|
||||
|
||||
const XFBSourceBase* const*
|
||||
FramebufferManagerBase::GetVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32* xfbCountP)
|
||||
{
|
||||
u32 xfbCount = 0;
|
||||
|
||||
if (m_virtualXFBList.empty()) // no Virtual XFBs available
|
||||
return nullptr;
|
||||
|
||||
u32 srcLower = xfbAddr;
|
||||
u32 srcUpper = xfbAddr + 2 * fbWidth * fbHeight;
|
||||
|
||||
VirtualXFBListType::reverse_iterator it = m_virtualXFBList.rbegin(),
|
||||
vlend = m_virtualXFBList.rend();
|
||||
for (; it != vlend; ++it)
|
||||
{
|
||||
VirtualXFB* vxfb = &*it;
|
||||
|
||||
u32 dstLower = vxfb->xfbAddr;
|
||||
u32 dstUpper = vxfb->xfbAddr + 2 * vxfb->xfbWidth * vxfb->xfbHeight;
|
||||
|
||||
if (AddressRangesOverlap(srcLower, srcUpper, dstLower, dstUpper))
|
||||
{
|
||||
m_overlappingXFBArray[xfbCount] = vxfb->xfbSource.get();
|
||||
++xfbCount;
|
||||
}
|
||||
}
|
||||
|
||||
*xfbCountP = xfbCount;
|
||||
return &m_overlappingXFBArray[0];
|
||||
}
|
||||
|
||||
void FramebufferManagerBase::CopyToXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight,
|
||||
const EFBRectangle& sourceRc, float Gamma)
|
||||
{
|
||||
|
@ -247,19 +165,3 @@ void FramebufferManagerBase::ReplaceVirtualXFB()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
int FramebufferManagerBase::ScaleToVirtualXfbWidth(int x, const TargetRectangle& target_rectangle)
|
||||
{
|
||||
if (g_ActiveConfig.RealXFBEnabled())
|
||||
return x;
|
||||
|
||||
return x * target_rectangle.GetWidth() / s_last_xfb_width;
|
||||
}
|
||||
|
||||
int FramebufferManagerBase::ScaleToVirtualXfbHeight(int y, const TargetRectangle& target_rectangle)
|
||||
{
|
||||
if (g_ActiveConfig.RealXFBEnabled())
|
||||
return y;
|
||||
|
||||
return y * target_rectangle.GetHeight() / s_last_xfb_height;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ inline bool AddressRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper)
|
|||
struct XFBSourceBase
|
||||
{
|
||||
virtual ~XFBSourceBase() {}
|
||||
virtual void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) = 0;
|
||||
|
||||
virtual void CopyEFB(float Gamma) = 0;
|
||||
|
||||
|
@ -50,15 +49,6 @@ public:
|
|||
|
||||
static void CopyToXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,
|
||||
float Gamma);
|
||||
static const XFBSourceBase* const* GetXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight,
|
||||
u32* xfbCount);
|
||||
|
||||
static void SetLastXfbWidth(unsigned int width) { s_last_xfb_width = width; }
|
||||
static void SetLastXfbHeight(unsigned int height) { s_last_xfb_height = height; }
|
||||
static unsigned int LastXfbWidth() { return s_last_xfb_width; }
|
||||
static unsigned int LastXfbHeight() { return s_last_xfb_height; }
|
||||
static int ScaleToVirtualXfbWidth(int x, const TargetRectangle& target_rectangle);
|
||||
static int ScaleToVirtualXfbHeight(int y, const TargetRectangle& target_rectangle);
|
||||
|
||||
static unsigned int GetEFBLayers() { return m_EFBLayers; }
|
||||
virtual std::pair<u32, u32> GetTargetSize() const = 0;
|
||||
|
@ -93,18 +83,10 @@ private:
|
|||
static void CopyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,
|
||||
float Gamma = 1.0f);
|
||||
|
||||
static const XFBSourceBase* const* GetRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight,
|
||||
u32* xfbCount);
|
||||
static const XFBSourceBase* const* GetVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight,
|
||||
u32* xfbCount);
|
||||
|
||||
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;
|
||||
|
||||
static unsigned int s_last_xfb_width;
|
||||
static unsigned int s_last_xfb_height;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<FramebufferManagerBase> g_framebuffer_manager;
|
||||
|
|
|
@ -84,9 +84,6 @@ static float AspectToWidescreen(float aspect)
|
|||
Renderer::Renderer(int backbuffer_width, int backbuffer_height)
|
||||
: m_backbuffer_width(backbuffer_width), m_backbuffer_height(backbuffer_height)
|
||||
{
|
||||
FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH);
|
||||
FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT);
|
||||
|
||||
UpdateActiveConfig();
|
||||
UpdateDrawRectangle();
|
||||
CalculateTargetSize();
|
||||
|
@ -116,19 +113,6 @@ void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStri
|
|||
return;
|
||||
|
||||
m_xfb_written = true;
|
||||
|
||||
if (g_ActiveConfig.bUseXFB)
|
||||
{
|
||||
FramebufferManagerBase::CopyToXFB(xfbAddr, fbStride, fbHeight, sourceRc, Gamma);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The timing is not predictable here. So try to use the XFB path to dump frames.
|
||||
u64 ticks = CoreTiming::GetTicks();
|
||||
|
||||
// below div two to convert from bytes to pixels - it expects width, not stride
|
||||
Swap(xfbAddr, fbStride / 2, fbStride / 2, fbHeight, sourceRc, ticks, Gamma);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Renderer::GetEFBScale() const
|
||||
|
@ -433,9 +417,7 @@ TargetRectangle Renderer::CalculateFrameDumpDrawRectangle() const
|
|||
rc.top = 0;
|
||||
|
||||
// If full-resolution frame dumping is disabled, just use the window draw rectangle.
|
||||
// Also do this if RealXFB is enabled, since the image has been downscaled for the XFB copy
|
||||
// anyway, and there's no point writing an upscaled frame with no filtering.
|
||||
if (!g_ActiveConfig.bInternalResolutionFrameDumps || g_ActiveConfig.RealXFBEnabled())
|
||||
if (!g_ActiveConfig.bInternalResolutionFrameDumps)
|
||||
{
|
||||
// But still remove the borders, since the caller expects this.
|
||||
rc.right = m_target_rectangle.GetWidth();
|
||||
|
@ -663,8 +645,19 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
|||
m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total;
|
||||
}
|
||||
|
||||
// TODO: merge more generic parts into VideoCommon
|
||||
SwapImpl(xfbAddr, fbWidth, fbStride, fbHeight, rc, ticks, Gamma);
|
||||
if (xfbAddr && fbWidth && fbStride && fbHeight)
|
||||
{
|
||||
constexpr int force_safe_texture_cache_hash = 0;
|
||||
// Get the current XFB from texture cache
|
||||
auto* xfb_entry = g_texture_cache->GetTexture(xfbAddr, fbWidth, fbHeight, TextureFormat::XFB,
|
||||
force_safe_texture_cache_hash);
|
||||
|
||||
// TODO, check if xfb_entry is a duplicate of the previous frame and skip SwapImpl
|
||||
|
||||
|
||||
// TODO: merge more generic parts into VideoCommon
|
||||
g_renderer->SwapImpl(xfb_entry->texture.get(), rc, ticks, Gamma);
|
||||
}
|
||||
|
||||
if (m_xfb_written)
|
||||
m_fps_counter.Update();
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
||||
class AbstractTexture;
|
||||
class PostProcessingShaderImplementation;
|
||||
enum class EFBAccessType;
|
||||
|
||||
|
@ -132,8 +133,7 @@ public:
|
|||
// Finish up the current frame, print some stats
|
||||
void Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, u64 ticks,
|
||||
float Gamma = 1.0f);
|
||||
virtual void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
|
||||
const EFBRectangle& rc, u64 ticks, float Gamma = 1.0f) = 0;
|
||||
virtual void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma = 1.0f) = 0;
|
||||
|
||||
PEControl::PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; }
|
||||
void StorePixelFormat(PEControl::PixelFormat new_format) { m_prev_efb_format = new_format; }
|
||||
|
|
|
@ -238,7 +238,8 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, u8* palette, TLUTForma
|
|||
if (!decoded_entry)
|
||||
return nullptr;
|
||||
|
||||
decoded_entry->SetGeneralParameters(entry->addr, entry->size_in_bytes, entry->format);
|
||||
decoded_entry->SetGeneralParameters(entry->addr, entry->size_in_bytes, entry->format,
|
||||
entry->should_force_safe_hashing);
|
||||
decoded_entry->SetDimensions(entry->native_width, entry->native_height, 1);
|
||||
decoded_entry->SetHashes(entry->base_hash, entry->hash);
|
||||
decoded_entry->frameCount = FRAMECOUNT_INVALID;
|
||||
|
@ -462,20 +463,6 @@ static u32 CalculateLevelSize(u32 level_0_size, u32 level)
|
|||
return std::max(level_0_size >> level, 1u);
|
||||
}
|
||||
|
||||
// Used by TextureCacheBase::Load
|
||||
TextureCacheBase::TCacheEntry* TextureCacheBase::ReturnEntry(unsigned int stage, TCacheEntry* entry)
|
||||
{
|
||||
entry->frameCount = FRAMECOUNT_INVALID;
|
||||
bound_textures[stage] = entry;
|
||||
|
||||
GFX_DEBUGGER_PAUSE_AT(NEXT_TEXTURE_CHANGE, true);
|
||||
|
||||
// We need to keep track of invalided textures until they have actually been replaced or re-loaded
|
||||
valid_bind_points.set(stage);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void TextureCacheBase::BindTextures()
|
||||
{
|
||||
for (size_t i = 0; i < bound_textures.size(); ++i)
|
||||
|
@ -625,7 +612,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
// if this stage was not invalidated by changes to texture registers, keep the current texture
|
||||
if (IsValidBindPoint(stage) && bound_textures[stage])
|
||||
{
|
||||
return ReturnEntry(stage, bound_textures[stage]);
|
||||
return bound_textures[stage];
|
||||
}
|
||||
|
||||
const FourTexUnits& tex = bpmem.tex[stage >> 2];
|
||||
|
@ -639,7 +626,35 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
const bool use_mipmaps = SamplerCommon::AreBpTexMode0MipmapsEnabled(tex.texMode0[id]);
|
||||
u32 tex_levels = use_mipmaps ? ((tex.texMode1[id].max_lod + 0xf) / 0x10 + 1) : 1;
|
||||
const bool from_tmem = tex.texImage1[id].image_type != 0;
|
||||
const u32 tmem_address_even = from_tmem ? tex.texImage1[id].tmem_even * TMEM_LINE_SIZE : 0;
|
||||
const u32 tmem_address_odd = from_tmem ? tex.texImage2[id].tmem_odd * TMEM_LINE_SIZE : 0;
|
||||
|
||||
auto entry = GetTexture(address, width, height, texformat,
|
||||
g_ActiveConfig.iSafeTextureCache_ColorSamples, tlutaddr, tlutfmt,
|
||||
use_mipmaps, tex_levels, from_tmem, tmem_address_even,
|
||||
tmem_address_odd);
|
||||
|
||||
if (!entry)
|
||||
return nullptr;
|
||||
|
||||
entry->frameCount = FRAMECOUNT_INVALID;
|
||||
bound_textures[stage] = entry;
|
||||
|
||||
GFX_DEBUGGER_PAUSE_AT(NEXT_TEXTURE_CHANGE, true);
|
||||
|
||||
// We need to keep track of invalided textures until they have actually been replaced or re-loaded
|
||||
valid_bind_points.set(stage);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
TextureCacheBase::TCacheEntry* TextureCacheBase::GetTexture(u32 address, u32 width, u32 height,
|
||||
const TextureFormat texformat,
|
||||
const int textureCacheSafetyColorSampleSize, u32 tlutaddr,
|
||||
TLUTFormat tlutfmt, bool use_mipmaps,
|
||||
u32 tex_levels, bool from_tmem, u32 tmem_address_even,
|
||||
u32 tmem_address_odd)
|
||||
{
|
||||
// TexelSizeInNibbles(format) * width * height / 16;
|
||||
const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat);
|
||||
const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat);
|
||||
|
@ -683,9 +698,12 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
TexDecoder_GetTextureSizeInBytes(expanded_mip_width, expanded_mip_height, texformat);
|
||||
}
|
||||
|
||||
// TODO: the texture cache lookup is based on address, but a texture from tmem has no reason
|
||||
// to have a unique and valid address. This could result in a regular texture and a tmem
|
||||
// texture aliasing onto the same texture cache entry.
|
||||
const u8* src_data;
|
||||
if (from_tmem)
|
||||
src_data = &texMem[bpmem.tex[stage / 4].texImage1[stage % 4].tmem_even * TMEM_LINE_SIZE];
|
||||
src_data = &texMem[tmem_address_even];
|
||||
else
|
||||
src_data = Memory::GetPointer(address);
|
||||
|
||||
|
@ -704,13 +722,13 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
|
||||
// TODO: This doesn't hash GB tiles for preloaded RGBA8 textures (instead, it's hashing more data
|
||||
// from the low tmem bank than it should)
|
||||
base_hash = GetHash64(src_data, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples);
|
||||
base_hash = GetHash64(src_data, texture_size, textureCacheSafetyColorSampleSize);
|
||||
u32 palette_size = 0;
|
||||
if (isPaletteTexture)
|
||||
{
|
||||
palette_size = TexDecoder_GetPaletteSize(texformat);
|
||||
full_hash = base_hash ^ GetHash64(&texMem[tlutaddr], palette_size,
|
||||
g_ActiveConfig.iSafeTextureCache_ColorSamples);
|
||||
textureCacheSafetyColorSampleSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -789,7 +807,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
// texture formats. I'm not sure what effect checking width/height/levels
|
||||
// would have.
|
||||
if (!isPaletteTexture || !g_Config.backend_info.bSupportsPaletteConversion)
|
||||
return ReturnEntry(stage, entry);
|
||||
return entry;
|
||||
|
||||
// Note that we found an unconverted EFB copy, then continue. We'll
|
||||
// perform the conversion later. Currently, we only convert EFB copies to
|
||||
|
@ -816,7 +834,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
{
|
||||
entry = DoPartialTextureUpdates(iter->second, &texMem[tlutaddr], tlutfmt);
|
||||
|
||||
return ReturnEntry(stage, entry);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -841,7 +859,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
|
||||
if (decoded_entry)
|
||||
{
|
||||
return ReturnEntry(stage, decoded_entry);
|
||||
return decoded_entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -851,9 +869,9 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
// textures cause unnecessary slowdowns
|
||||
// Example: Tales of Symphonia (GC) uses over 500 small textures in menus, but only around 70
|
||||
// different ones
|
||||
if (g_ActiveConfig.iSafeTextureCache_ColorSamples == 0 ||
|
||||
if (textureCacheSafetyColorSampleSize == 0 ||
|
||||
std::max(texture_size, palette_size) <=
|
||||
(u32)g_ActiveConfig.iSafeTextureCache_ColorSamples * 8)
|
||||
(u32)textureCacheSafetyColorSampleSize * 8)
|
||||
{
|
||||
auto hash_range = textures_by_hash.equal_range(full_hash);
|
||||
TexHashCache::iterator hash_iter = hash_range.first;
|
||||
|
@ -866,7 +884,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
{
|
||||
entry = DoPartialTextureUpdates(hash_iter->second, &texMem[tlutaddr], tlutfmt);
|
||||
|
||||
return ReturnEntry(stage, entry);
|
||||
return entry;
|
||||
}
|
||||
++hash_iter;
|
||||
}
|
||||
|
@ -936,64 +954,66 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
// Initialized to null because only software loading uses this buffer
|
||||
u8* dst_buffer = nullptr;
|
||||
|
||||
if (!hires_tex && decode_on_gpu)
|
||||
if (!hires_tex)
|
||||
{
|
||||
u32 row_stride = bytes_per_block * (expandedWidth / bsw);
|
||||
g_texture_cache->DecodeTextureOnGPU(entry, 0, src_data, texture_size, texformat, width, height,
|
||||
expandedWidth, expandedHeight, row_stride, tlut, tlutfmt);
|
||||
}
|
||||
else if (!hires_tex)
|
||||
{
|
||||
size_t decoded_texture_size = expandedWidth * sizeof(u32) * expandedHeight;
|
||||
|
||||
// Allocate memory for all levels at once
|
||||
size_t total_texture_size = decoded_texture_size;
|
||||
|
||||
// For the downsample, we need 2 buffers; 1 is 1/4 of the original texture, the other 1/16
|
||||
size_t mip_downsample_buffer_size = decoded_texture_size * 5 / 16;
|
||||
|
||||
size_t prev_level_size = decoded_texture_size;
|
||||
for (u32 i = 1; i < tex_levels; ++i)
|
||||
if (decode_on_gpu)
|
||||
{
|
||||
prev_level_size /= 4;
|
||||
total_texture_size += prev_level_size;
|
||||
}
|
||||
|
||||
// Add space for the downsampling at the end
|
||||
total_texture_size += mip_downsample_buffer_size;
|
||||
|
||||
CheckTempSize(total_texture_size);
|
||||
dst_buffer = temp;
|
||||
|
||||
if (!(texformat == TextureFormat::RGBA8 && from_tmem))
|
||||
{
|
||||
TexDecoder_Decode(dst_buffer, src_data, expandedWidth, expandedHeight, texformat, tlut,
|
||||
tlutfmt);
|
||||
u32 row_stride = bytes_per_block * (expandedWidth / bsw);
|
||||
g_texture_cache->DecodeTextureOnGPU(
|
||||
entry, 0, src_data, texture_size, texformat, width, height,
|
||||
expandedWidth, expandedHeight, row_stride, tlut, tlutfmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
u8* src_data_gb =
|
||||
&texMem[bpmem.tex[stage / 4].texImage2[stage % 4].tmem_odd * TMEM_LINE_SIZE];
|
||||
TexDecoder_DecodeRGBA8FromTmem(dst_buffer, src_data, src_data_gb, expandedWidth,
|
||||
expandedHeight);
|
||||
size_t decoded_texture_size = expandedWidth * sizeof(u32) * expandedHeight;
|
||||
|
||||
// Allocate memory for all levels at once
|
||||
size_t total_texture_size = decoded_texture_size;
|
||||
|
||||
// For the downsample, we need 2 buffers; 1 is 1/4 of the original texture, the other 1/16
|
||||
size_t mip_downsample_buffer_size = decoded_texture_size * 5 / 16;
|
||||
|
||||
size_t prev_level_size = decoded_texture_size;
|
||||
for (u32 i = 1; i < tex_levels; ++i)
|
||||
{
|
||||
prev_level_size /= 4;
|
||||
total_texture_size += prev_level_size;
|
||||
}
|
||||
|
||||
// Add space for the downsampling at the end
|
||||
total_texture_size += mip_downsample_buffer_size;
|
||||
|
||||
CheckTempSize(total_texture_size);
|
||||
dst_buffer = temp;
|
||||
if (!(texformat == TextureFormat::RGBA8 && from_tmem))
|
||||
{
|
||||
TexDecoder_Decode(dst_buffer, src_data, expandedWidth, expandedHeight, texformat, tlut,
|
||||
tlutfmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
u8* src_data_gb =
|
||||
&texMem[tmem_address_odd];
|
||||
TexDecoder_DecodeRGBA8FromTmem(dst_buffer, src_data, src_data_gb, expandedWidth, expandedHeight);
|
||||
}
|
||||
|
||||
entry->texture->Load(0, width, height, expandedWidth, dst_buffer, decoded_texture_size);
|
||||
|
||||
arbitrary_mip_detector.AddLevel(width, height, expandedWidth, dst_buffer);
|
||||
|
||||
dst_buffer += decoded_texture_size;
|
||||
}
|
||||
|
||||
entry->texture->Load(0, width, height, expandedWidth, dst_buffer, decoded_texture_size);
|
||||
|
||||
arbitrary_mip_detector.AddLevel(width, height, expandedWidth, dst_buffer);
|
||||
|
||||
dst_buffer += decoded_texture_size;
|
||||
}
|
||||
|
||||
iter = textures_by_address.emplace(address, entry);
|
||||
if (g_ActiveConfig.iSafeTextureCache_ColorSamples == 0 ||
|
||||
if (textureCacheSafetyColorSampleSize == 0 ||
|
||||
std::max(texture_size, palette_size) <=
|
||||
(u32)g_ActiveConfig.iSafeTextureCache_ColorSamples * 8)
|
||||
(u32)textureCacheSafetyColorSampleSize * 8)
|
||||
{
|
||||
entry->textures_by_hash_iter = textures_by_hash.emplace(full_hash, entry);
|
||||
}
|
||||
|
||||
entry->SetGeneralParameters(address, texture_size, full_format);
|
||||
entry->SetGeneralParameters(address, texture_size, full_format, false);
|
||||
entry->SetDimensions(nativeW, nativeH, tex_levels);
|
||||
entry->SetHashes(base_hash, full_hash);
|
||||
entry->is_efb_copy = false;
|
||||
|
@ -1025,9 +1045,8 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
const u8* ptr_odd = nullptr;
|
||||
if (from_tmem)
|
||||
{
|
||||
ptr_even = &texMem[bpmem.tex[stage / 4].texImage1[stage % 4].tmem_even * TMEM_LINE_SIZE +
|
||||
texture_size];
|
||||
ptr_odd = &texMem[bpmem.tex[stage / 4].texImage2[stage % 4].tmem_odd * TMEM_LINE_SIZE];
|
||||
ptr_even = &texMem[tmem_address_even + texture_size];
|
||||
ptr_odd = &texMem[tmem_address_odd];
|
||||
}
|
||||
|
||||
for (u32 level = 1; level != texLevels; ++level)
|
||||
|
@ -1081,7 +1100,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
|||
|
||||
entry = DoPartialTextureUpdates(iter->second, &texMem[tlutaddr], tlutfmt);
|
||||
|
||||
return ReturnEntry(stage, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstFormat,
|
||||
|
@ -1159,6 +1178,10 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
|
|||
unsigned int cbufid = UINT_MAX;
|
||||
PEControl::PixelFormat srcFormat = bpmem.zcontrol.pixel_format;
|
||||
bool efbHasAlpha = srcFormat == PEControl::RGBA6_Z24;
|
||||
|
||||
bool copy_to_ram = !g_ActiveConfig.bSkipEFBCopyToRam;
|
||||
bool copy_to_vram = g_ActiveConfig.backend_info.bSupportsCopyToVram;
|
||||
bool is_xfb_copy = false;
|
||||
|
||||
if (is_depth_copy)
|
||||
{
|
||||
|
@ -1388,6 +1411,15 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
|
|||
}
|
||||
break;
|
||||
|
||||
case EFBCopyFormat::XFB: // XFB copy, we just pretend it's an RGBX copy
|
||||
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1.0f;
|
||||
ColorMask[3] = 0.0f;
|
||||
fConstAdd[3] = 1.0f;
|
||||
cbufid = 30; // just re-use the RGBX8 cbufid from above
|
||||
copy_to_ram = g_ActiveConfig.bUseRealXFB;
|
||||
is_xfb_copy = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(VIDEO, "Unknown copy color format: 0x%X", static_cast<int>(dstFormat));
|
||||
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1.0f;
|
||||
|
@ -1430,9 +1462,6 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
|
|||
const u32 bytes_per_row = num_blocks_x * bytes_per_block;
|
||||
const u32 covered_range = num_blocks_y * dstStride;
|
||||
|
||||
bool copy_to_ram = !g_ActiveConfig.bSkipEFBCopyToRam;
|
||||
bool copy_to_vram = true;
|
||||
|
||||
if (copy_to_ram)
|
||||
{
|
||||
EFBCopyParams format(srcFormat, dstFormat, is_depth_copy, isIntensity);
|
||||
|
@ -1524,7 +1553,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
|
|||
|
||||
if (entry)
|
||||
{
|
||||
entry->SetGeneralParameters(dstAddr, 0, baseFormat);
|
||||
entry->SetGeneralParameters(dstAddr, 0, baseFormat, is_xfb_copy);
|
||||
entry->SetDimensions(tex_w, tex_h, 1);
|
||||
|
||||
entry->frameCount = FRAMECOUNT_INVALID;
|
||||
|
@ -1537,7 +1566,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
|
|||
u64 hash = entry->CalculateHash();
|
||||
entry->SetHashes(hash, hash);
|
||||
|
||||
if (g_ActiveConfig.bDumpEFBTarget)
|
||||
if (g_ActiveConfig.bDumpEFBTarget && !is_xfb_copy)
|
||||
{
|
||||
static int count = 0;
|
||||
entry->texture->Save(StringFromFormat("%sefb_frame_%i.png",
|
||||
|
@ -1699,12 +1728,22 @@ void TextureCacheBase::TCacheEntry::SetEfbCopy(u32 stride)
|
|||
size_in_bytes = memory_stride * NumBlocksY();
|
||||
}
|
||||
|
||||
int TextureCacheBase::TCacheEntry::HashSampleSize() const
|
||||
{
|
||||
if (should_force_safe_hashing)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return g_ActiveConfig.iSafeTextureCache_ColorSamples;
|
||||
}
|
||||
|
||||
u64 TextureCacheBase::TCacheEntry::CalculateHash() const
|
||||
{
|
||||
u8* ptr = Memory::GetPointer(addr);
|
||||
if (memory_stride == BytesPerRow())
|
||||
{
|
||||
return GetHash64(ptr, size_in_bytes, g_ActiveConfig.iSafeTextureCache_ColorSamples);
|
||||
return GetHash64(ptr, size_in_bytes, HashSampleSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1712,11 +1751,11 @@ u64 TextureCacheBase::TCacheEntry::CalculateHash() const
|
|||
u64 temp_hash = size_in_bytes;
|
||||
|
||||
u32 samples_per_row = 0;
|
||||
if (g_ActiveConfig.iSafeTextureCache_ColorSamples != 0)
|
||||
if (HashSampleSize() != 0)
|
||||
{
|
||||
// Hash at least 4 samples per row to avoid hashing in a bad pattern, like just on the left
|
||||
// side of the efb copy
|
||||
samples_per_row = std::max(g_ActiveConfig.iSafeTextureCache_ColorSamples / blocks, 4u);
|
||||
samples_per_row = std::max(HashSampleSize() / blocks, 4u);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < blocks; i++)
|
||||
|
|
|
@ -84,6 +84,7 @@ public:
|
|||
bool tmem_only = false; // indicates that this texture only exists in the tmem cache
|
||||
bool has_arbitrary_mips = false; // indicates that the mips in this texture are arbitrary
|
||||
// content, aren't just downscaled
|
||||
bool should_force_safe_hashing = false; // for XFB
|
||||
|
||||
unsigned int native_width,
|
||||
native_height; // Texture dimensions from the GameCube's point of view
|
||||
|
@ -105,11 +106,12 @@ public:
|
|||
|
||||
~TCacheEntry();
|
||||
|
||||
void SetGeneralParameters(u32 _addr, u32 _size, TextureAndTLUTFormat _format)
|
||||
void SetGeneralParameters(u32 _addr, u32 _size, TextureAndTLUTFormat _format, bool force_safe_hashing)
|
||||
{
|
||||
addr = _addr;
|
||||
size_in_bytes = _size;
|
||||
format = _format;
|
||||
should_force_safe_hashing = force_safe_hashing;
|
||||
}
|
||||
|
||||
void SetDimensions(unsigned int _native_width, unsigned int _native_height,
|
||||
|
@ -145,6 +147,7 @@ public:
|
|||
|
||||
u64 CalculateHash() const;
|
||||
|
||||
int HashSampleSize() const;
|
||||
u32 GetWidth() const { return texture->GetConfig().width; }
|
||||
u32 GetHeight() const { return texture->GetConfig().height; }
|
||||
u32 GetNumLevels() const { return texture->GetConfig().levels; }
|
||||
|
@ -172,7 +175,12 @@ public:
|
|||
TCacheEntry* Load(const u32 stage);
|
||||
static void InvalidateAllBindPoints() { valid_bind_points.reset(); }
|
||||
static bool IsValidBindPoint(u32 i) { return valid_bind_points.test(i); }
|
||||
void BindTextures();
|
||||
TCacheEntry* GetTexture(u32 address, u32 width, u32 height, const TextureFormat texformat,
|
||||
const int textureCacheSafetyColorSampleSize, u32 tlutaddr = 0,
|
||||
TLUTFormat tlutfmt = TLUTFormat::IA8, bool use_mipmaps = false,
|
||||
u32 tex_levels = 1, bool from_tmem = false, u32 tmem_address_even = 0,
|
||||
u32 tmem_address_odd = 0);
|
||||
virtual void BindTextures();
|
||||
void CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstFormat, u32 dstStride,
|
||||
bool is_depth_copy, const EFBRectangle& srcRect, bool isIntensity,
|
||||
bool scaleByHalf);
|
||||
|
@ -248,8 +256,6 @@ private:
|
|||
// Removes and unlinks texture from texture cache and returns it to the pool
|
||||
TexAddrCache::iterator InvalidateTexture(TexAddrCache::iterator t_iter);
|
||||
|
||||
TCacheEntry* ReturnEntry(unsigned int stage, TCacheEntry* entry);
|
||||
|
||||
TexAddrCache textures_by_address;
|
||||
TexHashCache textures_by_hash;
|
||||
TexPool texture_pool;
|
||||
|
|
|
@ -49,6 +49,8 @@ u16 GetEncodedSampleCount(EFBCopyFormat format)
|
|||
case EFBCopyFormat::RG8:
|
||||
case EFBCopyFormat::GB8:
|
||||
return 2;
|
||||
case EFBCopyFormat::XFB:
|
||||
return 2;
|
||||
default:
|
||||
PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEncodedSampleCount)", static_cast<int>(format));
|
||||
return 1;
|
||||
|
@ -656,6 +658,28 @@ static void WriteZ24Encoder(char*& p, APIType ApiType, const EFBCopyParams& para
|
|||
WriteEncoderEnd(p);
|
||||
}
|
||||
|
||||
static void WriteXFBEncoder(char*& p, APIType ApiType, const EFBCopyParams& params)
|
||||
{
|
||||
WriteSwizzler(p, EFBCopyFormat::XFB, ApiType);
|
||||
|
||||
WRITE(p, " float3 y_const = float3(0.257, 0.504, 0.098);\n");
|
||||
WRITE(p, " float3 u_const = float3(-0.148, -0.291, 0.439);\n");
|
||||
WRITE(p, " float3 v_const = float3(0.439, -0.368, -0.071);\n");
|
||||
WRITE(p, " float3 color0;\n");
|
||||
WRITE(p, " float3 color1;\n");
|
||||
|
||||
WriteSampleColor(p, "rgb", "color0", 0, ApiType, params);
|
||||
WriteSampleColor(p, "rgb", "color1", 1, ApiType, params);
|
||||
WRITE(p, " float3 average = (color0 + color1) * 0.5;\n");
|
||||
|
||||
WRITE(p, " ocol0.b = dot(color0, y_const) + 0.0625;\n");
|
||||
WRITE(p, " ocol0.g = dot(average, u_const) + 0.5;\n");
|
||||
WRITE(p, " ocol0.r = dot(color1, y_const) + 0.0625;\n");
|
||||
WRITE(p, " ocol0.a = dot(average, v_const) + 0.5;\n");
|
||||
|
||||
WriteEncoderEnd(p);
|
||||
}
|
||||
|
||||
const char* GenerateEncodingShader(const EFBCopyParams& params, APIType api_type)
|
||||
{
|
||||
text[sizeof(text) - 1] = 0x7C; // canary
|
||||
|
@ -728,6 +752,9 @@ const char* GenerateEncodingShader(const EFBCopyParams& params, APIType api_type
|
|||
else
|
||||
WriteCC8Encoder(p, "gb", api_type, params);
|
||||
break;
|
||||
case EFBCopyFormat::XFB:
|
||||
WriteXFBEncoder(p, api_type, params);
|
||||
break;
|
||||
default:
|
||||
PanicAlert("Invalid EFB Copy Format (0x%X)! (GenerateEncodingShader)",
|
||||
static_cast<int>(params.copy_format));
|
||||
|
|
|
@ -28,6 +28,11 @@ enum class TextureFormat
|
|||
C8 = 0x9,
|
||||
C14X2 = 0xA,
|
||||
CMPR = 0xE,
|
||||
|
||||
// Special texture format used to represent YUVY xfb copies.
|
||||
// They aren't really textures, but they share so much hardware and usecases that it makes sense
|
||||
// to emulate them as part of texture cache.
|
||||
XFB = 0xF,
|
||||
};
|
||||
|
||||
static inline bool IsColorIndexed(TextureFormat format)
|
||||
|
@ -73,6 +78,11 @@ enum class EFBCopyFormat
|
|||
B8 = 0xA, // B8, Z8L
|
||||
RG8 = 0xB, // RG8, Z16R (Note: G and R are reversed)
|
||||
GB8 = 0xC, // GB8, Z16L
|
||||
|
||||
// Special texture format used to represent YUVY xfb copies.
|
||||
// They aren't really textures, but they share so much hardware and usecases that it makes sense
|
||||
// to emulate them as part of texture cache.
|
||||
XFB = 0xF,
|
||||
};
|
||||
|
||||
enum class TLUTFormat
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Swap.h"
|
||||
|
||||
|
@ -46,6 +48,9 @@ int TexDecoder_GetTexelSizeInNibbles(TextureFormat format)
|
|||
// Compressed format
|
||||
case TextureFormat::CMPR:
|
||||
return 1;
|
||||
// Special formats
|
||||
case TextureFormat::XFB:
|
||||
return 4;
|
||||
default:
|
||||
PanicAlert("Invalid Texture Format (0x%X)! (GetTexelSizeInNibbles)", static_cast<int>(format));
|
||||
return 1;
|
||||
|
@ -82,6 +87,9 @@ int TexDecoder_GetBlockWidthInTexels(TextureFormat format)
|
|||
// Compressed format
|
||||
case TextureFormat::CMPR:
|
||||
return 8;
|
||||
// Special formats
|
||||
case TextureFormat::XFB:
|
||||
return 16;
|
||||
default:
|
||||
PanicAlert("Invalid Texture Format (0x%X)! (GetBlockWidthInTexels)", static_cast<int>(format));
|
||||
return 8;
|
||||
|
@ -113,6 +121,9 @@ int TexDecoder_GetBlockHeightInTexels(TextureFormat format)
|
|||
// Compressed format
|
||||
case TextureFormat::CMPR:
|
||||
return 8;
|
||||
// Special formats
|
||||
case TextureFormat::XFB:
|
||||
return 1;
|
||||
default:
|
||||
PanicAlert("Invalid Texture Format (0x%X)! (GetBlockHeightInTexels)", static_cast<int>(format));
|
||||
return 4;
|
||||
|
@ -144,6 +155,9 @@ int TexDecoder_GetEFBCopyBlockWidthInTexels(EFBCopyFormat format)
|
|||
// 32-bit formats
|
||||
case EFBCopyFormat::RGBA8:
|
||||
return 4;
|
||||
// Special formats
|
||||
case EFBCopyFormat::XFB:
|
||||
return 16;
|
||||
default:
|
||||
PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEFBCopyBlockWidthInTexels)",
|
||||
static_cast<int>(format));
|
||||
|
@ -176,6 +190,9 @@ int TexDecoder_GetEFBCopyBlockHeightInTexels(EFBCopyFormat format)
|
|||
// 32-bit formats
|
||||
case EFBCopyFormat::RGBA8:
|
||||
return 4;
|
||||
// Special formats
|
||||
case EFBCopyFormat::XFB:
|
||||
return 1;
|
||||
default:
|
||||
PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEFBCopyBlockHeightInTexels)",
|
||||
static_cast<int>(format));
|
||||
|
@ -226,6 +243,8 @@ TextureFormat TexDecoder_GetEFBCopyBaseFormat(EFBCopyFormat format)
|
|||
return TextureFormat::RGB5A3;
|
||||
case EFBCopyFormat::RGBA8:
|
||||
return TextureFormat::RGBA8;
|
||||
case EFBCopyFormat::XFB:
|
||||
return TextureFormat::XFB;
|
||||
default:
|
||||
PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEFBCopyBaseFormat)", static_cast<int>(format));
|
||||
return static_cast<TextureFormat>(format);
|
||||
|
@ -247,7 +266,7 @@ static const char* texfmt[] = {
|
|||
"0x1C", "0x1D", "0x1E", "0x1F",
|
||||
// pixel + copy
|
||||
"CR4", "0x21", "CRA4", "CRA8", "0x24", "0x25", "CYUVA8", "CA8", "CR8", "CG8", "CB8", "CRG8",
|
||||
"CGB8", "0x2D", "0x2E", "0x2F",
|
||||
"CGB8", "0x2D", "0x2E", "XFB",
|
||||
// Z + copy
|
||||
"CZ4", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", "0x38", "CZ8M", "CZ8L", "0x3B",
|
||||
"CZ16L", "0x3D", "0x3E", "0x3F",
|
||||
|
@ -619,6 +638,24 @@ void TexDecoder_DecodeTexel(u8* dst, const u8* src, int s, int t, int imageWidth
|
|||
*((u32*)dst) = color;
|
||||
}
|
||||
break;
|
||||
case TextureFormat::XFB:
|
||||
{
|
||||
// TODO: I should kind of like... ACTUALLY TEST THIS!!!!!
|
||||
size_t offset = (t * imageWidth + (s & (~1))) * 2;
|
||||
|
||||
// We do this one color sample (aka 2 RGB pixles) at a time
|
||||
int Y = int((s & 1) == 0 ? src[offset] : src[offset + 2]) - 16;
|
||||
int U = int(src[offset + 1]) - 128;
|
||||
int V = int(src[offset + 3]) - 128;
|
||||
|
||||
// We do the inverse BT.601 conversion for YCbCr to RGB
|
||||
// http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
|
||||
u8 R = MathUtil::Clamp(int(1.164f * Y + 1.596f * V), 0, 255);
|
||||
u8 G = MathUtil::Clamp(int(1.164f * Y - 0.392f * U - 0.813f * V), 0, 255);
|
||||
u8 B = MathUtil::Clamp(int(1.164f * Y + 2.017f * U), 0, 255);
|
||||
dst[t * imageWidth + s] = 0xff000000 | B << 16 | G << 8 | R;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "Common/CPUDetect.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Intrinsics.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Swap.h"
|
||||
|
||||
|
@ -1485,6 +1486,37 @@ void _TexDecoder_DecodeImpl(u32* dst, const u8* src, int width, int height, Text
|
|||
case TextureFormat::CMPR:
|
||||
TexDecoder_DecodeImpl_CMPR(dst, src, width, height, texformat, tlut, tlutfmt, Wsteps4, Wsteps8);
|
||||
break;
|
||||
|
||||
case TextureFormat::XFB:
|
||||
{
|
||||
for (int y = 0; y < height; y += 1)
|
||||
{
|
||||
for (int x = 0; x < width; x += 2)
|
||||
{
|
||||
size_t offset = static_cast<size_t>((y * width + x) * 2);
|
||||
|
||||
// We do this one color sample (aka 2 RGB pixles) at a time
|
||||
int Y1 = int(src[offset]) - 16;
|
||||
int U = int(src[offset + 1]) - 128;
|
||||
int Y2 = int(src[offset + 2]) - 16;
|
||||
int V = int(src[offset + 3]) - 128;
|
||||
|
||||
// We do the inverse BT.601 conversion for YCbCr to RGB
|
||||
// http://www.equasys.de/colorconversion.html#YCbCr-RGBColorFormatConversion
|
||||
u8 R1 = static_cast<u8>(MathUtil::Clamp(int(1.164f * Y1 + 1.596f * V), 0, 255));
|
||||
u8 G1 = static_cast<u8>(MathUtil::Clamp(int(1.164f * Y1 - 0.392f * U - 0.813f * V), 0, 255));
|
||||
u8 B1 = static_cast<u8>(MathUtil::Clamp(int(1.164f * Y1 + 2.017f * U), 0, 255));
|
||||
|
||||
u8 R2 = static_cast<u8>(MathUtil::Clamp(int(1.164f * Y2 + 1.596f * V), 0, 255));
|
||||
u8 G2 = static_cast<u8>(MathUtil::Clamp(int(1.164f * Y2 - 0.392f * U - 0.813f * V), 0, 255));
|
||||
u8 B2 = static_cast<u8>(MathUtil::Clamp(int(1.164f * Y2 + 2.017f * U), 0, 255));
|
||||
|
||||
dst[y * width + x] = 0xff000000 | B1 << 16 | G1 << 8 | R1;
|
||||
dst[y * width + x + 1] = 0xff000000 | B2 << 16 | G2 << 8 | R2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlert("Invalid Texture Format (0x%X)! (_TexDecoder_DecodeImpl)",
|
||||
|
|
|
@ -55,6 +55,8 @@ struct TargetRectangle : public MathUtil::Rectangle<int>
|
|||
return (RECT*)this;
|
||||
}
|
||||
#endif
|
||||
TargetRectangle(const MathUtil::Rectangle<int> &other) : MathUtil::Rectangle<int>(other) {}
|
||||
TargetRectangle() = default;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
|
@ -221,6 +221,7 @@ struct VideoConfig final
|
|||
bool bSupportsInternalResolutionFrameDumps;
|
||||
bool bSupportsGPUTextureDecoding;
|
||||
bool bSupportsST3CTextures;
|
||||
bool bSupportsCopyToVram;
|
||||
bool bSupportsBitfield; // Needed by UberShaders, so must stay in VideoCommon
|
||||
bool bSupportsDynamicSamplerIndexing; // Needed by UberShaders, so must stay in VideoCommon
|
||||
bool bSupportsBPTCTextures;
|
||||
|
|
Loading…
Reference in New Issue