Add support for hybrid XFB

This commit is contained in:
iwubcode 2017-05-29 17:02:09 -05:00
parent 84ca9a4aec
commit 79387dddb2
45 changed files with 400 additions and 985 deletions

View File

@ -52,7 +52,6 @@
<ClCompile Include="PixelShaderCache.cpp" /> <ClCompile Include="PixelShaderCache.cpp" />
<ClCompile Include="PSTextureEncoder.cpp" /> <ClCompile Include="PSTextureEncoder.cpp" />
<ClCompile Include="Render.cpp" /> <ClCompile Include="Render.cpp" />
<ClCompile Include="Television.cpp" />
<ClCompile Include="TextureCache.cpp" /> <ClCompile Include="TextureCache.cpp" />
<ClCompile Include="VertexManager.cpp" /> <ClCompile Include="VertexManager.cpp" />
<ClCompile Include="VertexShaderCache.cpp" /> <ClCompile Include="VertexShaderCache.cpp" />
@ -73,7 +72,6 @@
<ClInclude Include="PixelShaderCache.h" /> <ClInclude Include="PixelShaderCache.h" />
<ClInclude Include="PSTextureEncoder.h" /> <ClInclude Include="PSTextureEncoder.h" />
<ClInclude Include="Render.h" /> <ClInclude Include="Render.h" />
<ClInclude Include="Television.h" />
<ClInclude Include="TextureCache.h" /> <ClInclude Include="TextureCache.h" />
<ClInclude Include="VertexManager.h" /> <ClInclude Include="VertexManager.h" />
<ClInclude Include="VertexShaderCache.h" /> <ClInclude Include="VertexShaderCache.h" />

View File

@ -48,9 +48,6 @@
<ClCompile Include="Render.cpp"> <ClCompile Include="Render.cpp">
<Filter>Render</Filter> <Filter>Render</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Television.cpp">
<Filter>Render</Filter>
</ClCompile>
<ClCompile Include="TextureCache.cpp"> <ClCompile Include="TextureCache.cpp">
<Filter>Render</Filter> <Filter>Render</Filter>
</ClCompile> </ClCompile>
@ -108,9 +105,6 @@
<ClInclude Include="Render.h"> <ClInclude Include="Render.h">
<Filter>Render</Filter> <Filter>Render</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Television.h">
<Filter>Render</Filter>
</ClInclude>
<ClInclude Include="TextureCache.h"> <ClInclude Include="TextureCache.h">
<Filter>Render</Filter> <Filter>Render</Filter>
</ClInclude> </ClInclude>

View File

@ -330,12 +330,6 @@ std::pair<u32, u32> FramebufferManager::GetTargetSize() const
return std::make_pair(m_target_width, m_target_height); 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) void XFBSource::CopyEFB(float Gamma)
{ {
g_renderer->ResetAPIState(); // reset any game specific settings g_renderer->ResetAPIState(); // reset any game specific settings

View File

@ -50,7 +50,6 @@ struct XFBSource : public XFBSourceBase
{ {
XFBSource(D3DTexture2D* _tex, int slices) : tex(_tex), m_slices(slices) {} XFBSource(D3DTexture2D* _tex, int slices) : tex(_tex), m_slices(slices) {}
~XFBSource() { tex->Release(); } ~XFBSource() { tex->Release(); }
void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) override;
void CopyEFB(float Gamma) override; void CopyEFB(float Gamma) override;
D3DTexture2D* const tex; D3DTexture2D* const tex;

View File

@ -41,8 +41,11 @@ void PSTextureEncoder::Init()
HRESULT hr; HRESULT hr;
// Create output texture RGBA format // 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, 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); hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_out);
CHECK(SUCCEEDED(hr), "create efb encode output texture"); CHECK(SUCCEEDED(hr), "create efb encode output texture");
D3D::SetDebugObjectName(m_out, "efb encoder output texture"); D3D::SetDebugObjectName(m_out, "efb encoder output texture");

View File

@ -24,10 +24,10 @@
#include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/D3DState.h"
#include "VideoBackends/D3D/D3DUtil.h" #include "VideoBackends/D3D/D3DUtil.h"
#include "VideoBackends/D3D/DXTexture.h"
#include "VideoBackends/D3D/FramebufferManager.h" #include "VideoBackends/D3D/FramebufferManager.h"
#include "VideoBackends/D3D/GeometryShaderCache.h" #include "VideoBackends/D3D/GeometryShaderCache.h"
#include "VideoBackends/D3D/PixelShaderCache.h" #include "VideoBackends/D3D/PixelShaderCache.h"
#include "VideoBackends/D3D/Television.h"
#include "VideoBackends/D3D/TextureCache.h" #include "VideoBackends/D3D/TextureCache.h"
#include "VideoBackends/D3D/VertexShaderCache.h" #include "VideoBackends/D3D/VertexShaderCache.h"
@ -40,6 +40,7 @@
#include "VideoCommon/SamplerCommon.h" #include "VideoCommon/SamplerCommon.h"
#include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/XFMemory.h" #include "VideoCommon/XFMemory.h"
namespace DX11 namespace DX11
@ -66,11 +67,8 @@ struct GXPipelineState
static u32 s_last_multisamples = 1; static u32 s_last_multisamples = 1;
static bool s_last_stereo_mode = false; static bool s_last_stereo_mode = false;
static bool s_last_xfb_mode = false;
static bool s_last_fullscreen_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<ID3D11BlendState*, 4> s_clear_blend_states{};
static std::array<ID3D11DepthStencilState*, 3> s_clear_depth_states{}; static std::array<ID3D11DepthStencilState*, 3> s_clear_depth_states{};
static ID3D11BlendState* s_reset_blend_state = nullptr; static ID3D11BlendState* s_reset_blend_state = nullptr;
@ -85,8 +83,6 @@ static StateCache s_gx_state_cache;
static void SetupDeviceObjects() static void SetupDeviceObjects()
{ {
s_television.Init();
HRESULT hr; HRESULT hr;
D3D11_DEPTH_STENCIL_DESC ddesc; D3D11_DEPTH_STENCIL_DESC ddesc;
@ -182,8 +178,6 @@ static void TeardownDeviceObjects()
SAFE_RELEASE(s_screenshot_texture); SAFE_RELEASE(s_screenshot_texture);
SAFE_RELEASE(s_3d_vision_texture); SAFE_RELEASE(s_3d_vision_texture);
s_television.Shutdown();
s_gx_state_cache.Clear(); s_gx_state_cache.Clear();
} }
@ -241,7 +235,6 @@ Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferH
{ {
s_last_multisamples = g_ActiveConfig.iMultisamples; s_last_multisamples = g_ActiveConfig.iMultisamples;
s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0; s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
s_last_fullscreen_mode = D3D::GetFullscreenState(); s_last_fullscreen_mode = D3D::GetFullscreenState();
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height); 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. // This function has the final picture. We adjust the aspect ratio here.
void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma)
const EFBRectangle& rc, u64 ticks, float Gamma)
{ {
if ((!m_xfb_written && !g_ActiveConfig.RealXFBEnabled()) || !fbWidth || !fbHeight) if (!m_xfb_written)
{ {
Core::Callback_VideoCopiedToXFB(false); 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(); ResetAPIState();
@ -671,67 +653,11 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
// activate linear filtering for the buffer copies // activate linear filtering for the buffer copies
D3D::SetLinearCopySampler(); D3D::SetLinearCopySampler();
auto* xfb_texture = static_cast<DXTexture*>(texture);
TargetRectangle source_rc = xfb_texture->config.Rect();
if (g_ActiveConfig.bUseXFB && g_ActiveConfig.bUseRealXFB) BlitScreen(source_rc, targetRc, xfb_texture->GetRawTexIdentifier(), xfb_texture->config.width,
{ xfb_texture->config.height, Gamma);
// 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);
}
// Dump frames // Dump frames
if (IsFrameDumping()) if (IsFrameDumping())
@ -773,33 +699,20 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
g_texture_cache->OnConfigChanged(g_ActiveConfig); g_texture_cache->OnConfigChanged(g_ActiveConfig);
VertexShaderCache::RetreiveAsyncShaders(); VertexShaderCache::RetreiveAsyncShaders();
SetWindowSize(fbStride, fbHeight); SetWindowSize(xfb_texture->config.width, xfb_texture->config.height);
const bool window_resized = CheckForResize(); const bool window_resized = CheckForResize();
const bool fullscreen = D3D::GetFullscreenState(); const bool fullscreen = D3D::GetFullscreenState();
const bool fs_changed = s_last_fullscreen_mode != fullscreen; 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 // Flip/present backbuffer to frontbuffer here
D3D::Present(); D3D::Present();
// Resize the back buffers NOW to avoid flickering // 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_multisamples != g_ActiveConfig.iMultisamples ||
s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0)) s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0))
{ {
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
s_last_multisamples = g_ActiveConfig.iMultisamples; s_last_multisamples = g_ActiveConfig.iMultisamples;
s_last_fullscreen_mode = fullscreen; s_last_fullscreen_mode = fullscreen;
PixelShaderCache::InvalidateMSAAShaders(); PixelShaderCache::InvalidateMSAAShaders();

View File

@ -46,8 +46,7 @@ public:
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
u64 ticks, float Gamma) override;
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
u32 color, u32 z) override; u32 color, u32 z) override;

View File

@ -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.
}
}
}

