From d8cd2c3252ab6f2ce6c1656b5eb194863e834f52 Mon Sep 17 00:00:00 2001 From: Rodolfo Bogado Date: Mon, 29 Jun 2015 22:19:19 -0300 Subject: [PATCH] Implement scaled partial texture updates --- Data/Sys/GameSettings/SMN.ini | 4 +- .../Core/VideoBackends/D3D/TextureCache.cpp | 62 ++++++++++- Source/Core/VideoBackends/D3D/TextureCache.h | 5 +- .../Core/VideoBackends/OGL/TextureCache.cpp | 69 ++++++++++-- Source/Core/VideoBackends/OGL/TextureCache.h | 5 +- Source/Core/VideoCommon/TextureCacheBase.cpp | 100 +++++++++++++----- Source/Core/VideoCommon/TextureCacheBase.h | 9 +- 7 files changed, 211 insertions(+), 43 deletions(-) diff --git a/Data/Sys/GameSettings/SMN.ini b/Data/Sys/GameSettings/SMN.ini index 70e3592672..204d8a32df 100644 --- a/Data/Sys/GameSettings/SMN.ini +++ b/Data/Sys/GameSettings/SMN.ini @@ -5,7 +5,6 @@ [EmuState] # The Emulation State. 1 is worst, 5 is best, 0 is not set. -EmulationIssues = If "Store EFB Copies to Texture Only" is enabled, "Scaled EFB Copy" needs to be disabled for the coins to spin. EmulationStateId = 4 [OnLoad] @@ -20,5 +19,4 @@ EmulationStateId = 4 [Video_Settings] SafeTextureCacheColorSamples = 512 -[Video_Hacks] -EFBScaledCopy = False +[Video_Hacks] \ No newline at end of file diff --git a/Source/Core/VideoBackends/D3D/TextureCache.cpp b/Source/Core/VideoBackends/D3D/TextureCache.cpp index 2c820849d5..de204fa2af 100644 --- a/Source/Core/VideoBackends/D3D/TextureCache.cpp +++ b/Source/Core/VideoBackends/D3D/TextureCache.cpp @@ -77,11 +77,67 @@ bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int l return saved_png; } -void TextureCache::TCacheEntry::DoPartialTextureUpdate(TCacheEntryBase* entry_, u32 x, u32 y) +void TextureCache::TCacheEntry::CopyRectangleFromTexture( + const TCacheEntryBase* source, + const MathUtil::Rectangle &srcrect, + const MathUtil::Rectangle &dstrect) { - TCacheEntry* entry = (TCacheEntry*)entry_; + TCacheEntry* srcentry = (TCacheEntry*)source; + if (srcrect.GetWidth() == dstrect.GetWidth() + && srcrect.GetHeight() == dstrect.GetHeight()) + { + const D3D11_BOX *psrcbox = nullptr; + D3D11_BOX srcbox; + if (srcrect.left != 0 || srcrect.top != 0) + { + srcbox.left = srcrect.left; + srcbox.top = srcrect.top; + srcbox.right = srcrect.right; + srcbox.bottom = srcrect.bottom; + psrcbox = &srcbox; + } + D3D::context->CopySubresourceRegion( + texture->GetTex(), + 0, + dstrect.left, + dstrect.top, + 0, + srcentry->texture->GetTex(), + 0, + psrcbox); + return; + } + else if (!config.rendertarget) + { + return; + } + g_renderer->ResetAPIState(); // reset any game specific settings - D3D::context->CopySubresourceRegion(texture->GetTex(), 0, x , y , 0, entry->texture->GetTex(), 0, NULL); + const D3D11_VIEWPORT vp = CD3D11_VIEWPORT( + float(dstrect.left), + float(dstrect.top), + float(dstrect.GetWidth()), + float(dstrect.GetHeight())); + + D3D::context->OMSetRenderTargets(1, &texture->GetRTV(), nullptr); + D3D::context->RSSetViewports(1, &vp); + D3D::SetLinearCopySampler(); + D3D11_RECT srcRC; + srcRC.left = srcrect.left; + srcRC.right = srcrect.right; + srcRC.top = srcrect.top; + srcRC.bottom = srcrect.bottom; + D3D::drawShadedTexQuad(srcentry->texture->GetSRV(), &srcRC, + srcentry->config.width, srcentry->config.height, + PixelShaderCache::GetColorCopyProgram(false), + VertexShaderCache::GetSimpleVertexShader(), + VertexShaderCache::GetSimpleInputLayout(), nullptr, 1.0, 0); + + D3D::context->OMSetRenderTargets(1, + &FramebufferManager::GetEFBColorTexture()->GetRTV(), + FramebufferManager::GetEFBDepthTexture()->GetDSV()); + + g_renderer->RestoreAPIState(); } void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, diff --git a/Source/Core/VideoBackends/D3D/TextureCache.h b/Source/Core/VideoBackends/D3D/TextureCache.h index d6cb7eeef0..608fa47044 100644 --- a/Source/Core/VideoBackends/D3D/TextureCache.h +++ b/Source/Core/VideoBackends/D3D/TextureCache.h @@ -26,7 +26,10 @@ private: TCacheEntry(const TCacheEntryConfig& config, D3DTexture2D *_tex) : TCacheEntryBase(config), texture(_tex) {} ~TCacheEntry(); - void DoPartialTextureUpdate(TCacheEntryBase* entry, u32 x, u32 y) override; + void CopyRectangleFromTexture( + const TCacheEntryBase* source, + const MathUtil::Rectangle &srcrect, + const MathUtil::Rectangle &dstrect) override; void Load(unsigned int width, unsigned int height, unsigned int expanded_width, unsigned int levels) override; diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp index cefc8017d6..ebc3a672f1 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.cpp +++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp @@ -33,11 +33,13 @@ namespace OGL { +static SHADER s_ColorCopyProgram; static SHADER s_ColorMatrixProgram; static SHADER s_DepthMatrixProgram; static GLuint s_ColorMatrixUniform; static GLuint s_DepthMatrixUniform; static GLuint s_ColorCopyPositionUniform; +static GLuint s_ColorMatrixPositionUniform; static GLuint s_DepthCopyPositionUniform; static u32 s_ColorCbufid; static u32 s_DepthCbufid; @@ -137,12 +139,53 @@ TextureCache::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConf return entry; } -void TextureCache::TCacheEntry::DoPartialTextureUpdate(TCacheEntryBase* entry_, u32 x, u32 y) +void TextureCache::TCacheEntry::CopyRectangleFromTexture( + const TCacheEntryBase* source, + const MathUtil::Rectangle &srcrect, + const MathUtil::Rectangle &dstrect) { - - TCacheEntry* entry = (TCacheEntry*)entry_; - - glCopyImageSubData(entry->texture, GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, texture, GL_TEXTURE_2D_ARRAY, 0, x, y, 0, entry->native_width, entry->native_height, 1); + TCacheEntry* srcentry = (TCacheEntry*)source; + if (srcrect.GetWidth() == dstrect.GetWidth() + && srcrect.GetHeight() == dstrect.GetHeight() + && g_ActiveConfig.backend_info.bSupportsCopySubImage) + { + glCopyImageSubData( + srcentry->texture, + GL_TEXTURE_2D_ARRAY, + 0, + srcrect.left, + srcrect.top, + 0, + texture, + GL_TEXTURE_2D_ARRAY, + 0, + dstrect.left, + dstrect.top, + 0, + dstrect.GetWidth(), + dstrect.GetHeight(), + 1); + return; + } + else if (!config.rendertarget) + { + return; + } + g_renderer->ResetAPIState(); + FramebufferManager::SetFramebuffer(framebuffer); + glActiveTexture(GL_TEXTURE9); + glBindTexture(GL_TEXTURE_2D_ARRAY, srcentry->texture); + g_sampler_cache->BindLinearSampler(9); + glViewport(dstrect.left, dstrect.top, dstrect.GetWidth(), dstrect.GetHeight()); + s_ColorCopyProgram.Bind(); + glUniform4f(s_ColorCopyPositionUniform, + float(srcrect.left), + float(srcrect.top), + float(srcrect.GetWidth()), + float(srcrect.GetHeight())); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + FramebufferManager::SetFramebuffer(0); + g_renderer->RestoreAPIState(); } void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, @@ -208,7 +251,7 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo if (s_ColorCbufid != cbufid) glUniform4fv(s_ColorMatrixUniform, 7, colmat); s_ColorCbufid = cbufid; - uniform_location = s_ColorCopyPositionUniform; + uniform_location = s_ColorMatrixPositionUniform; } TargetRectangle R = g_renderer->ConvertEFBRectangle(srcRect); @@ -286,6 +329,16 @@ void TextureCache::SetStage() void TextureCache::CompileShaders() { + const char *pColorCopyProg = + "SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n" + "in vec3 f_uv0;\n" + "out vec4 ocol0;\n" + "\n" + "void main(){\n" + " vec4 texcol = texture(samp9, f_uv0);\n" + " ocol0 = texcol;\n" + "}\n"; + const char *pColorMatrixProg = "SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n" "uniform vec4 colmat[7];\n" @@ -357,6 +410,7 @@ void TextureCache::CompileShaders() const char* prefix = (GProgram == nullptr) ? "f" : "v"; const char* depth_layer = (g_ActiveConfig.bStereoEFBMonoDepth) ? "0.0" : "f_uv0.z"; + ProgramShaderCache::CompileShader(s_ColorCopyProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), pColorCopyProg, GProgram); ProgramShaderCache::CompileShader(s_ColorMatrixProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), pColorMatrixProg, GProgram); ProgramShaderCache::CompileShader(s_DepthMatrixProgram, StringFromFormat(VProgram, prefix, prefix).c_str(), StringFromFormat(pDepthMatrixProg, depth_layer).c_str(), GProgram); @@ -365,7 +419,8 @@ void TextureCache::CompileShaders() s_ColorCbufid = -1; s_DepthCbufid = -1; - s_ColorCopyPositionUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "copy_position"); + s_ColorCopyPositionUniform = glGetUniformLocation(s_ColorCopyProgram.glprogid, "copy_position"); + s_ColorMatrixPositionUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "copy_position"); s_DepthCopyPositionUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "copy_position"); std::string palette_shader = diff --git a/Source/Core/VideoBackends/OGL/TextureCache.h b/Source/Core/VideoBackends/OGL/TextureCache.h index 42c83d8dda..1ed85b0539 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.h +++ b/Source/Core/VideoBackends/OGL/TextureCache.h @@ -33,7 +33,10 @@ private: TCacheEntry(const TCacheEntryConfig& config); ~TCacheEntry(); - void DoPartialTextureUpdate(TCacheEntryBase* entry, u32 x, u32 y) override; + void CopyRectangleFromTexture( + const TCacheEntryBase* source, + const MathUtil::Rectangle &srcrect, + const MathUtil::Rectangle &dstrect) override; void Load(unsigned int width, unsigned int height, unsigned int expanded_width, unsigned int level) override; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 6baca12326..7d49e698da 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -211,46 +211,98 @@ bool TextureCache::TCacheEntryBase::OverlapsMemoryRange(u32 range_address, u32 r return true; } -void TextureCache::TCacheEntryBase::DoPartialTextureUpdates() +TextureCache::TCacheEntryBase* TextureCache::DoPartialTextureUpdates(TexCache::iterator iter_t) { - const bool isPaletteTexture = (format== GX_TF_C4 || format == GX_TF_C8 || format == GX_TF_C14X2 || format >= 0x10000); + TCacheEntryBase* entry_to_update = iter_t->second; + const bool isPaletteTexture = (entry_to_update->format == GX_TF_C4 + || entry_to_update->format == GX_TF_C8 + || entry_to_update->format == GX_TF_C14X2 + || entry_to_update->format >= 0x10000); // Efb copies and paletted textures are excluded from these updates, until there's an example where a game would // benefit from this. Both would require more work to be done. // TODO: Implement upscaling support for normal textures, and then remove the efb to ram and the scaled efb restrictions - if (!g_ActiveConfig.backend_info.bSupportsCopySubImage || !g_ActiveConfig.bSkipEFBCopyToRam || IsEfbCopy() - || isPaletteTexture || (g_ActiveConfig.bCopyEFBScaled && g_ActiveConfig.iEFBScale != SCALE_1X)) - return; + if (entry_to_update->IsEfbCopy() + || isPaletteTexture) + return entry_to_update; - u32 block_width = TexDecoder_GetBlockWidthInTexels(format); - u32 block_height = TexDecoder_GetBlockHeightInTexels(format); - u32 block_size = block_width * block_height * TexDecoder_GetTexelSizeInNibbles(format) / 2; + u32 block_width = TexDecoder_GetBlockWidthInTexels(entry_to_update->format); + u32 block_height = TexDecoder_GetBlockHeightInTexels(entry_to_update->format); + u32 block_size = block_width * block_height * TexDecoder_GetTexelSizeInNibbles(entry_to_update->format) / 2; - u32 numBlocksX = (native_width + block_width - 1) / block_width; - - TexCache::iterator iter = textures_by_address.lower_bound(addr); - TexCache::iterator iterend = textures_by_address.upper_bound(addr + size_in_bytes); + u32 numBlocksX = (entry_to_update->native_width + block_width - 1) / block_width; + TexCache::iterator iter = textures_by_address.lower_bound(entry_to_update->addr); + TexCache::iterator iterend = textures_by_address.upper_bound(entry_to_update->addr + entry_to_update->size_in_bytes); + bool entry_need_scaling = true; while (iter != iterend) { TCacheEntryBase* entry = iter->second; - if (entry->IsEfbCopy() && addr <= entry->addr && entry->addr + entry->size_in_bytes <= addr + size_in_bytes - && entry->frameCount == FRAMECOUNT_INVALID && entry->copyMipMapStrideChannels * 32 == numBlocksX * block_size) + if (entry != entry_to_update + && entry->IsEfbCopy() + && entry_to_update->addr <= entry->addr + && entry->addr + entry->size_in_bytes <= entry_to_update->addr + entry_to_update->size_in_bytes + && entry->frameCount == FRAMECOUNT_INVALID + && entry->copyMipMapStrideChannels * 32 == numBlocksX * block_size) { - u32 block_offset = (entry->addr - addr) / block_size; + u32 block_offset = (entry->addr - entry_to_update->addr) / block_size; u32 block_x = block_offset % numBlocksX; u32 block_y = block_offset / numBlocksX; u32 x = block_x * block_width; u32 y = block_y * block_height; - - DoPartialTextureUpdate(entry, x, y); - + MathUtil::Rectangle srcrect, dstrect; + srcrect.left = 0; + srcrect.top = 0; + dstrect.left = 0; + dstrect.top = 0; + if (entry_need_scaling) + { + entry_need_scaling = false; + u32 w = entry_to_update->native_width * entry->config.width / entry->native_width; + u32 h = entry_to_update->native_height * entry->config.height / entry->native_height; + u32 max = g_renderer->GetMaxTextureSize(); + if (max < w || max < h) + { + iter++; + continue; + } + if (entry_to_update->config.width != w || entry_to_update->config.height != h) + { + TextureCache::TCacheEntryConfig newconfig; + newconfig.width = w; + newconfig.height = h; + newconfig.rendertarget = true; + TCacheEntryBase* newentry = AllocateTexture(newconfig); + newentry->SetGeneralParameters(entry_to_update->addr, entry_to_update->size_in_bytes, entry_to_update->format); + newentry->SetDimensions(entry_to_update->native_width, entry_to_update->native_height, 1); + newentry->SetHashes(entry_to_update->hash); + newentry->frameCount = frameCount; + newentry->is_efb_copy = false; + srcrect.right = entry_to_update->config.width; + srcrect.bottom = entry_to_update->config.height; + dstrect.right = w; + dstrect.bottom = h; + newentry->CopyRectangleFromTexture(entry_to_update, srcrect, dstrect); + entry_to_update = newentry; + u64 key = iter_t->first; + iter_t = FreeTexture(iter_t); + textures_by_address.emplace(key, entry_to_update); + } + } + srcrect.right = entry->config.width; + srcrect.bottom = entry->config.height; + dstrect.left = x * entry_to_update->config.width / entry_to_update->native_width; + dstrect.top = y * entry_to_update->config.height / entry_to_update->native_height; + dstrect.right = (x + entry->native_width) * entry_to_update->config.width / entry_to_update->native_width; + dstrect.bottom = (y + entry->native_height) * entry_to_update->config.height / entry_to_update->native_height; + entry_to_update->CopyRectangleFromTexture(entry, srcrect, dstrect); // Mark the texture update as used, so it isn't applied more than once entry->frameCount = frameCount; } ++iter; } + return entry_to_update; } void TextureCache::DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level) @@ -323,7 +375,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage) const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat) - 1; const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat) - 1; - unsigned int expandedWidth = (width + bsw) & (~bsw); + unsigned int expandedWidth = (width + bsw) & (~bsw); unsigned int expandedHeight = (height + bsh) & (~bsh); const unsigned int nativeW = width; const unsigned int nativeH = height; @@ -440,7 +492,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage) if (entry->hash == full_hash && entry->format == full_format && entry->native_levels >= tex_levels && entry->native_width == nativeW && entry->native_height == nativeH) { - entry->DoPartialTextureUpdates(); + entry = DoPartialTextureUpdates(iter); return ReturnEntry(stage, entry); } @@ -494,7 +546,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage) if (entry->format == full_format && entry->native_levels >= tex_levels && entry->native_width == nativeW && entry->native_height == nativeH) { - entry->DoPartialTextureUpdates(); + entry = DoPartialTextureUpdates(iter); return ReturnEntry(stage, entry); } @@ -539,7 +591,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage) if (!(texformat == GX_TF_RGBA8 && from_tmem)) { const u8* tlut = &texMem[tlutaddr]; - TexDecoder_Decode(temp, src_data, expandedWidth, expandedHeight, texformat, tlut, (TlutFormat) tlutfmt); + TexDecoder_Decode(temp, src_data, expandedWidth, expandedHeight, texformat, tlut, (TlutFormat)tlutfmt); } else { @@ -560,7 +612,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage) TCacheEntryBase* entry = AllocateTexture(config); GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); - textures_by_address.emplace((u64)address, entry); + iter = textures_by_address.emplace((u64)address, entry); if (g_ActiveConfig.iSafeTextureCache_ColorSamples == 0 || std::max(texture_size, palette_size) <= (u32)g_ActiveConfig.iSafeTextureCache_ColorSamples * 8) { @@ -636,7 +688,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage) INCSTAT(stats.numTexturesUploaded); SETSTAT(stats.numTexturesAlive, textures_by_address.size()); - entry->DoPartialTextureUpdates(); + entry = DoPartialTextureUpdates(iter); return ReturnEntry(stage, entry); } diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index 5d4f9204fc..0a51ef64b9 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -89,7 +89,10 @@ public: virtual void Bind(unsigned int stage) = 0; virtual bool Save(const std::string& filename, unsigned int level) = 0; - virtual void DoPartialTextureUpdate(TCacheEntryBase* entry, u32 x, u32 y) = 0; + virtual void CopyRectangleFromTexture( + const TCacheEntryBase* source, + const MathUtil::Rectangle &srcrect, + const MathUtil::Rectangle &dstrect) = 0; virtual void Load(unsigned int width, unsigned int height, unsigned int expanded_width, unsigned int level) = 0; @@ -100,8 +103,6 @@ public: bool OverlapsMemoryRange(u32 range_address, u32 range_size) const; - void DoPartialTextureUpdates(); - bool IsEfbCopy() const { return is_efb_copy; } }; @@ -140,7 +141,7 @@ protected: private: typedef std::multimap TexCache; typedef std::unordered_multimap TexPool; - + static TCacheEntryBase* DoPartialTextureUpdates(TexCache::iterator iter); static void DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level); static void CheckTempSize(size_t required_size);