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