View File

@ -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;
};
}

View File

@ -78,6 +78,7 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = false; g_Config.backend_info.bSupportsInternalResolutionFrameDumps = false;
g_Config.backend_info.bSupportsGPUTextureDecoding = false; g_Config.backend_info.bSupportsGPUTextureDecoding = false;
g_Config.backend_info.bSupportsST3CTextures = false; g_Config.backend_info.bSupportsST3CTextures = false;
g_Config.backend_info.bSupportsCopyToVram = true;
g_Config.backend_info.bSupportsBitfield = false; g_Config.backend_info.bSupportsBitfield = false;
g_Config.backend_info.bSupportsDynamicSamplerIndexing = false; g_Config.backend_info.bSupportsDynamicSamplerIndexing = false;
g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsBPTCTextures = false;

View File

@ -12,7 +12,6 @@
class XFBSource : public XFBSourceBase class XFBSource : public XFBSourceBase
{ {
public: public:
void DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height) override {}
void CopyEFB(float gamma) override {} void CopyEFB(float gamma) override {}
}; };

View File

@ -36,7 +36,7 @@ TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
return result; return result;
} }
void Renderer::SwapImpl(u32, u32, u32, u32, const EFBRectangle&, u64, float) void Renderer::SwapImpl(AbstractTexture*, const EFBRectangle&, u64, float)
{ {
UpdateActiveConfig(); UpdateActiveConfig();
} }

View File

@ -21,8 +21,7 @@ public:
void BBoxWrite(int index, u16 value) override {} void BBoxWrite(int index, u16 value) override {}
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, const EFBRectangle& rc, void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
u64 ticks, float gamma) override;
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
u32 color, u32 z) override u32 color, u32 z) override

View File

@ -530,7 +530,7 @@ void FramebufferManager::ResolveEFBStencilTexture()
void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight,
const EFBRectangle& sourceRc, float Gamma) const EFBRectangle& sourceRc, float Gamma)
{ {
u8* xfb_in_ram = Memory::GetPointer(xfbAddr); /* u8* xfb_in_ram = Memory::GetPointer(xfbAddr);
if (!xfb_in_ram) if (!xfb_in_ram)
{ {
WARN_LOG(VIDEO, "Tried to copy to invalid XFB address"); 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); TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc);
TextureConverter::EncodeToRamYUYV(ResolveAndGetRenderTarget(sourceRc), targetRc, xfb_in_ram, TextureConverter::EncodeToRamYUYV(ResolveAndGetRenderTarget(sourceRc), targetRc, xfb_in_ram,
sourceRc.GetWidth(), fbStride, fbHeight); sourceRc.GetWidth(), fbStride, fbHeight);*/
} }
GLuint FramebufferManager::GetResolvedFramebuffer() GLuint FramebufferManager::GetResolvedFramebuffer()
@ -615,11 +615,6 @@ XFBSource::~XFBSource()
glDeleteTextures(1, &texture); glDeleteTextures(1, &texture);
} }
void XFBSource::DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
TextureConverter::DecodeToTexture(xfbAddr, fbWidth, fbHeight, texture);
}
void XFBSource::CopyEFB(float Gamma) void XFBSource::CopyEFB(float Gamma)
{ {
g_renderer->ResetAPIState(); g_renderer->ResetAPIState();

View File

@ -54,7 +54,6 @@ struct XFBSource : public XFBSourceBase
~XFBSource(); ~XFBSource();
void CopyEFB(float Gamma) override; void CopyEFB(float Gamma) override;
void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) override;
const GLuint texture; const GLuint texture;
const int m_layers; const int m_layers;

View File

@ -25,7 +25,7 @@ static const char s_vertex_shader[] = "out vec2 uv0;\n"
"void main(void) {\n" "void main(void) {\n"
" vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n" " vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
" gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\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"; "}\n";
OpenGLPostProcessing::OpenGLPostProcessing() : m_initialized(false) 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, glUniform4f(m_uniform_resolution, (float)src_width, (float)src_height, 1.0f / (float)src_width,
1.0f / (float)src_height); 1.0f / (float)src_height);
glUniform4f(m_uniform_src_rect, src.left / (float)src_width, src.bottom / (float)src_height, glUniform4f(m_uniform_src_rect, src.left / (float)src_width, src.top / (float)src_height,
src.GetWidth() / (float)src_width, src.GetHeight() / (float)src_height); src.right / (float)src_width, src.bottom / (float)src_height);
glUniform1ui(m_uniform_time, (GLuint)m_timer.GetTimeElapsed()); glUniform1ui(m_uniform_time, (GLuint)m_timer.GetTimeElapsed());
glUniform1i(m_uniform_layer, layer); glUniform1i(m_uniform_layer, layer);

View File

