From cf05cca7a69a1c61f60e781700642111150d5d4b Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Tue, 19 Oct 2010 22:24:27 +0000 Subject: [PATCH] Merged common texture cache code from video plugins into VideoCommon. (DX11 native mipmaps currently broken, disabled) Hopefully everything else should still be working. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6288 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/VideoCommon/Src/HiresTextures.cpp | 2 +- Source/Core/VideoCommon/Src/HiresTextures.h | 2 +- Source/Core/VideoCommon/Src/SConscript | 1 + .../Core/VideoCommon/Src/TextureCacheBase.cpp | 659 ++++++++++++++ .../Core/VideoCommon/Src/TextureCacheBase.h | 99 +++ .../VideoCommon/Src/VertexManagerBase.cpp | 158 +++- .../Core/VideoCommon/Src/VertexManagerBase.h | 4 +- Source/Core/VideoCommon/VideoCommon.vcproj | 8 + .../Plugin_VideoDX11/Src/BPFunctions.cpp | 2 +- .../Plugin_VideoDX11/Src/TextureCache.cpp | 676 +++----------- .../Plugin_VideoDX11/Src/TextureCache.h | 69 +- .../Plugin_VideoDX11/Src/VertexManager.cpp | 4 +- Source/Plugins/Plugin_VideoDX11/Src/main.cpp | 4 +- .../Plugin_VideoDX9/Src/BPFunctions.cpp | 2 +- .../Plugin_VideoDX9/Src/TextureCache.cpp | 678 +++----------- .../Plugin_VideoDX9/Src/TextureCache.h | 77 +- .../Plugin_VideoDX9/Src/VertexManager.cpp | 4 +- Source/Plugins/Plugin_VideoDX9/Src/main.cpp | 4 +- .../Plugin_VideoOGL/Src/BPFunctions.cpp | 2 +- Source/Plugins/Plugin_VideoOGL/Src/Render.cpp | 4 +- .../Plugin_VideoOGL/Src/TextureCache.cpp | 841 ++++-------------- .../Plugin_VideoOGL/Src/TextureCache.h | 91 +- .../Plugin_VideoOGL/Src/TextureConverter.cpp | 2 + .../Plugin_VideoOGL/Src/VertexManager.cpp | 10 +- Source/Plugins/Plugin_VideoOGL/Src/main.cpp | 4 +- 25 files changed, 1485 insertions(+), 1922 deletions(-) create mode 100644 Source/Core/VideoCommon/Src/TextureCacheBase.cpp create mode 100644 Source/Core/VideoCommon/Src/TextureCacheBase.h diff --git a/Source/Core/VideoCommon/Src/HiresTextures.cpp b/Source/Core/VideoCommon/Src/HiresTextures.cpp index 4cdf78f055..d584895b58 100644 --- a/Source/Core/VideoCommon/Src/HiresTextures.cpp +++ b/Source/Core/VideoCommon/Src/HiresTextures.cpp @@ -95,7 +95,7 @@ void Shutdown() textureMap.clear(); } -PC_TexFormat GetHiresTex(const char *fileName, int *pWidth, int *pHeight, int texformat, u8 *data) +PC_TexFormat GetHiresTex(const char *fileName, unsigned int *pWidth, unsigned int *pHeight, int texformat, u8 *data) { std::string key(fileName); diff --git a/Source/Core/VideoCommon/Src/HiresTextures.h b/Source/Core/VideoCommon/Src/HiresTextures.h index 0e65806085..dc4434e5d4 100644 --- a/Source/Core/VideoCommon/Src/HiresTextures.h +++ b/Source/Core/VideoCommon/Src/HiresTextures.h @@ -26,7 +26,7 @@ namespace HiresTextures { void Init(const char *gameCode); void Shutdown(); -PC_TexFormat GetHiresTex(const char *fileName, int *pWidth, int *pHeight, int texformat, u8 *data); +PC_TexFormat GetHiresTex(const char *fileName, unsigned int *pWidth, unsigned int *pHeight, int texformat, u8 *data); }; diff --git a/Source/Core/VideoCommon/Src/SConscript b/Source/Core/VideoCommon/Src/SConscript index 5763cdf150..5a397d653e 100644 --- a/Source/Core/VideoCommon/Src/SConscript +++ b/Source/Core/VideoCommon/Src/SConscript @@ -30,6 +30,7 @@ files = [ 'TextureConversionShader.cpp', 'ImageWrite.cpp', 'VertexManagerBase.cpp', + 'TextureCacheBase.cpp', 'Statistics.cpp', 'Fifo.cpp', 'VideoState.cpp', diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp new file mode 100644 index 0000000000..8b4a20ef3f --- /dev/null +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -0,0 +1,659 @@ + +#include "MemoryUtil.h" + +#include "VideoConfig.h" +#include "Statistics.h" +#include "HiresTextures.h" +#include "Render.h" +#include "FileUtil.h" +#include "Profiler.h" + +#include "PluginSpecs.h" + +#include "TextureCacheBase.h" + +// ugly +extern int frameCount; + +enum +{ + TEMP_SIZE = (1024 * 1024 * 4), + TEXTURE_KILL_THRESHOLD = 200, +}; + +TextureCache *g_texture_cache; + +u8 *TextureCache::temp; +TextureCache::TexCache TextureCache::textures; + +// returns the exponent of the smallest power of two which is greater than val +unsigned int GetPow2(unsigned int val) +{ + unsigned int ret = 0; + for (; val; val >>= 1) + ++ret; + return ret; +} + +TextureCache::TCacheEntryBase::~TCacheEntryBase() +{ + if (0 == addr) + return; + + if (!isRenderTarget && !g_ActiveConfig.bSafeTextureCache) + { + u32 *const ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr); + if (ptr && *ptr == hash) + *ptr = oldpixel; + } +} + +// TODO: uglyness +extern PLUGIN_GLOBALS *globals; + +TextureCache::TextureCache() +{ + temp = (u8*)AllocateMemoryPages(TEMP_SIZE); + TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter); + HiresTextures::Init(globals->unique_id); +} + +void TextureCache::Invalidate(bool shutdown) +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + for (; iter != tcend; ++iter) + { + if (shutdown) + iter->second->addr = 0; + delete iter->second; + } + + textures.clear(); + HiresTextures::Shutdown(); +} + +TextureCache::~TextureCache() +{ + Invalidate(true); + FreeMemoryPages(temp, TEMP_SIZE); + temp = NULL; +} + +void TextureCache::Cleanup() +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + while (iter != tcend) + { + if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second->frameCount) + { + delete iter->second; + textures.erase(iter++); + } + else + ++iter; + } +} + +void TextureCache::InvalidateRange(u32 start_address, u32 size) +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + while (iter != tcend) + { + const int rangePosition = iter->second->IntersectsMemoryRange(start_address, size); + if (0 == rangePosition) + { + delete iter->second; + textures.erase(iter++); + } + else + ++iter; + } +} + +void TextureCache::MakeRangeDynamic(u32 start_address, u32 size) +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + for (; iter != tcend; ++iter) + { + const int rangePosition = iter->second->IntersectsMemoryRange(start_address, size); + if (0 == rangePosition) + { + iter->second->hash = 0; + } + } +} + +int TextureCache::TCacheEntryBase::IntersectsMemoryRange(u32 range_address, u32 range_size) const +{ + if (addr + size_in_bytes < range_address) + return -1; + + if (addr >= range_address + range_size) + return 1; + + return 0; +} + +void TextureCache::ClearRenderTargets() +{ + TexCache::iterator + iter = textures.begin(), + tcend = textures.end(); + for (; iter!=tcend; ++iter) + iter->second->isRenderTarget = false; +} + +TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, + u32 address, unsigned int width, unsigned int height, int texformat, + unsigned int tlutaddr, int tlutfmt, bool UseNativeMips, unsigned int maxlevel) +{ + // necessary? + if (0 == address) + return NULL; + + u8* ptr = g_VideoInitialize.pGetMemoryPointer(address); + + // TexelSizeInNibbles(format)*width*height/16; + const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat) - 1; + const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat) - 1; + + unsigned int expandedWidth = (width + bsw) & (~bsw); + unsigned int expandedHeight = (height + bsh) & (~bsh); + + u64 hash_value = 0; + u64 texHash = 0; + u32 texID = address; + u32 full_format = texformat; + const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); + const u32 palette_size = TexDecoder_GetPaletteSize(texformat); + bool texture_is_dynamic = false; + + // someone who understands this var could rename it :p + const bool isC4_C8_C14X2 = (texformat == GX_TF_C4 || texformat == GX_TF_C8 || texformat == GX_TF_C14X2); + + if (isC4_C8_C14X2) + full_format = texformat | (tlutfmt << 16); + + // hires texture loading and texture dumping require accurate hashes + if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures) + { + texHash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); + + if (isC4_C8_C14X2) + { + // WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up) + // tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower. + // This trick (to change the texID depending on the TLUT addr) is a trick to get around + // an issue with metroid prime's fonts, where it has multiple sets of fonts on top of + // each other stored in a single texture, and uses the palette to make different characters + // visible or invisible. Thus, unless we want to recreate the textures for every drawn character, + // we must make sure that texture with different tluts get different IDs. + + const u64 tlutHash = GetHash64(texMem + tlutaddr, palette_size, + g_ActiveConfig.iSafeTextureCache_ColorSamples); + + texHash ^= tlutHash; + + if (g_ActiveConfig.bSafeTextureCache) + texID ^= ((u32)tlutHash) ^ (tlutHash >> 32); + } + + if (g_ActiveConfig.bSafeTextureCache) + hash_value = texHash; + } + + TCacheEntryBase *entry = textures[texID]; + if (entry) + { + if (false == g_ActiveConfig.bSafeTextureCache) + { + if (entry->isRenderTarget || entry->isDynamic) + { + if (false == g_ActiveConfig.bCopyEFBToTexture) + { + hash_value = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); + + if (isC4_C8_C14X2) + { + hash_value ^= GetHash64(&texMem[tlutaddr], palette_size, + g_ActiveConfig.iSafeTextureCache_ColorSamples); + } + } + else + { + hash_value = 0; + } + } + else + { + hash_value = *(u32*)ptr; + } + } + else if ((entry->isRenderTarget || entry->isDynamic) && g_ActiveConfig.bCopyEFBToTexture) + { + hash_value = 0; + } + + // TODO: Is the mipLevels check needed? + if (((entry->isRenderTarget || entry->isDynamic) && hash_value == entry->hash && address == entry->addr) + || ((address == entry->addr) && (hash_value == entry->hash) && full_format == entry->format && entry->mipLevels == maxlevel)) + { + entry->isDynamic = false; + goto return_entry; + } + else + { + // Let's reload the new texture data into the same texture, + // instead of destroying it and having to create a new one. + // Might speed up movie playback very, very slightly. + texture_is_dynamic = (entry->isRenderTarget || entry->isDynamic) && !g_ActiveConfig.bCopyEFBToTexture; + + // TODO: Is the mipLevels check needed? + if (!entry->isRenderTarget && + ((!entry->isDynamic && width == entry->w && height == entry->h && full_format == entry->format && entry->mipLevels == maxlevel) + || (entry->isDynamic && entry->w == width && entry->h == height))) + { + // reuse the texture + } + else + { + // delete the texture and make a new one + delete entry; + entry = NULL; + } + } + } + + const unsigned int nativeW = width; + const unsigned int nativeH = height; + + PC_TexFormat pcfmt = PC_TEX_FMT_NONE; + + if (g_ActiveConfig.bHiresTextures) + { + // Load Custom textures + char texPathTemp[MAX_PATH]; + + unsigned int newWidth = width; + unsigned int newHeight = height; + + sprintf(texPathTemp, "%s_%08x_%i", globals->unique_id, texHash, texformat); + pcfmt = HiresTextures::GetHiresTex(texPathTemp, &newWidth, &newHeight, texformat, temp); + + if (pcfmt != PC_TEX_FMT_NONE) + { + expandedWidth = width = newWidth; + expandedHeight = height = newHeight; + + // TODO: shouldn't generating mips be forced on now? + // native mips with a custom texture wouldn't make sense + } + } + + if (pcfmt == PC_TEX_FMT_NONE) + pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, + expandedHeight, texformat, tlutaddr, tlutfmt, !g_texture_cache->isOGL()); + + const bool isPow2 = !((width & (width - 1)) || (height & (height - 1))); + unsigned int texLevels = (isPow2 && UseNativeMips && maxlevel) ? + GetPow2(std::max(width, height)) : !isPow2; + + if ((texLevels > (maxlevel + 1)) && maxlevel) + texLevels = maxlevel + 1; + + // create the entry/texture + if (NULL == entry) + { + textures[texID] = entry = g_texture_cache->CreateTexture(width, height, expandedWidth, texLevels, pcfmt); + + entry->addr = address; + entry->format = full_format; + entry->mipLevels = maxlevel; + entry->size_in_bytes = texture_size; + + entry->scaledW = entry->w = width; + entry->scaledH = entry->h = height; + + entry->nativeH = nativeH; + entry->nativeW = nativeW; + + entry->isRenderTarget = false; + entry->isNonPow2 = false; + entry->isDynamic = texture_is_dynamic; + + entry->oldpixel = *(u32*)ptr; + + if (g_ActiveConfig.bSafeTextureCache || entry->isDynamic) + entry->hash = hash_value; + else + // don't like rand() here + entry->hash = *(u32*)ptr = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF); + } + + // load texture + entry->Load(width, height, expandedWidth, 0); + + // load mips + if (texLevels > 1 && pcfmt != PC_TEX_FMT_NONE) + { + const unsigned int bsdepth = TexDecoder_GetTexelSizeInNibbles(texformat); + + unsigned int level = 1; + unsigned int mipWidth = (width + 1) >> 1; + unsigned int mipHeight = (height + 1) >> 1; + ptr += texture_size; + + while ((mipHeight || mipWidth) && (level < texLevels)) + { + const unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1; + const unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1; + + expandedWidth = (currentWidth + bsw) & (~bsw); + expandedHeight = (currentHeight + bsh) & (~bsh); + + TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, texformat, tlutaddr, tlutfmt, !g_texture_cache->isOGL()); + entry->Load(currentWidth, currentHeight, expandedWidth, level); + + ptr += ((std::max(mipWidth, bsw) * std::max(mipHeight, bsh) * bsdepth) >> 1); + mipWidth >>= 1; + mipHeight >>= 1; + ++level; + } + } + + // TODO: won't this cause loaded hires textures to be dumped as well? + // dump texture to file + if (g_ActiveConfig.bDumpTextures) + { + char szTemp[MAX_PATH]; + char szDir[MAX_PATH]; + + // make sure that the directory exists + sprintf(szDir, "%s%s", File::GetUserPath(D_DUMPTEXTURES_IDX), globals->unique_id); + if (false == File::Exists(szDir) || false == File::IsDirectory(szDir)) + File::CreateDir(szDir); + + sprintf(szTemp, "%s/%s_%08x_%i.png", szDir, globals->unique_id, texHash, texformat); + + if (false == File::Exists(szTemp)) + entry->Save(szTemp); + } + + INCSTAT(stats.numTexturesCreated); + SETSTAT(stats.numTexturesAlive, textures.size()); + +return_entry: + + entry->frameCount = frameCount; + entry->Bind(stage); + + return entry; +} + +void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, + bool bIsIntensityFmt, u32 copyfmt, bool bScaleByHalf, const EFBRectangle &source_rect) +{ + DVSTARTPROFILE(); + + float colmat[20] = {}; + // last four floats of colmat for fConstAdd + float *const fConstAdd = colmat + 16; + unsigned int cbufid = -1; + + if (bFromZBuffer) + { + // TODO: these values differ slightly from the DX9/11 values, + // do they need to? or can this be removed + if (g_texture_cache->isOGL()) + { + switch(copyfmt) + { + case 0: // Z4 + case 1: // Z8 + colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1; + break; + + case 3: // Z16 //? + colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1; + break; + + case 11: // Z16 (reverse order) + colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1; + break; + + case 6: // Z24X8 + colmat[2] = colmat[5] = colmat[8] = colmat[15] = 1; + break; + + case 9: // Z8M + colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1; + break; + + case 10: // Z8L + colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1; + break; + + case 12: // Z16L + colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1; + break; + + default: + ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt); + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + break; + } + } + else + { + switch (copyfmt) + { + case 0: // Z4 + case 1: // Z8 + colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1.0f; + cbufid = 12; + break; + + case 3: // Z16 //? + colmat[1] = colmat[5] = colmat[9] = colmat[12] = 1.0f; + cbufid = 13; + break; + + case 11: // Z16 (reverse order) + colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1.0f; + cbufid = 14; + break; + + case 6: // Z24X8 + colmat[0] = colmat[5] = colmat[10] = 1.0f; + cbufid = 15; + break; + + case 9: // Z8M + colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1.0f; + cbufid = 16; + break; + + case 10: // Z8L + colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1.0f; + cbufid = 17; + break; + + case 12: // Z16L + colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1.0f; + cbufid = 18; + break; + + default: + ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt); + colmat[2] = colmat[5] = colmat[8] = 1.0f; + cbufid = 19; + break; + } + } + } + else if (bIsIntensityFmt) + { + fConstAdd[0] = fConstAdd[1] = fConstAdd[2] = 16.0f/255.0f; + switch (copyfmt) + { + case 0: // I4 + case 1: // I8 + case 2: // IA4 + case 3: // IA8 + // TODO - verify these coefficients + colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f; + colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f; + colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f; + + if (copyfmt < 2) + { + fConstAdd[3] = 16.0f / 255.0f; + colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f; + cbufid = 0; + } + else// alpha + { + colmat[15] = 1; + cbufid = 1; + } + + break; + + default: + ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%x", copyfmt); + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + break; + } + } + else + { + switch (copyfmt) + { + case 0: // R4 + case 8: // R8 + colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1; + cbufid = 2; + break; + + case 2: // RA4 + case 3: // RA8 + colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1; + cbufid = 3; + break; + + case 7: // A8 + colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1; + cbufid = 4; + break; + + case 9: // G8 + colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1; + cbufid = 5; + break; + + case 10: // B8 + colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1; + cbufid = 6; + break; + + case 11: // RG8 + colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1; + cbufid = 7; + break; + + case 12: // GB8 + colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1; + cbufid = 8; + break; + + case 4: // RGB565 + colmat[0] = colmat[5] = colmat[10] = 1; + fConstAdd[3] = 1; // set alpha to 1 + cbufid = 9; + break; + + case 5: // RGB5A3 + case 6: // RGBA8 + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + cbufid = 10; + break; + + default: + ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt); + colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; + cbufid = 11; + break; + } + } + + const unsigned int tex_w = (abs(source_rect.GetWidth()) >> (int)bScaleByHalf); + const unsigned int tex_h = (abs(source_rect.GetHeight()) >> (int)bScaleByHalf); + + const float xScale = Renderer::GetTargetScaleX(); + const float yScale = Renderer::GetTargetScaleY(); + + unsigned int scaled_tex_w = g_ActiveConfig.bCopyEFBScaled ? (int)(tex_w * xScale) : tex_w; + unsigned int scaled_tex_h = g_ActiveConfig.bCopyEFBScaled ? (int)(tex_h * yScale) : tex_h; + + bool texture_is_dynamic = false; + + TCacheEntryBase *entry = textures[address]; + if (entry) + { + if ((entry->isRenderTarget && entry->scaledW == scaled_tex_w && entry->scaledH == scaled_tex_h) + || (entry->isDynamic && entry->w == tex_w && entry->h == tex_h)) + { + texture_is_dynamic = entry->isDynamic; + } + else + { + // remove it and recreate it as a render target + delete entry; + entry = NULL; + } + } + + if (texture_is_dynamic) + { + scaled_tex_w = tex_w; + scaled_tex_h = tex_h; + } + + if (NULL == entry) + { + // create the texture + textures[address] = entry = g_texture_cache->CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h); + + entry->addr = address; + entry->hash = 0; + + entry->w = entry->nativeW = tex_w; + entry->h = entry->nativeH = tex_h; + + entry->scaledW = scaled_tex_w; + entry->scaledH = scaled_tex_h; + + entry->format = copyfmt; + entry->mipLevels = 0; + + entry->isRenderTarget = true; + entry->isNonPow2 = true; + entry->isDynamic = false; + } + + entry->frameCount = frameCount; + + Renderer::ResetAPIState(); // reset any game specific settings + + entry->FromRenderTarget(bFromZBuffer, bScaleByHalf, cbufid, colmat, source_rect, bIsIntensityFmt, copyfmt); + + Renderer::RestoreAPIState(); +} diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h new file mode 100644 index 0000000000..3db40c7164 --- /dev/null +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -0,0 +1,99 @@ + +#ifndef _TEXTURECACHEBASE_H +#define _TEXTURECACHEBASE_H + +#include + +//#include "VideoCommon.h" +#include "TextureDecoder.h" +#include "BPMemory.h" + +#include "CommonTypes.h" + +class TextureCache +{ +public: + struct TCacheEntryBase + { + // TODO: organize + u32 addr; + u32 size_in_bytes; + u64 hash; + //u32 paletteHash; + u32 oldpixel; + u32 format; + + int frameCount; + unsigned int w, h, mipLevels; + // TODO: it looks like scaledW/H can be removed and w/h can be used in their place + unsigned int scaledW, scaledH, nativeW, nativeH; + + bool isRenderTarget; + bool isDynamic; // mofified from cpu + bool isNonPow2; // doesn't seem to be used anywhere + + //TCacheEntryBase() + //{ + // // TODO: remove these + // isRenderTarget = 0; + // hash = 0; + // //paletteHash = 0; + // oldpixel = 0; + // addr = 0; + // size_in_bytes = 0; + // frameCount = 0; + // isNonPow2 = true; + // w = 0; + // h = 0; + // scaledW = 0; + // scaledH = 0; + //} + + virtual ~TCacheEntryBase(); + + virtual void Bind(unsigned int stage) = 0; + virtual bool Save(const char filename[]) = 0; + + virtual void Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level) = 0; + virtual void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float *colmat, const EFBRectangle &source_rect, + bool bIsIntensityFmt, u32 copyfmt) = 0; + + int IntersectsMemoryRange(u32 range_address, u32 range_size) const; + }; + + virtual ~TextureCache(); // needs virtual for DX11 dtor + + static void Cleanup(); + + static void Invalidate(bool shutdown); + static void InvalidateRange(u32 start_address, u32 size); + static void MakeRangeDynamic(u32 start_address, u32 size); + static void ClearRenderTargets(); // currently only used by OGL + + virtual TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) = 0; + virtual TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) = 0; + + static TCacheEntryBase* Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, + int format, unsigned int tlutaddr, int tlutfmt, bool UseNativeMips, unsigned int maxlevel); + static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, + u32 copyfmt, bool bScaleByHalf, const EFBRectangle &source_rect); + +protected: + TextureCache(); + + static u8 *temp; + +private: + typedef std::map TexCache; + + static TexCache textures; + + virtual bool isOGL() { return false; } // Hacks for TextureDecode_real support +}; + +extern TextureCache *g_texture_cache; + +#endif diff --git a/Source/Core/VideoCommon/Src/VertexManagerBase.cpp b/Source/Core/VideoCommon/Src/VertexManagerBase.cpp index 2c1f3ce26b..b41c2ab30f 100644 --- a/Source/Core/VideoCommon/Src/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/Src/VertexManagerBase.cpp @@ -4,6 +4,12 @@ #include "Statistics.h" #include "OpcodeDecoding.h" #include "IndexGenerator.h" +#include "VertexShaderManager.h" +#include "PixelShaderManager.h" +#include "NativeVertexFormat.h" +#include "TextureCacheBase.h" +#include "Render.h" +#include "Profiler.h" #include "VertexManagerBase.h" @@ -151,8 +157,158 @@ void VertexManager::AddVertices(int primitive, int numVertices) AddIndices(primitive, numVertices); } -// TODO: merge this func, (need to merge TextureCache) void VertexManager::Flush() { g_vertex_manager->vFlush(); } + +// TODO: need to merge more stuff into VideoCommon to use this +#if (0) +void VertexManager::Flush() +{ + if (LocalVBuffer == s_pCurBufferPointer || Flushed) + return; + + Flushed = true; + + VideoFifo_CheckEFBAccess(); + +#if defined(_DEBUG) || defined(DEBUGFAST) + PRIM_LOG("frame%d:\n texgen=%d, numchan=%d, dualtex=%d, ztex=%d, cole=%d, alpe=%d, ze=%d", g_ActiveConfig.iSaveTargetId, xfregs.numTexGens, + xfregs.nNumChans, (int)xfregs.bEnableDualTexTransform, bpmem.ztex2.op, + bpmem.blendmode.colorupdate, bpmem.blendmode.alphaupdate, bpmem.zmode.updateenable); + + for (int i = 0; i < xfregs.nNumChans; ++i) + { + LitChannel* ch = &xfregs.colChans[i].color; + PRIM_LOG("colchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d", i, ch->matsource, ch->GetFullLightMask(), ch->ambsource, ch->diffusefunc, ch->attnfunc); + ch = &xfregs.colChans[i].alpha; + PRIM_LOG("alpchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d", i, ch->matsource, ch->GetFullLightMask(), ch->ambsource, ch->diffusefunc, ch->attnfunc); + } + + for (int i = 0; i < xfregs.numTexGens; ++i) + { + TexMtxInfo tinfo = xfregs.texcoords[i].texmtxinfo; + if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP) tinfo.hex &= 0x7ff; + if (tinfo.texgentype != XF_TEXGEN_REGULAR) tinfo.projection = 0; + + PRIM_LOG("txgen%d: proj=%d, input=%d, gentype=%d, srcrow=%d, embsrc=%d, emblght=%d, postmtx=%d, postnorm=%d", + i, tinfo.projection, tinfo.inputform, tinfo.texgentype, tinfo.sourcerow, tinfo.embosssourceshift, tinfo.embosslightshift, + xfregs.texcoords[i].postmtxinfo.index, xfregs.texcoords[i].postmtxinfo.normalize); + } + + PRIM_LOG("pixel: tev=%d, ind=%d, texgen=%d, dstalpha=%d, alphafunc=0x%x", bpmem.genMode.numtevstages+1, bpmem.genMode.numindstages, + bpmem.genMode.numtexgens, (u32)bpmem.dstalpha.enable, (bpmem.alphaFunc.hex>>16)&0xff); +#endif + + DVSTARTPROFILE(); + + // set the textures + DVSTARTSUBPROFILE("VertexManager::Flush:textures"); + + u32 usedtextures = 0; + for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i) + if (bpmem.tevorders[i / 2].getEnable(i & 1)) + usedtextures |= 1 << bpmem.tevorders[i/2].getTexMap(i & 1); + + if (bpmem.genMode.numindstages > 0) + for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i) + if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) + usedtextures |= 1 << bpmem.tevindref.getTexMap(bpmem.tevind[i].bt); + + for (u32 i = 0; i < 8; ++i) + { + if (usedtextures & (1 << i)) + { + // TODO: + //glActiveTexture(GL_TEXTURE0 + i); + + Renderer::SetSamplerState(i & 3, i >> 2); + FourTexUnits &tex = bpmem.tex[i >> 2]; + + TCacheEntry::TCacheEntryBase* tentry = TextureCache::Load(i, + (tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5, + tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1, + tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9, + tex.texTlut[i&3].tlut_format, + (tex.texMode0[i&3].min_filter & 3) && (tex.texMode0[i&3].min_filter != 8) && g_ActiveConfig.bUseNativeMips, + (tex.texMode1[i&3].max_lod >> 4)); + + if (tentry) + { + // 0s are probably for no manual wrapping needed. + PixelShaderManager::SetTexDims(i, tentry->nativeW, tentry->nativeH, 0, 0); + + // TODO: + //if (g_ActiveConfig.iLog & CONF_SAVETEXTURES) + //{ + // // save the textures + // char strfile[255]; + // sprintf(strfile, "%stex%.3d_%d.tga", File::GetUserPath(D_DUMPFRAMES_IDX), g_Config.iSaveTargetId, i); + // SaveTexture(strfile, GL_TEXTURE_2D, tentry->texture, tentry->w, tentry->h); + //} + } + else + ERROR_LOG(VIDEO, "error loading texture"); + } + } + + // set global constants + VertexShaderManager::SetConstants(); + PixelShaderManager::SetConstants(); + + // finally bind + if (false == PixelShaderCache::SetShader(false, g_nativeVertexFmt->m_components)) + goto shader_fail; + if (false == VertexShaderCache::SetShader(g_nativeVertexFmt->m_components)) + goto shader_fail; + + const int stride = g_nativeVertexFmt->GetVertexStride(); + //if (g_nativeVertexFmt) + g_nativeVertexFmt->SetupVertexPointers(); + + g_vertex_manager->Draw(stride, false); + + // run through vertex groups again to set alpha + if (false == g_ActiveConfig.bDstAlphaPass && bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate) + { + if (false == PixelShaderCache::SetShader(true, g_nativeVertexFmt->m_components)) + goto shader_fail; + + g_vertex_manager->Draw(stride, true); + } + + // TODO: + //IndexGenerator::Start(TIBuffer, LIBuffer, PIBuffer); + +#if defined(_DEBUG) || defined(DEBUGFAST) + if (g_ActiveConfig.iLog & CONF_SAVESHADERS) + { + // save the shaders + char strfile[255]; + sprintf(strfile, "%sps%.3d.txt", File::GetUserPath(D_DUMPFRAMES_IDX), g_ActiveConfig.iSaveTargetId); + std::ofstream fps(strfile); + fps << ps->strprog.c_str(); + sprintf(strfile, "%svs%.3d.txt", File::GetUserPath(D_DUMPFRAMES_IDX), g_ActiveConfig.iSaveTargetId); + std::ofstream fvs(strfile); + fvs << vs->strprog.c_str(); + } + + if (g_ActiveConfig.iLog & CONF_SAVETARGETS) + { + char str[128]; + sprintf(str, "%starg%.3d.tga", File::GetUserPath(D_DUMPFRAMES_IDX), g_ActiveConfig.iSaveTargetId); + TargetRectangle tr; + tr.left = 0; + tr.right = Renderer::GetTargetWidth(); + tr.top = 0; + tr.bottom = Renderer::GetTargetHeight(); + Renderer::SaveRenderTarget(str, tr); + } +#endif + ++g_Config.iSaveTargetId; + +shader_fail: + ResetBuffer(); +} +#endif diff --git a/Source/Core/VideoCommon/Src/VertexManagerBase.h b/Source/Core/VideoCommon/Src/VertexManagerBase.h index e1982e5d06..2108172977 100644 --- a/Source/Core/VideoCommon/Src/VertexManagerBase.h +++ b/Source/Core/VideoCommon/Src/VertexManagerBase.h @@ -48,8 +48,10 @@ protected: private: static void AddIndices(int primitive, int numVertices); - // temporary + //virtual void Draw(u32 stride, bool alphapass) = 0; + // temp virtual void vFlush() = 0; + }; extern VertexManager *g_vertex_manager; diff --git a/Source/Core/VideoCommon/VideoCommon.vcproj b/Source/Core/VideoCommon/VideoCommon.vcproj index f74939b9da..5a932c3363 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcproj +++ b/Source/Core/VideoCommon/VideoCommon.vcproj @@ -730,6 +730,14 @@ + + + + diff --git a/Source/Plugins/Plugin_VideoDX11/Src/BPFunctions.cpp b/Source/Plugins/Plugin_VideoDX11/Src/BPFunctions.cpp index 2e3b41351a..2fa0a862be 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/BPFunctions.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/BPFunctions.cpp @@ -80,7 +80,7 @@ void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const // bpmem.zcontrol.pixel_format to PIXELFMT_Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format) if (!g_ActiveConfig.bEFBCopyDisable) { - TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc); + TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, !!scaleByHalf, rc); } } diff --git a/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.cpp b/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.cpp index 448d7a6975..86a27a44e6 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.cpp @@ -40,39 +40,143 @@ #include "TextureCache.h" #include "HiresTextures.h" +namespace DX11 +{ + ID3D11BlendState* efbcopyblendstate = NULL; ID3D11RasterizerState* efbcopyraststate = NULL; ID3D11DepthStencilState* efbcopydepthstate = NULL; -ID3D11Buffer* efbcopycbuf[20] = { NULL }; +ID3D11Buffer* efbcopycbuf[20] = {}; -u8* TextureCache::temp = NULL; -TextureCache::TexCache TextureCache::textures; - -extern int frameCount; - -#define TEMP_SIZE (2048*2048*4) -#define TEXTURE_KILL_THRESHOLD 200 - -void TextureCache::TCacheEntry::Destroy(bool shutdown) +TextureCache::TCacheEntry::~TCacheEntry() { - SAFE_RELEASE(texture); - - if (!isRenderTarget && !shutdown && !g_ActiveConfig.bSafeTextureCache) - { - u32 *ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr); - if (ptr && *ptr == hash) - *ptr = oldpixel; - } + texture->Release(); } -void TextureCache::Init() +void TextureCache::TCacheEntry::Bind(unsigned int stage) +{ + D3D::gfxstate->SetShaderResource(stage, texture->GetSRV()); +} + +bool TextureCache::TCacheEntry::Save(const char filename[]) +{ + return SUCCEEDED(PD3DX11SaveTextureToFileA(D3D::context, texture->GetTex(), D3DX11_IFF_PNG, filename)); +} + +void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level) +{ + // TODO: remove hax + if (level != 0) return; + + D3D::ReplaceRGBATexture2D(texture->GetTex(), TextureCache::temp, width, height, expanded_width, level, usage); +} + +TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, + unsigned int height, unsigned int expanded_width, + unsigned int tex_levels, PC_TexFormat pcfmt) +{ + D3D11_USAGE usage = D3D11_USAGE_DEFAULT; + D3D11_CPU_ACCESS_FLAG cpu_access = (D3D11_CPU_ACCESS_FLAG)0; + D3D11_SUBRESOURCE_DATA srdata, *data = NULL; + + // TODO: remove hax + tex_levels = 1; + + if (1 == tex_levels) + { + usage = D3D11_USAGE_DYNAMIC; + cpu_access = D3D11_CPU_ACCESS_WRITE; + + srdata.pSysMem = TextureCache::temp; + srdata.SysMemPitch = 4 * expanded_width; + // testing + //data = &srdata; + } + + const D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, + width, height, 1, tex_levels, D3D11_BIND_SHADER_RESOURCE, usage, cpu_access); + + ID3D11Texture2D *pTexture; + const HRESULT hr = D3D::device->CreateTexture2D(&texdesc, data, &pTexture); + CHECK(SUCCEEDED(hr), "Create texture of the TextureCache"); + + TCacheEntry* const entry = new TCacheEntry(new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE)); + entry->usage = usage; + + // TODO: better debug names + D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetTex(), "a texture of the TextureCache"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetSRV(), "shader resource view of a texture of the TextureCache"); + + SAFE_RELEASE(pTexture); + + if (0 == tex_levels) + PD3DX11FilterTexture(D3D::context, entry->texture->GetTex(), 0, D3DX11_DEFAULT); + + return entry; +} + +void TextureCache::TCacheEntry::FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect, + bool bIsIntensityFmt, u32 copyfmt) +{ + // stretch picture with increased internal resolution + const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)scaledW, (float)scaledH); + D3D::context->RSSetViewports(1, &vp); + + // set transformation + if (NULL == efbcopycbuf[cbufid]) + { + const D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(20 * sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT); + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = colmat; + HRESULT hr = D3D::device->CreateBuffer(&cbdesc, &data, &efbcopycbuf[cbufid]); + CHECK(SUCCEEDED(hr), "Create efb copy constant buffer %d", cbufid); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopycbuf[cbufid], "a constant buffer used in TextureCache::CopyRenderTargetToTexture"); + } + D3D::context->PSSetConstantBuffers(0, 1, &efbcopycbuf[cbufid]); + + const TargetRectangle targetSource = Renderer::ConvertEFBRectangle(source_rect); + // TODO: try targetSource.asRECT(); + const D3D11_RECT sourcerect = CD3D11_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom); + + // Use linear filtering if (bScaleByHalf), use point filtering otherwise + if (bScaleByHalf) + D3D::SetLinearCopySampler(); + else + D3D::SetPointCopySampler(); + + D3D::stateman->PushBlendState(efbcopyblendstate); + D3D::stateman->PushRasterizerState(efbcopyraststate); + D3D::stateman->PushDepthState(efbcopydepthstate); + + D3D::context->OMSetRenderTargets(1, &texture->GetRTV(), NULL); + + D3D::drawShadedTexQuad( + (bFromZBuffer) ? g_framebufferManager.GetEFBDepthTexture()->GetSRV() : g_framebufferManager.GetEFBColorTexture()->GetSRV(), + &sourcerect, Renderer::GetFullTargetWidth(), Renderer::GetFullTargetHeight(), + (bFromZBuffer) ? PixelShaderCache::GetDepthMatrixProgram() : PixelShaderCache::GetColorMatrixProgram(), + VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout()); + + D3D::context->OMSetRenderTargets(1, &g_framebufferManager.GetEFBColorTexture()->GetRTV(), g_framebufferManager.GetEFBDepthTexture()->GetDSV()); + + D3D::stateman->PopBlendState(); + D3D::stateman->PopDepthState(); + D3D::stateman->PopRasterizerState(); +} + +TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture( + unsigned int scaled_tex_w, unsigned int scaled_tex_h) +{ + return new TCacheEntry(D3DTexture2D::Create(scaled_tex_w, scaled_tex_h, + (D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET | (int)D3D11_BIND_SHADER_RESOURCE), + D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM)); +} + +TextureCache::TextureCache() { HRESULT hr; - temp = (u8*)AllocateMemoryPages(TEMP_SIZE); - TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter); - HiresTextures::Init(globals->unique_id); - D3D11_BLEND_DESC blenddesc; blenddesc.AlphaToCoverageEnable = FALSE; blenddesc.IndependentBlendEnable = FALSE; @@ -85,8 +189,8 @@ void TextureCache::Init() blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; hr = D3D::device->CreateBlendState(&blenddesc, &efbcopyblendstate); - CHECK(hr==S_OK, "Create blend state for TextureCache::CopyRenderTargetToTexture"); - D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyblendstate, "blend state used in TextureCache::CopyRenderTargetToTexture"); + CHECK(hr==S_OK, "Create blend state for CopyRenderTargetToTexture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyblendstate, "blend state used in CopyRenderTargetToTexture"); D3D11_DEPTH_STENCIL_DESC depthdesc; depthdesc.DepthEnable = FALSE; @@ -96,8 +200,8 @@ void TextureCache::Init() depthdesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; depthdesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; hr = D3D::device->CreateDepthStencilState(&depthdesc, &efbcopydepthstate); - CHECK(hr==S_OK, "Create depth state for TextureCache::CopyRenderTargetToTexture"); - D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopydepthstate, "depth stencil state used in TextureCache::CopyRenderTargetToTexture"); + CHECK(hr==S_OK, "Create depth state for CopyRenderTargetToTexture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopydepthstate, "depth stencil state used in CopyRenderTargetToTexture"); D3D11_RASTERIZER_DESC rastdesc; rastdesc.CullMode = D3D11_CULL_NONE; @@ -111,528 +215,18 @@ void TextureCache::Init() rastdesc.MultisampleEnable = false; rastdesc.AntialiasedLineEnable = false; hr = D3D::device->CreateRasterizerState(&rastdesc, &efbcopyraststate); - CHECK(hr==S_OK, "Create rasterizer state for TextureCache::CopyRenderTargetToTexture"); - D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyraststate, "rasterizer state used in TextureCache::CopyRenderTargetToTexture"); + CHECK(hr==S_OK, "Create rasterizer state for CopyRenderTargetToTexture"); + D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopyraststate, "rasterizer state used in CopyRenderTargetToTexture"); } -void TextureCache::Invalidate(bool shutdown) +TextureCache::~TextureCache() { - for (TexCache::iterator iter = textures.begin(); iter != textures.end(); ++iter) - iter->second.Destroy(shutdown); - textures.clear(); - HiresTextures::Shutdown(); -} - -void TextureCache::Shutdown() -{ - Invalidate(true); - FreeMemoryPages(temp, TEMP_SIZE); - temp = NULL; - SAFE_RELEASE(efbcopyblendstate); SAFE_RELEASE(efbcopyraststate); SAFE_RELEASE(efbcopydepthstate); - for (unsigned int k = 0; k < 20;k++) + for (unsigned int k = 0; k < 20; ++k) SAFE_RELEASE(efbcopycbuf[k]); } -void TextureCache::Cleanup() -{ - TexCache::iterator iter = textures.begin(); - while (iter != textures.end()) - { - if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second.frameCount) - { - iter->second.Destroy(false); - iter = textures.erase(iter); - } - else - { - ++iter; - } - } -} - -void TextureCache::InvalidateRange(u32 start_address, u32 size) -{ - TexCache::iterator iter = textures.begin(); - while (iter != textures.end()) - { - if (iter->second.IntersectsMemoryRange(start_address, size)) - { - iter->second.Destroy(false); - textures.erase(iter++); - } - else - { - ++iter; - } - } -} - -bool TextureCache::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size) -{ - if (addr + size_in_bytes < range_address) return false; - if (addr >= range_address + range_size) return false; - return true; -} - - -// returns the exponent of the smallest power of two which is greater than val -unsigned int GetPow2(unsigned int val) -{ - unsigned int ret = 0; - for (;val;val>>=1) ret++; - return ret; -} - -TextureCache::TCacheEntry* TextureCache::Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, unsigned int tex_format, unsigned int tlutaddr, unsigned int tlutfmt, bool UseNativeMips, unsigned int maxlevel) -{ - // notes (about "UNsafe texture cache"): - // Have to be removed soon. - // But we keep it until the "safe" way became rock solid - // pros: it has an unique ID held by the texture data itself (@address) once cached. - // cons: it writes this unique ID in the gc RAM <- very dangerous (break MP1) and ugly - - // notes (about "safe texture cache"): - // Metroids text issue (character table): - // Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs). - // That's why we have to hash the TLUT too for TLUT tex_format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2). - // And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but - // have to be a real unique ID. - // DONE but not satifiying yet -> may break copyEFBToTexture sometimes. - - // Pokemon Colosseum text issue (plain text): - // Use a GX_TF_I4 512x512 text-flush-texture at a const address. - // The problem here was just the sparse hash on the texture. This texture is partly overwrited (what is needed only) - // so lot's of remaning old text. Thin white chars on black bg too. - - // TODO: - clean this up when ready to kill old "unsafe texture cache" - // - fix the key index situation with CopyRenderTargetToTexture. - // Could happen only for GX_TF_C4, GX_TF_C8 and GX_TF_C14X2 fmt. - // Wonder if we can't use tex width&height to know if EFB might be copied to it... - // raw idea: TOCHECK if addresses are aligned we have few bits left... - - if (address == 0) - return NULL; - - u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address); - unsigned int bsw = TexDecoder_GetBlockWidthInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16; - unsigned int bsh = TexDecoder_GetBlockHeightInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16; - unsigned int bsdepth = TexDecoder_GetTexelSizeInNibbles(tex_format); - unsigned int expandedWidth = (width + bsw) & (~bsw); - unsigned int expandedHeight = (height + bsh) & (~bsh); - - u64 hash_value = 0; - u32 texID = address; - u64 texHash = 0; - u32 FullFormat = tex_format; - if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) - FullFormat = (tex_format | (tlutfmt << 16)); - - // hires texture loading and texture dumping require accurate hashes - if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures) - { - texHash = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) - { - // WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up) - // tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower. - // This trick (to change the texID depending on the TLUT addr) is a trick to get around - // an issue with metroid prime's fonts, where it has multiple sets of fonts on top of - // each other stored in a single texture, and uses the palette to make different characters - // visible or invisible. Thus, unless we want to recreate the textures for every drawn character, - // we must make sure that texture with different tluts get different IDs. - u64 tlutHash = GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - texHash ^= tlutHash; - if (g_ActiveConfig.bSafeTextureCache) - { - texID = texID ^ ((u32)(tlutHash & 0xFFFFFFFF)) ^ ((u32)((tlutHash >> 32) & 0xFFFFFFFF)); - } - } - if (g_ActiveConfig.bSafeTextureCache) - hash_value = texHash; - } - - bool skip_texture_create = false; - TexCache::iterator iter = textures.find(texID); - - if (iter != textures.end()) - { - TCacheEntry &entry = iter->second; - - if (!g_ActiveConfig.bSafeTextureCache) - hash_value = ((u32*)ptr)[0]; - - // TODO: Is the (entry.MipLevels == maxlevel) check needed? - if (entry.isRenderTarget || ((address == entry.addr) && (hash_value == entry.hash) && FullFormat == entry.fmt && entry.MipLevels == maxlevel)) - { - entry.frameCount = frameCount; - D3D::gfxstate->SetShaderResource(stage, entry.texture->GetSRV()); - return &entry; - } - else - { - // Let's reload the new texture data into the same texture, - // instead of destroying it and having to create a new one. - // Might speed up movie playback very, very slightly. - - // TODO: Is the (entry.MipLevels < maxlevel) check needed? - if (width == entry.w && height==entry.h && FullFormat == entry.fmt && entry.MipLevels < maxlevel) - { - skip_texture_create = true; - } - else - { - entry.Destroy(false); - textures.erase(iter); - } - } - } - - // Make an entry in the table - TCacheEntry& entry = textures[texID]; - entry.Realw = width; - entry.Realh = height; - PC_TexFormat pcfmt = PC_TEX_FMT_NONE; - - pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt, true); - if (g_ActiveConfig.bHiresTextures) - { - // Load Custom textures - char texPathTemp[MAX_PATH]; - int newWidth = width; - int newHeight = height; - - sprintf(texPathTemp, "%s_%08x_%i", globals->unique_id, texHash, tex_format); - pcfmt = HiresTextures::GetHiresTex(texPathTemp, &newWidth, &newHeight, GX_TF_RGBA8, temp); - if (pcfmt != PC_TEX_FMT_NONE) - { - expandedWidth = width = newWidth; - expandedHeight = height = newHeight; - } - } - - if (pcfmt == PC_TEX_FMT_NONE) - pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt, true); - - entry.oldpixel = ((u32*)ptr)[0]; - if (g_ActiveConfig.bSafeTextureCache) entry.hash = hash_value; - else entry.hash = ((u32*)ptr)[0] = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF); - - bool isPow2 = !((width & (width - 1)) || (height & (height - 1))); - unsigned int TexLevels = (isPow2 && UseNativeMips && maxlevel) ? GetPow2(max(width, height)) : ((isPow2)? 0 : 1); - if (TexLevels > (maxlevel + 1) && maxlevel) - TexLevels = maxlevel + 1; - - D3D11_USAGE usage = (TexLevels == 1) ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT; - - if (!skip_texture_create) - { - // TODO: A little more verbosity in the debug names would be quite helpful.. - D3D11_CPU_ACCESS_FLAG cpu_access = (TexLevels == 1) ? D3D11_CPU_ACCESS_WRITE : (D3D11_CPU_ACCESS_FLAG)0; - ID3D11Texture2D* pTexture = NULL; - HRESULT hr; - D3D11_SUBRESOURCE_DATA data; - data.pSysMem = temp; - data.SysMemPitch = 4*expandedWidth; - - D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1, TexLevels, D3D11_BIND_SHADER_RESOURCE, usage, cpu_access); - hr = D3D::device->CreateTexture2D(&texdesc, (TexLevels==1)?&data:NULL, &pTexture); - CHECK(hr==S_OK, "Create texture of the TextureCache"); - entry.texture = new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE); - CHECK(entry.texture!=NULL, "Create texture of the TextureCache"); - D3D::SetDebugObjectName((ID3D11DeviceChild*)entry.texture->GetTex(), "a texture of the TextureCache"); - D3D::SetDebugObjectName((ID3D11DeviceChild*)entry.texture->GetSRV(), "shader resource view of a texture of the TextureCache"); - SAFE_RELEASE(pTexture); - - // if (TexLevels == 1), we already loaded the data into our texture upon creation - if (TexLevels != 1) D3D::ReplaceRGBATexture2D(entry.texture->GetTex(), temp, width, height, expandedWidth, 0, usage); - } - else - { - D3D::ReplaceRGBATexture2D(entry.texture->GetTex(), temp, width, height, expandedWidth, 0, usage); - } - - entry.addr = address; - entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format); - entry.isRenderTarget = false; - entry.isNonPow2 = false; - entry.MipLevels = maxlevel; - - if (TexLevels == 0) PD3DX11FilterTexture(D3D::context, entry.texture->GetTex(), 0, D3DX11_DEFAULT); - else if (TexLevels > 1 && pcfmt != PC_TEX_FMT_NONE) - { - unsigned int level = 1; - unsigned int mipWidth = (width + 1) >> 1; - unsigned int mipHeight = (height + 1) >> 1; - ptr += entry.size_in_bytes; - while ((mipHeight || mipWidth) && (level < TexLevels)) - { - unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1; - unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1; - expandedWidth = (currentWidth + bsw) & (~bsw); - expandedHeight = (currentHeight + bsh) & (~bsh); - TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt, true); - D3D::ReplaceRGBATexture2D(entry.texture->GetTex(), temp, currentWidth, currentHeight, expandedWidth, level, usage); - u32 size = (max(mipWidth, bsw) * max(mipHeight, bsh) * bsdepth) >> 1; - ptr += size; - mipWidth >>= 1; - mipHeight >>= 1; - level++; - } - } - entry.frameCount = frameCount; - entry.w = width; - entry.h = height; - entry.Scaledw = width; - entry.Scaledh = height; - entry.fmt = FullFormat; - - // dump texture to file - if (g_ActiveConfig.bDumpTextures) - { - char szTemp[MAX_PATH]; - char szDir[MAX_PATH]; - - // make sure that the directory exists - sprintf(szDir, "%s%s", File::GetUserPath(D_DUMPTEXTURES_IDX), globals->unique_id); - if (!File::Exists(szDir) || !File::IsDirectory(szDir)) - File::CreateDir(szDir); - - sprintf(szTemp, "%s/%s_%08x_%i.png", szDir, globals->unique_id, texHash, tex_format); - if (!File::Exists(szTemp)) - if(FAILED(PD3DX11SaveTextureToFileA(D3D::context, entry.texture->GetTex(), D3DX11_IFF_PNG, szTemp))) PanicAlert("!!!"); - } - - INCSTAT(stats.numTexturesCreated); - SETSTAT(stats.numTexturesAlive, textures.size()); - - D3D::gfxstate->SetShaderResource(stage, entry.texture->GetSRV()); - - return &entry; -} - -void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, unsigned int bScaleByHalf, const EFBRectangle &source_rect) -{ - int efb_w = source_rect.GetWidth(); - int efb_h = source_rect.GetHeight(); - - int tex_w = (abs(source_rect.GetWidth()) >> bScaleByHalf); - int tex_h = (abs(source_rect.GetHeight()) >> bScaleByHalf); - - int Scaledtex_w = (g_ActiveConfig.bCopyEFBScaled) ? ((int)(Renderer::GetTargetScaleX() * tex_w)) : tex_w; - int Scaledtex_h = (g_ActiveConfig.bCopyEFBScaled) ? ((int)(Renderer::GetTargetScaleY() * tex_h)) : tex_h; - - TexCache::iterator iter; - D3DTexture2D* tex = NULL; - iter = textures.find(address); - if (iter != textures.end()) - { - if (iter->second.isRenderTarget && iter->second.Scaledw == Scaledtex_w && iter->second.Scaledh == Scaledtex_h) - { - tex = iter->second.texture; - iter->second.frameCount = frameCount; - } - else - { - // Remove it and recreate it as a render target - SAFE_RELEASE(iter->second.texture); - textures.erase(iter); - } - } - - if (!tex) - { - TCacheEntry entry; - entry.isRenderTarget = true; - entry.hash = 0; - entry.frameCount = frameCount; - entry.w = entry.Realw = tex_w; - entry.h = entry.Realh = tex_h; - entry.Scaledw = Scaledtex_w; - entry.Scaledh = Scaledtex_h; - entry.fmt = copyfmt; - entry.isNonPow2 = true; - entry.texture = D3DTexture2D::Create(Scaledtex_w, Scaledtex_h, (D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET|(int)D3D11_BIND_SHADER_RESOURCE), D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM); - if (entry.texture == NULL) PanicAlert("CopyRenderTargetToTexture failed to create entry.texture at %s %d\n", __FILE__, __LINE__); - textures[address] = entry; - tex = entry.texture; - } - - float colmat[20]= {0.0f}; // last four floats for fConstAdd - unsigned int cbufid = (unsigned int)-1; - - // TODO: Move this to TextureCache::Init() - if (bFromZBuffer) - { - switch(copyfmt) - { - case 0: // Z4 - case 1: // Z8 - colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1.0f; - cbufid = 12; - break; - case 3: // Z16 //? - colmat[1] = colmat[5] = colmat[9] = colmat[12] = 1.0f; - cbufid = 13; - break; - case 11: // Z16 (reverse order) - colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1.0f; - cbufid = 14; - break; - case 6: // Z24X8 - colmat[0] = colmat[5] = colmat[10] = 1.0f; - cbufid = 15; - break; - case 9: // Z8M - colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1.0f; - cbufid = 16; - break; - case 10: // Z8L - colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1.0f; - cbufid = 17; - break; - case 12: // Z16L - colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1.0f; - cbufid = 18; - break; - default: - ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt); - colmat[2] = colmat[5] = colmat[8] = 1.0f; - cbufid = 19; - break; - } - } - else if (bIsIntensityFmt) - { - colmat[16] = colmat[17] = colmat[18] = 16.0f/255.0f; - switch (copyfmt) - { - case 0: // I4 - case 1: // I8 - case 2: // IA4 - case 3: // IA8 - // TODO - verify these coefficients - colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f; - colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f; - colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f; - - if (copyfmt < 2) - { - colmat[19] = 16.0f / 255.0f; - colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f; - cbufid = 0; - } - else// alpha - { - colmat[15] = 1; - cbufid = 1; - } - - break; - default: - ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%x", copyfmt); - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - break; - } - } - else - { - switch (copyfmt) - { - case 0: // R4 - case 8: // R8 - colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1; - cbufid = 2; - break; - case 2: // RA4 - case 3: // RA8 - colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1; - cbufid = 3; - break; - - case 7: // A8 - colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1; - cbufid = 4; - break; - case 9: // G8 - colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1; - cbufid = 5; - break; - case 10: // B8 - colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1; - cbufid = 6; - break; - case 11: // RG8 - colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1; - cbufid = 7; - break; - case 12: // GB8 - colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1; - cbufid = 8; - break; - - case 4: // RGB565 - colmat[0] = colmat[5] = colmat[10] = 1; - colmat[19] = 1; // set alpha to 1 - cbufid = 9; - break; - case 5: // RGB5A3 - case 6: // RGBA8 - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - cbufid = 10; - break; - - default: - ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt); - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - cbufid = 11; - break; - } - } - - Renderer::ResetAPIState(); // reset any game specific settings - - // stretch picture with increased internal resolution - D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)Scaledtex_w, (float)Scaledtex_h); - D3D::context->RSSetViewports(1, &vp); - D3D11_RECT destrect = CD3D11_RECT(0, 0, Scaledtex_w, Scaledtex_h); - - // set transformation - if (efbcopycbuf[cbufid] == NULL) - { - D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(20*sizeof(float), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT); - D3D11_SUBRESOURCE_DATA data; - data.pSysMem = colmat; - HRESULT hr = D3D::device->CreateBuffer(&cbdesc, &data, &efbcopycbuf[cbufid]); - CHECK(hr==S_OK, "Create efb copy constant buffer %d", cbufid); - D3D::SetDebugObjectName((ID3D11DeviceChild*)efbcopycbuf[cbufid], "a constant buffer used in TextureCache::CopyRenderTargetToTexture"); - } - D3D::context->PSSetConstantBuffers(0, 1, &efbcopycbuf[cbufid]); - - TargetRectangle targetSource = Renderer::ConvertEFBRectangle(source_rect); - D3D11_RECT sourcerect = CD3D11_RECT(targetSource.left, targetSource.top, targetSource.right, targetSource.bottom); - - // Use linear filtering if (bScaleByHalf), use point filtering otherwise - if (bScaleByHalf) D3D::SetLinearCopySampler(); - else D3D::SetPointCopySampler(); - - D3D::stateman->PushBlendState(efbcopyblendstate); - D3D::stateman->PushRasterizerState(efbcopyraststate); - D3D::stateman->PushDepthState(efbcopydepthstate); - D3D::context->OMSetRenderTargets(1, &tex->GetRTV(), NULL); - D3D::drawShadedTexQuad( - (bFromZBuffer) ? g_framebufferManager.GetEFBDepthTexture()->GetSRV() : g_framebufferManager.GetEFBColorTexture()->GetSRV(), - &sourcerect, - Renderer::GetFullTargetWidth(), - Renderer::GetFullTargetHeight(), - (bFromZBuffer) ? PixelShaderCache::GetDepthMatrixProgram() : PixelShaderCache::GetColorMatrixProgram(), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout()); - - D3D::context->OMSetRenderTargets(1, &g_framebufferManager.GetEFBColorTexture()->GetRTV(), g_framebufferManager.GetEFBDepthTexture()->GetDSV()); - D3D::stateman->PopBlendState(); - D3D::stateman->PopDepthState(); - D3D::stateman->PopRasterizerState(); - Renderer::RestoreAPIState(); } diff --git a/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.h b/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.h index e1d4d97c6e..3980485a5f 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.h +++ b/Source/Plugins/Plugin_VideoDX11/Src/TextureCache.h @@ -24,45 +24,42 @@ #include "VideoCommon.h" #include "BPMemory.h" -class TextureCache +#include "TextureCacheBase.h" + +namespace DX11 +{ + +class TextureCache : public ::TextureCache { public: - struct TCacheEntry - { - D3DTexture2D* texture; - - u32 addr; - u32 size_in_bytes; - u64 hash; - u32 paletteHash; - u32 oldpixel; - - int frameCount; - unsigned int w, h, fmt, MipLevels; - int Realw, Realh, Scaledw, Scaledh; - - bool isRenderTarget; - bool isNonPow2; - - TCacheEntry() : texture(NULL), addr(0), size_in_bytes(0), hash(0), paletteHash(0), oldpixel(0), - frameCount(0), w(0), h(0), fmt(0), MipLevels(0), Realw(0), Realh(0), Scaledw(0), Scaledh(0), - isRenderTarget(false), isNonPow2(true) {} - - void Destroy(bool shutdown); - bool IntersectsMemoryRange(u32 range_address, u32 range_size); - }; - - static void Init(); - static void Cleanup(); - static void Shutdown(); - static void Invalidate(bool shutdown); - static void InvalidateRange(u32 start_address, u32 size); - static TCacheEntry* TextureCache::Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, unsigned int tex_format, unsigned int tlutaddr, unsigned int tlutfmt, bool UseNativeMips, unsigned int maxlevel); - static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, unsigned int bScaleByHalf, const EFBRectangle &source_rect); + TextureCache(); + ~TextureCache(); private: - typedef std::map TexCache; + struct TCacheEntry : TCacheEntryBase + { + D3DTexture2D *const texture; - static u8* temp; - static TexCache textures; + D3D11_USAGE usage; + + TCacheEntry(D3DTexture2D *_tex) : texture(_tex) {} + ~TCacheEntry(); + + void Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int levels); + + void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float* colmat, const EFBRectangle &source_rect, + bool bIsIntensityFmt, u32 copyfmt); + + void Bind(unsigned int stage); + bool Save(const char filename[]); + }; + + TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt); + + TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h); }; + +} diff --git a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp index 8280887dd7..9cc3c61d12 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp @@ -214,7 +214,7 @@ void VertexManager::vFlush() { Renderer::SetSamplerState(i & 3, i >> 2); FourTexUnits &tex = bpmem.tex[i >> 2]; - TextureCache::TCacheEntry* tentry = TextureCache::Load(i, + TextureCache::TCacheEntryBase* tentry = TextureCache::Load(i, (tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5, tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1, tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9, @@ -225,7 +225,7 @@ void VertexManager::vFlush() if (tentry) { // 0s are probably for no manual wrapping needed. - PixelShaderManager::SetTexDims(i, tentry->Realw, tentry->Realh, 0, 0); + PixelShaderManager::SetTexDims(i, tentry->nativeW, tentry->nativeH, 0, 0); } else ERROR_LOG(VIDEO, "error loading texture"); diff --git a/Source/Plugins/Plugin_VideoDX11/Src/main.cpp b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp index 39cef47abc..1d444bf039 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp @@ -215,7 +215,7 @@ void Video_Prepare() // internal interfaces Renderer::Init(); - TextureCache::Init(); + g_texture_cache = new DX11::TextureCache; g_vertex_manager = new DX11::VertexManager; VertexShaderCache::Init(); PixelShaderCache::Init(); @@ -258,7 +258,7 @@ void Shutdown() PixelShaderCache::Shutdown(); VertexShaderCache::Shutdown(); delete g_vertex_manager; - TextureCache::Shutdown(); + delete g_texture_cache; Renderer::Shutdown(); EmuWindow::Close(); diff --git a/Source/Plugins/Plugin_VideoDX9/Src/BPFunctions.cpp b/Source/Plugins/Plugin_VideoDX9/Src/BPFunctions.cpp index 8f6a993dfa..c9522efe4e 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/BPFunctions.cpp +++ b/Source/Plugins/Plugin_VideoDX9/Src/BPFunctions.cpp @@ -85,7 +85,7 @@ void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const // bpmem.zcontrol.pixel_format to PIXELFMT_Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format) if (!g_ActiveConfig.bEFBCopyDisable) { - TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc); + TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, !!scaleByHalf, rc); } } diff --git a/Source/Plugins/Plugin_VideoDX9/Src/TextureCache.cpp b/Source/Plugins/Plugin_VideoDX9/Src/TextureCache.cpp index d656fd90c4..08a2a6270e 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/TextureCache.cpp +++ b/Source/Plugins/Plugin_VideoDX9/Src/TextureCache.cpp @@ -43,570 +43,44 @@ #include "debugger/debugger.h" -u8 *TextureCache::temp = NULL; -TextureCache::TexCache TextureCache::textures; - extern int frameCount; -#define TEMP_SIZE (2048*2048*4) -#define TEXTURE_KILL_THRESHOLD 200 - -void TextureCache::TCacheEntry::Destroy(bool shutdown) +namespace DX9 { - if (texture) - texture->Release(); - texture = 0; - if (!isRenderTarget && !shutdown && !g_ActiveConfig.bSafeTextureCache) - { - u32 *ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr); - if (ptr && *ptr == hash) - *ptr = oldpixel; - } + +TextureCache::TCacheEntry::~TCacheEntry() +{ + texture->Release(); } -void TextureCache::Init() +void TextureCache::TCacheEntry::Bind(unsigned int stage) { - temp = (u8*)AllocateMemoryPages(TEMP_SIZE); - TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter); - HiresTextures::Init(globals->unique_id); + D3D::SetTexture(stage, texture); } -void TextureCache::Invalidate(bool shutdown) +bool TextureCache::TCacheEntry::Save(const char filename[]) { - for (TexCache::iterator iter = textures.begin(); iter != textures.end(); ++iter) - iter->second.Destroy(shutdown); - textures.clear(); - HiresTextures::Shutdown(); + return SUCCEEDED(PD3DXSaveTextureToFileA(filename, D3DXIFF_PNG, texture, 0)); } -void TextureCache::Shutdown() +void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level) { - Invalidate(true); - FreeMemoryPages(temp, TEMP_SIZE); - temp = NULL; + D3D::ReplaceTexture2D(texture, temp, width, height, expanded_width, d3d_fmt, swap_r_b, level); } -void TextureCache::Cleanup() +void TextureCache::TCacheEntry::FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float *colmat, const EFBRectangle &source_rect, + bool bIsIntensityFmt, u32 copyfmt) { - TexCache::iterator iter = textures.begin(); - while (iter != textures.end()) + const LPDIRECT3DTEXTURE9 read_texture = bFromZBuffer ? + g_framebufferManager.GetEFBDepthTexture(source_rect) : + g_framebufferManager.GetEFBColorTexture(source_rect); + + if (!isDynamic || g_ActiveConfig.bCopyEFBToTexture) { - if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second.frameCount) - { - iter->second.Destroy(false); - iter = textures.erase(iter); - } - else - { - ++iter; - } - } -} - -void TextureCache::InvalidateRange(u32 start_address, u32 size) -{ - TexCache::iterator iter = textures.begin(); - while (iter != textures.end()) - { - int rangePosition = iter->second.IntersectsMemoryRange(start_address, size); - if (rangePosition == 0) - { - iter->second.Destroy(false); - textures.erase(iter++); - } - else - { - ++iter; - } - } -} - -void TextureCache::MakeRangeDynamic(u32 start_address, u32 size) -{ - TexCache::iterator iter = textures.begin(); - while (iter != textures.end()) - { - int rangePosition = iter->second.IntersectsMemoryRange(start_address, size); - if ( rangePosition == 0) - { - iter->second.hash = 0; - } - ++iter; - } -} - -int TextureCache::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size) -{ - if (addr + size_in_bytes < range_address) - return -1; - if (addr >= range_address + range_size) - return 1; - return 0; -} - -TextureCache::TCacheEntry *TextureCache::Load(int stage, u32 address, int width, int height, int tex_format, int tlutaddr, int tlutfmt,bool UseNativeMips, int maxlevel) -{ - // notes (about "UNsafe texture cache"): - // Have to be removed soon. - // But we keep it until the "safe" way became rock solid - // pros: it has an unique ID held by the texture data itself (@address) once cached. - // cons: it writes this unique ID in the gc RAM <- very dangerous (break MP1) and ugly - - // notes (about "safe texture cache"): - // Metroids text issue (character table): - // Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs). - // That's why we have to hash the TLUT too for TLUT tex_format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2). - // And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but - // have to be a real unique ID. - // DONE but not satifiying yet -> may break copyEFBToTexture sometimes. - - // Pokemon Colosseum text issue (plain text): - // Use a GX_TF_I4 512x512 text-flush-texture at a const address. - // The problem here was just the sparse hash on the texture. This texture is partly overwrited (what is needed only) - // so lot's of remaning old text. Thin white chars on black bg too. - - // TODO: - clean this up when ready to kill old "unsafe texture cache" - // - fix the key index situation with CopyRenderTargetToTexture. - // Could happen only for GX_TF_C4, GX_TF_C8 and GX_TF_C14X2 fmt. - // Wonder if we can't use tex width&height to know if EFB might be copied to it... - // raw idea: TOCHECK if addresses are aligned we have few bits left... - - if (address == 0) - return NULL; - - u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address); - int bsw = TexDecoder_GetBlockWidthInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16; - int bsh = TexDecoder_GetBlockHeightInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16; - int bsdepth = TexDecoder_GetTexelSizeInNibbles(tex_format); - int expandedWidth = (width + bsw) & (~bsw); - int expandedHeight = (height + bsh) & (~bsh); - - u64 hash_value = 0; - u32 texID = address; - u64 texHash = 0; - u32 FullFormat = tex_format; - bool TextureisDynamic = false; - if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) - FullFormat = (tex_format | (tlutfmt << 16)); - - if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures) - { - texHash = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) - { - // WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up) - // tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower. - // This trick (to change the texID depending on the TLUT addr) is a trick to get around - // an issue with metroid prime's fonts, where it has multiple sets of fonts on top of - // each other stored in a single texture, and uses the palette to make different characters - // visible or invisible. Thus, unless we want to recreate the textures for every drawn character, - // we must make sure that texture with different tluts get different IDs. - u64 tlutHash = GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - texHash ^= tlutHash; - if (g_ActiveConfig.bSafeTextureCache) - { - texID = texID ^ ((u32)(tlutHash & 0xFFFFFFFF)) ^ ((u32)((tlutHash >> 32) & 0xFFFFFFFF)); - } - } - if (g_ActiveConfig.bSafeTextureCache) - hash_value = texHash; - } - - bool skip_texture_create = false; - TexCache::iterator iter = textures.find(texID); - - if (iter != textures.end()) - { - TCacheEntry &entry = iter->second; - - if (!g_ActiveConfig.bSafeTextureCache) - { - if(entry.isRenderTarget || entry.isDynamic) - { - if(!g_ActiveConfig.bCopyEFBToTexture) - { - hash_value = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) - { - hash_value ^= GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - } - } - else - { - hash_value = 0; - } - } - else - { - hash_value = ((u32 *)ptr)[0]; - } - } - else - { - if(entry.isRenderTarget || entry.isDynamic) - { - if(g_ActiveConfig.bCopyEFBToTexture) - { - hash_value = 0; - } - } - } - if (((entry.isRenderTarget || entry.isDynamic) && hash_value == entry.hash && address == entry.addr) - || ((address == entry.addr) && (hash_value == entry.hash) && FullFormat == entry.fmt/* && entry.MipLevels == maxlevel*/)) - { - entry.frameCount = frameCount; - entry.isDynamic = false; - D3D::SetTexture(stage, entry.texture); - return &entry; - } - else - { - // Let's reload the new texture data into the same texture, - // instead of destroying it and having to create a new one. - // Might speed up movie playback very, very slightly. - TextureisDynamic = (entry.isRenderTarget || entry.isDynamic) && !g_ActiveConfig.bCopyEFBToTexture; - - if (!entry.isRenderTarget && - ((!entry.isDynamic && width == entry.w && height==entry.h && FullFormat == entry.fmt /* && entry.MipLevels < maxlevel*/) - || (entry.isDynamic && entry.w == width && entry.h == height))) - { - skip_texture_create = true; - } - else - { - entry.Destroy(false); - textures.erase(iter); - } - } - } - - // Make an entry in the table - TCacheEntry& entry = textures[texID]; - entry.isDynamic = TextureisDynamic; - entry.Realw = width; - entry.Realh = height; - PC_TexFormat pcfmt = PC_TEX_FMT_NONE; - - if (g_ActiveConfig.bHiresTextures) - { - // Load Custom textures - char texPathTemp[MAX_PATH]; - int newWidth = width; - int newHeight = height; - - sprintf(texPathTemp, "%s_%08x_%i", globals->unique_id, texHash, tex_format); - pcfmt = HiresTextures::GetHiresTex(texPathTemp, &newWidth, &newHeight, tex_format, temp); - - if (pcfmt != PC_TEX_FMT_NONE) - { - expandedWidth = width = newWidth; - expandedHeight = height = newHeight; - } - } - - if (pcfmt == PC_TEX_FMT_NONE) - pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt); - - entry.oldpixel = ((u32 *)ptr)[0]; - if (g_ActiveConfig.bSafeTextureCache || entry.isDynamic) - entry.hash = hash_value; - else - { - entry.hash = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF); - ((u32 *)ptr)[0] = entry.hash; - } - - entry.addr = address; - entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format); - entry.isRenderTarget = false; - bool isPow2 = !((width & (width - 1)) || (height & (height - 1))); - entry.isNonPow2 = false; - int TexLevels = (width > height)?width:height; - TexLevels = (isPow2 && UseNativeMips && (maxlevel > 0)) ? (int)(log((double)TexLevels)/log((double)2)) + 1 : (isPow2? 0 : 1); - if(TexLevels > (maxlevel + 1) && maxlevel > 0) - TexLevels = (maxlevel + 1); - entry.MipLevels = maxlevel; - - D3DFORMAT d3d_fmt; - bool swap_r_b = false; - - switch (pcfmt) - { - case PC_TEX_FMT_BGRA32: - d3d_fmt = D3DFMT_A8R8G8B8; - break; - case PC_TEX_FMT_RGBA32: - d3d_fmt = D3DFMT_A8R8G8B8; - swap_r_b = true; - break; - case PC_TEX_FMT_RGB565: - d3d_fmt = D3DFMT_R5G6B5; - break; - case PC_TEX_FMT_IA4_AS_IA8: - d3d_fmt = D3DFMT_A8L8; - break; - case PC_TEX_FMT_I8: - case PC_TEX_FMT_I4_AS_I8: - // A hack which means the format is a packed - // 8-bit intensity texture. It is unpacked - // to A8L8 in D3DTexture.cpp - d3d_fmt = D3DFMT_A8P8; - break; - case PC_TEX_FMT_IA8: - d3d_fmt = D3DFMT_A8L8; - break; - case PC_TEX_FMT_DXT1: - d3d_fmt = D3DFMT_DXT1; - break; - } - - if (!skip_texture_create) - { - entry.texture = D3D::CreateTexture2D((BYTE*)temp, width, height, expandedWidth, d3d_fmt, swap_r_b, TexLevels); - } - else - { - D3D::ReplaceTexture2D(entry.texture, (BYTE*)temp, width, height, expandedWidth, d3d_fmt, swap_r_b, 0); - } - if(TexLevels > 1 && pcfmt != PC_TEX_FMT_NONE) - { - int level = 1; - int mipWidth = (width + 1) >> 1; - int mipHeight = (height + 1) >> 1; - ptr += entry.size_in_bytes; - while((mipHeight || mipWidth) && (level < TexLevels)) - { - u32 currentWidth = (mipWidth > 0)? mipWidth : 1; - u32 currentHeight = (mipHeight > 0)? mipHeight : 1; - expandedWidth = (currentWidth + bsw) & (~bsw); - expandedHeight = (currentHeight + bsh) & (~bsh); - TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt); - D3D::ReplaceTexture2D(entry.texture, (BYTE*)temp, currentWidth, currentHeight, expandedWidth, d3d_fmt, swap_r_b,level); - u32 size = (max(mipWidth, bsw) * max(mipHeight, bsh) * bsdepth) >> 1; - ptr += size; - mipWidth >>= 1; - mipHeight >>= 1; - level++; - } - } - entry.frameCount = frameCount; - entry.w = width; - entry.h = height; - entry.Scaledw = width; - entry.Scaledh = height; - entry.fmt = FullFormat; - - if (g_ActiveConfig.bDumpTextures) - { - // dump texture to file - char szTemp[MAX_PATH]; - char szDir[MAX_PATH]; - const char* uniqueId = globals->unique_id; - static bool bCheckedDumpDir = false; - - sprintf(szDir, "%s%s", File::GetUserPath(D_DUMPTEXTURES_IDX), uniqueId); - - if (!bCheckedDumpDir) - { - if (!File::Exists(szDir) || !File::IsDirectory(szDir)) - File::CreateDir(szDir); - - bCheckedDumpDir = true; - } - - sprintf(szTemp, "%s/%s_%08x_%i.png", szDir, uniqueId, texHash, tex_format); - - if (!File::Exists(szTemp)) - PD3DXSaveTextureToFileA(szTemp,D3DXIFF_PNG,entry.texture,0); - } - - INCSTAT(stats.numTexturesCreated); - SETSTAT(stats.numTexturesAlive, textures.size()); - - //Set the texture! - D3D::SetTexture(stage, entry.texture); - - DEBUGGER_PAUSE_LOG_AT(NEXT_NEW_TEXTURE,true,{printf("A new texture (%d x %d) is loaded", width, height);}); - return &entry; -} - -void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle &source_rect) -{ - int tex_w = (abs(source_rect.GetWidth()) >> bScaleByHalf); - int tex_h = (abs(source_rect.GetHeight()) >> bScaleByHalf); - //compensate the texture grow if supersampling is enabled to conserve memory usage - float SuperSampleCompensation = g_ActiveConfig.iMultisampleMode + 1; - SuperSampleCompensation = 1.0f / SuperSampleCompensation; - float xScale = Renderer::GetTargetScaleX(); - float yScale = Renderer::GetTargetScaleY(); - - int Scaledtex_w = (g_ActiveConfig.bCopyEFBScaled)?((int)(xScale * SuperSampleCompensation * tex_w)):tex_w; - int Scaledtex_h = (g_ActiveConfig.bCopyEFBScaled)?((int)(yScale * SuperSampleCompensation * tex_h)):tex_h; - - TexCache::iterator iter; - LPDIRECT3DTEXTURE9 tex = NULL; - iter = textures.find(address); - bool TextureisDynamic = false; - if (iter != textures.end()) - { - if ((iter->second.isRenderTarget && iter->second.Scaledw == Scaledtex_w && iter->second.Scaledh == Scaledtex_h) - || (iter->second.isDynamic && iter->second.w == tex_w && iter->second.h == tex_h)) - { - tex = iter->second.texture; - TextureisDynamic = iter->second.isDynamic; - iter->second.frameCount = frameCount; - } - else - { - // Remove it and recreate it as a render target - if(iter->second.texture) - iter->second.texture->Release(); - iter->second.texture = 0; - textures.erase(iter); - } - } - if (TextureisDynamic) - { - Scaledtex_w = tex_w; - Scaledtex_h = tex_h; - } - if (!tex) - { - TCacheEntry entry; - entry.addr = address; - entry.isRenderTarget = true; - entry.hash = 0; - entry.frameCount = frameCount; - entry.w = entry.Realw = tex_w; - entry.h = entry.Realh = tex_h; - entry.Scaledw = Scaledtex_w; - entry.Scaledh = Scaledtex_h; - entry.fmt = copyfmt; - entry.isNonPow2 = true; - entry.isDynamic = false; - D3D::dev->CreateTexture(Scaledtex_w, Scaledtex_h, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &entry.texture, 0); - textures[address] = entry; - tex = entry.texture; - } - - // Make sure to resolve anything we need to read from. - LPDIRECT3DTEXTURE9 read_texture = bFromZBuffer ? g_framebufferManager.GetEFBDepthTexture(source_rect) : g_framebufferManager.GetEFBColorTexture(source_rect); - - // We have to run a pixel shader, for color conversion. - Renderer::ResetAPIState(); // reset any game specific settings - if(!TextureisDynamic || g_ActiveConfig.bCopyEFBToTexture) - { - - float colmat[16]= {0.0f}; - float fConstAdd[4] = {0.0f}; - - if (bFromZBuffer) - { - switch(copyfmt) - { - case 0: // Z4 - case 1: // Z8 - colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1.0f; - break; - case 3: // Z16 //? - colmat[1] = colmat[5] = colmat[9] = colmat[12] = 1.0f; - break; - case 11: // Z16 (reverse order) - colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1.0f; - break; - case 6: // Z24X8 - colmat[0] = colmat[5] = colmat[10] = 1.0f; - break; - case 9: // Z8M - colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1.0f; - break; - case 10: // Z8L - colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1.0f; - break; - case 12: // Z16L - colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1.0f; - break; - default: - ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt); - colmat[2] = colmat[5] = colmat[8] = 1.0f; - break; - } - } - else if (bIsIntensityFmt) - { - fConstAdd[0] = fConstAdd[1] = fConstAdd[2] = 16.0f/255.0f; - switch (copyfmt) - { - case 0: // I4 - case 1: // I8 - case 2: // IA4 - case 3: // IA8 - colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f; - colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f; - colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f; - - if (copyfmt < 2) - { - fConstAdd[3] = 16.0f / 255.0f; - colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f; - } - else// alpha - colmat[15] = 1; - - break; - default: - ERROR_LOG(VIDEO, "Unknown copy intensity format: 0x%x", copyfmt); - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - break; - } - } - else - { - switch (copyfmt) - { - case 0: // R4 - case 8: // R8 - colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1; - break; - case 2: // RA4 - case 3: // RA8 - colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1; - break; - - case 7: // A8 - colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1; - break; - case 9: // G8 - colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1; - break; - case 10: // B8 - colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1; - break; - case 11: // RG8 - colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1; - break; - case 12: // GB8 - colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1; - break; - - case 4: // RGB565 - colmat[0] = colmat[5] = colmat[10] = 1; - fConstAdd[3] = 1; // set alpha to 1 - break; - case 5: // RGB5A3 - case 6: // RGBA8 - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - break; - - default: - ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt); - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - break; - } - } - LPDIRECT3DSURFACE9 Rendersurf = NULL; - tex->GetSurfaceLevel(0,&Rendersurf); + texture->GetSurfaceLevel(0, &Rendersurf); D3D::dev->SetDepthStencilSurface(NULL); D3D::dev->SetRenderTarget(0, Rendersurf); @@ -615,18 +89,18 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo // Stretch picture with increased internal resolution vp.X = 0; vp.Y = 0; - vp.Width = Scaledtex_w; - vp.Height = Scaledtex_h; + vp.Width = scaledW; + vp.Height = scaledH; vp.MinZ = 0.0f; vp.MaxZ = 1.0f; D3D::dev->SetViewport(&vp); RECT destrect; - destrect.bottom = Scaledtex_h; + destrect.bottom = scaledH; destrect.left = 0; - destrect.right = Scaledtex_w; + destrect.right = scaledW; destrect.top = 0; - + const float* const fConstAdd = colmat + 16; // fConstAdd is the last 4 floats of colmat PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation TargetRectangle targetSource = Renderer::ConvertEFBRectangle(source_rect); RECT sourcerect; @@ -635,10 +109,9 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo sourcerect.right = targetSource.right; sourcerect.top = targetSource.top; - - if(bFromZBuffer) + if (bFromZBuffer) { - if(bScaleByHalf || g_ActiveConfig.iMultisampleMode) + if (bScaleByHalf || g_ActiveConfig.iMultisampleMode) { D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); @@ -654,31 +127,30 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); } - D3DFORMAT bformat = g_framebufferManager.GetEFBDepthRTSurfaceFormat(); int SSAAMode = g_ActiveConfig.iMultisampleMode; - D3D::drawShadedTexQuad( - read_texture, - &sourcerect, - Renderer::GetFullTargetWidth() , - Renderer::GetFullTargetHeight(), - Scaledtex_w, - Scaledtex_h, - ((bformat != FOURCC_RAWZ && bformat != D3DFMT_D24X8) && bFromZBuffer)? PixelShaderCache::GetDepthMatrixProgram(SSAAMode): PixelShaderCache::GetColorMatrixProgram(SSAAMode), + + D3D::drawShadedTexQuad(read_texture, &sourcerect, + Renderer::GetFullTargetWidth(), Renderer::GetFullTargetHeight(), + scaledW, scaledH, + ((bformat != FOURCC_RAWZ && bformat != D3DFMT_D24X8) && bFromZBuffer) ? + PixelShaderCache::GetDepthMatrixProgram(SSAAMode) : + PixelShaderCache::GetColorMatrixProgram(SSAAMode), VertexShaderCache::GetSimpleVertexShader(SSAAMode)); - Rendersurf->Release(); + + Rendersurf->Release(); } - - if(!g_ActiveConfig.bCopyEFBToTexture) + + if (!g_ActiveConfig.bCopyEFBToTexture) { - textures[address].hash = TextureConverter::EncodeToRamFromTexture( - address, + hash = TextureConverter::EncodeToRamFromTexture( + addr, read_texture, Renderer::GetFullTargetWidth(), Renderer::GetFullTargetHeight(), - xScale, - yScale, + Renderer::GetTargetScaleX(), + Renderer::GetTargetScaleY(), (float)((Renderer::GetFullTargetWidth() - Renderer::GetTargetWidth()) / 2), (float)((Renderer::GetFullTargetHeight() - Renderer::GetTargetHeight()) / 2) , bFromZBuffer, @@ -690,8 +162,68 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER); D3D::RefreshSamplerState(0, D3DSAMP_MAGFILTER); - D3D::SetTexture(0,NULL); + D3D::SetTexture(0, NULL); D3D::dev->SetRenderTarget(0, g_framebufferManager.GetEFBColorRTSurface()); D3D::dev->SetDepthStencilSurface(g_framebufferManager.GetEFBDepthRTSurface()); - Renderer::RestoreAPIState(); +} + +TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt) +{ + D3DFORMAT d3d_fmt; + bool swap_r_b = false; + + switch (pcfmt) + { + case PC_TEX_FMT_BGRA32: + d3d_fmt = D3DFMT_A8R8G8B8; + break; + + case PC_TEX_FMT_RGBA32: + d3d_fmt = D3DFMT_A8R8G8B8; + swap_r_b = true; + break; + + case PC_TEX_FMT_RGB565: + d3d_fmt = D3DFMT_R5G6B5; + break; + + case PC_TEX_FMT_IA4_AS_IA8: + d3d_fmt = D3DFMT_A8L8; + break; + + case PC_TEX_FMT_I8: + case PC_TEX_FMT_I4_AS_I8: + // A hack which means the format is a packed + // 8-bit intensity texture. It is unpacked + // to A8L8 in D3DTexture.cpp + d3d_fmt = D3DFMT_A8P8; + break; + + case PC_TEX_FMT_IA8: + d3d_fmt = D3DFMT_A8L8; + break; + + case PC_TEX_FMT_DXT1: + d3d_fmt = D3DFMT_DXT1; + break; + } + + TCacheEntry* entry = new TCacheEntry(D3D::CreateTexture2D(temp, width, height, expanded_width, d3d_fmt, swap_r_b, tex_levels)); + entry->swap_r_b = swap_r_b; + entry->d3d_fmt = d3d_fmt; + + return entry; +} + +TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture( + unsigned int scaled_tex_w, unsigned int scaled_tex_h) +{ + LPDIRECT3DTEXTURE9 texture; + D3D::dev->CreateTexture(scaled_tex_w, scaled_tex_h, 1, D3DUSAGE_RENDERTARGET, + D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture, 0); + + return new TCacheEntry(texture); +} + } diff --git a/Source/Plugins/Plugin_VideoDX9/Src/TextureCache.h b/Source/Plugins/Plugin_VideoDX9/Src/TextureCache.h index 3a6ebe6b0d..23b6382f1b 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/TextureCache.h +++ b/Source/Plugins/Plugin_VideoDX9/Src/TextureCache.h @@ -25,64 +25,41 @@ #include "VideoCommon.h" #include "BPMemory.h" -class TextureCache +#include "TextureCacheBase.h" + +namespace DX9 { -public: - struct TCacheEntry + +class TextureCache : public ::TextureCache +{ +private: + struct TCacheEntry : TCacheEntryBase { - LPDIRECT3DTEXTURE9 texture; + const LPDIRECT3DTEXTURE9 texture; - u32 addr; - u32 size_in_bytes; - u64 hash; - u32 paletteHash; - u32 oldpixel; - - int frameCount; - int w, h, fmt,MipLevels, Realw, Realh, Scaledw, Scaledh; + D3DFORMAT d3d_fmt; + bool swap_r_b; - bool isRenderTarget; - bool isDynamic;// mofified from cpu - bool isNonPow2; + TCacheEntry(LPDIRECT3DTEXTURE9 _tex) : texture(_tex) {} + ~TCacheEntry(); - TCacheEntry() - { - texture = 0; - isRenderTarget = 0; - hash = 0; - paletteHash = 0; - oldpixel = 0; - addr = 0; - size_in_bytes = 0; - frameCount = 0; - isNonPow2 = true; - w = 0; - h = 0; - Realw = 0; - Realh = 0; - Scaledw = 0; - Scaledh = 0; - } - void Destroy(bool shutdown); - int IntersectsMemoryRange(u32 range_address, u32 range_size); + void Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int levels); + + void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float* colmat, const EFBRectangle &source_rect, + bool bIsIntensityFmt, u32 copyfmt); + + void Bind(unsigned int stage); + bool Save(const char filename[]); }; -private: + TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt); - typedef std::map TexCache; - - static u8 *temp; - static TexCache textures; - -public: - static void Init(); - static void Cleanup(); - static void Shutdown(); - static void Invalidate(bool shutdown); - static void InvalidateRange(u32 start_address, u32 size); - static void MakeRangeDynamic(u32 start_address, u32 size); - static TCacheEntry *Load(int stage, u32 address, int width, int height, int format, int tlutaddr, int tlutfmt,bool UseNativeMips, int maxlevel); - static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle &source_rect); + TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h); }; +} + #endif diff --git a/Source/Plugins/Plugin_VideoDX9/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoDX9/Src/VertexManager.cpp index 881aa6a9db..6cd91e58f0 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/VertexManager.cpp +++ b/Source/Plugins/Plugin_VideoDX9/Src/VertexManager.cpp @@ -133,7 +133,7 @@ void VertexManager::vFlush() { Renderer::SetSamplerState(i & 3, i >> 2); FourTexUnits &tex = bpmem.tex[i >> 2]; - TextureCache::TCacheEntry* tentry = TextureCache::Load(i, + TextureCache::TCacheEntryBase* tentry = TextureCache::Load(i, (tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5, tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1, tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9, @@ -144,7 +144,7 @@ void VertexManager::vFlush() if (tentry) { // 0s are probably for no manual wrapping needed. - PixelShaderManager::SetTexDims(i, tentry->Realw, tentry->Realh, 0, 0); + PixelShaderManager::SetTexDims(i, tentry->nativeW, tentry->nativeH, 0, 0); } else ERROR_LOG(VIDEO, "error loading texture"); diff --git a/Source/Plugins/Plugin_VideoDX9/Src/main.cpp b/Source/Plugins/Plugin_VideoDX9/Src/main.cpp index 2c1d4b0506..6a800f557c 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoDX9/Src/main.cpp @@ -238,7 +238,7 @@ void Video_Prepare() // internal interfaces Renderer::Init(); - TextureCache::Init(); + g_texture_cache = new DX9::TextureCache; g_vertex_manager = new DX9::VertexManager; // VideoCommon BPInit(); @@ -276,7 +276,7 @@ void Shutdown() PixelShaderCache::Shutdown(); VertexShaderCache::Shutdown(); delete g_vertex_manager; - TextureCache::Shutdown(); + delete g_texture_cache; Renderer::Shutdown(); D3D::Shutdown(); EmuWindow::Close(); diff --git a/Source/Plugins/Plugin_VideoOGL/Src/BPFunctions.cpp b/Source/Plugins/Plugin_VideoOGL/Src/BPFunctions.cpp index d4255f55b5..723d3d0b30 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/BPFunctions.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/BPFunctions.cpp @@ -83,7 +83,7 @@ void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const // bpmem.zcontrol.pixel_format to PIXELFMT_Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format) if (!g_ActiveConfig.bEFBCopyDisable) { - TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc); + TextureCache::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, !!scaleByHalf, rc); } } diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp index 2fee454a82..6d62777904 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp @@ -1082,7 +1082,7 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons // Disable all other stages for (int i = 1; i < 8; ++i) - TextureCache::DisableStage(i); + OGL::TextureCache::DisableStage(i); // Update GLViewPort glViewport(dst_rect.left, dst_rect.bottom, dst_rect.GetWidth(), dst_rect.GetHeight()); @@ -1246,7 +1246,7 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons } glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); - TextureCache::DisableStage(0); + OGL::TextureCache::DisableStage(0); // Wireframe if (g_ActiveConfig.bWireFrame) diff --git a/Source/Plugins/Plugin_VideoOGL/Src/TextureCache.cpp b/Source/Plugins/Plugin_VideoOGL/Src/TextureCache.cpp index 2b7baae227..a5b3190f3d 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/TextureCache.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/TextureCache.cpp @@ -53,15 +53,11 @@ #include "VertexShaderManager.h" #include "VideoConfig.h" -u8 *TextureCache::temp = NULL; -TextureCache::TexCache TextureCache::textures; +namespace OGL +{ -extern int frameCount; static u32 s_TempFramebuffer = 0; -#define TEMP_SIZE (2048*2048*4) -#define TEXTURE_KILL_THRESHOLD 200 - static const GLint c_MinLinearFilter[8] = { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, @@ -83,352 +79,63 @@ static const GLint c_WrapSettings[4] = { bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int height) { std::vector data(width * height); - glBindTexture(textarget, tex); - glGetTexImage(textarget, 0, GL_BGRA, GL_UNSIGNED_BYTE, &data[0]); - GLenum err = GL_REPORT_ERROR(); - if (err != GL_NO_ERROR) + glBindTexture(textarget, tex); + glGetTexImage(textarget, 0, GL_BGRA, GL_UNSIGNED_BYTE, &data[0]); + + const GLenum err = GL_REPORT_ERROR(); + if (GL_NO_ERROR != err) { PanicAlert("Can't save texture, GL Error: %s", gluErrorString(err)); - return false; - } + return false; + } return SaveTGA(filename, width, height, &data[0]); } -void TextureCache::TCacheEntry::SetTextureParameters(TexMode0 &newmode,TexMode1 &newmode1) +TextureCache::TCacheEntry::~TCacheEntry() { - mode = newmode; - mode1 = newmode1; - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - (newmode.mag_filter || g_Config.bForceFiltering) ? GL_LINEAR : GL_NEAREST); - - if (bHaveMipMaps) + if (texture) { - if (g_ActiveConfig.bForceFiltering && newmode.min_filter < 4) - mode.min_filter += 4; // take equivalent forced linear - int filt = newmode.min_filter; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c_MinLinearFilter[filt & 7]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, newmode1.min_lod >> 4); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, newmode1.max_lod >> 4); - glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, (newmode.lod_bias/32.0f)); - - } - else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - (g_ActiveConfig.bForceFiltering || newmode.min_filter >= 4) ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c_WrapSettings[newmode.wrap_s]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c_WrapSettings[newmode.wrap_t]); - - if (g_Config.iMaxAnisotropy >= 1) - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)(1 << g_ActiveConfig.iMaxAnisotropy)); -} - -void TextureCache::TCacheEntry::Destroy(bool shutdown) -{ - if (texture) glDeleteTextures(1, &texture); - texture = 0; - if (!isRenderTarget && !shutdown && !g_ActiveConfig.bSafeTextureCache) - { - u32 *ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr); - if (ptr && *ptr == hash) - *ptr = oldpixel; + texture = 0; } } -void TextureCache::Init() +TextureCache::TCacheEntry::TCacheEntry() { - temp = (u8*)AllocateMemoryPages(TEMP_SIZE); - TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter); - HiresTextures::Init(globals->unique_id); -} - -void TextureCache::Invalidate(bool shutdown) -{ - for (TexCache::iterator iter = textures.begin(); iter != textures.end(); ++iter) - iter->second.Destroy(shutdown); - textures.clear(); - HiresTextures::Shutdown(); -} - -void TextureCache::Shutdown() -{ - Invalidate(true); - - if (s_TempFramebuffer) - { - glDeleteFramebuffersEXT(1, (GLuint *)&s_TempFramebuffer); - s_TempFramebuffer = 0; - } - - FreeMemoryPages(temp, TEMP_SIZE); - temp = NULL; -} - -void TextureCache::Cleanup() -{ - TexCache::iterator iter = textures.begin(); - while (iter != textures.end()) - { - if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second.frameCount) - { - iter->second.Destroy(false); - textures.erase(iter++); - } - else - { - ++iter; - } - } -} - -void TextureCache::InvalidateRange(u32 start_address, u32 size) -{ - TexCache::iterator iter = textures.begin(); - while (iter != textures.end()) - { - int rangePosition = iter->second.IntersectsMemoryRange(start_address, size); - if (rangePosition == 0) - { - iter->second.Destroy(false); - textures.erase(iter++); - } - else - { - ++iter; - } - } -} - -void TextureCache::MakeRangeDynamic(u32 start_address, u32 size) -{ - TexCache::iterator iter = textures.begin(); - while (iter != textures.end()) - { - int rangePosition = iter->second.IntersectsMemoryRange(start_address, size); - if ( rangePosition == 0) - { - iter->second.hash = 0; - } - ++iter; - } -} - -int TextureCache::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size) -{ - if (addr + size_in_bytes < range_address) - return -1; - if (addr >= range_address + range_size) - return 1; - return 0; -} - -TextureCache::TCacheEntry* TextureCache::Load(int texstage, u32 address, int width, int height, u32 tex_format, int tlutaddr, int tlutfmt) -{ - // notes (about "UNsafe texture cache"): - // Have to be removed soon. - // But we keep it until the "safe" way became rock solid - // pros: it has an unique ID held by the texture data itself (@address) once cached. - // cons: it writes this unique ID in the gc RAM <- very dangerous (break MP1) and ugly - - // notes (about "safe texture cache"): - // Metroids text issue (character table): - // Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs). - // That's why we have to hash the TLUT too for TLUT tex_format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2). - // And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but - // have to be a real unique ID. - // DONE but not satifiying yet -> may break copyEFBToTexture sometimes. - - // Pokemon Colosseum text issue (plain text): - // Use a GX_TF_I4 512x512 text-flush-texture at a const address. - // The problem here was just the sparse hash on the texture. This texture is partly overwrited (what is needed only) - // so lot's of remaning old text. Thin white chars on black bg too. - - // TODO: - clean this up when ready to kill old "unsafe texture cache" - // - fix the key index situation with CopyRenderTargetToTexture. - // Could happen only for GX_TF_C4, GX_TF_C8 and GX_TF_C14X2 fmt. - // Wonder if we can't use tex width&height to know if EFB might be copied to it... - // raw idea: TOCHECK if addresses are aligned we have few bits left... - - if (address == 0) - return NULL; - - TexMode0 &tm0 = bpmem.tex[texstage >> 2].texMode0[texstage & 3]; - TexMode1 &tm1 = bpmem.tex[texstage >> 2].texMode1[texstage & 3]; - int maxlevel = (tm1.max_lod >> 4); - bool UseNativeMips = (tm0.min_filter & 3) && (tm0.min_filter != 8) && g_ActiveConfig.bUseNativeMips; - - u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address); - int bsw = TexDecoder_GetBlockWidthInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16; - int bsh = TexDecoder_GetBlockHeightInTexels(tex_format) - 1; // TexelSizeInNibbles(format)*width*height/16; - int bsdepth = TexDecoder_GetTexelSizeInNibbles(tex_format); - int expandedWidth = (width + bsw) & (~bsw); - int expandedHeight = (height + bsh) & (~bsh); - - u64 hash_value = 0; - u32 texID = address; - u64 texHash = 0; - u32 FullFormat = tex_format; - bool TextureisDynamic = false; - if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) - FullFormat = (tex_format | (tlutfmt << 16)); - - if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures) - { - texHash = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) - { - // WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up) - // tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower. - // This trick (to change the texID depending on the TLUT addr) is a trick to get around - // an issue with metroid prime's fonts, where it has multiple sets of fonts on top of - // each other stored in a single texture, and uses the palette to make different characters - // visible or invisible. Thus, unless we want to recreate the textures for every drawn character, - // we must make sure that texture with different tluts get different IDs. - u64 tlutHash = GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - texHash ^= tlutHash; - if (g_ActiveConfig.bSafeTextureCache) - { - texID = texID ^ ((u32)(tlutHash & 0xFFFFFFFF)) ^ ((u32)((tlutHash >> 32) & 0xFFFFFFFF)); - } - } - if (g_ActiveConfig.bSafeTextureCache) - hash_value = texHash; - } - - bool skip_texture_create = false; - TexCache::iterator iter = textures.find(texID); - - if (iter != textures.end()) - { - TCacheEntry &entry = iter->second; - - if (!g_ActiveConfig.bSafeTextureCache) - { - if(entry.isRenderTarget || entry.isDynamic) - { - if(!g_ActiveConfig.bCopyEFBToTexture) - { - hash_value = GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2)) - { - hash_value ^= GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples); - } - } - else - { - hash_value = 0; - } - } - else - { - hash_value = ((u32 *)ptr)[0]; - } - } - else - { - if(entry.isRenderTarget || entry.isDynamic) - { - if(g_ActiveConfig.bCopyEFBToTexture) - { - hash_value = 0; - } - } - } - if (((entry.isRenderTarget || entry.isDynamic) && hash_value == entry.hash && address == entry.addr) - || ((address == entry.addr) && (hash_value == entry.hash) && ((int) FullFormat == entry.fmt) && entry.MipLevels >= maxlevel)) - { - entry.frameCount = frameCount; - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, entry.texture); - GL_REPORT_ERRORD(); - entry.SetTextureParameters(tm0,tm1); - entry.isDynamic = false; - return &entry; - } - else - { - // Let's reload the new texture data into the same texture, - // instead of destroying it and having to create a new one. - // Might speed up movie playback very, very slightly. - TextureisDynamic = (entry.isRenderTarget || entry.isDynamic) && !g_ActiveConfig.bCopyEFBToTexture; - if (((!(entry.isRenderTarget || entry.isDynamic) && width == entry.w && height == entry.h && (int)FullFormat == entry.fmt) || - ((entry.isRenderTarget || entry.isDynamic) && entry.w == width && entry.h == height && entry.Scaledw == width && entry.Scaledh == height))) - { - glBindTexture(GL_TEXTURE_2D, entry.texture); - GL_REPORT_ERRORD(); - entry.SetTextureParameters(tm0,tm1); - skip_texture_create = true; - } - else - { - entry.Destroy(false); - textures.erase(iter); - } - } - } - - // Make an entry in the table - TCacheEntry& entry = textures[texID]; - entry.isDynamic = TextureisDynamic; - entry.isRenderTarget = false; - entry.Realw = width; - entry.Realh = height; - PC_TexFormat pcfmt = PC_TEX_FMT_NONE; - - if (g_ActiveConfig.bHiresTextures) - { - // Load Custom textures - char texPathTemp[MAX_PATH]; - - int newWidth = width; - int newHeight = height; - - sprintf(texPathTemp, "%s_%08x_%i", globals->unique_id, (unsigned int) texHash, tex_format); - pcfmt = HiresTextures::GetHiresTex(texPathTemp, &newWidth, &newHeight, tex_format, temp); - - if (pcfmt != PC_TEX_FMT_NONE) - { - expandedWidth = width = newWidth; - expandedHeight = height = newHeight; - } - } - - if (pcfmt == PC_TEX_FMT_NONE) - pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt); - - entry.oldpixel = ((u32 *)ptr)[0]; - - if (g_ActiveConfig.bSafeTextureCache || entry.isDynamic) - entry.hash = hash_value; - else - { - entry.hash = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF); - ((u32 *)ptr)[0] = entry.hash; - } - - entry.addr = address; - entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format); - - GLenum target = GL_TEXTURE_2D; - if (!skip_texture_create) { - glGenTextures(1, (GLuint *)&entry.texture); - glBindTexture(target, entry.texture); - } - - bool isPow2 = !((width & (width - 1)) || (height & (height - 1))); - int TexLevels = (width > height)?width:height; - TexLevels = (isPow2 && UseNativeMips && (maxlevel > 0)) ? (int)(log((double)TexLevels)/log((double)2)) + 1 : (isPow2? 0 : 1); - if(TexLevels > (maxlevel + 1) && maxlevel > 0) - TexLevels = (maxlevel + 1); - entry.MipLevels = maxlevel; - bool GenerateMipmaps = TexLevels > 1 || TexLevels == 0; - entry.bHaveMipMaps = GenerateMipmaps; - int gl_format = 0; - int gl_iformat = 0; - int gl_type = 0; + glGenTextures(1, &texture); GL_REPORT_ERRORD(); +} + +void TextureCache::TCacheEntry::Bind(unsigned int stage) +{ + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + GL_REPORT_ERRORD(); + + // TODO: is this already done somewhere else? + TexMode0 &tm0 = bpmem.tex[stage >> 2].texMode0[stage & 3]; + TexMode1 &tm1 = bpmem.tex[stage >> 2].texMode1[stage & 3]; + SetTextureParameters(tm0, tm1); +} + +bool TextureCache::TCacheEntry::Save(const char filename[]) +{ + // TODO: make ogl dump PNGs + std::string tga_filename(filename); + tga_filename.replace(tga_filename.size() - 3, 3, "tga"); + + return SaveTexture(tga_filename.c_str(), GL_TEXTURE_2D, texture, w, h); +} + +TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, + unsigned int height, unsigned int expanded_width, + unsigned int tex_levels, PC_TexFormat pcfmt) +{ + int gl_format = 0, + gl_iformat = 0, + gl_type = 0; + if (pcfmt != PC_TEX_FMT_DXT1) { switch (pcfmt) @@ -441,371 +148,160 @@ TextureCache::TCacheEntry* TextureCache::Load(int texstage, u32 address, int wid gl_iformat = 4; gl_type = GL_UNSIGNED_BYTE; break; + case PC_TEX_FMT_RGBA32: gl_format = GL_RGBA; gl_iformat = 4; gl_type = GL_UNSIGNED_BYTE; break; + case PC_TEX_FMT_I4_AS_I8: gl_format = GL_LUMINANCE; gl_iformat = GL_INTENSITY4; gl_type = GL_UNSIGNED_BYTE; break; + case PC_TEX_FMT_IA4_AS_IA8: gl_format = GL_LUMINANCE_ALPHA; gl_iformat = GL_LUMINANCE4_ALPHA4; gl_type = GL_UNSIGNED_BYTE; break; + case PC_TEX_FMT_I8: gl_format = GL_LUMINANCE; gl_iformat = GL_INTENSITY8; gl_type = GL_UNSIGNED_BYTE; break; + case PC_TEX_FMT_IA8: gl_format = GL_LUMINANCE_ALPHA; gl_iformat = GL_LUMINANCE8_ALPHA8; gl_type = GL_UNSIGNED_BYTE; break; + case PC_TEX_FMT_RGB565: gl_format = GL_RGB; gl_iformat = GL_RGB; gl_type = GL_UNSIGNED_SHORT_5_6_5; break; } - if (expandedWidth != width) - glPixelStorei(GL_UNPACK_ROW_LENGTH, expandedWidth); - //generate mipmaps even if we use native mips to suport textures with less levels - if(skip_texture_create) - { - glTexSubImage2D(target, 0,0,0,width, height, gl_format, gl_type, temp); - } - else - { - if (GenerateMipmaps) - { - if(UseNativeMips) - { - glTexImage2D(target, 0, gl_iformat, width, height, 0, gl_format, gl_type, temp); - } - else - { - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); - glTexImage2D(target, 0, gl_iformat, width, height, 0, gl_format, gl_type, temp); - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); - } - } - else - { - glTexImage2D(target, 0, gl_iformat, width, height, 0, gl_format, gl_type, temp); - } - } - - if (expandedWidth != width) // reset - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } - else - { - if (skip_texture_create) - { - glCompressedTexSubImage2D(target, 0,0,0,width, height, - GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,expandedWidth*expandedHeight/2, temp); - } - else - { - glCompressedTexImage2D(target, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, - width, height, 0, expandedWidth*expandedHeight/2, temp); - } - } - GL_REPORT_ERRORD(); - if(TexLevels > 1 && pcfmt != PC_TEX_FMT_NONE) - { - int level = 1; - int mipWidth = width >> 1; - int mipHeight = height >> 1; - ptr += entry.size_in_bytes; - while((mipHeight || mipWidth) && (level < TexLevels)) - { - u32 currentWidth = (mipWidth > 0)? mipWidth : 1; - u32 currentHeight = (mipHeight > 0)? mipHeight : 1; - expandedWidth = (currentWidth + bsw) & (~bsw); - expandedHeight = (currentHeight + bsh) & (~bsh); - TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt); - if (pcfmt != PC_TEX_FMT_DXT1) - { - if (expandedWidth != (int)currentWidth) - glPixelStorei(GL_UNPACK_ROW_LENGTH, expandedWidth); - glTexImage2D(target, level, gl_iformat, currentWidth, currentHeight, 0, gl_format, gl_type, temp); - if (expandedWidth != (int)currentWidth) - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } - else - { - glCompressedTexImage2D(target, level, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, currentWidth, currentHeight, 0, expandedWidth*expandedHeight/2, temp); - } - GL_REPORT_ERRORD(); - u32 size = (max(mipWidth, bsw) * max(mipHeight, bsh) * bsdepth) >> 1; - ptr += size; - mipWidth >>= 1; - mipHeight >>= 1; - level++; - } - } - entry.frameCount = frameCount; - entry.w = width; - entry.h = height; - entry.Scaledw = width; - entry.Scaledh = height; - entry.fmt = FullFormat; - entry.SetTextureParameters(tm0,tm1); - if (g_ActiveConfig.bDumpTextures) - { - // dump texture to file - char szTemp[MAX_PATH]; - char szDir[MAX_PATH]; - const char* uniqueId = globals->unique_id; - static bool bCheckedDumpDir = false; - - sprintf(szDir, "%s%s", File::GetUserPath(D_DUMPTEXTURES_IDX), uniqueId); - - if (!bCheckedDumpDir) - { - if (!File::Exists(szDir) || !File::IsDirectory(szDir)) - File::CreateDir(szDir); - - bCheckedDumpDir = true; - } - - sprintf(szTemp, "%s/%s_%08x_%i.tga", szDir, uniqueId, (unsigned int) texHash, tex_format); - if (!File::Exists(szTemp)) - SaveTexture(szTemp, target, entry.texture, entry.w, entry.h); } - INCSTAT(stats.numTexturesCreated); - SETSTAT(stats.numTexturesAlive, textures.size()); + TCacheEntry &entry = *new TCacheEntry; + entry.gl_format = gl_format; + entry.gl_iformat = gl_iformat; + entry.gl_type = gl_type; + entry.pcfmt = pcfmt; + + entry.bHaveMipMaps = tex_levels != 1; + return &entry; } -void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle &source_rect) +void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level) { - DVSTARTPROFILE(); + //glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + //GL_REPORT_ERRORD(); + + if (pcfmt != PC_TEX_FMT_DXT1) + { + if (expanded_width != width) + glPixelStorei(GL_UNPACK_ROW_LENGTH, expanded_width); + + if (bHaveMipMaps && 0 == level) + { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + glTexImage2D(GL_TEXTURE_2D, level, gl_iformat, width, height, 0, gl_format, gl_type, temp); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); + } + else + { + glTexImage2D(GL_TEXTURE_2D, level, gl_iformat, width, height, 0, gl_format, gl_type, temp); + } + + if (expanded_width != width) + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } + else + { + PanicAlert("PC_TEX_FMT_DXT1 support disabled"); + //glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, + //width, height, 0, expanded_width * expanded_height/2, temp); + } + GL_REPORT_ERRORD(); +} + +TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture( + unsigned int scaled_tex_w, unsigned int scaled_tex_h) +{ + TCacheEntry *const entry = new TCacheEntry; + glBindTexture(GL_TEXTURE_2D, entry->texture); GL_REPORT_ERRORD(); - // for intensity values, use Y of YUV format! - // for all purposes, treat 4bit equivalents as 8bit (probably just used for compression) - // RGBA8 - RGBA8 - // RGB565 - RGB565 - // RGB5A3 - RGB5A3 - // I4,R4,Z4 - I4 - // IA4,RA4 - IA4 - // Z8M,G8,I8,A8,Z8,R8,B8,Z8L - I8 - // Z16,GB8,RG8,Z16L,IA8,RA8 - IA8 - float colmat[16]; - float fConstAdd[4] = {0}; - memset(colmat, 0, sizeof(colmat)); + const GLenum + gl_format = GL_RGBA, + gl_iformat = 4, + gl_type = GL_UNSIGNED_BYTE; - if (bFromZBuffer) - { - switch(copyfmt) - { - case 0: // Z4 - case 1: // Z8 - colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1; - break; - case 3: // Z16 //? - colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1; - break; - case 11: // Z16 (reverse order) - colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1; - break; - case 6: // Z24X8 - colmat[2] = colmat[5] = colmat[8] = colmat[15] = 1; - break; - case 9: // Z8M - colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1; - break; - case 10: // Z8L - colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1; - break; - case 12: // Z16L - colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1; - break; - default: - ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt); - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - break; - } - } - else if (bIsIntensityFmt) - { - // TODO - verify these coefficients - fConstAdd[0] = fConstAdd[1] = fConstAdd[2] = 16.0f/255.0f; - colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f; - colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f; - colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f; - if (copyfmt < 2) - { - fConstAdd[3] = 16.0f / 255.0f; - colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f; - } - else// alpha - colmat[15] = 1; - } - else - { - switch (copyfmt) - { - case 0: // R4 - colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1; - break; - case 8: // R8 - colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1; - break; - case 2: // RA4 - colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1; - break; - case 3: // RA8 - colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1; - break; - case 7: // A8 - colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1; - break; - case 9: // G8 - colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1; - break; - case 10: // B8 - colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1; - break; - case 11: // RG8 - colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1; - break; - case 12: // GB8 - colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1; - break; - case 4: // RGB565 - colmat[0] = colmat[5] = colmat[10] = 1; - fConstAdd[3] = 1; // set alpha to 1 - break; - case 5: // RGB5A3 - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - break; - case 6: // RGBA8 - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - break; - default: - ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt); - colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1; - break; - } - } - - bool bIsInit = textures.find(address) != textures.end(); - - PRIM_LOG("copytarg: addr=0x%x, fromz=%d, intfmt=%d, copyfmt=%d", address, (int)bFromZBuffer, (int)bIsIntensityFmt,copyfmt); - - TCacheEntry& entry = textures[address]; - entry.hash = 0; - entry.frameCount = frameCount; - - int w = (abs(source_rect.GetWidth()) >> bScaleByHalf); - int h = (abs(source_rect.GetHeight()) >> bScaleByHalf); - - float xScale = Renderer::GetTargetScaleX(); - float yScale = Renderer::GetTargetScaleY(); + glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, scaled_tex_w, scaled_tex_h, 0, gl_format, gl_type, NULL); - int Scaledtex_w = (g_ActiveConfig.bCopyEFBScaled)?((int)(xScale * w)) : w; - int Scaledtex_h = (g_ActiveConfig.bCopyEFBScaled)?((int)(yScale * h)) : h; - - GLenum gl_format = GL_RGBA; - GLenum gl_iformat = 4; - GLenum gl_type = GL_UNSIGNED_BYTE; - - GL_REPORT_ERRORD(); - if (!bIsInit) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (GL_REPORT_ERROR() != GL_NO_ERROR) { - glGenTextures(1, (GLuint *)&entry.texture); - glBindTexture(GL_TEXTURE_2D, entry.texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); GL_REPORT_ERRORD(); - glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, Scaledtex_w, Scaledtex_h, 0, gl_format, gl_type, NULL); - GL_REPORT_ERRORD(); - entry.isRenderTarget = true; - entry.isDynamic = false; - } - else - { - _assert_(entry.texture); - GL_REPORT_ERRORD(); - if(entry.isDynamic) - { - Scaledtex_h = h; - Scaledtex_w = w; - } - if (((entry.isRenderTarget || entry.isDynamic) && entry.Scaledw == Scaledtex_w && entry.Scaledh == Scaledtex_h)) - { - glBindTexture(GL_TEXTURE_2D, entry.texture); - // for some reason mario sunshine errors here... - // Beyond Good and Evil does too, occasionally. - GL_REPORT_ERRORD(); - } else { - // Delete existing texture. - glDeleteTextures(1,(GLuint *)&entry.texture); - glGenTextures(1, (GLuint *)&entry.texture); - glBindTexture(GL_TEXTURE_2D, entry.texture); - glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, Scaledtex_w, Scaledtex_h, 0, gl_format, gl_type, NULL); - GL_REPORT_ERRORD(); - entry.isRenderTarget = !entry.isDynamic; - } } - if (!bIsInit) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + return entry; +} - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (GL_REPORT_ERROR() != GL_NO_ERROR) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - GL_REPORT_ERRORD(); - } - } - - entry.addr = address; - entry.w = entry.Realw = w; - entry.h = entry.Realh = h; - entry.Scaledw = Scaledtex_w; - entry.Scaledh = Scaledtex_h; - entry.fmt = copyfmt; - entry.hash = 0; +void TextureCache::TCacheEntry::FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect, + bool bIsIntensityFmt, u32 copyfmt) +{ + glBindTexture(GL_TEXTURE_2D, texture); // Make sure to resolve anything we need to read from. - GLuint read_texture = bFromZBuffer ? g_framebufferManager.ResolveAndGetDepthTarget(source_rect) : g_framebufferManager.ResolveAndGetRenderTarget(source_rect); + const GLuint read_texture = bFromZBuffer ? + g_framebufferManager.ResolveAndGetDepthTarget(source_rect) : + g_framebufferManager.ResolveAndGetRenderTarget(source_rect); + + // TODO: move + const float xScale = Renderer::GetTargetScaleX(); + const float yScale = Renderer::GetTargetScaleY(); GL_REPORT_ERRORD(); - // We have to run a pixel shader, for color conversion. - Renderer::ResetAPIState(); // reset any game specific settings - if(!entry.isDynamic || g_ActiveConfig.bCopyEFBToTexture) + if (false == isDynamic || g_ActiveConfig.bCopyEFBToTexture) { if (s_TempFramebuffer == 0) - glGenFramebuffersEXT(1, (GLuint *)&s_TempFramebuffer); + glGenFramebuffersEXT(1, (GLuint*)&s_TempFramebuffer); g_framebufferManager.SetFramebuffer(s_TempFramebuffer); // Bind texture to temporary framebuffer - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, entry.texture, 0); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0); GL_REPORT_FBO_ERROR(); GL_REPORT_ERRORD(); - + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_RECTANGLE_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, read_texture); - - glViewport(0, 0, Scaledtex_w, Scaledtex_h); + + glViewport(0, 0, scaledW, scaledH); PixelShaderCache::SetCurrentShader(bFromZBuffer ? PixelShaderCache::GetDepthMatrixProgram() : PixelShaderCache::GetColorMatrixProgram()); + const float* const fConstAdd = colmat + 16; // fConstAdd is the last 4 floats of colmat PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation GL_REPORT_ERRORD(); @@ -823,11 +319,11 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo // Unbind texture from temporary framebuffer glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0); } - - if(!g_ActiveConfig.bCopyEFBToTexture) + + if (false == g_ActiveConfig.bCopyEFBToTexture) { - textures[address].hash = TextureConverter::EncodeToRamFromTexture( - address, + hash = TextureConverter::EncodeToRamFromTexture( + addr, read_texture, xScale, yScale, @@ -836,32 +332,69 @@ void TextureCache::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, boo copyfmt, bScaleByHalf, source_rect); - } - // Return to the EFB. + g_framebufferManager.SetFramebuffer(0); - Renderer::RestoreAPIState(); VertexShaderManager::SetViewportChanged(); - TextureCache::DisableStage(0); + DisableStage(0); GL_REPORT_ERRORD(); - if (g_ActiveConfig.bDumpEFBTarget) - { + if (g_ActiveConfig.bDumpEFBTarget) + { static int count = 0; - SaveTexture(StringFromFormat("%sefb_frame_%i.tga", File::GetUserPath(D_DUMPTEXTURES_IDX), count++).c_str(), GL_TEXTURE_2D, entry.texture, entry.w, entry.h); - } + SaveTexture(StringFromFormat("%sefb_frame_%i.tga", File::GetUserPath(D_DUMPTEXTURES_IDX), + count++).c_str(), GL_TEXTURE_2D, texture, w, h); + } } -void TextureCache::DisableStage(int stage) +void TextureCache::TCacheEntry::SetTextureParameters(const TexMode0 &newmode, const TexMode1 &newmode1) +{ + // TODO: not used anywhere + TexMode0 mode = newmode; + //mode1 = newmode1; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + (newmode.mag_filter || g_Config.bForceFiltering) ? GL_LINEAR : GL_NEAREST); + + if (bHaveMipMaps) + { + // TODO: not used anywhere + if (g_ActiveConfig.bForceFiltering && newmode.min_filter < 4) + mode.min_filter += 4; // take equivalent forced linear + + int filt = newmode.min_filter; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c_MinLinearFilter[filt & 7]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, newmode1.min_lod >> 4); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, newmode1.max_lod >> 4); + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, (newmode.lod_bias / 32.0f)); + } + else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + (g_ActiveConfig.bForceFiltering || newmode.min_filter >= 4) ? GL_LINEAR : GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c_WrapSettings[newmode.wrap_s]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c_WrapSettings[newmode.wrap_t]); + + if (g_Config.iMaxAnisotropy >= 1) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (float)(1 << g_ActiveConfig.iMaxAnisotropy)); +} + +TextureCache::~TextureCache() +{ + if (s_TempFramebuffer) + { + glDeleteFramebuffersEXT(1, (GLuint*)&s_TempFramebuffer); + s_TempFramebuffer = 0; + } +} + +void TextureCache::DisableStage(unsigned int stage) { glActiveTexture(GL_TEXTURE0 + stage); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_RECTANGLE_ARB); } -void TextureCache::ClearRenderTargets() -{ - for (TexCache::iterator iter = textures.begin(); iter != textures.end(); ++iter) - iter->second.isRenderTarget = false; } diff --git a/Source/Plugins/Plugin_VideoOGL/Src/TextureCache.h b/Source/Plugins/Plugin_VideoOGL/Src/TextureCache.h index 46cda618c2..a9852d9a9a 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/TextureCache.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/TextureCache.h @@ -24,61 +24,62 @@ #include "GLUtil.h" #include "BPStructs.h" -class TextureCache +#include "TextureCacheBase.h" + +namespace OGL +{ + +class TextureCache : public ::TextureCache { public: - struct TCacheEntry - { - TCacheEntry() : texture(0), addr(0), size_in_bytes(0), hash(0), - w(0), h(0), MipLevels(0),Scaledw(0), Scaledh(0), - isRenderTarget(false), isDynamic(false), bHaveMipMaps(false) - { mode.hex = 0xFCFCFCFC; } - - GLuint texture; - u32 addr; - u32 size_in_bytes; - u64 hash; - u32 paletteHash; - u32 oldpixel; // used for simple cleanup - TexMode0 mode; // current filter and clamp modes that texture is set to - TexMode1 mode1; // current filter and clamp modes that texture is set to - - int frameCount; - int w, h, fmt,MipLevels, Realw, Realh; - int Scaledw, Scaledh; - - bool isRenderTarget; // if render texture, then rendertex is filled with the direct copy of the render target - // later conversions would have to convert properly from rendertexfmt to texfmt - bool isDynamic; // modified from cpu - bool bHaveMipMaps; - - void SetTextureParameters(TexMode0& newmode,TexMode1 &newmode1); - void Destroy(bool shutdown); - void ConvertFromRenderTarget(u32 taddr, int twidth, int theight, int tformat, int tlutaddr, int tlutfmt); - int IntersectsMemoryRange(u32 range_address, u32 range_size); - }; + static void DisableStage(unsigned int stage); private: - typedef std::map TexCache; + struct TCacheEntry : TCacheEntryBase + { + GLuint texture; - static u8 *temp; - static TexCache textures; + PC_TexFormat pcfmt; -public: - static void Init(); - static void Cleanup(); - static void Shutdown(); - static void Invalidate(bool shutdown); - static void InvalidateRange(u32 start_address, u32 size); - static void MakeRangeDynamic(u32 start_address, u32 size); - static TCacheEntry* Load(int texstage, u32 address, int width, int height, u32 format, int tlutaddr, int tlutfmt); - static void CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle &source); + int gl_format; + int gl_iformat; + int gl_type; - static void DisableStage(int stage); // sets active texture + bool bHaveMipMaps; - static void ClearRenderTargets(); // sets render target value of all textures to false + //TexMode0 mode; // current filter and clamp modes that texture is set to + //TexMode1 mode1; // current filter and clamp modes that texture is set to + + TCacheEntry(); + ~TCacheEntry(); + + void Load(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int level); + + void FromRenderTarget(bool bFromZBuffer, bool bScaleByHalf, + unsigned int cbufid, const float colmat[], const EFBRectangle &source_rect, + bool bIsIntensityFmt, u32 copyfmt); + + void Bind(unsigned int stage); + bool Save(const char filename[]); + + private: + void SetTextureParameters(const TexMode0 &newmode, const TexMode1 &newmode1); + }; + + ~TextureCache(); + + TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, + unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt); + + TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h); + +private: + bool isOGL() { return true; } }; bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int height); +} + #endif // _TEXTUREMNGR_H_ diff --git a/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp b/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp index 4e2adeda6c..9236f2a627 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/TextureConverter.cpp @@ -34,6 +34,8 @@ namespace TextureConverter { +using OGL::TextureCache; + static GLuint s_texConvFrameBuffer = 0; static GLuint s_srcTexture = 0; // for decoding from RAM static GLuint s_srcTextureWidth = 0; diff --git a/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp index 40938f4178..bf2b632b91 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/VertexManager.cpp @@ -154,23 +154,25 @@ void VertexManager::vFlush() { glActiveTexture(GL_TEXTURE0 + i); FourTexUnits &tex = bpmem.tex[i >> 2]; - TextureCache::TCacheEntry* tentry = TextureCache::Load(i, + TextureCache::TCacheEntryBase* tentry = TextureCache::Load(i, (tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5, tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1, tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9, - tex.texTlut[i&3].tlut_format); + tex.texTlut[i&3].tlut_format, + (tex.texMode0[i&3].min_filter & 3) && (tex.texMode0[i&3].min_filter != 8) && g_ActiveConfig.bUseNativeMips, + (tex.texMode1[i&3].max_lod >> 4)); if (tentry) { // 0s are probably for no manual wrapping needed. - PixelShaderManager::SetTexDims(i, tentry->Realw, tentry->Realh, 0, 0); + PixelShaderManager::SetTexDims(i, tentry->nativeW, tentry->nativeH, 0, 0); if (g_ActiveConfig.iLog & CONF_SAVETEXTURES) { // save the textures char strfile[255]; sprintf(strfile, "%stex%.3d_%d.tga", File::GetUserPath(D_DUMPFRAMES_IDX), g_Config.iSaveTargetId, i); - SaveTexture(strfile, GL_TEXTURE_2D, tentry->texture, tentry->w, tentry->h); + tentry->Save(strfile); } } else diff --git a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp index 94d4531a62..0f83984783 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp @@ -223,7 +223,7 @@ void Video_Prepare() CommandProcessor::Init(); PixelEngine::Init(); - TextureCache::Init(); + g_texture_cache = new OGL::TextureCache; BPInit(); g_vertex_manager = new OGL::VertexManager; @@ -267,7 +267,7 @@ void Shutdown() PixelShaderManager::Shutdown(); PixelShaderCache::Shutdown(); delete g_vertex_manager; - TextureCache::Shutdown(); + delete g_texture_cache; OpcodeDecoder_Shutdown(); Renderer::Shutdown(); OpenGL_Shutdown();