diff --git a/Source/Core/VideoBackends/D3D/FramebufferManager.cpp b/Source/Core/VideoBackends/D3D/FramebufferManager.cpp index 03cea928f0..85fa9a122c 100644 --- a/Source/Core/VideoBackends/D3D/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/D3D/FramebufferManager.cpp @@ -159,10 +159,10 @@ FramebufferManager::~FramebufferManager() SAFE_RELEASE(m_efb.resolved_depth_tex); } -void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) +void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) { u8* dst = Memory::GetPointer(xfbAddr); - s_xfbEncoder.Encode(dst, fbWidth, fbHeight, sourceRc, Gamma); + s_xfbEncoder.Encode(dst, fbStride, fbHeight, sourceRc, Gamma); } XFBSourceBase* FramebufferManager::CreateXFBSource(unsigned int target_width, unsigned int target_height, unsigned int layers) diff --git a/Source/Core/VideoBackends/D3D/FramebufferManager.h b/Source/Core/VideoBackends/D3D/FramebufferManager.h index 11ee646b4c..9151c7ef56 100644 --- a/Source/Core/VideoBackends/D3D/FramebufferManager.h +++ b/Source/Core/VideoBackends/D3D/FramebufferManager.h @@ -83,7 +83,7 @@ private: XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height, unsigned int layers) override; void GetTargetSize(unsigned int *width, unsigned int *height) override; - void CopyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) override; + void CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) override; static struct Efb { diff --git a/Source/Core/VideoBackends/D3D/XFBEncoder.cpp b/Source/Core/VideoBackends/D3D/XFBEncoder.cpp index 560b13fa7a..c2bcac54df 100644 --- a/Source/Core/VideoBackends/D3D/XFBEncoder.cpp +++ b/Source/Core/VideoBackends/D3D/XFBEncoder.cpp @@ -288,7 +288,7 @@ void XFBEncoder::Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcR D3D::stateman->PushDepthState(m_xfbEncodeDepthState); D3D::stateman->PushRasterizerState(m_xfbEncodeRastState); - D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, FLOAT(width/2), FLOAT(height)); + D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, FLOAT(width/4), FLOAT(height)); D3D::context->RSSetViewports(1, &vp); D3D::stateman->SetInputLayout(m_quadLayout); @@ -300,7 +300,7 @@ void XFBEncoder::Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcR TargetRectangle targetRect = g_renderer->ConvertEFBRectangle(srcRect); XFBEncodeParams params = { 0 }; - params.Width = FLOAT(width); + params.Width = FLOAT(width/2); params.Height = FLOAT(height); params.TexLeft = FLOAT(targetRect.left) / g_renderer->GetTargetWidth(); params.TexTop = FLOAT(targetRect.top) / g_renderer->GetTargetHeight(); @@ -325,7 +325,7 @@ void XFBEncoder::Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcR // Copy to staging buffer - D3D11_BOX srcBox = CD3D11_BOX(0, 0, 0, width/2, height, 1); + D3D11_BOX srcBox = CD3D11_BOX(0, 0, 0, width/4, height, 1); D3D::context->CopySubresourceRegion(m_outStage, 0, 0, 0, 0, m_out, 0, &srcBox); // Clean up state @@ -353,7 +353,7 @@ void XFBEncoder::Encode(u8* dst, u32 width, u32 height, const EFBRectangle& srcR u8* src = (u8*)map.pData; for (unsigned int y = 0; y < height; ++y) { - memcpy(dst, src, 2*width); + memcpy(dst, src, width); dst += bpmem.copyMipMapStrideChannels*32; src += map.RowPitch; } diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp index 3fee033c57..a4489a67ff 100644 --- a/Source/Core/VideoBackends/OGL/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/OGL/FramebufferManager.cpp @@ -481,7 +481,7 @@ GLuint FramebufferManager::GetEFBDepthTexture(const EFBRectangle& sourceRc) } } -void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) +void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) { u8* xfb_in_ram = Memory::GetPointer(xfbAddr); if (!xfb_in_ram) @@ -491,7 +491,7 @@ void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, c } TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc); - TextureConverter::EncodeToRamYUYV(ResolveAndGetRenderTarget(sourceRc), targetRc, xfb_in_ram, fbWidth, fbHeight); + TextureConverter::EncodeToRamYUYV(ResolveAndGetRenderTarget(sourceRc), targetRc, xfb_in_ram, sourceRc.GetWidth(), fbStride, fbHeight); } void FramebufferManager::SetFramebuffer(GLuint fb) diff --git a/Source/Core/VideoBackends/OGL/FramebufferManager.h b/Source/Core/VideoBackends/OGL/FramebufferManager.h index bf04b41536..d0aa6a2c18 100644 --- a/Source/Core/VideoBackends/OGL/FramebufferManager.h +++ b/Source/Core/VideoBackends/OGL/FramebufferManager.h @@ -97,7 +97,7 @@ private: XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height, unsigned int layers) override; void GetTargetSize(unsigned int *width, unsigned int *height) override; - void CopyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) override; + void CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) override; static int m_targetWidth; static int m_targetHeight; diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 85e7956b09..7fefeaff81 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1456,10 +1456,16 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co xfbSource = (const XFBSource*) xfbSourceList[i]; TargetRectangle drawRc; + TargetRectangle sourceRc; + sourceRc.left = xfbSource->sourceRc.left; + sourceRc.right = xfbSource->sourceRc.right; + sourceRc.top = xfbSource->sourceRc.top; + sourceRc.bottom = xfbSource->sourceRc.bottom; if (g_ActiveConfig.bUseRealXFB) { drawRc = flipped_trc; + sourceRc.right -= fbStride - fbWidth; } else { @@ -1481,18 +1487,12 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, co //drawRc.bottom *= vScale; //drawRc.left *= hScale; //drawRc.right *= hScale; + + sourceRc.right -= Renderer::EFBToScaledX(fbStride - fbWidth); } // Tell the OSD Menu about the current internal resolution OSDInternalW = xfbSource->sourceRc.GetWidth(); OSDInternalH = xfbSource->sourceRc.GetHeight(); - TargetRectangle sourceRc; - sourceRc.left = xfbSource->sourceRc.left; - sourceRc.right = xfbSource->sourceRc.right; - sourceRc.top = xfbSource->sourceRc.top; - sourceRc.bottom = xfbSource->sourceRc.bottom; - - sourceRc.right -= Renderer::EFBToScaledX(fbStride - fbWidth); - BlitScreen(sourceRc, drawRc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight); } } diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp index ebc3a672f1..92f2b8a788 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.cpp +++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp @@ -269,7 +269,8 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo isIntensity, dstFormat, scaleByHalf, - srcRect); + srcRect, + copyMipMapStrideChannels * 32); u8* dst = Memory::GetPointer(dstAddr); u64 const new_hash = GetHash64(dst,encoded_size,g_ActiveConfig.iSafeTextureCache_ColorSamples); diff --git a/Source/Core/VideoBackends/OGL/TextureConverter.cpp b/Source/Core/VideoBackends/OGL/TextureConverter.cpp index c696ccf687..53b615a5aa 100644 --- a/Source/Core/VideoBackends/OGL/TextureConverter.cpp +++ b/Source/Core/VideoBackends/OGL/TextureConverter.cpp @@ -213,12 +213,12 @@ void Shutdown() s_texConvFrameBuffer[1] = 0; } +// dst_line_size, writeStride in bytes + static void EncodeToRamUsingShader(GLuint srcTexture, - u8* destAddr, int dstWidth, int dstHeight, int readStride, - bool linearFilter) + u8* destAddr, u32 dst_line_size, u32 dstHeight, + u32 writeStride, bool linearFilter) { - - // switch to texture converter frame buffer // attach render buffer as color destination FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[0]); @@ -234,19 +234,13 @@ static void EncodeToRamUsingShader(GLuint srcTexture, else g_sampler_cache->BindNearestSampler(9); - glViewport(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight); + glViewport(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - // .. and then read back the results. - // TODO: make this less slow. + int dstSize = dst_line_size * dstHeight; - int writeStride = bpmem.copyMipMapStrideChannels * 32; - int dstSize = dstWidth*dstHeight*4; - int readHeight = readStride / dstWidth / 4; // 4 bytes per pixel - int readLoops = dstHeight / readHeight; - - if (writeStride != readStride && readLoops > 1) + if ((writeStride != dst_line_size) && (dstHeight > 1)) { // writing to a texture of a different size // also copy more then one block line, so the different strides matters @@ -255,13 +249,13 @@ static void EncodeToRamUsingShader(GLuint srcTexture, // CPU overhead because of the pbo glBindBuffer(GL_PIXEL_PACK_BUFFER, s_PBO); glBufferData(GL_PIXEL_PACK_BUFFER, dstSize, nullptr, GL_STREAM_READ); - glReadPixels(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); + glReadPixels(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); u8* pbo = (u8*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, dstSize, GL_MAP_READ_BIT); - for (int i = 0; i < readLoops; i++) + for (size_t i = 0; i < dstHeight; ++i) { - memcpy(destAddr, pbo, readStride); - pbo += readStride; + memcpy(destAddr, pbo, dst_line_size); + pbo += dst_line_size; destAddr += writeStride; } @@ -270,11 +264,11 @@ static void EncodeToRamUsingShader(GLuint srcTexture, } else { - glReadPixels(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, destAddr); + glReadPixels(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, destAddr); } } -int EncodeToRamFromTexture(u32 address,GLuint source_texture, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) +int EncodeToRamFromTexture(u32 address,GLuint source_texture, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source, u32 writeStride) { u32 format = copyfmt; @@ -323,13 +317,13 @@ int EncodeToRamFromTexture(u32 address,GLuint source_texture, bool bFromZBuffer, cacheLinesPerRow = numBlocksX; EncodeToRamUsingShader(source_texture, - dest_ptr, cacheLinesPerRow * 8, numBlocksY, cacheLinesPerRow * 32, - bScaleByHalf > 0 && !bFromZBuffer); + dest_ptr, cacheLinesPerRow * 32, numBlocksY, + writeStride, bScaleByHalf > 0 && !bFromZBuffer); return size_in_bytes; // TODO: D3D11 is calculating this value differently! } -void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, u8* destAddr, int dstWidth, int dstHeight) +void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, u8* destAddr, u32 dstWidth, u32 dstStride, u32 dstHeight) { g_renderer->ResetAPIState(); @@ -341,7 +335,7 @@ void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, u8* des // We enable linear filtering, because the GameCube does filtering in the vertical direction when // yscale is enabled. // Otherwise we get jaggies when a game uses yscaling (most PAL games) - EncodeToRamUsingShader(srcTexture, destAddr, dstWidth / 2, dstHeight, dstWidth*dstHeight*2, true); + EncodeToRamUsingShader(srcTexture, destAddr, dstWidth * 2, dstHeight, dstStride, true); FramebufferManager::SetFramebuffer(0); TextureCache::DisableStage(0); g_renderer->RestoreAPIState(); diff --git a/Source/Core/VideoBackends/OGL/TextureConverter.h b/Source/Core/VideoBackends/OGL/TextureConverter.h index e0c6cbd5fb..c41dfe10ac 100644 --- a/Source/Core/VideoBackends/OGL/TextureConverter.h +++ b/Source/Core/VideoBackends/OGL/TextureConverter.h @@ -19,12 +19,12 @@ void Init(); void Shutdown(); void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, - u8* destAddr, int dstWidth, int dstHeight); + u8* destAddr, u32 dstWidth, u32 dstStride, u32 dstHeight); void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture); // returns size of the encoded data (in bytes) -int EncodeToRamFromTexture(u32 address, GLuint source_texture, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source); +int EncodeToRamFromTexture(u32 address, GLuint source_texture, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source, u32 writeStride); } diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 410d4d44cc..5d1f3cfb08 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -251,9 +251,10 @@ static void BPWritten(const BPCmd& bp) height = MAX_XFB_HEIGHT; } - u32 width = bpmem.copyMipMapStrideChannels << 4; - - Renderer::RenderToXFB(destAddr, srcRect, width, height, s_gammaLUT[PE_copy.gamma]); + u32 stride = bpmem.copyMipMapStrideChannels << 5; + WARN_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, stride, height); + Renderer::RenderToXFB(destAddr, srcRect, stride, height, s_gammaLUT[PE_copy.gamma]); } // Clear the rectangular region after copying it. diff --git a/Source/Core/VideoCommon/FramebufferManagerBase.cpp b/Source/Core/VideoCommon/FramebufferManagerBase.cpp index 10d1cfa414..1b9fb40410 100644 --- a/Source/Core/VideoCommon/FramebufferManagerBase.cpp +++ b/Source/Core/VideoCommon/FramebufferManagerBase.cpp @@ -115,17 +115,17 @@ const XFBSourceBase* const* FramebufferManagerBase::GetVirtualXFBSource(u32 xfbA return &m_overlappingXFBArray[0]; } -void FramebufferManagerBase::CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) +void FramebufferManagerBase::CopyToXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma) { if (g_ActiveConfig.bUseRealXFB) - g_framebuffer_manager->CopyToRealXFB(xfbAddr, fbWidth, fbHeight, sourceRc,Gamma); + g_framebuffer_manager->CopyToRealXFB(xfbAddr, fbStride, fbHeight, sourceRc, Gamma); else - CopyToVirtualXFB(xfbAddr, fbWidth, fbHeight, sourceRc,Gamma); + CopyToVirtualXFB(xfbAddr, fbStride, fbHeight, sourceRc, Gamma); } -void FramebufferManagerBase::CopyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma) +void FramebufferManagerBase::CopyToVirtualXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma) { - VirtualXFBListType::iterator vxfb = FindVirtualXFB(xfbAddr, fbWidth, fbHeight); + VirtualXFBListType::iterator vxfb = FindVirtualXFB(xfbAddr, sourceRc.GetWidth(), fbHeight); if (m_virtualXFBList.end() == vxfb) { @@ -165,7 +165,7 @@ void FramebufferManagerBase::CopyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHe } vxfb->xfbSource->srcAddr = vxfb->xfbAddr = xfbAddr; - vxfb->xfbSource->srcWidth = vxfb->xfbWidth = fbWidth; + vxfb->xfbSource->srcWidth = vxfb->xfbWidth = sourceRc.GetWidth(); vxfb->xfbSource->srcHeight = vxfb->xfbHeight = fbHeight; vxfb->xfbSource->sourceRc = g_renderer->ConvertEFBRectangle(sourceRc); diff --git a/Source/Core/VideoCommon/FramebufferManagerBase.h b/Source/Core/VideoCommon/FramebufferManagerBase.h index bd6360877c..6127037268 100644 --- a/Source/Core/VideoCommon/FramebufferManagerBase.h +++ b/Source/Core/VideoCommon/FramebufferManagerBase.h @@ -45,7 +45,7 @@ public: FramebufferManagerBase(); virtual ~FramebufferManagerBase(); - static void CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma); + 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; } @@ -87,7 +87,7 @@ private: static void ReplaceVirtualXFB(); // TODO: merge these virtual funcs, they are nearly all the same - virtual void CopyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma = 1.0f) = 0; + virtual void CopyToRealXFB(u32 xfbAddr, u32 fbStride, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma = 1.0f) = 0; static void CopyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma = 1.0f); static const XFBSourceBase* const* GetRealXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32* xfbCount); diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index a688c86792..cd98f5dbf0 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -112,22 +112,22 @@ Renderer::~Renderer() #endif } -void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbWidth, u32 fbHeight, float Gamma) +void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight, float Gamma) { CheckFifoRecording(); - if (!fbWidth || !fbHeight) + if (!fbStride || !fbHeight) return; XFBWrited = true; if (g_ActiveConfig.bUseXFB) { - FramebufferManagerBase::CopyToXFB(xfbAddr, fbWidth, fbHeight, sourceRc, Gamma); + FramebufferManagerBase::CopyToXFB(xfbAddr, fbStride, fbHeight, sourceRc, Gamma); } else { - Swap(xfbAddr, fbWidth, fbWidth, fbHeight, sourceRc, Gamma); + Swap(xfbAddr, sourceRc.GetWidth(), fbStride, fbHeight, sourceRc, Gamma); } } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index a17525ca2e..c717483d1e 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -109,7 +109,7 @@ public: virtual void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z) = 0; virtual void ReinterpretPixelData(unsigned int convtype) = 0; - static void RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbWidth, u32 fbHeight, float Gamma = 1.0f); + static void RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight, float Gamma = 1.0f); virtual u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) = 0; virtual void PokeEFB(EFBAccessType type, const std::vector& data); diff --git a/Source/Core/VideoCommon/VideoCommon.h b/Source/Core/VideoCommon/VideoCommon.h index b81ebb8929..de27145c4a 100644 --- a/Source/Core/VideoCommon/VideoCommon.h +++ b/Source/Core/VideoCommon/VideoCommon.h @@ -19,9 +19,10 @@ enum EFB_HEIGHT = 528, }; -// XFB width is decided by EFB copy operation. The VI can do horizontal -// scaling (TODO: emulate). -const u32 MAX_XFB_WIDTH = EFB_WIDTH; +// Max XFB width is 720. You can only copy out 640 wide areas of efb to XFB +// so you need multiple copies to do the full width. +// The VI can do horizontal scaling (TODO: emulate). +const u32 MAX_XFB_WIDTH = 720; // Although EFB height is 528, 574-line XFB's can be created either with // vertical scaling by the EFB copy operation or copying to multiple XFB's