@ -66,7 +66,6 @@ static std::unique_ptr<RasterFont> s_raster_font;
static int s_MSAASamples = 1; static int s_MSAASamples = 1;
static u32 s_last_multisamples = 1; static u32 s_last_multisamples = 1;
static bool s_last_stereo_mode = false; static bool s_last_stereo_mode = false;
static bool s_last_xfb_mode = false;
static bool s_vsync; static bool s_vsync;
@ -726,7 +725,6 @@ Renderer::Renderer()
s_MSAASamples = s_last_multisamples; s_MSAASamples = s_last_multisamples;
s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0; s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;
s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
// Handle VSync on/off // Handle VSync on/off
s_vsync = g_ActiveConfig.IsVSync(); 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. // This function has the final picture. We adjust the aspect ratio here.
void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma)
const EFBRectangle& rc, u64 ticks, float Gamma)
{ {
if (g_ogl_config.bSupportsDebug) if (g_ogl_config.bSupportsDebug)
{ {
@ -1342,20 +1339,13 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
glDisable(GL_DEBUG_OUTPUT); glDisable(GL_DEBUG_OUTPUT);
} }
if ((!m_xfb_written && !g_ActiveConfig.RealXFBEnabled()) || !fbWidth || !fbHeight) auto* xfb_texture = static_cast<OGLTexture*>(texture);
{
Core::Callback_VideoCopiedToXFB(false);
return;
}
u32 xfbCount = 0; TargetRectangle sourceRc = ConvertEFBRectangle(rc);
const XFBSourceBase* const* xfbSourceList = sourceRc.left = 0;
FramebufferManager::GetXFBSource(xfbAddr, fbStride, fbHeight, &xfbCount); sourceRc.right = xfb_texture->config.width;
if (g_ActiveConfig.VirtualXFBEnabled() && (!xfbSourceList || xfbCount == 0)) sourceRc.top = xfb_texture->config.height;
{ sourceRc.bottom = 0;
Core::Callback_VideoCopiedToXFB(false);
return;
}
ResetAPIState(); ResetAPIState();
@ -1366,7 +1356,8 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
std::swap(flipped_trc.top, flipped_trc.bottom); std::swap(flipped_trc.top, flipped_trc.bottom);
// Copy the framebuffer to screen. // 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. // 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. // 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) if (use_offscreen_buffer)
{ {
// DumpFrameUsingFBO resets GL_FRAMEBUFFER, so change back to the window for drawing OSD. // 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 else
{ {
@ -1391,24 +1382,10 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
// Finish up the current frame, print some stats // 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 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; bool window_resized = false;
int window_width = static_cast<int>(std::max(GLInterface->GetBackBufferWidth(), 1u)); int window_width = static_cast<int>(std::max(GLInterface->GetBackBufferWidth(), 1u));
int window_height = static_cast<int>(std::max(GLInterface->GetBackBufferHeight(), 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() || stencil_buffer_enabled != BoundingBox::NeedsStencilBuffer() ||
s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0); 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(); UpdateDrawRectangle();
} }
if (fb_needs_update) if (fb_needs_update)
@ -1523,110 +1499,13 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
ClearEFBCache(); 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, 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 // for msaa mode, we must resolve the efb content to non-msaa
GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(source_rc); GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(source_rc);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
BlitScreen(scaled_source_rc, target_rc, tex, m_target_width, m_target_height); BlitScreen(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);
}
} }
void Renderer::FlushFrameDump() void Renderer::FlushFrameDump()
@ -1683,9 +1562,7 @@ void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
} }
void Renderer::DumpFrameUsingFBO(const EFBRectangle& source_rc, u32 xfb_addr, void Renderer::DumpFrameUsingFBO(const TargetRectangle& source_rc, u64 ticks)
const XFBSourceBase* const* xfb_sources, u32 xfb_count,
u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks)
{ {
// This needs to be converted to the GL bottom-up window coordinate system. // This needs to be converted to the GL bottom-up window coordinate system.
TargetRectangle render_rc = CalculateFrameDumpDrawRectangle(); 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 // Render the frame into the frame dump render texture. Disable alpha writes in case the
// post-processing shader writes a non-1.0 value. // post-processing shader writes a non-1.0 value.
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); 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, DrawEFB(m_frame_dump_render_framebuffer, render_rc, source_rc);
fb_width, fb_stride, fb_height);
// Copy frame to output buffer. This assumes that GL_FRAMEBUFFER has been set. // Copy frame to output buffer. This assumes that GL_FRAMEBUFFER has been set.
DumpFrame(render_rc, ticks); DumpFrame(render_rc, ticks);

View File

@ -98,8 +98,7 @@ public:
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
u64 ticks, float Gamma) override;
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
u32 color, u32 z) override; u32 color, u32 z) override;
@ -112,27 +111,14 @@ private:
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
const TargetRectangle& targetPixelRc, const void* data); const TargetRectangle& targetPixelRc, const void* data);
// Draw either the EFB, or specified XFB sources to the currently-bound framebuffer. void DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc, const TargetRectangle& source_rc);
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 BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width, void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width,
int src_height); int src_height);
void FlushFrameDump(); void FlushFrameDump();
void DumpFrame(const TargetRectangle& flipped_trc, u64 ticks); void DumpFrame(const TargetRectangle& flipped_trc, u64 ticks);
void DumpFrameUsingFBO(const EFBRectangle& source_rc, u32 xfb_addr, void DumpFrameUsingFBO(const TargetRectangle& source_rc, u64 ticks);
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height, u64 ticks);
// Frame dumping framebuffer, we render to this, then read it back // Frame dumping framebuffer, we render to this, then read it back
void PrepareFrameDumpRenderTexture(u32 width, u32 height); void PrepareFrameDumpRenderTexture(u32 width, u32 height);

View File

@ -91,6 +91,7 @@ void VideoBackend::InitBackendInfo()
g_Config.backend_info.bSupportsReversedDepthRange = true; g_Config.backend_info.bSupportsReversedDepthRange = true;
g_Config.backend_info.bSupportsMultithreading = false; g_Config.backend_info.bSupportsMultithreading = false;
g_Config.backend_info.bSupportsInternalResolutionFrameDumps = true; 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 // 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 // will show the option when it is not supported. The only way around this would be

View File

@ -520,15 +520,6 @@ void CopyToXFB(yuv422_packed* xfb_in_ram, u32 fbWidth, u32 fbHeight, const EFBRe
// Scanline buffer, leave room for borders // Scanline buffer, leave room for borders
yuv444 scanline[EFB_WIDTH + 2]; 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++) for (u16 y = sourceRc.top; y < sourceRc.bottom; y++)
{ {
// Get a scanline of YUV pixels in 4:4:4 format // 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); 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 // And Downsample them to 4:2:2
for (int i = 1, x = left; x < right; i += 2, x += 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 // main memory or doing a yuyv conversion
void BypassXFB(u8* texture, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma) void BypassXFB(u8* texture, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma)
{ {
if (fbWidth * fbHeight > MAX_XFB_WIDTH * MAX_XFB_HEIGHT) // TODO: Upload directly to texture cache.
{
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);
}
}
} }
bool ZCompare(u16 x, u16 y, u32 z) bool ZCompare(u16 x, u16 y, u32 z)

View File

@ -9,6 +9,7 @@
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "VideoBackends/Software/SWOGLWindow.h" #include "VideoBackends/Software/SWOGLWindow.h"
#include "VideoCommon/AbstractTexture.h"
std::unique_ptr<SWOGLWindow> SWOGLWindow::s_instance; std::unique_ptr<SWOGLWindow> SWOGLWindow::s_instance;
@ -53,9 +54,9 @@ void SWOGLWindow::Prepare()
std::string frag_shader = "in vec2 TexCoord;\n" std::string frag_shader = "in vec2 TexCoord;\n"
"out vec4 ColorOut;\n" "out vec4 ColorOut;\n"
"uniform sampler2D Texture;\n" "uniform sampler2DArray samp;\n"
"void main() {\n" "void main() {\n"
" ColorOut = texture(Texture, TexCoord);\n" " ColorOut = texture(samp, vec3(TexCoord, 0.0));\n"
"}\n"; "}\n";
std::string vertex_shader = "out vec2 TexCoord;\n" std::string vertex_shader = "out vec2 TexCoord;\n"
@ -74,12 +75,9 @@ void SWOGLWindow::Prepare()
glUseProgram(m_image_program); glUseProgram(m_image_program);
glUniform1i(glGetUniformLocation(m_image_program, "Texture"), 0); glUniform1i(glGetUniformLocation(m_image_program, "samp"), 0);
glGenTextures(1, &m_image_texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
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);
glGenVertexArrays(1, &m_image_vao); 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}); 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(); // just updates the render window position and the backbuffer size
GLInterface->Update();
Prepare();
GLsizei glWidth = (GLsizei)GLInterface->GetBackBufferWidth(); GLsizei glWidth = (GLsizei)GLInterface->GetBackBufferWidth();
GLsizei glHeight = (GLsizei)GLInterface->GetBackBufferHeight(); GLsizei glHeight = (GLsizei)GLInterface->GetBackBufferHeight();
glViewport(0, 0, glWidth, glHeight); glViewport(0, 0, glWidth, glHeight);
glBindTexture(GL_TEXTURE_2D, m_image_texture); image->Bind(0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride / 4); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, data);
glUseProgram(m_image_program); glUseProgram(m_image_program);
@ -119,7 +113,6 @@ void SWOGLWindow::ShowImage(const u8* data, int stride, int width, int height, f
m_text.clear(); m_text.clear();
GLInterface->Swap(); GLInterface->Swap();
GLInterface->ClearCurrent();
} }
int SWOGLWindow::PeekMessages() int SWOGLWindow::PeekMessages()

View File

