diff --git a/Common/File/Path.cpp b/Common/File/Path.cpp index d0bda53328..ba3372e78a 100644 --- a/Common/File/Path.cpp +++ b/Common/File/Path.cpp @@ -258,8 +258,13 @@ std::wstring Path::ToWString() const { std::string Path::ToVisualString() const { if (type_ == PathType::CONTENT_URI) { return AndroidContentURI(path_).ToVisualString(); +#if PPSSPP_PLATFORM(WINDOWS) + } else if (type_ == PathType::NATIVE) { + return ReplaceAll(path_, "/", "\\"); +#endif + } else { + return path_; } - return path_; } bool Path::CanNavigateUp() const { diff --git a/Core/TextureReplacer.cpp b/Core/TextureReplacer.cpp index c0b965e9b1..4544fdcd72 100644 --- a/Core/TextureReplacer.cpp +++ b/Core/TextureReplacer.cpp @@ -573,7 +573,7 @@ public: if (png.warning_or_error >= 2) { ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors."); } else if (success) { - NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", replacedInfoHash, w, h); + NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d in '%s'", replacedInfoHash, w, h, saveFilename.ToVisualString().c_str()); } } }; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index faa03ad261..acce603667 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -2388,6 +2388,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt plan.replaceValid = plan.replaced->Valid(); } + plan.saveTexture = false; if (plan.replaceValid) { // We're replacing, so we won't scale. plan.scaleFactor = 1; @@ -2397,6 +2398,18 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt // But, we still need to create the texture at a larger size. plan.replaced->GetSize(0, plan.createW, plan.createH); } else { + if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { + ReplacedTextureDecodeInfo replacedInfo; + // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? + replacedInfo.cachekey = entry->CacheKey(); + replacedInfo.hash = entry->fullhash; + replacedInfo.addr = entry->addr; + replacedInfo.isVideo = plan.isVideo; + replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = plan.scaleFactor; + replacedInfo.fmt = Draw::DataFormat::R8G8B8A8_UNORM; + plan.saveTexture = replacer_.WillSave(replacedInfo); + } plan.createW = plan.w * plan.scaleFactor; plan.createH = plan.h * plan.scaleFactor; } @@ -2454,7 +2467,7 @@ void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, i decPitch = stride; } - bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1; + bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || dstFmt == Draw::DataFormat::R8G8B8A8_UNORM; CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, srcLevel, bufw, reverseColors, expand32); entry.SetAlphaStatus(alphaResult, srcLevel); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index e394923090..7cb79d44ce 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -272,6 +272,7 @@ struct BuildTexturePlan { ReplacedTexture *replaced; // Need to only check once since it can change during the load! bool replaceValid; + bool saveTexture; void GetMipSize(int level, int *w, int *h) const { if (replaceValid) { diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index baa8ee5e04..5cc42adee6 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -279,7 +279,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); if (plan.replaceValid) { dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc)); - } else if (plan.scaleFactor > 1) { + } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM; } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 0ef616914d..289d52d3b7 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -241,7 +241,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) { D3DFORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); if (plan.replaceValid) { dstFmt = ToD3D9Format(plan.replaced->Format(plan.baseLevelSrc)); - } else if (plan.scaleFactor > 1) { + } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = D3DFMT_A8R8G8B8; } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index cf9a8e13af..928854f1e2 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -258,15 +258,13 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { // the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not // be as good quality as the game's own (might even be better in some cases though). - int tw = plan.w; - int th = plan.h; + int tw = plan.createW; + int th = plan.createH; Draw::DataFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) { dstFmt = plan.replaced->Format(plan.baseLevelSrc); - } else if (plan.scaleFactor > 1) { - tw *= plan.scaleFactor; - th *= plan.scaleFactor; + } else if (plan.scaleFactor > 1 || plan.saveTexture) { dstFmt = Draw::DataFormat::R8G8B8A8_UNORM; } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 2f82eae187..e72464a469 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -499,12 +499,16 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { imageLayout = VK_IMAGE_LAYOUT_GENERAL; } + if (plan.saveTexture) { + actualFmt = VULKAN_8888_FORMAT; + } + char texName[128]{}; snprintf(texName, sizeof(texName), "tex_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat())); image->SetTag(texName); bool allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping); - if (!allocSuccess && !lowMemoryMode_) { + if (!allocSuccess && !lowMemoryMode_ && !replacer_.Enabled()) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; decimationCounter_ = 0; @@ -540,20 +544,6 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { return; } - ReplacedTextureDecodeInfo replacedInfo; - bool willSaveTex = false; - if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) { - // TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save? - replacedInfo.cachekey = entry->CacheKey(); - replacedInfo.hash = entry->fullhash; - replacedInfo.addr = entry->addr; - replacedInfo.isVideo = plan.isVideo; - replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; - replacedInfo.scaleFactor = plan.scaleFactor; - replacedInfo.fmt = FromVulkanFormat(actualFmt); - willSaveTex = replacer_.WillSave(replacedInfo); - } - VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, "Texture Upload (%08x) video=%d", entry->addr, plan.isVideo); @@ -585,14 +575,14 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { std::vector saveData; auto loadLevel = [&](int sz, int srcLevel, int lstride, int lfactor) { - if (willSaveTex) { + if (plan.saveTexture) { saveData.resize(sz); data = &saveData[0]; } else { data = drawEngine_->GetPushBufferForTextureData()->PushAligned(sz, &bufferOffset, &texBuf, pushAlignment); } - LoadTextureLevel(*entry, (uint8_t *)data, lstride, srcLevel, lfactor, dstFmt); - if (willSaveTex) + LoadTextureLevel(*entry, (uint8_t *)data, lstride, srcLevel, lfactor, actualFmt); + if (plan.saveTexture) bufferOffset = drawEngine_->GetPushBufferForTextureData()->PushAligned(&saveData[0], sz, pushAlignment, &texBuf); }; @@ -643,6 +633,15 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { int w = dataScaled ? mipWidth : mipUnscaledWidth; int h = dataScaled ? mipHeight : mipUnscaledHeight; // At this point, data should be saveData, and not slow. + ReplacedTextureDecodeInfo replacedInfo; + replacedInfo.cachekey = entry->CacheKey(); + replacedInfo.hash = entry->fullhash; + replacedInfo.addr = entry->addr; + replacedInfo.isVideo = IsVideo(entry->addr); + replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0; + replacedInfo.scaleFactor = plan.scaleFactor; + replacedInfo.fmt = FromVulkanFormat(actualFmt); + replacer_.NotifyTextureDecoded(replacedInfo, data, stride, plan.baseLevelSrc + i, w, h); } }