From a397c74140cae4f46b40bc5ffe462071df013021 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 13 Sep 2022 19:07:25 -0700 Subject: [PATCH 01/29] GPU: Fix simulating logicop with blend and shader. We only need to do it in the shader, if we're doing it in the shader. --- GPU/Common/GPUStateUtils.cpp | 37 ++++++++++++++++++++++++------------ GPU/Common/GPUStateUtils.h | 1 + 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index 7690156d9d..fb90850cb9 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -856,7 +856,7 @@ static inline bool blendColorSimilar(uint32_t a, uint32_t b, int margin = 25) { // Try to simulate some common logic ops by using blend, if needed. // The shader might also need modification, the below function SimulateLogicOpShaderTypeIfNeeded // takes care of that. -static void SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) { +static bool SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &blendEq) { // Note: our shader solution applies logic ops BEFORE blending, not correctly after. // This is however fine for the most common ones, like CLEAR/NOOP/SET, etc. if (!gstate_c.Supports(GPU_SUPPORTS_LOGIC_OP)) { @@ -866,7 +866,7 @@ static void SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend srcBlend = BlendFactor::ZERO; dstBlend = BlendFactor::ZERO; blendEq = BlendEq::ADD; - break; + return true; case GE_LOGIC_AND: case GE_LOGIC_AND_REVERSE: WARN_LOG_REPORT_ONCE(d3dLogicOpAnd, G3D, "Unsupported AND logic op: %x", gstate.getLogicOp()); @@ -889,21 +889,23 @@ static void SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend dstBlend = BlendFactor::ONE; blendEq = BlendEq::SUBTRACT; WARN_LOG_REPORT_ONCE(d3dLogicOpInverted, G3D, "Attempted inverse for logic op: %x", gstate.getLogicOp()); - break; + return true; case GE_LOGIC_NOOP: srcBlend = BlendFactor::ZERO; dstBlend = BlendFactor::ONE; blendEq = BlendEq::ADD; - break; + return true; case GE_LOGIC_XOR: WARN_LOG_REPORT_ONCE(d3dLogicOpOrXor, G3D, "Unsupported XOR logic op: %x", gstate.getLogicOp()); break; case GE_LOGIC_OR: case GE_LOGIC_OR_INVERTED: // Inverted in shader. + srcBlend = BlendFactor::ONE; dstBlend = BlendFactor::ONE; + blendEq = BlendEq::ADD; WARN_LOG_REPORT_ONCE(d3dLogicOpOr, G3D, "Attempted or for logic op: %x", gstate.getLogicOp()); - break; + return true; case GE_LOGIC_OR_REVERSE: WARN_LOG_REPORT_ONCE(d3dLogicOpOrReverse, G3D, "Unsupported OR REVERSE logic op: %x", gstate.getLogicOp()); break; @@ -912,10 +914,12 @@ static void SimulateLogicOpIfNeeded(BlendFactor &srcBlend, BlendFactor &dstBlend dstBlend = BlendFactor::ONE; blendEq = BlendEq::ADD; WARN_LOG_REPORT_ONCE(d3dLogicOpSet, G3D, "Attempted set for logic op: %x", gstate.getLogicOp()); - break; + return true; } } } + + return false; } // Choose the shader part of the above logic op fallback simulation. @@ -950,7 +954,6 @@ void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithS BlendFactor srcBlend = BlendFactor::ONE; BlendFactor dstBlend = BlendFactor::ZERO; BlendEq blendEq = BlendEq::ADD; - SimulateLogicOpIfNeeded(srcBlend, dstBlend, blendEq); // We're not blending, but we may still want to "blend" for stencil. // This is only useful for INCR/DECR/INVERT. Others can write directly. @@ -1252,11 +1255,6 @@ static void ConvertBlendState(GenericBlendState &blendState, bool forceReplaceBl colorEq = eqLookupNoMinMax[blendFuncEq]; } - // Attempt to apply simulated logic ops, if any and if needed. - if (!forceReplaceBlend) { - SimulateLogicOpIfNeeded(glBlendFuncA, glBlendFuncB, colorEq); - } - // The stencil-to-alpha in fragment shader doesn't apply here (blending is enabled), and we shouldn't // do any blending in the alpha channel as that doesn't seem to happen on PSP. So, we attempt to // apply the stencil to the alpha, since that's what should be stored. @@ -1581,5 +1579,20 @@ void ComputedPipelineState::Convert(bool shaderBitOpsSuppported) { if (blendState.applyFramebufferRead || logicState.applyFramebufferRead) { maskState.ConvertToShaderBlend(); logicState.ConvertToShaderBlend(); + } else { + // If it isn't a read, we may need to change blending to apply the logic op. + logicState.ApplyToBlendState(blendState); + } +} + +void GenericLogicState::ApplyToBlendState(GenericBlendState &blendState) { + if (SimulateLogicOpIfNeeded(blendState.srcColor, blendState.dstColor, blendState.eqColor)) { + if (!blendState.blendEnabled) { + // If it wasn't turned on, make sure it is now. + blendState.blendEnabled = true; + blendState.srcAlpha = BlendFactor::ONE; + blendState.dstAlpha = BlendFactor::ZERO; + blendState.eqAlpha = BlendEq::ADD; + } } } diff --git a/GPU/Common/GPUStateUtils.h b/GPU/Common/GPUStateUtils.h index bc0c9ae7b1..038091f7d8 100644 --- a/GPU/Common/GPUStateUtils.h +++ b/GPU/Common/GPUStateUtils.h @@ -226,6 +226,7 @@ struct GenericLogicState { // Hardware and shader generation GELogicOp logicOp; + void ApplyToBlendState(GenericBlendState &blendState); void ConvertToShaderBlend() { if (logicOp != GE_LOGIC_COPY) { logicOpEnabled = false; From 440062530e05739932bc4d193d190c222ecd63b4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 13 Sep 2022 19:07:54 -0700 Subject: [PATCH 02/29] GE Debugger: Allow fb copies while stepping. This is now causing crashes and should be allowed. --- GPU/Common/FramebufferManagerCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 1a8bd673d7..538c0ded3d 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1073,7 +1073,7 @@ bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualF // currentRenderVfb_ will always be set when this is called, except from the GE debugger. // Let's just not bother with the copy in that case. - bool skipCopy = !(flags & BINDFBCOLOR_MAY_COPY) || GPUStepping::IsStepping(); + bool skipCopy = !(flags & BINDFBCOLOR_MAY_COPY); // Currently rendering to this framebuffer. Need to make a copy. if (!skipCopy && framebuffer == currentRenderVfb_) { From 776c650017a6c125a603ea713a91aaba4ff3e482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Sep 2022 08:58:06 +0200 Subject: [PATCH 03/29] Fix regression in Tantalus games. See #15914 --- GPU/Common/FramebufferManagerCommon.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 80ddcdba0a..bac8514562 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -784,8 +784,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra float scaleFactorX = 1.0f; pipeline = GetReinterpretPipeline(src->fb_format, dst->fb_format, &scaleFactorX); - dstX1 *= 0.5f; - dstX2 *= 0.5f; + dstX1 *= scaleFactorX; + dstX2 *= scaleFactorX; pass_name = reinterpretStrings[(int)src->fb_format][(int)dst->fb_format]; From 0ed1f3d46145ad60e87329010abea85184cac9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Sep 2022 18:40:08 +0200 Subject: [PATCH 04/29] Switch multiple bools as parameters to DecodeTextureLevel to a flags enum. --- GPU/Common/TextureCacheCommon.cpp | 13 +++++++++---- GPU/Common/TextureCacheCommon.h | 10 ++++++++-- GPU/D3D11/TextureCacheD3D11.cpp | 2 +- GPU/Directx9/TextureCacheDX9.cpp | 6 ++---- GPU/GLES/TextureCacheGLES.cpp | 4 ++-- GPU/Vulkan/TextureCacheVulkan.cpp | 7 +++++-- 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index e4b463360d..77b47e8b9e 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1471,10 +1471,13 @@ inline u32 TfmtRawToFullAlpha(GETextureFormat fmt) { } } -CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, bool reverseColors, bool expandTo32bit) { +CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, TexDecodeFlags flags) { u32 alphaSum = 0xFFFFFFFF; u32 fullAlphaMask = 0x0; + bool expandTo32bit = (flags & TexDecodeFlags::EXPAND32) != 0; + bool reverseColors = (flags & TexDecodeFlags::REVERSE_COLORS) != 0; + bool swizzled = gstate.isTextureSwizzled(); if ((texaddr & 0x00600000) != 0 && Memory::IsVRAMAddress(texaddr)) { // This means it's in a mirror, possibly a swizzled mirror. Let's report. @@ -2459,7 +2462,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt return true; } -void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, int stride, ReplacedTexture &replaced, int srcLevel, int scaleFactor, Draw::DataFormat dstFmt, bool reverseColors) { +void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, int stride, ReplacedTexture &replaced, int srcLevel, int scaleFactor, Draw::DataFormat dstFmt, TexDecodeFlags texDecFlags) { int w = gstate.getTextureWidth(srcLevel); int h = gstate.getTextureHeight(srcLevel); @@ -2486,9 +2489,11 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i decPitch = stride; } - bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || dstFmt == Draw::DataFormat::R8G8B8A8_UNORM; + if (!gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || dstFmt == Draw::DataFormat::R8G8B8A8_UNORM) { + texDecFlags |= TexDecodeFlags::EXPAND32; + } - CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, srcLevel, bufw, reverseColors, expand32); + CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, srcLevel, bufw, texDecFlags); entry.SetAlphaStatus(alphaResult, srcLevel); if (scaleFactor > 1) { diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 82c79773a3..78ad53df1f 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -50,6 +50,12 @@ struct VirtualFramebuffer; class TextureReplacer; class ShaderManagerCommon; +enum class TexDecodeFlags { + EXPAND32 = 1, + REVERSE_COLORS = 2, +}; +ENUM_CLASS_BITOPS(TexDecodeFlags); + namespace Draw { class DrawContext; class Texture; @@ -354,13 +360,13 @@ protected: virtual void BindAsClutTexture(Draw::Texture *tex, bool smooth) {} - CheckAlphaResult DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, bool reverseColors, bool expandTo32Bit); + CheckAlphaResult DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, TexDecodeFlags flags); void UnswizzleFromMem(u32 *dest, u32 destPitch, const u8 *texptr, u32 bufw, u32 height, u32 bytesPerPixel); CheckAlphaResult ReadIndexedTex(u8 *out, int outPitch, int level, const u8 *texptr, int bytesPerIndex, int bufw, bool reverseColors, bool expandTo32Bit); ReplacedTexture &FindReplacement(TexCacheEntry *entry, int &w, int &h, int &d); // Return value is mapData normally, but could be another buffer allocated with AllocateAlignedMemory. - void LoadTextureLevel(TexCacheEntry &entry, uint8_t *mapData, int mapRowPitch, ReplacedTexture &replaced, int srcLevel, int scaleFactor, Draw::DataFormat dstFmt, bool reverseColors); + void LoadTextureLevel(TexCacheEntry &entry, uint8_t *mapData, int mapRowPitch, ReplacedTexture &replaced, int srcLevel, int scaleFactor, Draw::DataFormat dstFmt, TexDecodeFlags texDecFlags); template inline const T *GetCurrentClut() { diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 3a7ead0829..fb6cfba8b7 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -378,7 +378,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { return; } - LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, texFmt, false); + LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, texFmt, TexDecodeFlags{}); if (plan.depth == 1) { context_->UpdateSubresource(texture, i, nullptr, data, stride, 0); } else { diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 3a06c3b4f5..bfb859bb40 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -290,8 +290,6 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { return; } - Draw::DataFormat texFmt = FromD3D9Format(dstFmt); - if (plan.depth == 1) { // Regular loop. for (int i = 0; i < levels; i++) { @@ -307,7 +305,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { } uint8_t *data = (uint8_t *)rect.pBits; int stride = rect.Pitch; - LoadTextureLevel(*entry, data, stride, *plan.replaced, (i == 0) ? plan.baseLevelSrc : i, plan.scaleFactor, texFmt, false); + LoadTextureLevel(*entry, data, stride, *plan.replaced, (i == 0) ? plan.baseLevelSrc : i, plan.scaleFactor, FromD3D9Format(dstFmt), TexDecodeFlags{}); ((LPDIRECT3DTEXTURE9)texture)->UnlockRect(dstLevel); } } else { @@ -322,7 +320,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { uint8_t *data = (uint8_t *)box.pBits; int stride = box.RowPitch; for (int i = 0; i < plan.depth; i++) { - LoadTextureLevel(*entry, data, stride, *plan.replaced, (i == 0) ? plan.baseLevelSrc : i, plan.scaleFactor, texFmt, false); + LoadTextureLevel(*entry, data, stride, *plan.replaced, (i == 0) ? plan.baseLevelSrc : i, plan.scaleFactor, FromD3D9Format(dstFmt), TexDecodeFlags{}); data += box.SlicePitch; } ((LPDIRECT3DVOLUMETEXTURE9)texture)->UnlockBox(0); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 14ebad20be..c28fc6d1e5 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -325,7 +325,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { return; } - LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, dstFmt, true); + LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, dstFmt, TexDecodeFlags::REVERSE_COLORS); // NOTE: TextureImage takes ownership of data, so we don't free it afterwards. render_->TextureImage(entry->textureName, i, mipWidth, mipHeight, 1, dstFmt, data, GLRAllocType::ALIGNED); @@ -344,7 +344,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { u8 *p = data; for (int i = 0; i < plan.depth; i++) { - LoadTextureLevel(*entry, p, stride, *plan.replaced, i, plan.scaleFactor, dstFmt, true); + LoadTextureLevel(*entry, p, stride, *plan.replaced, i, plan.scaleFactor, dstFmt, TexDecodeFlags::REVERSE_COLORS); p += levelStride; } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 443670e903..a67210cfc7 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -728,7 +728,10 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt u32 *pixelData; int decPitch; - bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1 || dstFmt == VULKAN_8888_FORMAT; + TexDecodeFlags texDecFlags{}; + if (!gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1 || dstFmt == VULKAN_8888_FORMAT) { + texDecFlags |= TexDecodeFlags::EXPAND32; + } if (scaleFactor > 1) { tmpTexBufRearrange_.resize(std::max(bufw, w) * h); @@ -740,7 +743,7 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt decPitch = rowPitch; } - CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, level, bufw, false, expand32); + CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, level, bufw, texDecFlags); entry.SetAlphaStatus(alphaResult, level); if (scaleFactor > 1) { From 57ef2bb0d5f761e3419896b65b24b7a4f7971e64 Mon Sep 17 00:00:00 2001 From: Lubos Date: Wed, 14 Sep 2022 20:36:33 +0200 Subject: [PATCH 05/29] OpenXR - Ensure the head pose is locked during frame rendering --- Common/GPU/OpenGL/GLRenderManager.cpp | 31 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index efdf0bd7f4..23123533fc 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -200,6 +200,7 @@ bool GLRenderManager::ThreadFrame() { std::unique_lock lock(mutex_); if (!run_) return false; + bool vrlock = false; // In case of syncs or other partial completion, we keep going until we complete a frame. do { @@ -235,11 +236,22 @@ bool GLRenderManager::ThreadFrame() { INFO_LOG(G3D, "Running first frame (%d)", threadFrame_); firstFrame = false; } + if (IsVRBuild() && !vrlock) { + if (PreVRRender()) { + vrlock = true; + } else { + return false; + } + } Run(threadFrame_); VLOG("PULL: Finished frame %d", threadFrame_); } while (!nextFrame); + if (IsVRBuild() && vrlock) { + PostVRRender(); + } + return true; } @@ -579,17 +591,14 @@ void GLRenderManager::Run(int frame) { } if (IsVRBuild()) { - if (PreVRRender()) { - int passes = 1; - if (!IsMultiviewSupported() && g_Config.bEnableStereo) { - passes = 2; - } - for (int i = 0; i < passes; i++) { - PreVRFrameRender(i); - queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1); - PostVRFrameRender(); - } - PostVRRender(); + int passes = 1; + if (!IsMultiviewSupported() && g_Config.bEnableStereo) { + passes = 2; + } + for (int i = 0; i < passes; i++) { + PreVRFrameRender(i); + queueRunner_.RunSteps(stepsOnThread, skipGLCalls_, i < passes - 1); + PostVRFrameRender(); } } else { queueRunner_.RunSteps(stepsOnThread, skipGLCalls_); From 4803825f836701ed1341e5104a64b814d5a50a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Sep 2022 11:16:30 +0200 Subject: [PATCH 06/29] Buffer copy matching workaround --- GPU/Common/FramebufferManagerCommon.cpp | 35 ++++++++++++++++++++----- GPU/Common/FramebufferManagerCommon.h | 2 +- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index bac8514562..22a0907518 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1017,7 +1017,7 @@ void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) { // TODO: This doesn't seem quite right anymore. fmt = displayFormat_; } - DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height); + DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height, RASTER_COLOR, "UpdateFromMemory_DrawPixels"); SetColorUpdated(vfb, gstate_c.skipDrawReason); } else { INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%dx%d %s)", vfb->fb_address, vfb->width, vfb->height, GeBufferFormatToString(vfb->fb_format)); @@ -1033,7 +1033,10 @@ void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) { gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE); } -void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) { +void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag) { + // Add depth support later for depth uploads. + _dbg_assert_(channel == RASTER_COLOR); + textureCache_->ForgetLastTexture(); shaderManager_->DirtyLastShader(); // On GL, important that this is BEFORE drawing float u0 = 0.0f, u1 = 1.0f; @@ -1042,7 +1045,7 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int DrawTextureFlags flags; if (useBufferedRendering_ && vfb && vfb->fbo) { flags = DRAWTEX_LINEAR; - draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "DrawPixels"); + draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag); gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); SetViewport2D(0, 0, vfb->renderWidth, vfb->renderHeight); draw_->SetScissorRect(0, 0, vfb->renderWidth, vfb->renderHeight); @@ -1063,6 +1066,7 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int Draw::Texture *pixelsTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height); if (pixelsTex) { draw_->BindTextures(0, 1, &pixelsTex); + // TODO: Replace with BlitUsingRaster for simplicity. DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); gpuStats.numUploads++; pixelsTex->Release(); @@ -1210,7 +1214,7 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G // better be fast. Draw::Texture *tex = draw_->CreateTexture(desc); if (!tex) - ERROR_LOG(G3D, "Failed to create drawpixels texture"); + ERROR_LOG(G3D, "Failed to create DrawPixels texture"); return tex; } @@ -1654,7 +1658,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, WARN_LOG_ONCE(btucpy, G3D, "Memcpy fbo upload %08x -> %08x (size: %x)", src, dst, size); FlushBeforeCopy(); const u8 *srcBase = Memory::GetPointerUnchecked(src); - DrawPixels(dstBuffer, 0, dstY, srcBase, dstBuffer->fb_format, dstBuffer->fb_stride, dstBuffer->width, dstH); + DrawPixels(dstBuffer, 0, dstY, srcBase, dstBuffer->fb_format, dstBuffer->fb_stride, dstBuffer->width, dstH, RASTER_COLOR, "MemcpyFboUpload_DrawPixels"); SetColorUpdated(dstBuffer, skipDrawReason); RebindFramebuffer("RebindFramebuffer - Memcpy fbo upload"); // This is a memcpy, let's still copy just in case. @@ -1700,7 +1704,16 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p // that of their buffers. Then after matching we try to map the copy to the simplest operation that does // what we need. + // We are only looking at color for now, have not found any block transfers of depth data (although it's plausible). + for (auto vfb : vfbs_) { + // Check for easily detected depth copies for logging purposes. + // Depth copies are not that useful though because you manually need to account for swizzle, so + // not sure if games will use them. + if ((vfb->z_address & 0x3FFFFFFF) == basePtr) { + WARN_LOG_N_TIMES(z_xfer, 5, G3D, "FindTransferFramebuffer: found matching depth buffer, %08x (dest=%d, bpp=%d)", basePtr, (int)destination, bpp); + } + const u32 vfb_address = vfb->fb_address & 0x3FFFFFFF; const u32 vfb_size = ColorBufferByteSize(vfb); @@ -1773,7 +1786,14 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p // Sort candidates by just recency for now, we might add other. for (size_t i = 0; i < candidates.size(); i++) { const BlockTransferRect *candidate = &candidates[i]; - if (!best || candidate->vfb->colorBindSeq > best->vfb->colorBindSeq) { + + bool better = !best || candidate->vfb->colorBindSeq > best->vfb->colorBindSeq; + if ((candidate->vfb->usageFlags & FB_USAGE_CLUT) && candidate->x_bytes == 0 && candidate->y == 0 && destination) { + // Hack to prioritize copies to clut buffers. + best = candidate; + break; + } + if (better) { best = candidate; } } @@ -2153,6 +2173,7 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS WARN_LOG_ONCE(btu, G3D, "Block transfer upload %08x -> %08x", srcBasePtr, dstBasePtr); FlushBeforeCopy(); const u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; + int dstBpp = BufferFormatBytesPerPixel(dstRect.vfb->fb_format); float dstXFactor = (float)bpp / dstBpp; if (dstRect.w_bytes / bpp > dstRect.vfb->width || dstRect.h > dstRect.vfb->height) { @@ -2166,7 +2187,7 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS // Resizing may change the viewport/etc. gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE); } - DrawPixels(dstRect.vfb, static_cast(dstX * dstXFactor), dstY, srcBase, dstRect.vfb->fb_format, static_cast(srcStride * dstXFactor), static_cast(dstRect.w_bytes / bpp * dstXFactor), dstRect.h); + DrawPixels(dstRect.vfb, static_cast(dstX * dstXFactor), dstY, srcBase, dstRect.vfb->fb_format, static_cast(srcStride * dstXFactor), static_cast(dstRect.w_bytes / bpp * dstXFactor), dstRect.h, RASTER_COLOR, "BlockTransferCopy_DrawPixels"); SetColorUpdated(dstRect.vfb, skipDrawReason); RebindFramebuffer("RebindFramebuffer - NotifyBlockTransferAfter"); } diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 905996979b..f7107dc029 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -327,7 +327,7 @@ public: void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes); void DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat); - void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height); + void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag); size_t NumVFBs() const { return vfbs_.size(); } From 8d00df12a40c8a3db5eec6d70809a257e5f878e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 11 Sep 2022 18:23:57 +0200 Subject: [PATCH 07/29] Detect the CLUT8/8888 situation Implement CLUT8 depal from 8888 textures. Hard to tell if working --- GPU/Common/FragmentShaderGenerator.cpp | 22 ++++++++++++++++++++-- GPU/Common/ShaderId.cpp | 2 +- GPU/Common/TextureCacheCommon.cpp | 21 +++++++++++++++++---- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index 9586d01b48..74acc84886 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -734,8 +734,26 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " }\n"); break; case ShaderDepalMode::CLUT8_8888: - // Not yet implemented. - WRITE(p, " vec4 t = vec4(0.0, 0.0, 0.0, 0.0);\n"); + if (doTextureProjection) { + // We don't use textureProj because we need better control and it's probably not much of a savings anyway. + // However it is good for precision on older hardware like PowerVR. + p.F(" vec2 uv = %s.xy/%s.z;\n vec2 uv_round;\n", texcoord, texcoord); + } else { + p.F(" vec2 uv = %s.xy;\n vec2 uv_round;\n", texcoord); + } + p.C(" vec2 tsize = vec2(textureSize(tex, 0).xy);\n"); + p.C(" uv_round = floor(uv * tsize);\n"); + p.C(" int component = int(uv_round.x) & 3;\n"); + p.C(" uv_round.x *= 0.25;\n"); + p.C(" vec4 t = ivec4(").LoadTexture2D("tex", "ivec2(uv_round)", 0).C(");\n"); + p.C(" int index;\n"); + p.C(" switch (component) {\n"); + p.C(" case 0: index = int(t.x * 255.99); break;\n"); + p.C(" case 1: index = int(t.y * 255.99); break;\n"); + p.C(" case 2: index = int(t.z * 255.99); break;\n"); + p.C(" case 3: index = int(t.w * 255.99); break;\n"); + p.C(" }\n"); + p.C(" t = ").LoadTexture2D("pal", "ivec2(index, 0)", 0).C(";\n"); break; } diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index f873024c39..7ca42ccba6 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -180,7 +180,7 @@ std::string FragmentShaderDesc(const FShaderID &id) { if (id.Bit(FS_BIT_COLOR_DOUBLE)) desc << "2x "; if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat "; if (id.Bit(FS_BIT_BGRA_TEXTURE)) desc << "BGRA "; - switch ((ShaderDepalMode)id.Bit(FS_BIT_SHADER_DEPAL_MODE)) { + switch ((ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2)) { case ShaderDepalMode::OFF: break; case ShaderDepalMode::NORMAL: desc << "Depal "; break; case ShaderDepalMode::SMOOTHED: desc << "SmoothDepal "; break; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 77b47e8b9e..65869ad428 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -947,12 +947,13 @@ bool TextureCacheCommon::MatchFramebuffer( return false; } - // Check works for D16 too (???) + // Check works for D16 too. const bool matchingClutFormat = (fb_format == GE_FORMAT_DEPTH16 && entry.format == GE_TFMT_CLUT16) || (fb_format == GE_FORMAT_DEPTH16 && entry.format == GE_TFMT_5650) || (fb_format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT32) || - (fb_format != GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT16); + (fb_format != GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT16) || + (fb_format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT8); const int texBitsPerPixel = std::max(1U, (u32)textureBitsPerPixel[entry.format]); const int byteOffset = texaddr - addr; @@ -991,6 +992,10 @@ bool TextureCacheCommon::MatchFramebuffer( if (fb_stride_in_bytes != tex_stride_in_bytes) { // Probably irrelevant. Although, as we shall see soon, there are exceptions. + // Burnout Dominator lens flare trick special case. + if (fb_format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT8 && texWidth == 4 && texHeight == 1) { + return true; + } return false; } @@ -1876,7 +1881,7 @@ static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferForma } break; case GE_FORMAT_8888: - if (texFormat == GE_TFMT_CLUT32) { + if (texFormat == GE_TFMT_CLUT32 || texFormat == GE_TFMT_CLUT8) { // clut8 takes a special depal mode. return true; } break; @@ -1964,9 +1969,17 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer samplerKey.mipEnable = false; ApplySamplingParams(samplerKey); + ShaderDepalMode mode = ShaderDepalMode::NORMAL; + if (texFormat == GE_TFMT_CLUT8 && framebuffer->fb_format == GE_FORMAT_8888) { + mode = ShaderDepalMode::CLUT8_8888; + smoothedDepal = false; // just in case + } else if (smoothedDepal) { + mode = ShaderDepalMode::SMOOTHED; + } + // Since we started/ended render passes, might need these. gstate_c.Dirty(DIRTY_DEPAL); - gstate_c.SetUseShaderDepal(smoothedDepal ? ShaderDepalMode::SMOOTHED : ShaderDepalMode::NORMAL); + gstate_c.SetUseShaderDepal(mode); gstate_c.depalFramebufferFormat = framebuffer->fb_format; const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); From d551d0dd8f9cbd326ebde0fc268037029b12986d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 11 Sep 2022 18:51:12 +0200 Subject: [PATCH 08/29] Enable DownloadCLUT and ShaderBitmask for Burnout Dominator --- assets/compat.ini | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/assets/compat.ini b/assets/compat.ini index 3d2846d377..ce3afae275 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1152,6 +1152,10 @@ ULES00262 = true ULUS10064 = true ULKS46087 = true +# Burnout Dominator - lens flare effect (issue )#11100) +ULUS10236 = true +ULES00703 = true + [DisableFirstFrameReadback] # Wipeout Pure: Temporary workaround for lens flare flicker. See #13344 UCUS98612 = true @@ -1302,3 +1306,7 @@ ULKS46087 = true [AllowDownloadCLUT] # Temporary compatibility option, while developing a GPU CLUT-from-framebuffer path. + +# Burnout Dominator - lens flare effect (issue )#11100) +ULUS10236 = true +ULES00703 = true From a52e62a187a595ffab2088144eab41f68da65358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Sep 2022 15:34:32 +0200 Subject: [PATCH 09/29] Enable depth uploads on render-to-clut-buffer. Esoteric but needed for #11100. Compat flag for now. --- Common/GPU/D3D11/thin3d_d3d11.cpp | 1 + Common/GPU/D3D9/thin3d_d3d9.cpp | 1 + Common/GPU/DataFormat.h | 2 + Common/GPU/OpenGL/DataFormatGL.cpp | 8 +++- Common/GPU/OpenGL/DataFormatGL.h | 2 +- Common/GPU/OpenGL/GLQueueRunner.cpp | 4 +- Common/GPU/Vulkan/thin3d_vulkan.cpp | 8 ++++ Common/GPU/thin3d.cpp | 3 ++ Common/GPU/thin3d.h | 1 + Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + GPU/Common/FragmentShaderGenerator.cpp | 11 +++-- GPU/Common/FramebufferManagerCommon.cpp | 63 ++++++++++++++++++------- GPU/Common/FramebufferManagerCommon.h | 1 + assets/compat.ini | 8 +++- 15 files changed, 87 insertions(+), 28 deletions(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 2bce9ebaff..99e2eb2577 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -471,6 +471,7 @@ static DXGI_FORMAT dataFormatToD3D11(DataFormat format) { case DataFormat::R8G8B8A8_UNORM_SRGB: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; case DataFormat::B8G8R8A8_UNORM: return DXGI_FORMAT_B8G8R8A8_UNORM; case DataFormat::B8G8R8A8_UNORM_SRGB: return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + case DataFormat::R16_UNORM: return DXGI_FORMAT_R16_UNORM; case DataFormat::R16_FLOAT: return DXGI_FORMAT_R16_FLOAT; case DataFormat::R16G16_FLOAT: return DXGI_FORMAT_R16G16_FLOAT; case DataFormat::R16G16B16A16_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 8554961119..9bfe824c09 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -114,6 +114,7 @@ static const D3DSTENCILOP stencilOpToD3D9[] = { D3DFORMAT FormatToD3DFMT(DataFormat fmt) { switch (fmt) { + case DataFormat::R16_UNORM: return D3DFMT_L16; // closest match, should be a fine substitution if we ignore channels except R. case DataFormat::R8G8B8A8_UNORM: return D3DFMT_A8R8G8B8; case DataFormat::B8G8R8A8_UNORM: return D3DFMT_A8R8G8B8; case DataFormat::R4G4B4A4_UNORM_PACK16: return D3DFMT_A4R4G4B4; // emulated diff --git a/Common/GPU/DataFormat.h b/Common/GPU/DataFormat.h index b07d0a2f34..0c8a93e627 100644 --- a/Common/GPU/DataFormat.h +++ b/Common/GPU/DataFormat.h @@ -32,6 +32,8 @@ enum class DataFormat : uint8_t { A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit. A1B5G5R5_UNORM_PACK16, // A1 in the UPPER bit. OpenGL-only. + R16_UNORM, + R16_FLOAT, R16G16_FLOAT, R16G16B16A16_FLOAT, diff --git a/Common/GPU/OpenGL/DataFormatGL.cpp b/Common/GPU/OpenGL/DataFormatGL.cpp index 1b8ba63acc..859624b28f 100644 --- a/Common/GPU/OpenGL/DataFormatGL.cpp +++ b/Common/GPU/OpenGL/DataFormatGL.cpp @@ -4,9 +4,15 @@ namespace Draw { // TODO: Also output storage format (GL_RGBA8 etc) for modern GL usage. -bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment) { +bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment) { alignment = 4; switch (fmt) { + case DataFormat::R16_UNORM: + internalFormat = GL_R16; + format = GL_R; + type = GL_UNSIGNED_SHORT; + break; + case DataFormat::R8G8B8A8_UNORM: internalFormat = GL_RGBA; format = GL_RGBA; diff --git a/Common/GPU/OpenGL/DataFormatGL.h b/Common/GPU/OpenGL/DataFormatGL.h index 63fc0a22ec..163f155db3 100644 --- a/Common/GPU/OpenGL/DataFormatGL.h +++ b/Common/GPU/OpenGL/DataFormatGL.h @@ -5,6 +5,6 @@ namespace Draw { -bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment); +bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment); } diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index e19075f109..c680062b78 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -385,7 +385,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps, bool ski GLenum internalFormat, format, type; int alignment; - Thin3DFormatToFormatAndType(step.texture_image.format, internalFormat, format, type, alignment); + Thin3DFormatToGLFormatAndType(step.texture_image.format, internalFormat, format, type, alignment); if (step.texture_image.depth == 1) { glTexImage2D(tex->target, step.texture_image.level, internalFormat, @@ -1279,7 +1279,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last // For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage. GLuint internalFormat, format, type; int alignment; - Thin3DFormatToFormatAndType(c.texture_subimage.format, internalFormat, format, type, alignment); + Thin3DFormatToGLFormatAndType(c.texture_subimage.format, internalFormat, format, type, alignment); glTexSubImage2D(tex->target, c.texture_subimage.level, c.texture_subimage.x, c.texture_subimage.y, c.texture_subimage.width, c.texture_subimage.height, format, type, c.texture_subimage.data); if (c.texture_subimage.allocType == GLRAllocType::ALIGNED) { FreeAlignedMemory(c.texture_subimage.data); diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index f673691224..c836b41499 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -563,6 +563,11 @@ static int GetBpp(VkFormat format) { case VK_FORMAT_R8G8B8A8_UNORM: case VK_FORMAT_B8G8R8A8_UNORM: return 32; + case VK_FORMAT_R8_UNORM: + return 8; + case VK_FORMAT_R8G8_UNORM: + case VK_FORMAT_R16_UNORM: + return 16; case VK_FORMAT_R4G4B4A4_UNORM_PACK16: case VK_FORMAT_B4G4R4A4_UNORM_PACK16: case VK_FORMAT_R5G5B5A1_UNORM_PACK16: @@ -586,6 +591,9 @@ static VkFormat DataFormatToVulkan(DataFormat format) { case DataFormat::D32F: return VK_FORMAT_D32_SFLOAT; case DataFormat::D32F_S8: return VK_FORMAT_D32_SFLOAT_S8_UINT; case DataFormat::S8: return VK_FORMAT_S8_UINT; + + case DataFormat::R16_UNORM: return VK_FORMAT_R16_UNORM; + case DataFormat::R16_FLOAT: return VK_FORMAT_R16_SFLOAT; case DataFormat::R16G16_FLOAT: return VK_FORMAT_R16G16_SFLOAT; case DataFormat::R16G16B16A16_FLOAT: return VK_FORMAT_R16G16B16A16_SFLOAT; diff --git a/Common/GPU/thin3d.cpp b/Common/GPU/thin3d.cpp index d712195c7b..1cc3c11fa0 100644 --- a/Common/GPU/thin3d.cpp +++ b/Common/GPU/thin3d.cpp @@ -33,6 +33,9 @@ size_t DataFormatSizeInBytes(DataFormat fmt) { case DataFormat::R8G8B8A8_SNORM: return 4; case DataFormat::R8G8B8A8_UINT: return 4; case DataFormat::R8G8B8A8_SINT: return 4; + + case DataFormat::R16_UNORM: return 2; + case DataFormat::R16_FLOAT: return 2; case DataFormat::R16G16_FLOAT: return 4; case DataFormat::R16G16B16A16_FLOAT: return 8; diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 6196f494f9..46b6305fd9 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -557,6 +557,7 @@ typedef std::functionfb_format = params.fb_format; vfb->usageFlags = FB_USAGE_RENDER_COLOR; - u32 byteSize = ColorBufferByteSize(vfb); - if (Memory::IsVRAMAddress(params.fb_address) && params.fb_address + byteSize > framebufRangeEnd_) { - framebufRangeEnd_ = params.fb_address + byteSize; + u32 colorByteSize = ColorBufferByteSize(vfb); + if (Memory::IsVRAMAddress(params.fb_address) && params.fb_address + colorByteSize > framebufRangeEnd_) { + framebufRangeEnd_ = params.fb_address + colorByteSize; } // This is where we actually create the framebuffer. The true is "force". @@ -499,9 +499,9 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer // Assume that if we're clearing right when switching to a new framebuffer, we don't need to upload. if (useBufferedRendering_ && params.isDrawing) { - gpu->PerformMemoryUpload(params.fb_address, byteSize); + gpu->PerformMemoryUpload(params.fb_address, colorByteSize); // Alpha was already done by PerformMemoryUpload. - PerformStencilUpload(params.fb_address, byteSize, StencilUpload::STENCIL_IS_ZERO | StencilUpload::IGNORE_ALPHA); + PerformStencilUpload(params.fb_address, colorByteSize, StencilUpload::STENCIL_IS_ZERO | StencilUpload::IGNORE_ALPHA); // TODO: Is it worth trying to upload the depth buffer (only if it wasn't copied above..?) } @@ -551,9 +551,20 @@ void FramebufferManagerCommon::SetDepthFrameBuffer(bool isClearingDepth) { // by copying from any overlapping buffers with fresher content. if (!isClearingDepth) { CopyToDepthFromOverlappingFramebuffers(currentRenderVfb_); - } + // Special compatibility trick for Burnout Dominator lens flares. Not sure how to best generalize this. See issue #11100 + if (PSP_CoreParameter().compat.flags().UploadDepthForCLUTTextures && (currentRenderVfb_->usageFlags & FB_USAGE_CLUT) != 0) { + // Set the flag, then upload memory contents to depth channel. + // Sanity check the depth buffer pointer. + if (currentRenderVfb_->z_address != 0 && currentRenderVfb_->z_address != currentRenderVfb_->fb_address) { + const u16 *src = (const u16 *)Memory::GetPointerUnchecked(currentRenderVfb_->z_address); + DrawPixels(currentRenderVfb_, 0, 0, (const u8 *)src, GE_FORMAT_DEPTH16, currentRenderVfb_->z_stride, currentRenderVfb_->width, currentRenderVfb_->height, RASTER_DEPTH, "Depth Upload"); + } + } + } + // First time use of this framebuffer's depth buffer. currentRenderVfb_->usageFlags |= FB_USAGE_RENDER_DEPTH; + currentRenderVfb_->depthBindSeq = GetBindSeqCount(); } @@ -1034,22 +1045,19 @@ void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) { } void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag) { - // Add depth support later for depth uploads. - _dbg_assert_(channel == RASTER_COLOR); - textureCache_->ForgetLastTexture(); - shaderManager_->DirtyLastShader(); // On GL, important that this is BEFORE drawing + shaderManager_->DirtyLastShader(); float u0 = 0.0f, u1 = 1.0f; float v0 = 0.0f, v1 = 1.0f; DrawTextureFlags flags; if (useBufferedRendering_ && vfb && vfb->fbo) { - flags = DRAWTEX_LINEAR; + flags = channel == RASTER_COLOR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST; draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); SetViewport2D(0, 0, vfb->renderWidth, vfb->renderHeight); draw_->SetScissorRect(0, 0, vfb->renderWidth, vfb->renderHeight); } else { + _dbg_assert_(channel == RASTER_COLOR); // We are drawing directly to the back buffer so need to flip. // Should more of this be handled by the presentation engine? if (needBackBufferYSwap_) @@ -1063,11 +1071,18 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_); } + if (channel == RASTER_DEPTH) { + _dbg_assert_(srcPixelFormat == GE_FORMAT_DEPTH16); + flags = flags | DRAWTEX_DEPTH; + } + Draw::Texture *pixelsTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height); if (pixelsTex) { draw_->BindTextures(0, 1, &pixelsTex); - // TODO: Replace with BlitUsingRaster for simplicity. + + // TODO: Replace with draw2D_.Blit() directly. DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); + gpuStats.numUploads++; pixelsTex->Release(); draw_->InvalidateCachedState(); @@ -1157,6 +1172,7 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y; const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y; u32 *dst = (u32 *)(data + byteStride * y); + u16 *dst16 = (u16 *)(data + byteStride * y); switch (srcPixelFormat) { case GE_FORMAT_565: if (preferredPixelsFormat_ == Draw::DataFormat::B8G8R8A8_UNORM) @@ -1189,18 +1205,28 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G memcpy(dst, src32, width * 4); break; - case GE_FORMAT_INVALID: case GE_FORMAT_DEPTH16: - _dbg_assert_msg_(false, "Invalid pixelFormat passed to DrawPixels()."); + // TODO: Must take the depth range into account, unless it's already 0-1. + // TODO: Depending on the color buffer format used with this depth buffer, we need + // to do one of two different swizzle operations. However, for the only use of this so far, + // the Burnout lens flare trickery, swizzle doesn't matter since it's just a 0, 7fff, 0, 7fff pattern + // which comes out the same. + memcpy(dst16, src16, w * 2); + break; + + case GE_FORMAT_INVALID: + // Bad break; } } return true; }; + // Note: For depth, we create an R16_UNORM texture, that'll be just fine for uploading depth through a shader, + // and likely more efficient. Draw::TextureDesc desc{ Draw::TextureType::LINEAR2D, - preferredPixelsFormat_, + srcPixelFormat == GE_FORMAT_DEPTH16 ? Draw::DataFormat::R16_UNORM : preferredPixelsFormat_, width, height, 1, @@ -1210,6 +1236,7 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G { (uint8_t *)srcPixels }, generateTexture, }; + // Hot Shots Golf (#12355) does tons of these in a frame in some situations! So creating textures // better be fast. Draw::Texture *tex = draw_->CreateTexture(desc); @@ -1233,7 +1260,7 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, int if (needBackBufferYSwap_) { flags |= OutputFlags::BACKBUFFER_FLIPPED; } - // DrawActiveTexture reverses these, probably to match "up". + // CopyToOutput reverses these, probably to match "up". if (GetGPUBackend() == GPUBackend::DIRECT3D9 || GetGPUBackend() == GPUBackend::DIRECT3D11) { flags |= OutputFlags::POSITION_FLIPPED; } @@ -2727,7 +2754,7 @@ void FramebufferManagerCommon::DrawActiveTexture(float x, float y, float w, floa // Rearrange to strip form. std::swap(coord[2], coord[3]); - draw2D_.DrawStrip2D(nullptr, coord, 4, (flags & DRAWTEX_LINEAR) != 0, Get2DPipeline(DRAW2D_COPY_COLOR)); + draw2D_.DrawStrip2D(nullptr, coord, 4, (flags & DRAWTEX_LINEAR) != 0, Get2DPipeline((flags & DRAWTEX_DEPTH) ? DRAW2D_COPY_DEPTH : DRAW2D_COPY_COLOR)); gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); } diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index f7107dc029..9337a92b2e 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -192,6 +192,7 @@ enum DrawTextureFlags { DRAWTEX_NEAREST = 0, DRAWTEX_LINEAR = 1, DRAWTEX_TO_BACKBUFFER = 8, + DRAWTEX_DEPTH = 16, }; inline DrawTextureFlags operator | (const DrawTextureFlags &lhs, const DrawTextureFlags &rhs) { diff --git a/assets/compat.ini b/assets/compat.ini index ce3afae275..5d5be53e44 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1307,6 +1307,12 @@ ULKS46087 = true [AllowDownloadCLUT] # Temporary compatibility option, while developing a GPU CLUT-from-framebuffer path. -# Burnout Dominator - lens flare effect (issue )#11100) +# Burnout Dominator - lens flare effect (issue #11100) +ULUS10236 = true +ULES00703 = true + +[UploadDepthForCLUTTextures] +# Burnout Dominator - lens flare effect (issue #11100) +# We need a preinitialized depth buffer ULUS10236 = true ULES00703 = true From 650ce22359802516a4c38f2912c7305b2688ac0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Sep 2022 21:10:27 +0200 Subject: [PATCH 10/29] Force low resolution for effects in Burnout Dominator. Fixes lens flare at high res --- GPU/Common/ShaderUniforms.h | 10 +++++----- assets/compat.ini | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index 691835a262..f64fc3417b 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -36,14 +36,14 @@ struct UB_VS_FS_Base { uint32_t spline_counts; uint32_t depal_mask_shift_off_fmt; // 4 params packed into one. uint32_t colorWriteMask; float mipBias; // Fragment data - float fogColor[4]; - float texEnvColor[4]; + float fogColor[4]; // .w is unused + float texEnvColor[4]; // .w is unused int alphaColorRef[4]; int colorTestMask[4]; - float blendFixA[4]; - float blendFixB[4]; + float blendFixA[4]; // .w is unused + float blendFixB[4]; // .w is unused float texClamp[4]; - float texClampOffset[4]; + float texClampOffset[4]; // .zw are unused }; static const char *ub_baseStr = diff --git a/assets/compat.ini b/assets/compat.ini index 5d5be53e44..ba515164e2 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1304,6 +1304,11 @@ ULES00262 = true ULUS10064 = true ULKS46087 = true +# Burnout Dominator - lens flare effect (issue #11100) +# Some of the steps don't work at high resolution yet. +ULUS10236 = true +ULES00703 = true + [AllowDownloadCLUT] # Temporary compatibility option, while developing a GPU CLUT-from-framebuffer path. From 7213e9b7c2507a5d1dd3ee56963aca241feb5b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Sep 2022 21:26:13 +0200 Subject: [PATCH 11/29] Comment --- GPU/Common/FramebufferManagerCommon.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 88fa93fb25..9ce5305fd9 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -2974,6 +2974,10 @@ static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, in margin = true; } } + + // TODO: Implement this for Burnout Dominator. It has to handle self-reads inside + // the margin framebuffer though, so framebuffer copies are still needed, just smaller. + // It uses 0x0080019f (through, float texcoords, ABGR 8888 colors, float positions). } if (margin) { From 6744fe3da15f9a8cef03e50326afcd7a6574a621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 12 Sep 2022 23:32:38 +0200 Subject: [PATCH 12/29] OpenGL fix --- Common/GPU/OpenGL/DataFormatGL.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Common/GPU/OpenGL/DataFormatGL.cpp b/Common/GPU/OpenGL/DataFormatGL.cpp index 859624b28f..f34bebd193 100644 --- a/Common/GPU/OpenGL/DataFormatGL.cpp +++ b/Common/GPU/OpenGL/DataFormatGL.cpp @@ -8,8 +8,8 @@ bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuin alignment = 4; switch (fmt) { case DataFormat::R16_UNORM: - internalFormat = GL_R16; - format = GL_R; + internalFormat = GL_RGBA; + format = GL_RED; type = GL_UNSIGNED_SHORT; break; From 431f142413960c185b62094c60394b63c8b31404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Sep 2022 10:00:48 +0200 Subject: [PATCH 13/29] Cleanup in ge_constants.h, add virtual CLUT8 buffer format --- GPU/ge_constants.h | 65 +++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/GPU/ge_constants.h b/GPU/ge_constants.h index 91ef35b3ec..cdb7cbce58 100644 --- a/GPU/ge_constants.h +++ b/GPU/ge_constants.h @@ -278,20 +278,6 @@ enum GECommand { GE_CMD_NOP_FF = 0xFF, }; -enum GEBufferFormat : uint8_t { - GE_FORMAT_565 = 0, - GE_FORMAT_5551 = 1, - GE_FORMAT_4444 = 2, - GE_FORMAT_8888 = 3, - GE_FORMAT_DEPTH16 = 4, // Virtual format, just used to pass into Depal - GE_FORMAT_INVALID = 0xFF, -}; - -const char *GeBufferFormatToString(GEBufferFormat fmt); -inline bool IsGeBufferFormat16BitColor(GEBufferFormat fmt) { - return (int)fmt < 3; -} - #define GE_VTYPE_TRANSFORM (0<<23) #define GE_VTYPE_THROUGH (1<<23) #define GE_VTYPE_THROUGH_MASK (1<<23) @@ -413,8 +399,10 @@ enum GELightComputation GE_LIGHTCOMP_ONLYPOWDIFFUSE = 2, }; -enum GETextureFormat : uint8_t -{ +// TODO: Consolidate the below three to one enum? The first four are the same in all, +// and we sometimes need to interpret back and forth between them. + +enum GETextureFormat : uint8_t { GE_TFMT_5650 = 0, GE_TFMT_5551 = 1, GE_TFMT_4444 = 2, @@ -428,6 +416,32 @@ enum GETextureFormat : uint8_t GE_TFMT_DXT5 = 10, }; +enum GEBufferFormat : uint8_t { + GE_FORMAT_565 = 0, + GE_FORMAT_5551 = 1, + GE_FORMAT_4444 = 2, + GE_FORMAT_8888 = 3, + GE_FORMAT_DEPTH16 = 4, // Virtual format, just used to pass into Depal + GE_FORMAT_CLUT8 = 5, // Virtual format, for pre-decoded static textures with dynamic CLUT + GE_FORMAT_INVALID = 0xFF, +}; + +enum GEPaletteFormat : uint8_t { + GE_CMODE_16BIT_BGR5650, + GE_CMODE_16BIT_ABGR5551, + GE_CMODE_16BIT_ABGR4444, + GE_CMODE_32BIT_ABGR8888, +}; + +const char *GEPaletteFormatToString(GEPaletteFormat pfmt); +const char *GeTextureFormatToString(GETextureFormat tfmt); +const char *GeTextureFormatToString(GETextureFormat tfmt, GEPaletteFormat pfmt); + +const char *GeBufferFormatToString(GEBufferFormat fmt); +inline bool IsGeBufferFormat16BitColor(GEBufferFormat fmt) { + return (int)fmt < 3; +} + inline bool IsClutFormat(GETextureFormat tfmt) { return tfmt == GE_TFMT_CLUT4 || tfmt == GE_TFMT_CLUT8 || tfmt == GE_TFMT_CLUT16 || tfmt == GE_TFMT_CLUT32; } @@ -445,7 +459,12 @@ inline bool IsTextureFormat16Bit(GETextureFormat tfmt) { } inline int BufferFormatBytesPerPixel(GEBufferFormat format) { - return format == GE_FORMAT_8888 ? 4 : 2; // applies to depth as well. + switch (format) { + case GE_FORMAT_8888: return 4; // applies to depth as well. + case GE_FORMAT_CLUT8: return 1; + default: + return 2; + } } inline bool TextureFormatMatchesBufferFormat(GETextureFormat fmt, GEBufferFormat bfmt) { @@ -613,15 +632,3 @@ inline GEPrimitiveType PatchPrimToPrim(GEPatchPrimType type) { case GE_PATCHPRIM_UNKNOWN: default: return GE_PRIM_POINTS; // Treated as points. } } - -enum GEPaletteFormat -{ - GE_CMODE_16BIT_BGR5650, - GE_CMODE_16BIT_ABGR5551, - GE_CMODE_16BIT_ABGR4444, - GE_CMODE_32BIT_ABGR8888, -}; - -const char *GEPaletteFormatToString(GEPaletteFormat pfmt); -const char *GeTextureFormatToString(GETextureFormat tfmt); -const char *GeTextureFormatToString(GETextureFormat tfmt, GEPaletteFormat pfmt); From 9907957242400d2ead3f3dc56122ba5737714373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Sep 2022 23:55:57 +0200 Subject: [PATCH 14/29] Start implementing ApplyTextureDepal --- GPU/Common/DepalettizeShaderCommon.cpp | 6 ++ GPU/Common/FramebufferManagerCommon.h | 15 +-- GPU/Common/TextureCacheCommon.cpp | 121 ++++++++++++++++++++++++- GPU/Common/TextureCacheCommon.h | 7 ++ GPU/Common/TextureShaderCommon.cpp | 2 +- 5 files changed, 142 insertions(+), 9 deletions(-) diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index ee04359135..ee62064d3d 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -84,6 +84,9 @@ void GenerateDepalShader300(ShaderWriter &writer, const DepalConfig &config) { int shiftedMask = mask << shift; switch (config.bufferFormat) { + case GE_FORMAT_CLUT8: + writer.C(" int index = int(color.r * 255.99);\n"); + break; case GE_FORMAT_8888: if (shiftedMask & 0xFF) writer.C(" int r = int(color.r * 255.99);\n"); else writer.C(" int r = 0;\n"); if (shiftedMask & 0xFF00) writer.C(" int g = int(color.g * 255.99);\n"); else writer.C(" int g = 0;\n"); @@ -168,6 +171,9 @@ void GenerateDepalShaderFloat(ShaderWriter &writer, const DepalConfig &config) { // pixelformat is the format of the texture we are sampling. bool formatOK = true; switch (config.bufferFormat) { + case GE_FORMAT_CLUT8: + _dbg_assert_(false); // to be implemented + break; case GE_FORMAT_8888: if ((mask & (mask + 1)) == 0) { // If the value has all bits contiguous (bitmask check above), we can mod by it + 1. diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 9337a92b2e..04c604972b 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -425,6 +425,14 @@ public: Draw2DPipeline *Get2DPipeline(Draw2DShader shader); Draw2DPipeline *GetReinterpretPipeline(GEBufferFormat from, GEBufferFormat to, float *scaleFactorX); + // Public to be used from the texture cache's depal shenanigans. + void BlitUsingRaster( + Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, + Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, + bool linearFilter, + int scaleFactor, // usually unused, except for swizzle... + Draw2DPipeline *pipeline, const char *tag); + protected: virtual void PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel); void SetViewport2D(int x, int y, int w, int h); @@ -442,13 +450,6 @@ protected: // Used by ReadFramebufferToMemory and later framebuffer block copies void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag); - void BlitUsingRaster( - Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2, - Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2, - bool linearFilter, - int scaleFactor, // usually unused, except for swizzle... - Draw2DPipeline *pipeline, const char *tag); - void CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags); void EstimateDrawingSize(u32 fb_address, int fb_stride, GEBufferFormat fb_format, int viewport_width, int viewport_height, int region_width, int region_height, int scissor_width, int scissor_height, int &drawing_width, int &drawing_height); diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 65869ad428..22fcedca98 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1923,7 +1923,6 @@ static bool CanUseSmoothDepal(const GPUgstate &gstate, GEBufferFormat framebuffe return false; } - void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer, GETextureFormat texFormat, RasterChannel channel) { Draw2DPipeline *textureShader = nullptr; uint32_t clutMode = gstate.clutformat & 0xFFFFFF; @@ -2079,6 +2078,126 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); } +void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { + Draw2DPipeline *textureShader = nullptr; + uint32_t clutMode = gstate.clutformat & 0xFFFFFF; + + switch (entry->format) { + case GE_TFMT_CLUT4: + case GE_TFMT_CLUT8: + break; // These are OK + default: + _dbg_assert_(false); + return; + } + + // Create GPU resources. + if (!dynamicClutFbo_) { + Draw::FramebufferDesc desc{}; + desc.width = 512; + desc.height = 1; + desc.depth = 1; + desc.z_stencil = false; + desc.numColorAttachments = 1; + dynamicClutFbo_ = draw_->CreateFramebuffer(desc); + dynamicClutReinterpreted_ = draw_->CreateFramebuffer(desc); + } + + const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); + u32 depthUpperBits = 0; + + // The CLUT texture is dynamic, it's the framebuffer pointed to by clutRenderAddress. + // Instead of texturing directly from that, we copy to a temporary CLUT texture. + GEBufferFormat expectedCLUTBufferFormat = (GEBufferFormat)clutFormat; // All entries from clutFormat correspond directly to buffer formats. + + VirtualFramebuffer *src = framebufferManager_->GetVFBAt(clutRenderAddress_); + if (!src) { + // What do we do? + return; + } + + Draw::Framebuffer *clutFbo = dynamicClutFbo_; + + // First we use a blit (with nearest interpolation, so we don't mash pixels together) + // to shrink to the correct size, if we are running with scaling. + // We can always blit 512 pixels even if we only need less, the cost will be negligible. + framebufferManager_->BlitUsingRaster( + src->fbo, 0.0f, 0.0f, 512.0f * src->renderScaleFactor, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut"); + + // OK, figure out what format we want our framebuffer in, so it can be reinterpreted if needed. + if (expectedCLUTBufferFormat != src->fb_format) { + float scaleFactorX = 1.0f; + Draw2DPipeline *reinterpret = framebufferManager_->GetReinterpretPipeline(src->fb_format, expectedCLUTBufferFormat, &scaleFactorX); + framebufferManager_->BlitUsingRaster( + dynamicClutFbo_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutReinterpreted_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut"); + clutFbo = dynamicClutReinterpreted_; + } + + textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, GE_TFMT_CLUT8, GE_FORMAT_CLUT8, false, 0); + gstate_c.SetUseShaderDepal(ShaderDepalMode::OFF); + + int texWidth = gstate.getTextureWidth(0); + int texHeight = gstate.getTextureHeight(0); + + // If min is not < max, then we don't have values (wasn't set during decode.) + const KnownVertexBounds &bounds = gstate_c.vertBounds; + float u1 = 0.0f; + float v1 = 0.0f; + float u2 = 1.0f; + float v2 = 1.0f; + if (bounds.minV < bounds.maxV) { + u1 = (bounds.minU + gstate_c.curTextureXOffset) * texWidth; + v1 = (bounds.minV + gstate_c.curTextureYOffset) * texHeight; + u2 = (bounds.maxU + gstate_c.curTextureXOffset) * texWidth; + v2 = (bounds.maxV + gstate_c.curTextureYOffset) * texHeight; + // We need to reapply the texture next time since we cropped UV. + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); + } + + Draw::Framebuffer *depalFBO = framebufferManager_->GetTempFBO(TempFBO::DEPAL, texWidth, texHeight); + draw_->BindTexture(0, nullptr); + draw_->BindTexture(1, nullptr); + draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Depal"); + draw_->InvalidateFramebuffer(Draw::FB_INVALIDATION_STORE, Draw::FB_DEPTH_BIT | Draw::FB_STENCIL_BIT); + draw_->SetScissorRect(u1, v1, u2 - u1, v2 - v1); + Draw::Viewport vp{ 0.0f, 0.0f, (float)texWidth, (float)texHeight, 0.0f, 1.0f }; + draw_->SetViewports(1, &vp); + + draw_->BindTexture(0, framebuffer->fbo); + draw_->BindFramebufferAsTexture(clutFbo, 1, Draw::FB_COLOR_BIT, 0); + Draw::SamplerState *nearest = textureShaderCache_->GetSampler(false); + Draw::SamplerState *clutSampler = textureShaderCache_->GetSampler(false); + draw_->BindSamplerStates(0, 1, &nearest); + draw_->BindSamplerStates(1, 1, &clutSampler); + + draw2D_->Blit(textureShader, u1, v1, u2, v2, u1, v1, u2, v2, texWidth, texHeight, texWidth, texHeight, false, 1); + + gpuStats.numDepal++; + + gstate_c.curTextureWidth = texWidth; + + draw_->BindTexture(0, nullptr); + framebufferManager_->RebindFramebuffer("ApplyTextureFramebuffer"); + + draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0); + BoundFramebufferTexture(); + + const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); + const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; + + CheckAlphaResult alphaStatus = CheckCLUTAlpha((const uint8_t *)clutBufRaw_, clutFormat, clutTotalColors); + gstate_c.SetTextureFullAlpha(alphaStatus == CHECKALPHA_FULL); + + draw_->InvalidateCachedState(); + shaderManager_->DirtyLastShader(); + + SamplerCacheKey samplerKey = GetFramebufferSamplingParams(texWidth, texHeight); + ApplySamplingParams(samplerKey); + + // Since we started/ended render passes, might need these. + gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); +} + void TextureCacheCommon::Clear(bool delete_them) { textureShaderCache_->Clear(); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 78ad53df1f..f864b1a9e0 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -144,6 +144,8 @@ struct TexCacheEntry { STATUS_FORCE_REBUILD = 0x2000, STATUS_3D = 0x4000, + + STATUS_CLUT_GPU = 0x8000, }; // Status, but int so we can zero initialize. @@ -352,6 +354,7 @@ protected: void Decimate(bool forcePressure = false); void ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer, GETextureFormat texFormat, RasterChannel channel); + void ApplyTextureDepal(TexCacheEntry *entry); void HandleTextureChange(TexCacheEntry *const entry, const char *reason, bool initialMatch, bool doDelete); virtual void BuildTexture(TexCacheEntry *const entry) = 0; @@ -480,6 +483,10 @@ protected: bool clutAlphaLinear_ = false; u16 clutAlphaLinearColor_; + // Facilities for GPU depal of static textures. + Draw::Framebuffer *dynamicClutFbo_ = nullptr; + Draw::Framebuffer *dynamicClutReinterpreted_ = nullptr; + int standardScaleFactor_; int shaderScaleFactor_ = 0; diff --git a/GPU/Common/TextureShaderCommon.cpp b/GPU/Common/TextureShaderCommon.cpp index f364016e72..1c67818a85 100644 --- a/GPU/Common/TextureShaderCommon.cpp +++ b/GPU/Common/TextureShaderCommon.cpp @@ -102,7 +102,7 @@ ClutTexture TextureShaderCache::GetClutTexture(GEPaletteFormat clutFormat, const int lastA = 0; int rampLength = 0; - // Quick check for how many continouosly growing entries we have at the start. + // Quick check for how many continuously growing entries we have at the start. // Bilinearly filtering CLUTs only really makes sense for this kind of ramp. for (int i = 0; i < maxClutEntries; i++) { rampLength = i; From f745e9489974e64d458765300f82749782ca1ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Sep 2022 00:13:29 +0200 Subject: [PATCH 15/29] Allow binding "native" texture objects to thin3d --- Common/GPU/D3D11/thin3d_d3d11.cpp | 7 +++++++ Common/GPU/D3D9/thin3d_d3d9.cpp | 7 +++++++ Common/GPU/OpenGL/thin3d_gl.cpp | 6 ++++++ Common/GPU/Vulkan/thin3d_vulkan.cpp | 6 ++++++ Common/GPU/thin3d.h | 7 +++++++ GPU/Common/TextureCacheCommon.cpp | 2 +- 6 files changed, 34 insertions(+), 1 deletion(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 99e2eb2577..c17bb39919 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -105,6 +105,7 @@ public: void InvalidateCachedState() override; void BindTextures(int start, int count, Texture **textures) override; + void BindNativeTexture(int index, void *nativeTexture) override; void BindSamplerStates(int start, int count, SamplerState **states) override; void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override; void BindIndexBuffer(Buffer *indexBuffer, int offset) override; @@ -1389,6 +1390,12 @@ void D3D11DrawContext::BindTextures(int start, int count, Texture **textures) { context_->PSSetShaderResources(start, count, views); } +void D3D11DrawContext::BindNativeTexture(int index, void *nativeTexture) { + // Collect the resource views from the textures. + ID3D11ShaderResourceView *view = (ID3D11ShaderResourceView *)nativeTexture; + context_->PSSetShaderResources(index, 1, &view); +} + void D3D11DrawContext::BindSamplerStates(int start, int count, SamplerState **states) { ID3D11SamplerState *samplers[MAX_BOUND_TEXTURES]; _assert_(start + count <= ARRAY_SIZE(samplers)); diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 9bfe824c09..c47d8235a1 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -521,6 +521,8 @@ public: void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override; void BindTextures(int start, int count, Texture **textures) override; + void BindNativeTexture(int index, void *nativeTexture) override; + void BindSamplerStates(int start, int count, SamplerState **states) override { _assert_(start + count <= MAX_BOUND_TEXTURES); for (int i = 0; i < count; ++i) { @@ -814,6 +816,11 @@ void D3D9Context::BindTextures(int start, int count, Texture **textures) { } } +void D3D9Context::BindNativeTexture(int index, void *nativeTexture) { + LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)nativeTexture; + device_->SetTexture(index, texture); +} + void D3D9Context::EndFrame() { curPipeline_ = nullptr; } diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 2e93fc8791..908a764ecc 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -401,6 +401,8 @@ public: } void BindTextures(int start, int count, Texture **textures) override; + void BindNativeTexture(int sampler, void *nativeTexture) override; + void BindPipeline(Pipeline *pipeline) override; void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override { _assert_(start + count <= ARRAY_SIZE(curVBuffers_)); @@ -1138,6 +1140,10 @@ void OpenGLContext::BindTextures(int start, int count, Texture **textures) { } } +void OpenGLContext::BindNativeTexture(int index, void *nativeTexture) { + boundTextures_[index] = (GLRTexture *)nativeTexture; +} + void OpenGLContext::ApplySamplers() { for (int i = 0; i < MAX_TEXTURE_SLOTS; i++) { const OpenGLSamplerState *samp = boundSamplers_[i]; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index c836b41499..8420c01450 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -414,6 +414,7 @@ public: void BindSamplerStates(int start, int count, SamplerState **state) override; void BindTextures(int start, int count, Texture **textures) override; + void BindNativeTexture(int sampler, void *nativeTexture) override; void BindPipeline(Pipeline *pipeline) override { curPipeline_ = (VKPipeline *)pipeline; @@ -1292,6 +1293,11 @@ void VKContext::BindTextures(int start, int count, Texture **textures) { } } +void VKContext::BindNativeTexture(int sampler, void *nativeTexture) { + boundTextures_[sampler] = nullptr; + boundImageView_[sampler] = (VkImageView)nativeTexture; +} + ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const char *tag) { VKShaderModule *shader = new VKShaderModule(stage, tag); if (shader->Compile(vulkan_, language, data, size)) { diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 46b6305fd9..843e6e8813 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -675,6 +675,13 @@ public: virtual void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) = 0; virtual void BindIndexBuffer(Buffer *indexBuffer, int offset) = 0; + // Sometimes it's necessary to bind a texture not created by thin3d, and use with a thin3d pipeline. + // Not pretty, and one way in the future could be to create all textures through thin3d. + // Data types: + // * Vulkan: VkImageView + // * D3D11: ID3D11ShaderResourceView* + virtual void BindNativeTexture(int sampler, void *nativeTexture) = 0; + // Only supports a single dynamic uniform buffer, for maximum compatibility with the old APIs and ease of emulation. // More modern methods will be added later. virtual void UpdateDynamicUniformBuffer(const void *ub, size_t size) = 0; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 22fcedca98..ca83804954 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2163,7 +2163,7 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { Draw::Viewport vp{ 0.0f, 0.0f, (float)texWidth, (float)texHeight, 0.0f, 1.0f }; draw_->SetViewports(1, &vp); - draw_->BindTexture(0, framebuffer->fbo); + draw_->BindNativeTexture(0, framebuffer->fbo); draw_->BindFramebufferAsTexture(clutFbo, 1, Draw::FB_COLOR_BIT, 0); Draw::SamplerState *nearest = textureShaderCache_->GetSampler(false); Draw::SamplerState *clutSampler = textureShaderCache_->GetSampler(false); From abd58199ce9a05e207c5e6ff4d1ce8bc7027d6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Sep 2022 08:42:25 +0200 Subject: [PATCH 16/29] Add way to bind cached textures to a DrawContext --- GPU/Common/TextureCacheCommon.cpp | 6 +++--- GPU/Common/TextureCacheCommon.h | 3 +++ GPU/D3D11/TextureCacheD3D11.cpp | 5 +++++ GPU/D3D11/TextureCacheD3D11.h | 5 +++-- GPU/Directx9/TextureCacheDX9.cpp | 5 +++++ GPU/Directx9/TextureCacheDX9.h | 3 ++- GPU/GLES/TextureCacheGLES.cpp | 5 +++++ GPU/GLES/TextureCacheGLES.h | 1 + GPU/Vulkan/TextureCacheVulkan.cpp | 5 +++++ GPU/Vulkan/TextureCacheVulkan.h | 1 + 10 files changed, 33 insertions(+), 6 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index ca83804954..31ee03e82b 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2163,7 +2163,7 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { Draw::Viewport vp{ 0.0f, 0.0f, (float)texWidth, (float)texHeight, 0.0f, 1.0f }; draw_->SetViewports(1, &vp); - draw_->BindNativeTexture(0, framebuffer->fbo); + draw_->BindNativeTexture(0, GetNativeTextureView(entry)); draw_->BindFramebufferAsTexture(clutFbo, 1, Draw::FB_COLOR_BIT, 0); Draw::SamplerState *nearest = textureShaderCache_->GetSampler(false); Draw::SamplerState *clutSampler = textureShaderCache_->GetSampler(false); @@ -2185,8 +2185,8 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; - CheckAlphaResult alphaStatus = CheckCLUTAlpha((const uint8_t *)clutBufRaw_, clutFormat, clutTotalColors); - gstate_c.SetTextureFullAlpha(alphaStatus == CHECKALPHA_FULL); + // We don't know about alpha at all. + gstate_c.SetTextureFullAlpha(false); draw_->InvalidateCachedState(); shaderManager_->DirtyLastShader(); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index f864b1a9e0..84b03683ac 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -108,6 +108,8 @@ struct TextureDefinition { // NOTE: These only handle textures loaded directly from PSP memory contents. // Framebuffer textures do not have entries, we bind the framebuffers directly. +// At one point we might merge the concepts of framebuffers and textures, but that +// moment is far away. struct TexCacheEntry { ~TexCacheEntry() { if (texturePtr || textureName || vkTex) @@ -345,6 +347,7 @@ public: virtual bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { return false; } protected: + virtual void *GetNativeTextureView(const TexCacheEntry *entry) = 0; bool PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEntry *entry); virtual void BindTexture(TexCacheEntry *entry) = 0; diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index fb6cfba8b7..47c7b40eee 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -525,3 +525,8 @@ bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level stagingCopy->Release(); return true; } + +void *TextureCacheD3D11::GetNativeTextureView(const TexCacheEntry *entry) { + ID3D11ShaderResourceView *textureView = DxView(entry); + return (void *)textureView; +} diff --git a/GPU/D3D11/TextureCacheD3D11.h b/GPU/D3D11/TextureCacheD3D11.h index 03ec04a481..e1be7d3aaf 100644 --- a/GPU/D3D11/TextureCacheD3D11.h +++ b/GPU/D3D11/TextureCacheD3D11.h @@ -61,6 +61,7 @@ protected: void ReleaseTexture(TexCacheEntry *entry, bool delete_them) override; void BindAsClutTexture(Draw::Texture *tex, bool smooth) override; void ApplySamplingParams(const SamplerCacheKey &key) override; + void *GetNativeTextureView(const TexCacheEntry *entry) override; private: DXGI_FORMAT GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const; @@ -72,10 +73,10 @@ private: ID3D11Device *device_; ID3D11DeviceContext *context_; - ID3D11Resource *&DxTex(TexCacheEntry *entry) { + ID3D11Resource *&DxTex(const TexCacheEntry *entry) { return (ID3D11Resource *&)entry->texturePtr; } - ID3D11ShaderResourceView *DxView(TexCacheEntry *entry) { + ID3D11ShaderResourceView *DxView(const TexCacheEntry *entry) { return (ID3D11ShaderResourceView *)entry->textureView; } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index bfb859bb40..3a3feda7f3 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -454,3 +454,8 @@ bool TextureCacheDX9::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) return success; } + +void *TextureCacheDX9::GetNativeTextureView(const TexCacheEntry *entry) { + LPDIRECT3DBASETEXTURE9 tex = DxTex(entry); + return (void *)tex; +} diff --git a/GPU/Directx9/TextureCacheDX9.h b/GPU/Directx9/TextureCacheDX9.h index e8f4d9e58f..58c8e17db6 100644 --- a/GPU/Directx9/TextureCacheDX9.h +++ b/GPU/Directx9/TextureCacheDX9.h @@ -50,6 +50,7 @@ protected: void Unbind() override; void ReleaseTexture(TexCacheEntry *entry, bool delete_them) override; void BindAsClutTexture(Draw::Texture *tex, bool smooth) override; + void *GetNativeTextureView(const TexCacheEntry *entry) override; private: void ApplySamplingParams(const SamplerCacheKey &key) override; @@ -60,7 +61,7 @@ private: void BuildTexture(TexCacheEntry *const entry) override; - LPDIRECT3DBASETEXTURE9 &DxTex(TexCacheEntry *entry) { + LPDIRECT3DBASETEXTURE9 &DxTex(const TexCacheEntry *entry) const { return *(LPDIRECT3DBASETEXTURE9 *)&entry->texturePtr; } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index c28fc6d1e5..5ea95a9eed 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -443,3 +443,8 @@ void TextureCacheGLES::DeviceRestore(Draw::DrawContext *draw) { render_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); textureShaderCache_->DeviceRestore(draw); } + +void *TextureCacheGLES::GetNativeTextureView(const TexCacheEntry *entry) { + GLRTexture *tex = entry->textureName; + return (void *)tex; +} diff --git a/GPU/GLES/TextureCacheGLES.h b/GPU/GLES/TextureCacheGLES.h index 1eae540b87..2a4d04574d 100644 --- a/GPU/GLES/TextureCacheGLES.h +++ b/GPU/GLES/TextureCacheGLES.h @@ -66,6 +66,7 @@ protected: void ReleaseTexture(TexCacheEntry *entry, bool delete_them) override; void BindAsClutTexture(Draw::Texture *tex, bool smooth) override; + void *GetNativeTextureView(const TexCacheEntry *entry) override; private: void ApplySamplingParams(const SamplerCacheKey &key) override; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index a67210cfc7..8a9e9f5a6a 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -857,3 +857,8 @@ std::vector TextureCacheVulkan::DebugGetSamplerIDs() const { std::string TextureCacheVulkan::DebugGetSamplerString(std::string id, DebugShaderStringType stringType) { return samplerCache_.DebugGetSamplerString(id, stringType); } + +void *TextureCacheVulkan::GetNativeTextureView(const TexCacheEntry *entry) { + VkImageView view = entry->vkTex->GetImageView(); + return (void *)view; +} diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 64b9da9eab..7dfb38fdc4 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -97,6 +97,7 @@ protected: void BindAsClutTexture(Draw::Texture *tex, bool smooth) override; void ApplySamplingParams(const SamplerCacheKey &key) override; void BoundFramebufferTexture() override; + void *GetNativeTextureView(const TexCacheEntry *entry) override; private: void LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePtr, int rowPitch, int level, int scaleFactor, VkFormat dstFmt); From 51c97c7a7f7c0bee7c9d5b8b82b99f1869b8b0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Sep 2022 10:26:54 +0200 Subject: [PATCH 17/29] Set texcache entry status flag if CLUT needs to be dynamic --- GPU/Common/TextureCacheCommon.cpp | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 31ee03e82b..6b72287407 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -377,13 +377,20 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { } bool hasClut = gstate.isTextureFormatIndexed(); + bool hasClutGPU = false; u32 cluthash; if (hasClut) { - if (clutLastFormat_ != gstate.clutformat) { - // We update here because the clut format can be specified after the load. - UpdateCurrentClut(gstate.getClutPaletteFormat(), gstate.getClutIndexStartPos(), gstate.isClutIndexSimple()); + if (clutRenderAddress_ != 0xFFFFFFFF) { + hasClutGPU = true; + cluthash = 0; // Or should we use some other marker value? + } else { + if (clutLastFormat_ != gstate.clutformat) { + // We update here because the clut format can be specified after the load. + // TODO: Unify this as far as possible (I think only GLES backend really needs its own implementation due to different component order). + UpdateCurrentClut(gstate.getClutPaletteFormat(), gstate.getClutIndexStartPos(), gstate.isClutIndexSimple()); + } + cluthash = clutHash_ ^ gstate.clutformat; } - cluthash = clutHash_ ^ gstate.clutformat; } else { cluthash = 0; } @@ -412,6 +419,13 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { bool match = entry->Matches(dim, texFormat, maxLevel); const char *reason = "different params"; + // Check for dynamic CLUT status + if (((entry->status & TexCacheEntry::STATUS_CLUT_GPU) != 0) != hasClutGPU) { + // Need to recreate, suddenly a CLUT GPU texture was used without it, or vice versa. + // I think this can only happen on a clut hash collision with the marker value, so highly unlikely. + match = false; + } + // Check for FBO changes. if (entry->status & TexCacheEntry::STATUS_FRAMEBUFFER_OVERLAP) { // Fall through to the end where we'll delete the entry if there's a framebuffer. @@ -553,10 +567,6 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { entry = new TexCacheEntry{}; cache_[cachekey].reset(entry); - if (hasClut && clutRenderAddress_ != 0xFFFFFFFF) { - WARN_LOG_REPORT_ONCE(clutUseRender, G3D, "Using texture with rendered CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat()); - } - if (PPGeIsFontTextureAddress(texaddr)) { // It's the builtin font texture. entry->status = TexCacheEntry::STATUS_RELIABLE; @@ -566,6 +576,11 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { entry->status = TexCacheEntry::STATUS_UNRELIABLE; } + if (hasClutGPU) { + WARN_LOG_REPORT_ONCE(clutUseRender, G3D, "Using texture with dynamic CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat()); + entry->status |= TexCacheEntry::STATUS_CLUT_GPU; + } + if (hasClut && clutRenderAddress_ == 0xFFFFFFFF) { const u64 cachekeyMin = (u64)(texaddr & 0x3FFFFFFF) << 32; const u64 cachekeyMax = cachekeyMin + (1ULL << 32); From d6d7a15d25261a02dc20689e36a2f10e339a502f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Sep 2022 21:57:09 +0200 Subject: [PATCH 18/29] Get depal-from-dynamic-CLUT working --- Common/GPU/Vulkan/VulkanRenderManager.cpp | 2 + GPU/Common/TextureCacheCommon.cpp | 91 +++++++++++++++++++++-- GPU/Common/TextureCacheCommon.h | 4 + GPU/Vulkan/TextureCacheVulkan.cpp | 22 +++++- GPU/Vulkan/VulkanUtil.h | 1 + assets/compat.ini | 4 +- 6 files changed, 111 insertions(+), 13 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 10cc890009..56f40655f7 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -150,6 +150,8 @@ VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRe width = _width; height = _height; + _dbg_assert_(tag); + CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag); CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag); diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 6b72287407..b1ddc448aa 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -381,6 +381,8 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { u32 cluthash; if (hasClut) { if (clutRenderAddress_ != 0xFFFFFFFF) { + gstate_c.curTextureXOffset = 0.0f; + gstate_c.curTextureYOffset = 0.0f; hasClutGPU = true; cluthash = 0; // Or should we use some other marker value? } else { @@ -1491,12 +1493,28 @@ inline u32 TfmtRawToFullAlpha(GETextureFormat fmt) { } } +// Used for converting CLUT4 to CLUT8. +// Could SIMD or whatever, though will hardly be a bottleneck. +static void Expand4To8Bits(u8 *dest, const u8 *src, int srcWidth) { + for (int i = 0; i < (srcWidth + 1) / 2; i++) { + u8 lower = src[i] & 0xF; + u8 upper = src[i] >> 4; + dest[i * 2] = lower; + dest[i * 2 + 1] = upper; + } +} + CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, TexDecodeFlags flags) { u32 alphaSum = 0xFFFFFFFF; u32 fullAlphaMask = 0x0; bool expandTo32bit = (flags & TexDecodeFlags::EXPAND32) != 0; bool reverseColors = (flags & TexDecodeFlags::REVERSE_COLORS) != 0; + bool toClut8 = (flags & TexDecodeFlags::TO_CLUT8) != 0; + + if (toClut8 && format != GE_TFMT_CLUT8 && format != GE_TFMT_CLUT4) { + _dbg_assert_(false); + } bool swizzled = gstate.isTextureSwizzled(); if ((texaddr & 0x00600000) != 0 && Memory::IsVRAMAddress(texaddr)) { @@ -1531,6 +1549,15 @@ CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, G texptr = (u8 *)tmpTexBuf32_.data(); } + if (toClut8) { + // We just need to expand from 4 to 8 bits. + for (int y = 0; y < h; ++y) { + Expand4To8Bits((u8 *)out + outPitch * y, texptr + (bufw * y) / 2, w); + } + // We can't know anything about alpha. + return CHECKALPHA_ANY; + } + switch (clutformat) { case GE_CMODE_16BIT_BGR5650: case GE_CMODE_16BIT_ABGR5551: @@ -1593,6 +1620,19 @@ CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, G break; case GE_TFMT_CLUT8: + if (toClut8) { + if (gstate.isTextureSwizzled()) { + tmpTexBuf32_.resize(bufw * ((h + 7) & ~7)); + UnswizzleFromMem(tmpTexBuf32_.data(), bufw, texptr, bufw, h, 1); + texptr = (u8 *)tmpTexBuf32_.data(); + } + // After deswizzling, we are in the correct format and can just copy. + for (int y = 0; y < h; ++y) { + memcpy((u8 *)out + outPitch * y, texptr + (bufw * y), w); + } + // We can't know anything about alpha. + return CHECKALPHA_ANY; + } return ReadIndexedTex(out, outPitch, level, texptr, 1, bufw, reverseColors, expandTo32bit); case GE_TFMT_CLUT16: @@ -1878,10 +1918,18 @@ void TextureCacheCommon::ApplyTexture() { InvalidateLastTexture(); } - entry->lastFrame = gpuStats.numFlips; - BindTexture(entry); - gstate_c.SetTextureFullAlpha(entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL); - gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0); + if (entry->status & TexCacheEntry::STATUS_CLUT_GPU) { + // Special process. + ApplyTextureDepal(entry); + entry->lastFrame = gpuStats.numFlips; + gstate_c.SetTextureFullAlpha(false); + gstate_c.SetTextureIs3D(false); + } else { + entry->lastFrame = gpuStats.numFlips; + BindTexture(entry); + gstate_c.SetTextureFullAlpha(entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL); + gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0); + } } static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferFormat) { @@ -2093,6 +2141,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); } +// Applies depal to a normal (non-framebuffer) texture, pre-decoded to CLUT8 format. void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { Draw2DPipeline *textureShader = nullptr; uint32_t clutMode = gstate.clutformat & 0xFFFFFF; @@ -2114,6 +2163,7 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { desc.depth = 1; desc.z_stencil = false; desc.numColorAttachments = 1; + desc.tag = "dynamic_clut"; dynamicClutFbo_ = draw_->CreateFramebuffer(desc); dynamicClutReinterpreted_ = draw_->CreateFramebuffer(desc); } @@ -2144,7 +2194,7 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { float scaleFactorX = 1.0f; Draw2DPipeline *reinterpret = framebufferManager_->GetReinterpretPipeline(src->fb_format, expectedCLUTBufferFormat, &scaleFactorX); framebufferManager_->BlitUsingRaster( - dynamicClutFbo_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutReinterpreted_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut"); + dynamicClutFbo_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutReinterpreted_, 0.0f, 0.0f, scaleFactorX * 512.0f, 1.0f, false, 1.0f, reinterpret, "reinterpret_clut"); clutFbo = dynamicClutReinterpreted_; } @@ -2158,8 +2208,8 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { const KnownVertexBounds &bounds = gstate_c.vertBounds; float u1 = 0.0f; float v1 = 0.0f; - float u2 = 1.0f; - float v2 = 1.0f; + float u2 = texWidth; + float v2 = texHeight; if (bounds.minV < bounds.maxV) { u1 = (bounds.minU + gstate_c.curTextureXOffset) * texWidth; v1 = (bounds.minV + gstate_c.curTextureYOffset) * texHeight; @@ -2232,6 +2282,15 @@ void TextureCacheCommon::Clear(bool delete_them) { secondCacheSizeEstimate_ = 0; } videos_.clear(); + + if (dynamicClutFbo_) { + dynamicClutFbo_->Release(); + dynamicClutFbo_ = nullptr; + } + if (dynamicClutReinterpreted_) { + dynamicClutReinterpreted_->Release(); + dynamicClutReinterpreted_ = nullptr; + } } void TextureCacheCommon::DeleteTexture(TexCache::iterator it) { @@ -2598,6 +2657,21 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt plan.maxPossibleLevels = log2i(std::min(plan.createW, plan.createH)) + 1; } + if (entry->status & TexCacheEntry::TexStatus::STATUS_CLUT_GPU) { + _dbg_assert_(entry->format == GE_TFMT_CLUT4 || entry->format == GE_TFMT_CLUT8); + plan.decodeToClut8 = true; + // We only support 1 mip level when doing CLUT on GPU for now. + // Supporting more would be possible, just not very interesting until we need it. + plan.levelsToCreate = 1; + plan.levelsToLoad = 1; + plan.maxPossibleLevels = 1; + plan.scaleFactor = 1; + plan.saveTexture = false; // Can't yet save these properly. + // TODO: Also forcibly disable replacement, or check that the replacement is a 8-bit paletted texture. + } else { + plan.decodeToClut8 = false; + } + if (plan.levelsToCreate == 1) { entry->status |= TexCacheEntry::STATUS_NO_MIPS; } else { @@ -2639,6 +2713,9 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i if (!gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || dstFmt == Draw::DataFormat::R8G8B8A8_UNORM) { texDecFlags |= TexDecodeFlags::EXPAND32; } + if (entry.status & TexCacheEntry::STATUS_CLUT_GPU) { + texDecFlags |= TexDecodeFlags::TO_CLUT8; + } CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, srcLevel, bufw, texDecFlags); entry.SetAlphaStatus(alphaResult, srcLevel); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 84b03683ac..b24c50b95c 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -53,6 +53,7 @@ class ShaderManagerCommon; enum class TexDecodeFlags { EXPAND32 = 1, REVERSE_COLORS = 2, + TO_CLUT8 = 4, }; ENUM_CLASS_BITOPS(TexDecodeFlags); @@ -285,6 +286,9 @@ struct BuildTexturePlan { bool replaceValid; bool saveTexture; + // TODO: Expand32 should probably also be decided in PrepareBuildTexture. + bool decodeToClut8; + void GetMipSize(int level, int *w, int *h) const { if (replaceValid) { replaced->GetSize(level, *w, *h); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 8a9e9f5a6a..56345d4db5 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -107,6 +107,15 @@ void main() { )"; +static int VkFormatBytesPerPixel(VkFormat format) { + switch (format) { + case VULKAN_8888_FORMAT: return 4; + case VULKAN_CLUT8_FORMAT: return 1; + default: break; + } + return 2; +} + SamplerCache::~SamplerCache() { DeviceLost(); } @@ -448,6 +457,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { if (plan.scaleFactor > 1) { // Whether hardware or software scaling, this is the dest format. dstFmt = VULKAN_8888_FORMAT; + } else if (plan.decodeToClut8) { + dstFmt = VULKAN_CLUT8_FORMAT; } // We don't generate mipmaps for 512x512 textures because they're almost exclusively used for menu backgrounds @@ -479,7 +490,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { case VULKAN_4444_FORMAT: mapping = &VULKAN_4444_SWIZZLE; break; case VULKAN_1555_FORMAT: mapping = &VULKAN_1555_SWIZZLE; break; case VULKAN_565_FORMAT: mapping = &VULKAN_565_SWIZZLE; break; - default: mapping = &VULKAN_8888_SWIZZLE; break; + default: mapping = &VULKAN_8888_SWIZZLE; break; // no swizzle } VkImageLayout imageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; @@ -562,7 +573,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { int mipHeight; plan.GetMipSize(i, &mipWidth, &mipHeight); - int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2; // output bpp + int bpp = VkFormatBytesPerPixel(actualFmt); int stride = (mipWidth * bpp + 15) & ~15; // output stride int uploadSize = stride * mipHeight; @@ -602,7 +613,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { loadLevel(uploadSize, i, stride, plan.scaleFactor); entry->vkTex->UploadMip(cmdInit, 0, mipWidth, mipHeight, i, texBuf, bufferOffset, stride / bpp); } else if (computeUpload) { - int srcBpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2; + int srcBpp = VkFormatBytesPerPixel(dstFmt); int srcStride = mipUnscaledWidth * srcBpp; int srcSize = srcStride * mipUnscaledHeight; loadLevel(srcSize, i == 0 ? plan.baseLevelSrc : i, srcStride, 1); @@ -723,7 +734,7 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt _assert_msg_(texaddr != 0, "Can't load a texture from address null") int bufw = GetTextureBufw(level, texaddr, tfmt); - int bpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2; + int bpp = VkFormatBytesPerPixel(dstFmt); u32 *pixelData; int decPitch; @@ -732,6 +743,9 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt if (!gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1 || dstFmt == VULKAN_8888_FORMAT) { texDecFlags |= TexDecodeFlags::EXPAND32; } + if (entry.status & TexCacheEntry::STATUS_CLUT_GPU) { + texDecFlags |= TexDecodeFlags::TO_CLUT8; + } if (scaleFactor > 1) { tmpTexBufRearrange_.resize(std::max(bufw, w) * h); diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 2753fd79eb..b31fdb75f9 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -36,6 +36,7 @@ extern const VkComponentMapping VULKAN_8888_SWIZZLE; #define VULKAN_1555_FORMAT VK_FORMAT_A1R5G5B5_UNORM_PACK16 #define VULKAN_565_FORMAT VK_FORMAT_B5G6R5_UNORM_PACK16 // TODO: Does not actually have mandatory support, though R5G6B5 does! See #14602 #define VULKAN_8888_FORMAT VK_FORMAT_R8G8B8A8_UNORM +#define VULKAN_CLUT8_FORMAT VK_FORMAT_R8_UNORM // Manager for compute shaders that upload things (and those have two bindings: a storage buffer to read from and an image to write to). class VulkanComputeShaderManager { diff --git a/assets/compat.ini b/assets/compat.ini index ba515164e2..3345917f5c 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1313,8 +1313,8 @@ ULES00703 = true # Temporary compatibility option, while developing a GPU CLUT-from-framebuffer path. # Burnout Dominator - lens flare effect (issue #11100) -ULUS10236 = true -ULES00703 = true +# ULUS10236 = true +# ULES00703 = true [UploadDepthForCLUTTextures] # Burnout Dominator - lens flare effect (issue #11100) From 1f6d27bd552ecc1bfdae916288df88802b61da29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Sep 2022 22:46:17 +0200 Subject: [PATCH 19/29] Ridge Racer fix --- GPU/Common/TextureCacheCommon.cpp | 43 +++++++++++++++++++------------ 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index b1ddc448aa..f4a8dff574 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1190,29 +1190,40 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { if (Memory::IsValidAddress(clutAddr)) { if (Memory::IsVRAMAddress(clutAddr)) { // Clear the uncached bit, etc. to match framebuffers. - const u32 clutFramebufAddr = clutAddr & 0x3FFFFFFF; - const u32 clutFramebufEnd = clutFramebufAddr + loadBytes; + const u32 clutLoadAddr = clutAddr & 0x3FFFFFFF; + const u32 clutLoadEnd = clutLoadAddr + loadBytes; static const u32 MAX_CLUT_OFFSET = 4096; clutRenderOffset_ = MAX_CLUT_OFFSET; const std::vector &framebuffers = framebufferManager_->Framebuffers(); for (VirtualFramebuffer *framebuffer : framebuffers) { const u32 fb_address = framebuffer->fb_address & 0x3FFFFFFF; - const u32 bpp = BufferFormatBytesPerPixel(framebuffer->fb_format); - u32 offset = clutFramebufAddr - fb_address; + const u32 fb_bpp = BufferFormatBytesPerPixel(framebuffer->fb_format); + int offset = clutLoadAddr - fb_address; - // Is this inside the framebuffer at all? - bool matchRange = fb_address + framebuffer->fb_stride * bpp > clutFramebufAddr && fb_address < clutFramebufEnd; - // And is it inside the rendered area? Sometimes games pack data outside. - bool matchRegion = ((offset / bpp) % framebuffer->fb_stride) < framebuffer->width; - if (matchRange && matchRegion && offset < clutRenderOffset_) { - WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d", loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset); - framebuffer->last_frame_clut = gpuStats.numFlips; - framebuffer->usageFlags |= FB_USAGE_CLUT; - clutRenderAddress_ = framebuffer->fb_address; - clutRenderOffset_ = offset; - if (offset == 0) { - break; + // Is this inside the framebuffer at all? Note that we only check the first line here, this should + // be changed. + bool matchRange = offset >= 0 && offset < framebuffer->fb_stride * fb_bpp; + if (matchRange) { + // And is it inside the rendered area? Sometimes games pack data in the margin between width and stride. + // If the framebuffer width was detected as 512, we're gonna assume it's really 480. + int fbMatchWidth = framebuffer->width; + if (fbMatchWidth == 512) { + fbMatchWidth = 480; + } + bool inMargin = ((offset / fb_bpp) % framebuffer->fb_stride) == fbMatchWidth; + if (matchRange && !inMargin && offset < clutRenderOffset_) { + WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d", loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset); + framebuffer->last_frame_clut = gpuStats.numFlips; + framebuffer->usageFlags |= FB_USAGE_CLUT; + clutRenderAddress_ = framebuffer->fb_address; + clutRenderOffset_ = offset; + if (offset == 0) { + break; + } + + // TODO: Could automatically trigger the copy here and make it partial. + // That way, multiple partial copies would work. Though games don't really do that. } } } From 85bb6786a1315ab4a866af0e1ab0eb145db5d3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Sep 2022 23:13:46 +0200 Subject: [PATCH 20/29] Log improvements --- GPU/Common/FramebufferManagerCommon.cpp | 2 +- GPU/Common/TextureCacheCommon.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 9ce5305fd9..c735bad062 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -2197,7 +2197,7 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS } if (dstBuffer && !srcBuffer) { - WARN_LOG_ONCE(btu, G3D, "Block transfer upload %08x -> %08x", srcBasePtr, dstBasePtr); + WARN_LOG_ONCE(btu, G3D, "Block transfer upload %08x -> %08x (%dx%d %d,%d bpp=%d)", srcBasePtr, dstBasePtr, width, height, dstX, dstY, bpp); FlushBeforeCopy(); const u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index f4a8dff574..68db4b8e45 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1039,8 +1039,8 @@ bool TextureCacheCommon::MatchFramebuffer( return true; } } else { - WARN_LOG_ONCE(diffFormat2, G3D, "Ignoring possible texturing from framebuffer with incompatible format %s != %s at %08x", - GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format), fb_address); + WARN_LOG_ONCE(diffFormat2, G3D, "Ignoring possible texturing from framebuffer with incompatible format %s != %s at %08x (+%dx%d)", + GeTextureFormatToString(entry.format), GeBufferFormatToString(fb_format), fb_address, matchInfo->xOffset, matchInfo->yOffset); return false; } } From 0c2d47d74d148b34ec42b9231af45c06c4be0ea3 Mon Sep 17 00:00:00 2001 From: Lubos Date: Wed, 14 Sep 2022 23:23:28 +0200 Subject: [PATCH 21/29] OpenXR - Add code comments --- Common/GPU/OpenGL/GLRenderManager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index 23123533fc..a179697f3a 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -236,6 +236,9 @@ bool GLRenderManager::ThreadFrame() { INFO_LOG(G3D, "Running first frame (%d)", threadFrame_); firstFrame = false; } + + // Creation of OpenXR frame. This updates user's head pose and VR timestamps. + // For fluent rendering, the time between PreVRRender and PostVRRender must be really short. if (IsVRBuild() && !vrlock) { if (PreVRRender()) { vrlock = true; @@ -243,11 +246,14 @@ bool GLRenderManager::ThreadFrame() { return false; } } + + // Render the scene. Run(threadFrame_); VLOG("PULL: Finished frame %d", threadFrame_); } while (!nextFrame); + // Post OpenXR frame on a screen. if (IsVRBuild() && vrlock) { PostVRRender(); } From a208afb7b88fd561e05100c78eaf08b0c25f08fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Sep 2022 23:35:15 +0200 Subject: [PATCH 22/29] OpenGL and D3D11 fixes --- Common/GPU/OpenGL/DataFormatGL.cpp | 8 ++++++++ Common/GPU/OpenGL/thin3d_gl.cpp | 7 +++++-- GPU/D3D11/TextureCacheD3D11.cpp | 4 ++++ GPU/GLES/TextureCacheGLES.cpp | 6 ++++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Common/GPU/OpenGL/DataFormatGL.cpp b/Common/GPU/OpenGL/DataFormatGL.cpp index f34bebd193..48e439a76d 100644 --- a/Common/GPU/OpenGL/DataFormatGL.cpp +++ b/Common/GPU/OpenGL/DataFormatGL.cpp @@ -11,6 +11,14 @@ bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuin internalFormat = GL_RGBA; format = GL_RED; type = GL_UNSIGNED_SHORT; + alignment = 2; + break; + + case DataFormat::R8_UNORM: + internalFormat = GL_RGBA; + format = GL_RED; + type = GL_UNSIGNED_BYTE; + alignment = 1; break; case DataFormat::R8G8B8A8_UNORM: diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 908a764ecc..047cc8b8fb 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -1141,7 +1141,9 @@ void OpenGLContext::BindTextures(int start, int count, Texture **textures) { } void OpenGLContext::BindNativeTexture(int index, void *nativeTexture) { - boundTextures_[index] = (GLRTexture *)nativeTexture; + GLRTexture *tex = (GLRTexture *)nativeTexture; + boundTextures_[index] = tex; + renderManager_.BindTexture(index, tex); } void OpenGLContext::ApplySamplers() { @@ -1489,7 +1491,8 @@ uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const { return FMT_INPUTLAYOUT; case DataFormat::R8_UNORM: - return 0; + return FMT_TEXTURE; + case DataFormat::BC1_RGBA_UNORM_BLOCK: case DataFormat::BC2_UNORM_BLOCK: case DataFormat::BC3_UNORM_BLOCK: diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 47c7b40eee..0930aec655 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -65,6 +65,8 @@ Draw::DataFormat FromD3D11Format(u32 fmt) { return Draw::DataFormat::A1R5G5B5_UNORM_PACK16; case DXGI_FORMAT_B5G6R5_UNORM: return Draw::DataFormat::R5G6B5_UNORM_PACK16; + case DXGI_FORMAT_R8_UNORM: + return Draw::DataFormat::R8_UNORM; case DXGI_FORMAT_B8G8R8A8_UNORM: default: return Draw::DataFormat::R8G8B8A8_UNORM; @@ -290,6 +292,8 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc)); } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM; + } else if (plan.decodeToClut8) { + dstFmt = DXGI_FORMAT_R8_UNORM; } int levels; diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 5ea95a9eed..83309208b8 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -266,6 +266,8 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { dstFmt = plan.replaced->Format(plan.baseLevelSrc); } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = Draw::DataFormat::R8G8B8A8_UNORM; + } else if (plan.decodeToClut8) { + dstFmt = Draw::DataFormat::R8_UNORM; } if (plan.depth == 1) { @@ -313,7 +315,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { if (plan.scaleFactor > 1) { bpp = 4; } else { - bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2; + bpp = (int)Draw::DataFormatSizeInBytes(dstFmt); } } @@ -335,7 +337,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { render_->FinalizeTexture(entry->textureName, plan.levelsToLoad, genMips); } else { - int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2; + int bpp = (int)Draw::DataFormatSizeInBytes(dstFmt); int stride = bpp * (plan.w * plan.scaleFactor); int levelStride = stride * (plan.h * plan.scaleFactor); From 3331fedab79d68352f0e609a7d649c905950655e Mon Sep 17 00:00:00 2001 From: Lubos Date: Thu, 15 Sep 2022 00:03:41 +0200 Subject: [PATCH 23/29] OpenXR - Better variable/methods naming --- Common/GPU/OpenGL/GLRenderManager.cpp | 16 ++++++++-------- Common/GPU/OpenGL/GLRenderManager.h | 1 + Common/VR/PPSSPPVR.cpp | 4 ++-- Common/VR/PPSSPPVR.h | 8 ++++---- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index a179697f3a..ae5cb9dd17 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -200,7 +200,6 @@ bool GLRenderManager::ThreadFrame() { std::unique_lock lock(mutex_); if (!run_) return false; - bool vrlock = false; // In case of syncs or other partial completion, we keep going until we complete a frame. do { @@ -237,11 +236,11 @@ bool GLRenderManager::ThreadFrame() { firstFrame = false; } - // Creation of OpenXR frame. This updates user's head pose and VR timestamps. - // For fluent rendering, the time between PreVRRender and PostVRRender must be really short. - if (IsVRBuild() && !vrlock) { - if (PreVRRender()) { - vrlock = true; + // Start of an OpenXR frame. This updates user's head pose and VR timestamps. + // For fluent rendering, delay between StartVRRender and FinishVRRender must be very short. + if (IsVRBuild() && !vrRenderStarted) { + if (StartVRRender()) { + vrRenderStarted = true; } else { return false; } @@ -254,8 +253,9 @@ bool GLRenderManager::ThreadFrame() { } while (!nextFrame); // Post OpenXR frame on a screen. - if (IsVRBuild() && vrlock) { - PostVRRender(); + if (IsVRBuild() && vrRenderStarted) { + FinishVRRender(); + vrRenderStarted = false; } return true; diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 5423262c98..719603863e 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -1041,6 +1041,7 @@ private: bool nextFrame = false; bool firstFrame = true; + bool vrRenderStarted = false; GLDeleter deleter_; bool skipGLCalls_ = false; diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index 71b4919bf1..1e0931ebfe 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -220,7 +220,7 @@ void BindVRFramebuffer() { VR_BindFramebuffer(VR_GetEngine()); } -bool PreVRRender() { +bool StartVRRender() { if (!VR_GetConfig(VR_CONFIG_VIEWPORT_VALID)) { VR_InitRenderer(VR_GetEngine(), IsMultiviewSupported()); VR_SetConfig(VR_CONFIG_VIEWPORT_VALID, true); @@ -245,7 +245,7 @@ bool PreVRRender() { return false; } -void PostVRRender() { +void FinishVRRender() { VR_FinishFrame(VR_GetEngine()); } diff --git a/Common/VR/PPSSPPVR.h b/Common/VR/PPSSPPVR.h index e7151130ea..cf385735db 100644 --- a/Common/VR/PPSSPPVR.h +++ b/Common/VR/PPSSPPVR.h @@ -15,8 +15,8 @@ void UpdateVRScreenKey(const KeyInput &key); // VR rendering integration void BindVRFramebuffer(); -bool PreVRRender(); -void PostVRRender(); +bool StartVRRender(); +void FinishVRRender(); void PreVRFrameRender(int fboIndex); void PostVRFrameRender(); int GetVRFBOIndex(); @@ -38,8 +38,8 @@ inline void UpdateVRScreenKey(const KeyInput &key) {} // VR rendering integration inline void BindVRFramebuffer() {} -inline bool PreVRRender() { return false; } -inline void PostVRRender() {} +inline bool StartVRRender() { return false; } +inline void FinishVRRender() {} inline void PreVRFrameRender(int fboIndex) {} inline void PostVRFrameRender() {} inline int GetVRFBOIndex() { return 0; } From 9c2bfad7492091e50fc8a9e1099fb925d18d81dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Sep 2022 00:18:26 +0200 Subject: [PATCH 24/29] Seems Need for Speed: Shift has exactly the same lens flare, enable the same compat settings. --- GPU/Common/TextureCacheCommon.cpp | 6 +++--- assets/compat.ini | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 68db4b8e45..70be614c85 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1203,7 +1203,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { // Is this inside the framebuffer at all? Note that we only check the first line here, this should // be changed. - bool matchRange = offset >= 0 && offset < framebuffer->fb_stride * fb_bpp; + bool matchRange = offset >= 0 && offset < (int)(framebuffer->fb_stride * fb_bpp); if (matchRange) { // And is it inside the rendered area? Sometimes games pack data in the margin between width and stride. // If the framebuffer width was detected as 512, we're gonna assume it's really 480. @@ -1212,12 +1212,12 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { fbMatchWidth = 480; } bool inMargin = ((offset / fb_bpp) % framebuffer->fb_stride) == fbMatchWidth; - if (matchRange && !inMargin && offset < clutRenderOffset_) { + if (matchRange && !inMargin && offset < (int)clutRenderOffset_) { WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d", loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset); framebuffer->last_frame_clut = gpuStats.numFlips; framebuffer->usageFlags |= FB_USAGE_CLUT; clutRenderAddress_ = framebuffer->fb_address; - clutRenderOffset_ = offset; + clutRenderOffset_ = (u32)offset; if (offset == 0) { break; } diff --git a/assets/compat.ini b/assets/compat.ini index 3345917f5c..7bc570faaf 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1156,6 +1156,13 @@ ULKS46087 = true ULUS10236 = true ULES00703 = true +# Need for Speed - Shift (same as Burnout Dominator) +ULUS10462 = true +ULES01275 = true +ULJM05494 = true +NPJH50143 = true +ULJM05738 = true + [DisableFirstFrameReadback] # Wipeout Pure: Temporary workaround for lens flare flicker. See #13344 UCUS98612 = true @@ -1309,6 +1316,13 @@ ULKS46087 = true ULUS10236 = true ULES00703 = true +# Need for Speed - Shift (same as Burnout Dominator) +ULUS10462 = true +ULES01275 = true +ULJM05494 = true +NPJH50143 = true +ULJM05738 = true + [AllowDownloadCLUT] # Temporary compatibility option, while developing a GPU CLUT-from-framebuffer path. @@ -1321,3 +1335,10 @@ ULES00703 = true # We need a preinitialized depth buffer ULUS10236 = true ULES00703 = true + +# Need for Speed - Shift (same as Burnout Dominator) +ULUS10462 = true +ULES01275 = true +ULJM05494 = true +NPJH50143 = true +ULJM05738 = true From 36c98ab367b4a1c0bbb874dc6fd4fdc876a599d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Sep 2022 09:15:02 +0200 Subject: [PATCH 25/29] Address feedback, change roles of the two dynamic clut fbos to eliminate the need for state (see next commit) --- GPU/Common/FramebufferManagerCommon.cpp | 7 +++++-- GPU/Common/TextureCacheCommon.cpp | 27 +++++++++++++------------ GPU/Common/TextureCacheCommon.h | 2 +- assets/compat.ini | 9 +++------ 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index c735bad062..006452ac9c 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -557,11 +557,14 @@ void FramebufferManagerCommon::SetDepthFrameBuffer(bool isClearingDepth) { // Set the flag, then upload memory contents to depth channel. // Sanity check the depth buffer pointer. if (currentRenderVfb_->z_address != 0 && currentRenderVfb_->z_address != currentRenderVfb_->fb_address) { - const u16 *src = (const u16 *)Memory::GetPointerUnchecked(currentRenderVfb_->z_address); - DrawPixels(currentRenderVfb_, 0, 0, (const u8 *)src, GE_FORMAT_DEPTH16, currentRenderVfb_->z_stride, currentRenderVfb_->width, currentRenderVfb_->height, RASTER_DEPTH, "Depth Upload"); + if (Memory::IsValidRange(currentRenderVfb_->z_address, currentRenderVfb_->width * 2)) { + const u16 *src = (const u16 *)Memory::GetPointerUnchecked(currentRenderVfb_->z_address); + DrawPixels(currentRenderVfb_, 0, 0, (const u8 *)src, GE_FORMAT_DEPTH16, currentRenderVfb_->z_stride, currentRenderVfb_->width, currentRenderVfb_->height, RASTER_DEPTH, "Depth Upload"); + } } } } + // First time use of this framebuffer's depth buffer. currentRenderVfb_->usageFlags |= FB_USAGE_RENDER_DEPTH; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 70be614c85..ec730a5a59 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2176,7 +2176,8 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { desc.numColorAttachments = 1; desc.tag = "dynamic_clut"; dynamicClutFbo_ = draw_->CreateFramebuffer(desc); - dynamicClutReinterpreted_ = draw_->CreateFramebuffer(desc); + desc.tag = "dynamic_clut_temp"; + dynamicClutTemp_ = draw_->CreateFramebuffer(desc); } const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); @@ -2192,21 +2193,21 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { return; } - Draw::Framebuffer *clutFbo = dynamicClutFbo_; - // First we use a blit (with nearest interpolation, so we don't mash pixels together) // to shrink to the correct size, if we are running with scaling. // We can always blit 512 pixels even if we only need less, the cost will be negligible. - framebufferManager_->BlitUsingRaster( - src->fbo, 0.0f, 0.0f, 512.0f * src->renderScaleFactor, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut"); - // OK, figure out what format we want our framebuffer in, so it can be reinterpreted if needed. - if (expectedCLUTBufferFormat != src->fb_format) { + if (expectedCLUTBufferFormat == src->fb_format) { + framebufferManager_->BlitUsingRaster( + src->fbo, 0.0f, 0.0f, 512.0f * src->renderScaleFactor, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut"); + } else { + framebufferManager_->BlitUsingRaster( + src->fbo, 0.0f, 0.0f, 512.0f * src->renderScaleFactor, 1.0f, dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut_to_temp"); + // OK, figure out what format we want our framebuffer in, so it can be reinterpreted if needed. float scaleFactorX = 1.0f; Draw2DPipeline *reinterpret = framebufferManager_->GetReinterpretPipeline(src->fb_format, expectedCLUTBufferFormat, &scaleFactorX); framebufferManager_->BlitUsingRaster( - dynamicClutFbo_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutReinterpreted_, 0.0f, 0.0f, scaleFactorX * 512.0f, 1.0f, false, 1.0f, reinterpret, "reinterpret_clut"); - clutFbo = dynamicClutReinterpreted_; + dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, scaleFactorX * 512.0f, 1.0f, false, 1.0f, reinterpret, "reinterpret_clut"); } textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, GE_TFMT_CLUT8, GE_FORMAT_CLUT8, false, 0); @@ -2240,7 +2241,7 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { draw_->SetViewports(1, &vp); draw_->BindNativeTexture(0, GetNativeTextureView(entry)); - draw_->BindFramebufferAsTexture(clutFbo, 1, Draw::FB_COLOR_BIT, 0); + draw_->BindFramebufferAsTexture(dynamicClutFbo_, 1, Draw::FB_COLOR_BIT, 0); Draw::SamplerState *nearest = textureShaderCache_->GetSampler(false); Draw::SamplerState *clutSampler = textureShaderCache_->GetSampler(false); draw_->BindSamplerStates(0, 1, &nearest); @@ -2298,9 +2299,9 @@ void TextureCacheCommon::Clear(bool delete_them) { dynamicClutFbo_->Release(); dynamicClutFbo_ = nullptr; } - if (dynamicClutReinterpreted_) { - dynamicClutReinterpreted_->Release(); - dynamicClutReinterpreted_ = nullptr; + if (dynamicClutTemp_) { + dynamicClutTemp_->Release(); + dynamicClutTemp_ = nullptr; } } diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index b24c50b95c..126a497bb6 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -491,8 +491,8 @@ protected: u16 clutAlphaLinearColor_; // Facilities for GPU depal of static textures. + Draw::Framebuffer *dynamicClutTemp_ = nullptr; Draw::Framebuffer *dynamicClutFbo_ = nullptr; - Draw::Framebuffer *dynamicClutReinterpreted_ = nullptr; int standardScaleFactor_; int shaderScaleFactor_ = 0; diff --git a/assets/compat.ini b/assets/compat.ini index 7bc570faaf..f79978e9fd 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1152,7 +1152,7 @@ ULES00262 = true ULUS10064 = true ULKS46087 = true -# Burnout Dominator - lens flare effect (issue )#11100) +# Burnout Dominator - lens flare effect (issue #11100) ULUS10236 = true ULES00703 = true @@ -1324,11 +1324,8 @@ NPJH50143 = true ULJM05738 = true [AllowDownloadCLUT] -# Temporary compatibility option, while developing a GPU CLUT-from-framebuffer path. - -# Burnout Dominator - lens flare effect (issue #11100) -# ULUS10236 = true -# ULES00703 = true +# Temporary compatibility option, while working on the GPU CLUT-from-framebuffer path. +# Not required for any games now that it works, but might be useful for development. [UploadDepthForCLUTTextures] # Burnout Dominator - lens flare effect (issue #11100) From 662126a0a16b966cad97dc490431ad63b7a574a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Sep 2022 16:57:03 +0200 Subject: [PATCH 26/29] Small simplification --- GPU/Common/FramebufferManagerCommon.cpp | 7 +++++++ GPU/Common/FramebufferManagerCommon.h | 2 ++ GPU/Common/TextureCacheCommon.cpp | 24 +++++++++--------------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 006452ac9c..ed43996676 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -825,6 +825,11 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra } Draw2DPipeline *FramebufferManagerCommon::GetReinterpretPipeline(GEBufferFormat from, GEBufferFormat to, float *scaleFactorX) { + if (from == to) { + *scaleFactorX = 1.0f; + return Get2DPipeline(DRAW2D_COPY_COLOR); + } + if (IsBufferFormat16Bit(from) && !IsBufferFormat16Bit(to)) { // We halve the X coordinates in the destination framebuffer. // The shader will collect two pixels worth of input data and merge into one. @@ -833,6 +838,8 @@ Draw2DPipeline *FramebufferManagerCommon::GetReinterpretPipeline(GEBufferFormat // We double the X coordinates in the destination framebuffer. // The shader will sample and depending on the X coordinate & 1, use the upper or lower bits. *scaleFactorX = 2.0f; + } else { + *scaleFactorX = 1.0f; } Draw2DPipeline *pipeline = reinterpretFromTo_[(int)from][(int)to]; diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 04c604972b..5ef9159868 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -423,6 +423,8 @@ public: VirtualFramebuffer *ResolveFramebufferColorToFormat(VirtualFramebuffer *vfb, GEBufferFormat newFormat); Draw2DPipeline *Get2DPipeline(Draw2DShader shader); + + // If from==to, returns a copy pipeline. Draw2DPipeline *GetReinterpretPipeline(GEBufferFormat from, GEBufferFormat to, float *scaleFactorX); // Public to be used from the texture cache's depal shenanigans. diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index ec730a5a59..8d6b5ea5d3 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2154,7 +2154,6 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer // Applies depal to a normal (non-framebuffer) texture, pre-decoded to CLUT8 format. void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { - Draw2DPipeline *textureShader = nullptr; uint32_t clutMode = gstate.clutformat & 0xFFFFFF; switch (entry->format) { @@ -2196,21 +2195,16 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { // First we use a blit (with nearest interpolation, so we don't mash pixels together) // to shrink to the correct size, if we are running with scaling. // We can always blit 512 pixels even if we only need less, the cost will be negligible. + framebufferManager_->BlitUsingRaster( + src->fbo, 0.0f, 0.0f, 512.0f * src->renderScaleFactor, 1.0f, dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut_to_temp"); + // OK, figure out what format we want our framebuffer in, so it can be reinterpreted if needed. + // If no reinterpretation is needed, we'll automatically just get a copy shader. + float scaleFactorX = 1.0f; + Draw2DPipeline *reinterpret = framebufferManager_->GetReinterpretPipeline(src->fb_format, expectedCLUTBufferFormat, &scaleFactorX); + framebufferManager_->BlitUsingRaster( + dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, scaleFactorX * 512.0f, 1.0f, false, 1.0f, reinterpret, "reinterpret_clut"); - if (expectedCLUTBufferFormat == src->fb_format) { - framebufferManager_->BlitUsingRaster( - src->fbo, 0.0f, 0.0f, 512.0f * src->renderScaleFactor, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut"); - } else { - framebufferManager_->BlitUsingRaster( - src->fbo, 0.0f, 0.0f, 512.0f * src->renderScaleFactor, 1.0f, dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut_to_temp"); - // OK, figure out what format we want our framebuffer in, so it can be reinterpreted if needed. - float scaleFactorX = 1.0f; - Draw2DPipeline *reinterpret = framebufferManager_->GetReinterpretPipeline(src->fb_format, expectedCLUTBufferFormat, &scaleFactorX); - framebufferManager_->BlitUsingRaster( - dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, scaleFactorX * 512.0f, 1.0f, false, 1.0f, reinterpret, "reinterpret_clut"); - } - - textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, GE_TFMT_CLUT8, GE_FORMAT_CLUT8, false, 0); + Draw2DPipeline *textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, GE_TFMT_CLUT8, GE_FORMAT_CLUT8, false, 0); gstate_c.SetUseShaderDepal(ShaderDepalMode::OFF); int texWidth = gstate.getTextureWidth(0); From 5d4075376d291ae668b6f4e2849b53d1b0610997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Sep 2022 17:09:03 +0200 Subject: [PATCH 27/29] Copy the texels directly in LoadCLUT --- GPU/Common/TextureCacheCommon.cpp | 60 ++++++++++++++++--------------- GPU/Common/TextureCacheCommon.h | 2 ++ 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 8d6b5ea5d3..66d70f7c02 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1196,6 +1196,8 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { clutRenderOffset_ = MAX_CLUT_OFFSET; const std::vector &framebuffers = framebufferManager_->Framebuffers(); + + VirtualFramebuffer *chosenFramebuffer = nullptr; for (VirtualFramebuffer *framebuffer : framebuffers) { const u32 fb_address = framebuffer->fb_address & 0x3FFFFFFF; const u32 fb_bpp = BufferFormatBytesPerPixel(framebuffer->fb_format); @@ -1212,22 +1214,47 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { fbMatchWidth = 480; } bool inMargin = ((offset / fb_bpp) % framebuffer->fb_stride) == fbMatchWidth; + + // The offset check here means, in the context of the loop, that we'll pick + // the framebuffer with the smallest offset. This is yet another framebuffer matching + // loop with its own rules, eventually we'll probably want to do something + // more systematic. if (matchRange && !inMargin && offset < (int)clutRenderOffset_) { WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d", loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset); framebuffer->last_frame_clut = gpuStats.numFlips; framebuffer->usageFlags |= FB_USAGE_CLUT; clutRenderAddress_ = framebuffer->fb_address; clutRenderOffset_ = (u32)offset; + chosenFramebuffer = framebuffer; if (offset == 0) { + // Not gonna find a better match according to the smallest-offset rule, so we'll go with this one. break; } - - // TODO: Could automatically trigger the copy here and make it partial. - // That way, multiple partial copies would work. Though games don't really do that. } } } + if (chosenFramebuffer) { + if (!dynamicClutTemp_) { + Draw::FramebufferDesc desc{}; + desc.width = 512; + desc.height = 1; + desc.depth = 1; + desc.z_stencil = false; + desc.numColorAttachments = 1; + desc.tag = "dynamic_clut"; + dynamicClutFbo_ = draw_->CreateFramebuffer(desc); + desc.tag = "dynamic_clut_temp"; + dynamicClutTemp_ = draw_->CreateFramebuffer(desc); + } + + // Download the pixels to our temp clut, scaling down if needed. + framebufferManager_->BlitUsingRaster( + chosenFramebuffer->fbo, 0.0f, 0.0f, 512.0f * chosenFramebuffer->renderScaleFactor, 1.0f, + dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, + false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut_to_temp"); + clutRenderFormat_ = chosenFramebuffer->fb_format; + } NotifyMemInfo(MemBlockFlags::ALLOC, clutAddr, loadBytes, "CLUT"); } @@ -2165,20 +2192,6 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { return; } - // Create GPU resources. - if (!dynamicClutFbo_) { - Draw::FramebufferDesc desc{}; - desc.width = 512; - desc.height = 1; - desc.depth = 1; - desc.z_stencil = false; - desc.numColorAttachments = 1; - desc.tag = "dynamic_clut"; - dynamicClutFbo_ = draw_->CreateFramebuffer(desc); - desc.tag = "dynamic_clut_temp"; - dynamicClutTemp_ = draw_->CreateFramebuffer(desc); - } - const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); u32 depthUpperBits = 0; @@ -2186,21 +2199,10 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { // Instead of texturing directly from that, we copy to a temporary CLUT texture. GEBufferFormat expectedCLUTBufferFormat = (GEBufferFormat)clutFormat; // All entries from clutFormat correspond directly to buffer formats. - VirtualFramebuffer *src = framebufferManager_->GetVFBAt(clutRenderAddress_); - if (!src) { - // What do we do? - return; - } - - // First we use a blit (with nearest interpolation, so we don't mash pixels together) - // to shrink to the correct size, if we are running with scaling. - // We can always blit 512 pixels even if we only need less, the cost will be negligible. - framebufferManager_->BlitUsingRaster( - src->fbo, 0.0f, 0.0f, 512.0f * src->renderScaleFactor, 1.0f, dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, false, 1.0f, framebufferManager_->Get2DPipeline(DRAW2D_COPY_COLOR), "copy_clut_to_temp"); // OK, figure out what format we want our framebuffer in, so it can be reinterpreted if needed. // If no reinterpretation is needed, we'll automatically just get a copy shader. float scaleFactorX = 1.0f; - Draw2DPipeline *reinterpret = framebufferManager_->GetReinterpretPipeline(src->fb_format, expectedCLUTBufferFormat, &scaleFactorX); + Draw2DPipeline *reinterpret = framebufferManager_->GetReinterpretPipeline(clutRenderFormat_, expectedCLUTBufferFormat, &scaleFactorX); framebufferManager_->BlitUsingRaster( dynamicClutTemp_, 0.0f, 0.0f, 512.0f, 1.0f, dynamicClutFbo_, 0.0f, 0.0f, scaleFactorX * 512.0f, 1.0f, false, 1.0f, reinterpret, "reinterpret_clut"); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 126a497bb6..9922e9070a 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -486,6 +486,8 @@ protected: u32 clutMaxBytes_ = 0; u32 clutRenderAddress_ = 0xFFFFFFFF; u32 clutRenderOffset_; + GEBufferFormat clutRenderFormat_; + // True if the clut is just alpha values in the same order (RGBA4444-bit only.) bool clutAlphaLinear_ = false; u16 clutAlphaLinearColor_; From e8aaf221635e68c72ab92b35aa1e98f1aa96cfb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 15 Sep 2022 17:22:09 +0200 Subject: [PATCH 28/29] D3D9 crashfix (flare doesn't render correctly though) --- Common/GPU/D3D9/thin3d_d3d9.cpp | 11 +++++++++++ GPU/Common/DepalettizeShaderCommon.cpp | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index c47d8235a1..a84d42f64e 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -443,6 +443,17 @@ void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int d if (data != rect.pBits) memcpy(dest, source, sizeof(uint32_t) * width); break; + + case DataFormat::R8_UNORM: + if (data != rect.pBits) + memcpy(dest, source, width); + break; + + case DataFormat::R16_UNORM: + if (data != rect.pBits) + memcpy(dest, source, sizeof(uint16_t) * width); + break; + default: // Unhandled data format copy. DebugBreak(); diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index ee62064d3d..a1e593e626 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -172,7 +172,14 @@ void GenerateDepalShaderFloat(ShaderWriter &writer, const DepalConfig &config) { bool formatOK = true; switch (config.bufferFormat) { case GE_FORMAT_CLUT8: - _dbg_assert_(false); // to be implemented + if (shift == 0 && mask == 0xFF) { + // Easy peasy. + sprintf(lookupMethod, "index.r"); + formatOK = true; + } else { + // Deal with this if we find it. + formatOK = false; + } break; case GE_FORMAT_8888: if ((mask & (mask + 1)) == 0) { From 0ed86188b0e5e294f09352d6847c78e4bc65d882 Mon Sep 17 00:00:00 2001 From: Sprite Date: Thu, 15 Sep 2022 22:50:57 +0800 Subject: [PATCH 29/29] Fix const pointer conversion error for RISC-V --- Common/RiscVEmitter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index 2fc0966c1f..3f0d47499b 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -628,7 +628,7 @@ void RiscVEmitter::FlushIcache() { void RiscVEmitter::FlushIcacheSection(const u8 *start, const u8 *end) { #if PPSSPP_ARCH(RISCV64) - __builtin___clear_cache(start, end); + __builtin___clear_cache((void *)start, (void *)end); #endif }