@ -10,17 +10,20 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
class AbstractTexture;
class SWOGLWindow class SWOGLWindow
{ {
public: public:
static void Init(void* window_handle); static void Init(void* window_handle);
static void Shutdown(); static void Shutdown();
void Prepare();
// Will be printed on the *next* image // Will be printed on the *next* image
void PrintText(const std::string& text, int x, int y, u32 color); void PrintText(const std::string& text, int x, int y, u32 color);
// Image to show, will be swapped immediately // 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(); int PeekMessages();
@ -28,7 +31,6 @@ public:
private: private:
SWOGLWindow() {} SWOGLWindow() {}
void Prepare();
struct TextData struct TextData
{ {
@ -40,5 +42,5 @@ private:
bool m_init{false}; bool m_init{false};
u32 m_image_program, m_image_texture, m_image_vao; u32 m_image_program, m_image_vao;
}; };

View File

@ -23,26 +23,13 @@
#include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
static u8* s_xfbColorTexture[2];
static int s_currentColorTexture = 0;
SWRenderer::SWRenderer() SWRenderer::SWRenderer()
: ::Renderer(static_cast<int>(MAX_XFB_WIDTH), static_cast<int>(MAX_XFB_HEIGHT)) : ::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() 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() 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); 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 // Called on the GPU thread
void SWRenderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, void SWRenderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma)
const EFBRectangle& rc, u64 ticks, float Gamma)
{ {
if (g_ActiveConfig.bUseXFB) SWOGLWindow::s_instance->ShowImage(texture, 1.0);
{
EfbInterface::yuv422_packed* xfb = (EfbInterface::yuv422_packed*)Memory::GetPointer(xfbAddr);
UpdateColorTexture(xfb, fbWidth, fbHeight);
}
else
{
EfbInterface::BypassXFB(GetCurrentColorTexture(), fbWidth, fbHeight, rc, Gamma);
}
// Save screenshot // Save screenshot
if (IsFrameDumping()) if (IsFrameDumping())
{ {
AVIDump::Frame state = AVIDump::FetchState(ticks); AVIDump::Frame state = AVIDump::FetchState(ticks);
DumpFrameData(GetCurrentColorTexture(), fbWidth, fbHeight, fbWidth * 4, state); //DumpFrameData(GetCurrentColorTexture(), fbWidth, fbHeight, fbWidth * 4, state);
FinishFrameData(); FinishFrameData();
} }
@ -136,15 +59,9 @@ void SWRenderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
DrawDebugText(); DrawDebugText();
SWOGLWindow::s_instance->ShowImage(GetCurrentColorTexture(), fbWidth * 4, fbWidth, fbHeight, 1.0); SWOGLWindow::s_instance->ShowImage(texture, 1.0);
UpdateActiveConfig(); 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) u32 SWRenderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData)

View File

@ -14,16 +14,10 @@ class SWRenderer : public Renderer
{ {
public: public:
SWRenderer(); SWRenderer();
~SWRenderer() override;
static void Init(); static void Init();
static void Shutdown(); 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; 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; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {} void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override {}
@ -32,8 +26,7 @@ public:
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
u64 ticks, float Gamma) override;
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
u32 color, u32 z) override; u32 color, u32 z) override;

View File

@ -8,16 +8,17 @@
#include <utility> #include <utility>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/GL/GLInterfaceBase.h"
#include "VideoBackends/Software/Clipper.h" #include "VideoBackends/Software/Clipper.h"
#include "VideoBackends/Software/DebugUtil.h" #include "VideoBackends/Software/DebugUtil.h"
#include "VideoBackends/Software/EfbCopy.h"
#include "VideoBackends/Software/EfbInterface.h" #include "VideoBackends/Software/EfbInterface.h"
#include "VideoBackends/Software/Rasterizer.h" #include "VideoBackends/Software/Rasterizer.h"
#include "VideoBackends/Software/SWOGLWindow.h" #include "VideoBackends/Software/SWOGLWindow.h"
#include "VideoBackends/Software/SWRenderer.h" #include "VideoBackends/Software/SWRenderer.h"
#include "VideoBackends/Software/SWTexture.h" #include "VideoBackends/Software/SWTexture.h"
#include "VideoBackends/Software/SWVertexLoader.h" #include "VideoBackends/Software/SWVertexLoader.h"
#include "VideoBackends/Software/TextureCache.h"
#include "VideoBackends/Software/VideoBackend.h" #include "VideoBackends/Software/VideoBackend.h"
#include "VideoCommon/FramebufferManagerBase.h" #include "VideoCommon/FramebufferManagerBase.h"
@ -46,58 +47,6 @@ public:
bool IsFlushed() const override { return true; } 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 std::string VideoSoftware::GetName() const
{ {
return "Software Renderer"; return "Software Renderer";
@ -123,6 +72,7 @@ void VideoSoftware::InitBackendInfo()
g_Config.backend_info.bSupportsGPUTextureDecoding = false; g_Config.backend_info.bSupportsGPUTextureDecoding = false;
g_Config.backend_info.bSupportsST3CTextures = false; g_Config.backend_info.bSupportsST3CTextures = false;
g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsBPTCTextures = false;
g_Config.backend_info.bSupportsCopyToVram = false;
// aamodes // aamodes
g_Config.backend_info.AAModes = {1}; g_Config.backend_info.AAModes = {1};
@ -169,12 +119,14 @@ void VideoSoftware::Video_Cleanup()
// This is called after Video_Initialize() from the Core // This is called after Video_Initialize() from the Core
void VideoSoftware::Video_Prepare() void VideoSoftware::Video_Prepare()
{ {
GLInterface->MakeCurrent();
SWOGLWindow::s_instance->Prepare();
g_renderer = std::make_unique<SWRenderer>(); g_renderer = std::make_unique<SWRenderer>();
g_vertex_manager = std::make_unique<SWVertexLoader>(); g_vertex_manager = std::make_unique<SWVertexLoader>();
g_perf_query = std::make_unique<PerfQuery>(); g_perf_query = std::make_unique<PerfQuery>();
g_texture_cache = std::make_unique<TextureCache>(); g_texture_cache = std::make_unique<TextureCache>();
SWRenderer::Init(); SWRenderer::Init();
g_framebuffer_manager = std::make_unique<FramebufferManager>();
} }
unsigned int VideoSoftware::PeekMessages() unsigned int VideoSoftware::PeekMessages()

View File

@ -65,6 +65,7 @@
<ClInclude Include="SWTexture.h" /> <ClInclude Include="SWTexture.h" />
<ClInclude Include="SWVertexLoader.h" /> <ClInclude Include="SWVertexLoader.h" />
<ClInclude Include="Tev.h" /> <ClInclude Include="Tev.h" />
<ClInclude Include="TextureCache.h" />
<ClInclude Include="TextureEncoder.h" /> <ClInclude Include="TextureEncoder.h" />
<ClInclude Include="TextureSampler.h" /> <ClInclude Include="TextureSampler.h" />
<ClInclude Include="TransformUnit.h" /> <ClInclude Include="TransformUnit.h" />

View File

@ -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

View File

@ -89,11 +89,6 @@ MultisamplingState FramebufferManager::GetEFBMultisamplingState() const
return ms; return ms;
} }
std::pair<u32, u32> FramebufferManager::GetTargetSize() const
{
return std::make_pair(GetEFBWidth(), GetEFBHeight());
}
bool FramebufferManager::Initialize() bool FramebufferManager::Initialize()
{ {
if (!CreateEFBRenderPass()) if (!CreateEFBRenderPass())
@ -1450,15 +1445,6 @@ VKTexture* XFBSource::GetTexture() const
return static_cast<VKTexture*>(m_texture.get()); 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) void XFBSource::CopyEFB(float gamma)
{ {
// Pending/batched EFB pokes should be included in the copied image. // Pending/batched EFB pokes should be included in the copied image.

View File

@ -43,7 +43,6 @@ public:
u32 GetEFBLayers() const; u32 GetEFBLayers() const;
VkSampleCountFlagBits GetEFBSamples() const; VkSampleCountFlagBits GetEFBSamples() const;
MultisamplingState GetEFBMultisamplingState() const; MultisamplingState GetEFBMultisamplingState() const;
std::pair<u32, u32> GetTargetSize() const override;
std::unique_ptr<XFBSourceBase> CreateXFBSource(unsigned int target_width, std::unique_ptr<XFBSourceBase> CreateXFBSource(unsigned int target_width,
unsigned int target_height, unsigned int target_height,
@ -178,8 +177,6 @@ public:
~XFBSource(); ~XFBSource();
VKTexture* GetTexture() const; VKTexture* GetTexture() const;
// Guest -> GPU EFB Textures
void DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height) override;
// Used for virtual XFB // Used for virtual XFB
void CopyEFB(float gamma) override; void CopyEFB(float gamma) override;

View File

@ -41,6 +41,7 @@
#include "VideoCommon/SamplerCommon.h" #include "VideoCommon/SamplerCommon.h"
#include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/TextureCacheBase.h"
#include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
#include "VideoCommon/XFMemory.h" #include "VideoCommon/XFMemory.h"
@ -485,27 +486,19 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
BindEFBToStateTracker(); BindEFBToStateTracker();
} }
void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma)
const EFBRectangle& rc, u64 ticks, float gamma)
{ {
// Pending/batched EFB pokes should be included in the final image. // Pending/batched EFB pokes should be included in the final image.
FramebufferManager::GetInstance()->FlushEFBPokes(); FramebufferManager::GetInstance()->FlushEFBPokes();
// Check that we actually have an image to render in XFB-on modes. // 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); 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. // End the current render pass.
StateTracker::GetInstance()->EndRenderPass(); StateTracker::GetInstance()->EndRenderPass();
StateTracker::GetInstance()->OnEndFrame(); 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. // are determined by guest state. Currently, the only way to catch these is to update every frame.
UpdateDrawRectangle(); 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. // Render the frame dump image if enabled.
if (IsFrameDumping()) 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) if (!m_frame_dumping_active)
StartFrameDumping(); StartFrameDumping();
DrawFrameDump(scaled_efb_rect, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height, /* DrawFrameDump(scaled_efb_rect, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride, fb_height,
ticks); ticks);*/
} }
else 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. // Draw to the screen if we have a swap chain.
if (m_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 // 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 // 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. // Handle host window resizes.
CheckForSurfaceChange(); CheckForSurfaceChange();
// Handle output size changes from the guest. if (CalculateTargetSize())
// There is a downside to doing this here is that if the game changes its XFB source area, ResizeEFBTextures();
// 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);
// Update the window size based on the frame that was just rendered. // 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. // 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. // Clean up stale textures.
TextureCache::GetInstance()->Cleanup(frameCount); 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, void Renderer::DrawScreen(VKTexture* xfb_texture)
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height)
{ {
VkResult res; VkResult res;
if (!g_command_buffer_mgr->CheckLastPresentFail()) 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, vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &info,
VK_SUBPASS_CONTENTS_INLINE); VK_SUBPASS_CONTENTS_INLINE);
// Draw guest buffers (EFB or XFB) // Draw
DrawFrame(m_swap_chain->GetRenderPass(), GetTargetRectangle(), scaled_efb_rect, xfb_addr, TargetRectangle source_rc = xfb_texture->config.Rect();
xfb_sources, xfb_count, fb_width, fb_stride, fb_height); BlitScreen(m_swap_chain->GetRenderPass(), GetTargetRectangle(), source_rc, xfb_texture->GetRawTexIdentifier());
// Draw OSD // Draw OSD
Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0, 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) 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) FramebufferManagerBase::LastXfbHeight() == fb_height)
{ {
return; 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. // Changing the XFB source area may alter the target size.
if (CalculateTargetSize()) if (CalculateTargetSize())
ResizeEFBTextures(); ResizeEFBTextures();*/
} }
void Renderer::CheckForSurfaceChange() void Renderer::CheckForSurfaceChange()

