diff --git a/Source/Core/VideoBackends/D3D/TextureCache.cpp b/Source/Core/VideoBackends/D3D/TextureCache.cpp index bbc23c6e59..2c820849d5 100644 --- a/Source/Core/VideoBackends/D3D/TextureCache.cpp +++ b/Source/Core/VideoBackends/D3D/TextureCache.cpp @@ -77,6 +77,13 @@ bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int l return saved_png; } +void TextureCache::TCacheEntry::DoPartialTextureUpdate(TCacheEntryBase* entry_, u32 x, u32 y) +{ + TCacheEntry* entry = (TCacheEntry*)entry_; + + D3D::context->CopySubresourceRegion(texture->GetTex(), 0, x , y , 0, entry->texture->GetTex(), 0, NULL); +} + void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, unsigned int expanded_width, unsigned int level) { diff --git a/Source/Core/VideoBackends/D3D/TextureCache.h b/Source/Core/VideoBackends/D3D/TextureCache.h index 7621e86fa3..d6cb7eeef0 100644 --- a/Source/Core/VideoBackends/D3D/TextureCache.h +++ b/Source/Core/VideoBackends/D3D/TextureCache.h @@ -26,6 +26,8 @@ private: TCacheEntry(const TCacheEntryConfig& config, D3DTexture2D *_tex) : TCacheEntryBase(config), texture(_tex) {} ~TCacheEntry(); + void DoPartialTextureUpdate(TCacheEntryBase* entry, u32 x, u32 y) override; + void Load(unsigned int width, unsigned int height, unsigned int expanded_width, unsigned int levels) override; diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index e6f0074f1b..dc378a7073 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -84,6 +84,7 @@ void InitBackendInfo() g_Config.backend_info.bSupportsPostProcessing = false; g_Config.backend_info.bSupportsPaletteConversion = true; g_Config.backend_info.bSupportsClipControl = false; + g_Config.backend_info.bSupportsCopySubImage = true; IDXGIFactory* factory; IDXGIAdapter* ad; diff --git a/Source/Core/VideoBackends/OGL/GLExtensions/ARB_copy_image.h b/Source/Core/VideoBackends/OGL/GLExtensions/ARB_copy_image.h new file mode 100644 index 0000000000..2ff3d9472c --- /dev/null +++ b/Source/Core/VideoBackends/OGL/GLExtensions/ARB_copy_image.h @@ -0,0 +1,10 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoBackends/OGL/GLExtensions/gl_common.h" + +typedef void (GLAPIENTRY * PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); + +extern PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData; + diff --git a/Source/Core/VideoBackends/OGL/GLExtensions/GLExtensions.cpp b/Source/Core/VideoBackends/OGL/GLExtensions/GLExtensions.cpp index 6777bec627..de200343e4 100644 --- a/Source/Core/VideoBackends/OGL/GLExtensions/GLExtensions.cpp +++ b/Source/Core/VideoBackends/OGL/GLExtensions/GLExtensions.cpp @@ -788,6 +788,9 @@ PFNGLGETOCCLUSIONQUERYUIVNVPROC glGetOcclusionQueryuivNV; // ARB_clip_control PFNGLCLIPCONTROLPROC glClipControl; +// ARB_copy_image +PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData; + // Creates a GLFunc object that requires a feature #define GLFUNC_REQUIRES(x, y) { (void**)&x, #x, y } // Creates a GLFunc object with a different function suffix @@ -1278,6 +1281,18 @@ const GLFunc gl_function_array[] = // ARB_clip_control GLFUNC_REQUIRES(glClipControl, "GL_ARB_clip_control"), + // ARB_copy_image + GLFUNC_REQUIRES(glCopyImageSubData, "GL_ARB_copy_image"), + + // NV_copy_image + GLFUNC_SUFFIX(glCopyImageSubData, NV, "GL_NV_copy_image !GL_ARB_copy_image"), + + // OES_copy_image + GLFUNC_SUFFIX(glCopyImageSubData, OES, "GL_OES_copy_image"), + + // EXT_copy_image + GLFUNC_SUFFIX(glCopyImageSubData, EXT, "GL_EXT_copy_image !GL_OES_copy_image"), + // gl_1_1 // OpenGL 1.1 is at the end due to a bug in Android's EGL stack. // eglGetProcAddress can only return a finite amount of function pointers diff --git a/Source/Core/VideoBackends/OGL/GLExtensions/GLExtensions.h b/Source/Core/VideoBackends/OGL/GLExtensions/GLExtensions.h index 869aaefe36..28e8c95e02 100644 --- a/Source/Core/VideoBackends/OGL/GLExtensions/GLExtensions.h +++ b/Source/Core/VideoBackends/OGL/GLExtensions/GLExtensions.h @@ -9,6 +9,7 @@ #include "VideoBackends/OGL/GLExtensions/ARB_blend_func_extended.h" #include "VideoBackends/OGL/GLExtensions/ARB_buffer_storage.h" #include "VideoBackends/OGL/GLExtensions/ARB_clip_control.h" +#include "VideoBackends/OGL/GLExtensions/ARB_copy_image.h" #include "VideoBackends/OGL/GLExtensions/ARB_debug_output.h" #include "VideoBackends/OGL/GLExtensions/ARB_draw_elements_base_vertex.h" #include "VideoBackends/OGL/GLExtensions/ARB_ES2_compatibility.h" diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 6efdd7fd40..6ae102305e 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -469,6 +469,10 @@ Renderer::Renderer() g_Config.backend_info.bSupportsGeometryShaders = GLExtensions::Version() >= 320; g_Config.backend_info.bSupportsPaletteConversion = GLExtensions::Supports("GL_ARB_texture_buffer_object"); g_Config.backend_info.bSupportsClipControl = GLExtensions::Supports("GL_ARB_clip_control"); + g_Config.backend_info.bSupportsCopySubImage = GLExtensions::Supports("GL_ARB_copy_image") || + GLExtensions::Supports("GL_NV_copy_image") || + GLExtensions::Supports("GL_EXT_copy_image") || + GLExtensions::Supports("GL_OES_copy_image"); // Desktop OpenGL supports the binding layout if it supports 420pack // OpenGL ES 3.1 supports it implicitly without an extension @@ -598,7 +602,8 @@ Renderer::Renderer() g_ogl_config.bSupportsMSAA ? "" : "MSAA ", g_ogl_config.bSupportSampleShading ? "" : "SSAA ", g_ActiveConfig.backend_info.bSupportsGSInstancing ? "" : "GSInstancing ", - g_ActiveConfig.backend_info.bSupportsClipControl ? "" : "ClipControl " + g_ActiveConfig.backend_info.bSupportsClipControl ? "" : "ClipControl ", + g_ActiveConfig.backend_info.bSupportsCopySubImage ? "" : "CopyImageSubData " ); s_last_multisample_mode = g_ActiveConfig.iMultisampleMode; diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp index 0256b303e7..cefc8017d6 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.cpp +++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp @@ -137,6 +137,14 @@ TextureCache::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConf return entry; } +void TextureCache::TCacheEntry::DoPartialTextureUpdate(TCacheEntryBase* entry_, u32 x, u32 y) +{ + + 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); +} + void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, unsigned int expanded_width, unsigned int level) { diff --git a/Source/Core/VideoBackends/OGL/TextureCache.h b/Source/Core/VideoBackends/OGL/TextureCache.h index d957422799..42c83d8dda 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.h +++ b/Source/Core/VideoBackends/OGL/TextureCache.h @@ -33,6 +33,8 @@ private: TCacheEntry(const TCacheEntryConfig& config); ~TCacheEntry(); + void DoPartialTextureUpdate(TCacheEntryBase* entry, u32 x, u32 y) 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 af78a4c62e..33424f654c 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -211,6 +211,48 @@ bool TextureCache::TCacheEntryBase::OverlapsMemoryRange(u32 range_address, u32 r return true; } +void TextureCache::TCacheEntryBase::DoPartialTextureUpdates() +{ + const bool isPaletteTexture = (format== GX_TF_C4 || format == GX_TF_C8 || format == GX_TF_C14X2 || 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; + + u32 block_width = TexDecoder_GetBlockWidthInTexels(format); + u32 block_height = TexDecoder_GetBlockHeightInTexels(format); + u32 block_size = block_width * block_height * TexDecoder_GetTexelSizeInNibbles(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); + + 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) + { + u32 block_offset = (entry->addr - 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); + + // Mark the texture update as used, so it isn't applied more than once + entry->frameCount = frameCount; + } + ++iter; + } +} + void TextureCache::DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level) { std::string szDir = File::GetUserPath(D_DUMPTEXTURES_IDX) + @@ -398,6 +440,8 @@ 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(); + return ReturnEntry(stage, entry); } } @@ -450,6 +494,8 @@ 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(); + return ReturnEntry(stage, entry); } ++iter; @@ -590,6 +636,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage) INCSTAT(stats.numTexturesUploaded); SETSTAT(stats.numTexturesAlive, textures_by_address.size()); + entry->DoPartialTextureUpdates(); + return ReturnEntry(stage, entry); } @@ -904,6 +952,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat entry->frameCount = FRAMECOUNT_INVALID; entry->is_efb_copy = true; entry->is_custom_tex = false; + entry->copyMipMapStrideChannels = bpmem.copyMipMapStrideChannels; entry->FromRenderTarget(dstAddr, dstFormat, srcFormat, srcRect, isIntensity, scaleByHalf, cbufid, colmat); diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index ec775b72b4..5d4f9204fc 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -53,6 +53,7 @@ public: u32 format; bool is_efb_copy; bool is_custom_tex; + u32 copyMipMapStrideChannels; unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view unsigned int native_levels; @@ -88,6 +89,8 @@ 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 Load(unsigned int width, unsigned int height, unsigned int expanded_width, unsigned int level) = 0; virtual void FromRenderTarget(u32 dstAddr, unsigned int dstFormat, @@ -97,6 +100,8 @@ public: bool OverlapsMemoryRange(u32 range_address, u32 range_size) const; + void DoPartialTextureUpdates(); + bool IsEfbCopy() const { return is_efb_copy; } }; diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index 48ed3fc005..13451bbf5a 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -160,6 +160,7 @@ struct VideoConfig final bool bSupportsPostProcessing; bool bSupportsPaletteConversion; bool bSupportsClipControl; // Needed by VertexShaderGen, so must stay in VideoCommon + bool bSupportsCopySubImage; // Needed for partial texture updates } backend_info; // Utility