View File

@ -23,6 +23,7 @@ class SwapChain;
class StagingTexture2D; class StagingTexture2D;
class Texture2D; class Texture2D;
class RasterFont; class RasterFont;
class VKTexture;
class Renderer : public ::Renderer class Renderer : public ::Renderer
{ {
@ -43,8 +44,7 @@ public:
void BBoxWrite(int index, u16 value) override; void BBoxWrite(int index, u16 value) override;
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override; TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
void SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, const EFBRectangle& rc, void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma) override;
u64 ticks, float gamma) override;
void ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable, bool z_enable, void ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha_enable, bool z_enable,
u32 color, u32 z) override; u32 color, u32 z) override;
@ -106,9 +106,7 @@ private:
u32 fb_stride, u32 fb_height); u32 fb_stride, u32 fb_height);
// Draw the frame, as well as the OSD to the swap chain. // Draw the frame, as well as the OSD to the swap chain.
void DrawScreen(const TargetRectangle& scaled_efb_rect, u32 xfb_addr, void DrawScreen(VKTexture* xfb_texture);
const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
u32 fb_stride, u32 fb_height);
// Draw the frame only to the screenshot buffer. // Draw the frame only to the screenshot buffer.
bool DrawFrameDump(const TargetRectangle& scaled_efb_rect, u32 xfb_addr, bool DrawFrameDump(const TargetRectangle& scaled_efb_rect, u32 xfb_addr,

View File

@ -246,6 +246,7 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config)
config->backend_info.bSupportsST3CTextures = false; // Dependent on features. config->backend_info.bSupportsST3CTextures = false; // Dependent on features.
config->backend_info.bSupportsBPTCTextures = 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.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) void VulkanContext::PopulateBackendInfoAdapters(VideoConfig* config, const GPUList& gpu_list)

View File

@ -250,16 +250,18 @@ static void BPWritten(const BPCmd& bp)
float num_xfb_lines = 1.0f + bpmem.copyTexSrcWH.y * yScale; float num_xfb_lines = 1.0f + bpmem.copyTexSrcWH.y * yScale;
u32 height = static_cast<u32>(num_xfb_lines); 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 | " DEBUG_LOG(VIDEO, "RenderToXFB: destAddr: %08x | srcRect {%d %d %d %d} | fbWidth: %u | "
"fbStride: %u | fbHeight: %u", "fbStride: %u | fbHeight: %u",
destAddr, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom, destAddr, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom,
bpmem.copyTexSrcWH.x + 1, destStride, height); 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]); g_renderer->RenderToXFB(destAddr, srcRect, destStride, height, s_gammaLUT[PE_copy.gamma]);
} }

View File

@ -21,9 +21,6 @@ FramebufferManagerBase::VirtualXFBListType
std::array<const XFBSourceBase*, FramebufferManagerBase::MAX_VIRTUAL_XFB> std::array<const XFBSourceBase*, FramebufferManagerBase::MAX_VIRTUAL_XFB>
FramebufferManagerBase::m_overlappingXFBArray; 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; unsigned int FramebufferManagerBase::m_EFBLayers = 1;
FramebufferManagerBase::FramebufferManagerBase() FramebufferManagerBase::FramebufferManagerBase()
@ -40,85 +37,6 @@ FramebufferManagerBase::~FramebufferManagerBase()
m_realXFBSource.reset(); 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, void FramebufferManagerBase::CopyToXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight,
const EFBRectangle& sourceRc, float Gamma) 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;
}

View File

@ -20,7 +20,6 @@ inline bool AddressRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper)
struct XFBSourceBase struct XFBSourceBase
{ {
virtual ~XFBSourceBase() {} virtual ~XFBSourceBase() {}
virtual void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight) = 0;
virtual void CopyEFB(float Gamma) = 0; virtual void CopyEFB(float Gamma) = 0;
@ -50,15 +49,6 @@ public:
static void CopyToXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc, static void CopyToXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,
float Gamma); 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; } static unsigned int GetEFBLayers() { return m_EFBLayers; }
virtual std::pair<u32, u32> GetTargetSize() const = 0; 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, static void CopyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,
float Gamma = 1.0f); 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 std::unique_ptr<XFBSourceBase> m_realXFBSource; // Only used in Real XFB mode
static VirtualXFBListType m_virtualXFBList; // Only used in Virtual XFB mode static VirtualXFBListType m_virtualXFBList; // Only used in Virtual XFB mode
static std::array<const XFBSourceBase*, MAX_VIRTUAL_XFB> m_overlappingXFBArray; 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; extern std::unique_ptr<FramebufferManagerBase> g_framebuffer_manager;

View File

@ -84,9 +84,6 @@ static float AspectToWidescreen(float aspect)
Renderer::Renderer(int backbuffer_width, int backbuffer_height) Renderer::Renderer(int backbuffer_width, int backbuffer_height)
: m_backbuffer_width(backbuffer_width), m_backbuffer_height(backbuffer_height) : m_backbuffer_width(backbuffer_width), m_backbuffer_height(backbuffer_height)
{ {
FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH);
FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT);
UpdateActiveConfig(); UpdateActiveConfig();
UpdateDrawRectangle(); UpdateDrawRectangle();
CalculateTargetSize(); CalculateTargetSize();
@ -116,19 +113,6 @@ void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStri
return; return;
m_xfb_written = true; 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 unsigned int Renderer::GetEFBScale() const
@ -433,9 +417,7 @@ TargetRectangle Renderer::CalculateFrameDumpDrawRectangle() const
rc.top = 0; rc.top = 0;
// If full-resolution frame dumping is disabled, just use the window draw rectangle. // 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 if (!g_ActiveConfig.bInternalResolutionFrameDumps)
// anyway, and there's no point writing an upscaled frame with no filtering.
if (!g_ActiveConfig.bInternalResolutionFrameDumps || g_ActiveConfig.RealXFBEnabled())
{ {
// But still remove the borders, since the caller expects this. // But still remove the borders, since the caller expects this.
rc.right = m_target_rectangle.GetWidth(); 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; m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total;
} }
// TODO: merge more generic parts into VideoCommon if (xfbAddr && fbWidth && fbStride && fbHeight)
SwapImpl(xfbAddr, fbWidth, fbStride, fbHeight, rc, ticks, Gamma); {
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) if (m_xfb_written)
m_fps_counter.Update(); m_fps_counter.Update();

View File

@ -32,6 +32,7 @@
#include "VideoCommon/RenderState.h" #include "VideoCommon/RenderState.h"
#include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoCommon.h"
class AbstractTexture;
class PostProcessingShaderImplementation; class PostProcessingShaderImplementation;
enum class EFBAccessType; enum class EFBAccessType;
@ -132,8 +133,7 @@ public:
// Finish up the current frame, print some stats // Finish up the current frame, print some stats
void Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, u64 ticks, void Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const EFBRectangle& rc, u64 ticks,
float Gamma = 1.0f); float Gamma = 1.0f);
virtual void SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, virtual void SwapImpl(AbstractTexture* texture, const EFBRectangle& rc, u64 ticks, float Gamma = 1.0f) = 0;
const EFBRectangle& rc, u64 ticks, float Gamma = 1.0f) = 0;
PEControl::PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; } PEControl::PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; }
void StorePixelFormat(PEControl::PixelFormat new_format) { m_prev_efb_format = new_format; } void StorePixelFormat(PEControl::PixelFormat new_format) { m_prev_efb_format = new_format; }

View File

@ -238,7 +238,8 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, u8* palette, TLUTForma
if (!decoded_entry) if (!decoded_entry)
return nullptr; 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->SetDimensions(entry->native_width, entry->native_height, 1);
decoded_entry->SetHashes(entry->base_hash, entry->hash); decoded_entry->SetHashes(entry->base_hash, entry->hash);
decoded_entry->frameCount = FRAMECOUNT_INVALID; 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); 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() void TextureCacheBase::BindTextures()
{ {
for (size_t i = 0; i < bound_textures.size(); ++i) 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 this stage was not invalidated by changes to texture registers, keep the current texture
if (IsValidBindPoint(stage) && bound_textures[stage]) if (IsValidBindPoint(stage) && bound_textures[stage])
{ {
return ReturnEntry(stage, bound_textures[stage]); return bound_textures[stage];
} }
const FourTexUnits& tex = bpmem.tex[stage >> 2]; 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]); const bool use_mipmaps = SamplerCommon::AreBpTexMode0MipmapsEnabled(tex.texMode0[id]);
u32 tex_levels = use_mipmaps ? ((tex.texMode1[id].max_lod + 0xf) / 0x10 + 1) : 1; 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 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; // TexelSizeInNibbles(format) * width * height / 16;
const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat); const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat);
const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(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); 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; const u8* src_data;
if (from_tmem) 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 else
src_data = Memory::GetPointer(address); 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 // 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) // 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; u32 palette_size = 0;
if (isPaletteTexture) if (isPaletteTexture)
{ {
palette_size = TexDecoder_GetPaletteSize(texformat); palette_size = TexDecoder_GetPaletteSize(texformat);
full_hash = base_hash ^ GetHash64(&texMem[tlutaddr], palette_size, full_hash = base_hash ^ GetHash64(&texMem[tlutaddr], palette_size,
g_ActiveConfig.iSafeTextureCache_ColorSamples); textureCacheSafetyColorSampleSize);
} }
else else
{ {
@ -789,7 +807,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
// texture formats. I'm not sure what effect checking width/height/levels // texture formats. I'm not sure what effect checking width/height/levels
// would have. // would have.
if (!isPaletteTexture || !g_Config.backend_info.bSupportsPaletteConversion) 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 // Note that we found an unconverted EFB copy, then continue. We'll
// perform the conversion later. Currently, we only convert EFB copies to // 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); 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) 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 // textures cause unnecessary slowdowns
// Example: Tales of Symphonia (GC) uses over 500 small textures in menus, but only around 70 // Example: Tales of Symphonia (GC) uses over 500 small textures in menus, but only around 70
// different ones // different ones
if (g_ActiveConfig.iSafeTextureCache_ColorSamples == 0 || if (textureCacheSafetyColorSampleSize == 0 ||
std::max(texture_size, palette_size) <= 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); auto hash_range = textures_by_hash.equal_range(full_hash);
TexHashCache::iterator hash_iter = hash_range.first; 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); entry = DoPartialTextureUpdates(hash_iter->second, &texMem[tlutaddr], tlutfmt);
return ReturnEntry(stage, entry); return entry;
} }
++hash_iter; ++hash_iter;
} }
@ -936,64 +954,66 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
// Initialized to null because only software loading uses this buffer // Initialized to null because only software loading uses this buffer
u8* dst_buffer = nullptr; u8* dst_buffer = nullptr;
if (!hires_tex && decode_on_gpu) if (!hires_tex)
{ {
u32 row_stride = bytes_per_block * (expandedWidth / bsw); if (decode_on_gpu)
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)
{ {
prev_level_size /= 4; u32 row_stride = bytes_per_block * (expandedWidth / bsw);
total_texture_size += prev_level_size; g_texture_cache->DecodeTextureOnGPU(
} entry, 0, src_data, texture_size, texformat, width, height,
expandedWidth, expandedHeight, row_stride, tlut, tlutfmt);
// 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 else
{ {
u8* src_data_gb = size_t decoded_texture_size = expandedWidth * sizeof(u32) * expandedHeight;
&texMem[bpmem.tex[stage / 4].texImage2[stage % 4].tmem_odd * TMEM_LINE_SIZE];
TexDecoder_DecodeRGBA8FromTmem(dst_buffer, src_data, src_data_gb, expandedWidth, // Allocate memory for all levels at once
expandedHeight); 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); iter = textures_by_address.emplace(address, entry);
if (g_ActiveConfig.iSafeTextureCache_ColorSamples == 0 || if (textureCacheSafetyColorSampleSize == 0 ||
std::max(texture_size, palette_size) <= 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->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->SetDimensions(nativeW, nativeH, tex_levels);
entry->SetHashes(base_hash, full_hash); entry->SetHashes(base_hash, full_hash);
entry->is_efb_copy = false; entry->is_efb_copy = false;
@ -1025,9 +1045,8 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
const u8* ptr_odd = nullptr; const u8* ptr_odd = nullptr;
if (from_tmem) if (from_tmem)
{ {
ptr_even = &texMem[bpmem.tex[stage / 4].texImage1[stage % 4].tmem_even * TMEM_LINE_SIZE + ptr_even = &texMem[tmem_address_even + texture_size];
texture_size]; ptr_odd = &texMem[tmem_address_odd];
ptr_odd = &texMem[bpmem.tex[stage / 4].texImage2[stage % 4].tmem_odd * TMEM_LINE_SIZE];
} }
for (u32 level = 1; level != texLevels; ++level) 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); entry = DoPartialTextureUpdates(iter->second, &texMem[tlutaddr], tlutfmt);
return ReturnEntry(stage, entry); return entry;
} }
void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstFormat, void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstFormat,
@ -1159,6 +1178,10 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
unsigned int cbufid = UINT_MAX; unsigned int cbufid = UINT_MAX;
PEControl::PixelFormat srcFormat = bpmem.zcontrol.pixel_format; PEControl::PixelFormat srcFormat = bpmem.zcontrol.pixel_format;
bool efbHasAlpha = srcFormat == PEControl::RGBA6_Z24; 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) if (is_depth_copy)
{ {
@ -1388,6 +1411,15 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
} }
break; 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: default:
ERROR_LOG(VIDEO, "Unknown copy color format: 0x%X", static_cast<int>(dstFormat)); ERROR_LOG(VIDEO, "Unknown copy color format: 0x%X", static_cast<int>(dstFormat));
colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1.0f; 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 bytes_per_row = num_blocks_x * bytes_per_block;
const u32 covered_range = num_blocks_y * dstStride; const u32 covered_range = num_blocks_y * dstStride;
bool copy_to_ram = !g_ActiveConfig.bSkipEFBCopyToRam;
bool copy_to_vram = true;
if (copy_to_ram) if (copy_to_ram)
{ {
EFBCopyParams format(srcFormat, dstFormat, is_depth_copy, isIntensity); EFBCopyParams format(srcFormat, dstFormat, is_depth_copy, isIntensity);
@ -1524,7 +1553,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
if (entry) if (entry)
{ {
entry->SetGeneralParameters(dstAddr, 0, baseFormat); entry->SetGeneralParameters(dstAddr, 0, baseFormat, is_xfb_copy);
entry->SetDimensions(tex_w, tex_h, 1); entry->SetDimensions(tex_w, tex_h, 1);
entry->frameCount = FRAMECOUNT_INVALID; entry->frameCount = FRAMECOUNT_INVALID;
@ -1537,7 +1566,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
u64 hash = entry->CalculateHash(); u64 hash = entry->CalculateHash();
entry->SetHashes(hash, hash); entry->SetHashes(hash, hash);
if (g_ActiveConfig.bDumpEFBTarget) if (g_ActiveConfig.bDumpEFBTarget && !is_xfb_copy)
{ {
static int count = 0; static int count = 0;
entry->texture->Save(StringFromFormat("%sefb_frame_%i.png", entry->texture->Save(StringFromFormat("%sefb_frame_%i.png",
@ -1699,12 +1728,22 @@ void TextureCacheBase::TCacheEntry::SetEfbCopy(u32 stride)
size_in_bytes = memory_stride * NumBlocksY(); 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 u64 TextureCacheBase::TCacheEntry::CalculateHash() const
{ {
u8* ptr = Memory::GetPointer(addr); u8* ptr = Memory::GetPointer(addr);
if (memory_stride == BytesPerRow()) if (memory_stride == BytesPerRow())
{ {
return GetHash64(ptr, size_in_bytes, g_ActiveConfig.iSafeTextureCache_ColorSamples); return GetHash64(ptr, size_in_bytes, HashSampleSize());
} }
else else
{ {
@ -1712,11 +1751,11 @@ u64 TextureCacheBase::TCacheEntry::CalculateHash() const
u64 temp_hash = size_in_bytes; u64 temp_hash = size_in_bytes;
u32 samples_per_row = 0; 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 // Hash at least 4 samples per row to avoid hashing in a bad pattern, like just on the left
// side of the efb copy // 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++) for (u32 i = 0; i < blocks; i++)

View File

@ -84,6 +84,7 @@ public:
bool tmem_only = false; // indicates that this texture only exists in the tmem cache 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 bool has_arbitrary_mips = false; // indicates that the mips in this texture are arbitrary
// content, aren't just downscaled // content, aren't just downscaled
bool should_force_safe_hashing = false; // for XFB
unsigned int native_width, unsigned int native_width,
native_height; // Texture dimensions from the GameCube's point of view native_height; // Texture dimensions from the GameCube's point of view
@ -105,11 +106,12 @@ public:
~TCacheEntry(); ~TCacheEntry();
void SetGeneralParameters(u32 _addr, u32 _size, TextureAndTLUTFormat _format) void SetGeneralParameters(u32 _addr, u32 _size, TextureAndTLUTFormat _format, bool force_safe_hashing)
{ {
addr = _addr; addr = _addr;
size_in_bytes = _size; size_in_bytes = _size;
format = _format; format = _format;
should_force_safe_hashing = force_safe_hashing;
} }
void SetDimensions(unsigned int _native_width, unsigned int _native_height, void SetDimensions(unsigned int _native_width, unsigned int _native_height,
@ -145,6 +147,7 @@ public:
u64 CalculateHash() const; u64 CalculateHash() const;
int HashSampleSize() const;
u32 GetWidth() const { return texture->GetConfig().width; } u32 GetWidth() const { return texture->GetConfig().width; }
u32 GetHeight() const { return texture->GetConfig().height; } u32 GetHeight() const { return texture->GetConfig().height; }
u32 GetNumLevels() const { return texture->GetConfig().levels; } u32 GetNumLevels() const { return texture->GetConfig().levels; }
@ -172,7 +175,12 @@ public:
TCacheEntry* Load(const u32 stage); TCacheEntry* Load(const u32 stage);
static void InvalidateAllBindPoints() { valid_bind_points.reset(); } static void InvalidateAllBindPoints() { valid_bind_points.reset(); }
static bool IsValidBindPoint(u32 i) { return valid_bind_points.test(i); } 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, void CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstFormat, u32 dstStride,
bool is_depth_copy, const EFBRectangle& srcRect, bool isIntensity, bool is_depth_copy, const EFBRectangle& srcRect, bool isIntensity,
bool scaleByHalf); bool scaleByHalf);
@ -248,8 +256,6 @@ private:
// Removes and unlinks texture from texture cache and returns it to the pool // Removes and unlinks texture from texture cache and returns it to the pool
TexAddrCache::iterator InvalidateTexture(TexAddrCache::iterator t_iter); TexAddrCache::iterator InvalidateTexture(TexAddrCache::iterator t_iter);
TCacheEntry* ReturnEntry(unsigned int stage, TCacheEntry* entry);
TexAddrCache textures_by_address; TexAddrCache textures_by_address;
TexHashCache textures_by_hash; TexHashCache textures_by_hash;
TexPool texture_pool; TexPool texture_pool;

View File

@ -49,6 +49,8 @@ u16 GetEncodedSampleCount(EFBCopyFormat format)
case EFBCopyFormat::RG8: case EFBCopyFormat::RG8:
case EFBCopyFormat::GB8: case EFBCopyFormat::GB8:
return 2; return 2;
case EFBCopyFormat::XFB:
return 2;
default: default:
PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEncodedSampleCount)", static_cast<int>(format)); PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEncodedSampleCount)", static_cast<int>(format));
return 1; return 1;
@ -656,6 +658,28 @@ static void WriteZ24Encoder(char*& p, APIType ApiType, const EFBCopyParams& para
WriteEncoderEnd(p); 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) const char* GenerateEncodingShader(const EFBCopyParams& params, APIType api_type)
{ {
text[sizeof(text) - 1] = 0x7C; // canary text[sizeof(text) - 1] = 0x7C; // canary
@ -728,6 +752,9 @@ const char* GenerateEncodingShader(const EFBCopyParams& params, APIType api_type
else else
WriteCC8Encoder(p, "gb", api_type, params); WriteCC8Encoder(p, "gb", api_type, params);
break; break;
case EFBCopyFormat::XFB:
WriteXFBEncoder(p, api_type, params);
break;
default: default:
PanicAlert("Invalid EFB Copy Format (0x%X)! (GenerateEncodingShader)", PanicAlert("Invalid EFB Copy Format (0x%X)! (GenerateEncodingShader)",
static_cast<int>(params.copy_format)); static_cast<int>(params.copy_format));

View File

@ -28,6 +28,11 @@ enum class TextureFormat
C8 = 0x9, C8 = 0x9,
C14X2 = 0xA, C14X2 = 0xA,
CMPR = 0xE, 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) static inline bool IsColorIndexed(TextureFormat format)
@ -73,6 +78,11 @@ enum class EFBCopyFormat
B8 = 0xA, // B8, Z8L B8 = 0xA, // B8, Z8L
RG8 = 0xB, // RG8, Z16R (Note: G and R are reversed) RG8 = 0xB, // RG8, Z16R (Note: G and R are reversed)
GB8 = 0xC, // GB8, Z16L 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 enum class TLUTFormat

View File

@ -3,9 +3,11 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm> #include <algorithm>
#include <cstddef>
#include <cmath> #include <cmath>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Common/Swap.h" #include "Common/Swap.h"
@ -46,6 +48,9 @@ int TexDecoder_GetTexelSizeInNibbles(TextureFormat format)
// Compressed format // Compressed format
case TextureFormat::CMPR: case TextureFormat::CMPR:
return 1; return 1;
// Special formats
case TextureFormat::XFB:
return 4;
default: default:
PanicAlert("Invalid Texture Format (0x%X)! (GetTexelSizeInNibbles)", static_cast<int>(format)); PanicAlert("Invalid Texture Format (0x%X)! (GetTexelSizeInNibbles)", static_cast<int>(format));
return 1; return 1;
@ -82,6 +87,9 @@ int TexDecoder_GetBlockWidthInTexels(TextureFormat format)
// Compressed format // Compressed format
case TextureFormat::CMPR: case TextureFormat::CMPR:
return 8; return 8;
// Special formats
case TextureFormat::XFB:
return 16;
default: default:
PanicAlert("Invalid Texture Format (0x%X)! (GetBlockWidthInTexels)", static_cast<int>(format)); PanicAlert("Invalid Texture Format (0x%X)! (GetBlockWidthInTexels)", static_cast<int>(format));
return 8; return 8;
@ -113,6 +121,9 @@ int TexDecoder_GetBlockHeightInTexels(TextureFormat format)
// Compressed format // Compressed format
case TextureFormat::CMPR: case TextureFormat::CMPR:
return 8; return 8;
// Special formats
case TextureFormat::XFB:
return 1;
default: default:
PanicAlert("Invalid Texture Format (0x%X)! (GetBlockHeightInTexels)", static_cast<int>(format)); PanicAlert("Invalid Texture Format (0x%X)! (GetBlockHeightInTexels)", static_cast<int>(format));
return 4; return 4;
@ -144,6 +155,9 @@ int TexDecoder_GetEFBCopyBlockWidthInTexels(EFBCopyFormat format)
// 32-bit formats // 32-bit formats
case EFBCopyFormat::RGBA8: case EFBCopyFormat::RGBA8:
return 4; return 4;
// Special formats
case EFBCopyFormat::XFB:
return 16;
default: default:
PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEFBCopyBlockWidthInTexels)", PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEFBCopyBlockWidthInTexels)",
static_cast<int>(format)); static_cast<int>(format));
@ -176,6 +190,9 @@ int TexDecoder_GetEFBCopyBlockHeightInTexels(EFBCopyFormat format)
// 32-bit formats // 32-bit formats
case EFBCopyFormat::RGBA8: case EFBCopyFormat::RGBA8:
return 4; return 4;
// Special formats
case EFBCopyFormat::XFB:
return 1;
default: default:
PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEFBCopyBlockHeightInTexels)", PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEFBCopyBlockHeightInTexels)",
static_cast<int>(format)); static_cast<int>(format));
@ -226,6 +243,8 @@ TextureFormat TexDecoder_GetEFBCopyBaseFormat(EFBCopyFormat format)
return TextureFormat::RGB5A3; return TextureFormat::RGB5A3;
case EFBCopyFormat::RGBA8: case EFBCopyFormat::RGBA8:
return TextureFormat::RGBA8; return TextureFormat::RGBA8;
case EFBCopyFormat::XFB:
return TextureFormat::XFB;
default: default:
PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEFBCopyBaseFormat)", static_cast<int>(format)); PanicAlert("Invalid EFB Copy Format (0x%X)! (GetEFBCopyBaseFormat)", static_cast<int>(format));
return static_cast<TextureFormat>(format); return static_cast<TextureFormat>(format);
@ -247,7 +266,7 @@ static const char* texfmt[] = {
"0x1C", "0x1D", "0x1E", "0x1F", "0x1C", "0x1D", "0x1E", "0x1F",
// pixel + copy // pixel + copy
"CR4", "0x21", "CRA4", "CRA8", "0x24", "0x25", "CYUVA8", "CA8", "CR8", "CG8", "CB8", "CRG8", "CR4", "0x21", "CRA4", "CRA8", "0x24", "0x25", "CYUVA8", "CA8", "CR8", "CG8", "CB8", "CRG8",
"CGB8", "0x2D", "0x2E", "0x2F", "CGB8", "0x2D", "0x2E", "XFB",
// Z + copy // Z + copy
"CZ4", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", "0x38", "CZ8M", "CZ8L", "0x3B", "CZ4", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", "0x38", "CZ8M", "CZ8L", "0x3B",
"CZ16L", "0x3D", "0x3E", "0x3F", "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; *((u32*)dst) = color;
} }
break; 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;
} }
} }

View File

@ -9,6 +9,7 @@
#include "Common/CPUDetect.h" #include "Common/CPUDetect.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Intrinsics.h" #include "Common/Intrinsics.h"
#include "Common/MathUtil.h"
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Common/Swap.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: case TextureFormat::CMPR:
TexDecoder_DecodeImpl_CMPR(dst, src, width, height, texformat, tlut, tlutfmt, Wsteps4, Wsteps8); TexDecoder_DecodeImpl_CMPR(dst, src, width, height, texformat, tlut, tlutfmt, Wsteps4, Wsteps8);
break; 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: default:
PanicAlert("Invalid Texture Format (0x%X)! (_TexDecoder_DecodeImpl)", PanicAlert("Invalid Texture Format (0x%X)! (_TexDecoder_DecodeImpl)",

View File

@ -55,6 +55,8 @@ struct TargetRectangle : public MathUtil::Rectangle<int>
return (RECT*)this; return (RECT*)this;
} }
#endif #endif
TargetRectangle(const MathUtil::Rectangle<int> &other) : MathUtil::Rectangle<int>(other) {}
TargetRectangle() = default;
}; };
#ifdef _WIN32 #ifdef _WIN32

View File

@ -221,6 +221,7 @@ struct VideoConfig final
bool bSupportsInternalResolutionFrameDumps; bool bSupportsInternalResolutionFrameDumps;
bool bSupportsGPUTextureDecoding; bool bSupportsGPUTextureDecoding;
bool bSupportsST3CTextures; bool bSupportsST3CTextures;
bool bSupportsCopyToVram;
bool bSupportsBitfield; // Needed by UberShaders, so must stay in VideoCommon bool bSupportsBitfield; // Needed by UberShaders, so must stay in VideoCommon
bool bSupportsDynamicSamplerIndexing; // Needed by UberShaders, so must stay in VideoCommon bool bSupportsDynamicSamplerIndexing; // Needed by UberShaders, so must stay in VideoCommon
bool bSupportsBPTCTextures; bool bSupportsBPTCTextures;