Merge pull request #1899 from degasus/texcache

Texture Pool
This commit is contained in:
Markus Wick 2015-01-19 00:12:22 +01:00
commit 5357b9c95f
12 changed files with 185 additions and 390 deletions

View file

@ -81,40 +81,43 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
D3D::ReplaceRGBATexture2D(texture->GetTex(), TextureCache::temp, width, height, expanded_width, level, usage); D3D::ReplaceRGBATexture2D(texture->GetTex(), TextureCache::temp, width, height, expanded_width, level, usage);
} }
TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, unsigned int height, TextureCache::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config)
unsigned int tex_levels, PC_TexFormat pcfmt)
{ {
D3D11_USAGE usage = D3D11_USAGE_DEFAULT; if (config.rendertarget)
D3D11_CPU_ACCESS_FLAG cpu_access = (D3D11_CPU_ACCESS_FLAG)0;
if (tex_levels == 1)
{ {
usage = D3D11_USAGE_DYNAMIC; return new TCacheEntry(config, D3DTexture2D::Create(config.width, config.height,
cpu_access = D3D11_CPU_ACCESS_WRITE; (D3D11_BIND_FLAG)((int)D3D11_BIND_RENDER_TARGET | (int)D3D11_BIND_SHADER_RESOURCE),
D3D11_USAGE_DEFAULT, DXGI_FORMAT_R8G8B8A8_UNORM, 1, config.layers));
} }
else
{
D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
D3D11_CPU_ACCESS_FLAG cpu_access = (D3D11_CPU_ACCESS_FLAG)0;
const D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, if (config.levels == 1)
width, height, 1, tex_levels, D3D11_BIND_SHADER_RESOURCE, usage, cpu_access); {
usage = D3D11_USAGE_DYNAMIC;
cpu_access = D3D11_CPU_ACCESS_WRITE;
}
ID3D11Texture2D *pTexture; const D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM,
const HRESULT hr = D3D::device->CreateTexture2D(&texdesc, nullptr, &pTexture); config.width, config.height, 1, config.levels, D3D11_BIND_SHADER_RESOURCE, usage, cpu_access);
CHECK(SUCCEEDED(hr), "Create texture of the TextureCache");
TCacheEntryConfig config; ID3D11Texture2D *pTexture;
config.width = width; const HRESULT hr = D3D::device->CreateTexture2D(&texdesc, nullptr, &pTexture);
config.height = height; CHECK(SUCCEEDED(hr), "Create texture of the TextureCache");
config.levels = tex_levels;
TCacheEntry* const entry = new TCacheEntry(config, new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE)); TCacheEntry* const entry = new TCacheEntry(config, new D3DTexture2D(pTexture, D3D11_BIND_SHADER_RESOURCE));
entry->usage = usage; entry->usage = usage;
// TODO: better debug names // TODO: better debug names
D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetTex(), "a 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"); D3D::SetDebugObjectName((ID3D11DeviceChild*)entry->texture->GetSRV(), "shader resource view of a texture of the TextureCache");
SAFE_RELEASE(pTexture); SAFE_RELEASE(pTexture);
return entry; return entry;
}
} }
void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFormat, void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFormat,
@ -192,20 +195,6 @@ void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFo
} }
} }
TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
unsigned int scaled_tex_w, unsigned int scaled_tex_h, unsigned int layers)
{
TCacheEntryConfig config;
config.width = scaled_tex_w;
config.height = scaled_tex_h;
config.layers = layers;
config.rendertarget = true;
return new TCacheEntry(config, 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, 1, layers));
}
TextureCache::TextureCache() TextureCache::TextureCache()
{ {
// FIXME: Is it safe here? // FIXME: Is it safe here?

View file

@ -38,10 +38,8 @@ private:
bool Save(const std::string& filename, unsigned int level) override; bool Save(const std::string& filename, unsigned int level) override;
}; };
TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override;
unsigned int tex_levels, PC_TexFormat pcfmt) override;
TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h, unsigned int layers) override;
u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) {return 0;}; u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source) {return 0;};
void CompileShaders() override { } void CompileShaders() override { }

View file

@ -109,80 +109,28 @@ bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int l
return SaveTexture(filename, GL_TEXTURE_2D_ARRAY, texture, config.width, config.height, level); return SaveTexture(filename, GL_TEXTURE_2D_ARRAY, texture, config.width, config.height, level);
} }
TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width, unsigned int height, TextureCache::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config)
unsigned int tex_levels, PC_TexFormat pcfmt)
{ {
int gl_format = 0, TCacheEntry* entry = new TCacheEntry(config);
gl_iformat = 0,
gl_type = 0;
if (pcfmt != PC_TEX_FMT_DXT1)
{
switch (pcfmt)
{
default:
case PC_TEX_FMT_NONE:
PanicAlert("Invalid PC texture format %i", pcfmt);
case PC_TEX_FMT_BGRA32:
gl_format = GL_BGRA;
gl_iformat = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_RGBA32:
gl_format = GL_RGBA;
gl_iformat = GL_RGBA;
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;
}
}
TCacheEntryConfig config;
config.width = width;
config.height = height;
config.levels = tex_levels;
TCacheEntry &entry = *new TCacheEntry(config);
entry.gl_format = gl_format;
entry.gl_iformat = gl_iformat;
entry.gl_type = gl_type;
entry.pcfmt = pcfmt;
glActiveTexture(GL_TEXTURE0+9); glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D_ARRAY, entry.texture); glBindTexture(GL_TEXTURE_2D_ARRAY, entry->texture);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, tex_levels - 1);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, config.levels - 1);
if (config.rendertarget)
{
for (u32 level = 0; level <= config.levels; level++)
{
glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, config.width, config.height, config.layers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
glGenFramebuffers(1, &entry->framebuffer);
FramebufferManager::SetFramebuffer(entry->framebuffer);
FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_ARRAY, entry->texture, 0);
}
TextureCache::SetStage(); TextureCache::SetStage();
return entry;
return &entry;
} }
void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
@ -194,57 +142,18 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
PanicAlert("size of level %d must be %dx%d, but %dx%d requested", PanicAlert("size of level %d must be %dx%d, but %dx%d requested",
level, std::max(1u, config.width >> level), std::max(1u, config.height >> level), width, height); level, std::max(1u, config.width >> level), std::max(1u, config.height >> level), width, height);
if (pcfmt != PC_TEX_FMT_DXT1)
{
glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
if (expanded_width != width)
glPixelStorei(GL_UNPACK_ROW_LENGTH, expanded_width);
glTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_iformat, width, height, 1, 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);
}
TextureCache::SetStage();
}
TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
unsigned int scaled_tex_w, unsigned int scaled_tex_h, unsigned int layers)
{
TCacheEntryConfig config;
config.width = scaled_tex_w;
config.height = scaled_tex_h;
config.layers = layers;
config.rendertarget = true;
TCacheEntry *const entry = new TCacheEntry(config);
glActiveTexture(GL_TEXTURE0+9); glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D_ARRAY, entry->texture); glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
const GLenum if (expanded_width != width)
gl_format = GL_RGBA, glPixelStorei(GL_UNPACK_ROW_LENGTH, expanded_width);
gl_iformat = GL_RGBA,
gl_type = GL_UNSIGNED_BYTE;
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0); glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, width, height, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, gl_iformat, scaled_tex_w, scaled_tex_h, layers, 0, gl_format, gl_type, nullptr);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
glGenFramebuffers(1, &entry->framebuffer); if (expanded_width != width)
FramebufferManager::SetFramebuffer(entry->framebuffer); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_ARRAY, entry->texture, 0);
SetStage(); TextureCache::SetStage();
return entry;
} }
void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFormat, void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFormat,

View file

@ -27,12 +27,6 @@ private:
GLuint texture; GLuint texture;
GLuint framebuffer; GLuint framebuffer;
PC_TexFormat pcfmt;
int gl_format;
int gl_iformat;
int gl_type;
//TexMode0 mode; // current filter and clamp modes that texture is set to //TexMode0 mode; // current filter and clamp modes that texture is set to
//TexMode1 mode1; // current filter and clamp modes that texture is set to //TexMode1 mode1; // current filter and clamp modes that texture is set to
@ -53,10 +47,7 @@ private:
~TextureCache(); ~TextureCache();
TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) override;
unsigned int tex_levels, PC_TexFormat pcfmt) override;
TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h, unsigned int layers) override;
void CompileShaders() override; void CompileShaders() override;
void DeleteShaders() override; void DeleteShaders() override;

View file

@ -28,6 +28,7 @@ std::string Statistics::ToString()
{ {
std::string str; std::string str;
str += StringFromFormat("Textures created: %i\n", stats.numTexturesCreated); str += StringFromFormat("Textures created: %i\n", stats.numTexturesCreated);
str += StringFromFormat("Textures uploaded: %i\n", stats.numTexturesUploaded);
str += StringFromFormat("Textures alive: %i\n", stats.numTexturesAlive); str += StringFromFormat("Textures alive: %i\n", stats.numTexturesAlive);
str += StringFromFormat("pshaders created: %i\n", stats.numPixelShadersCreated); str += StringFromFormat("pshaders created: %i\n", stats.numPixelShadersCreated);
str += StringFromFormat("pshaders alive: %i\n", stats.numPixelShadersAlive); str += StringFromFormat("pshaders alive: %i\n", stats.numPixelShadersAlive);

View file

@ -18,6 +18,7 @@ struct Statistics
int numVertexShadersAlive; int numVertexShadersAlive;
int numTexturesCreated; int numTexturesCreated;
int numTexturesUploaded;
int numTexturesAlive; int numTexturesAlive;
int numVertexLoaders; int numVertexLoaders;

View file

@ -22,7 +22,7 @@
static const u64 TEXHASH_INVALID = 0; static const u64 TEXHASH_INVALID = 0;
static const int TEXTURE_KILL_THRESHOLD = 200; static const int TEXTURE_KILL_THRESHOLD = 200;
static const int RENDER_TARGET_KILL_THRESHOLD = 3; static const int TEXTURE_POOL_KILL_THRESHOLD = 3;
static const u64 FRAMECOUNT_INVALID = 0; static const u64 FRAMECOUNT_INVALID = 0;
TextureCache *g_texture_cache; TextureCache *g_texture_cache;
@ -31,7 +31,7 @@ GC_ALIGNED16(u8 *TextureCache::temp) = nullptr;
size_t TextureCache::temp_size; size_t TextureCache::temp_size;
TextureCache::TexCache TextureCache::textures; TextureCache::TexCache TextureCache::textures;
TextureCache::RenderTargetPool TextureCache::render_target_pool; TextureCache::TexPool TextureCache::texture_pool;
TextureCache::BackupConfig TextureCache::backup_config; TextureCache::BackupConfig TextureCache::backup_config;
@ -80,11 +80,11 @@ void TextureCache::Invalidate()
} }
textures.clear(); textures.clear();
for (auto& rt : render_target_pool) for (auto& rt : texture_pool)
{ {
delete rt; delete rt.second;
} }
render_target_pool.clear(); texture_pool.clear();
} }
TextureCache::~TextureCache() TextureCache::~TextureCache()
@ -152,7 +152,7 @@ void TextureCache::Cleanup(int _frameCount)
// EFB copies living on the host GPU are unrecoverable and thus shouldn't be deleted // EFB copies living on the host GPU are unrecoverable and thus shouldn't be deleted
!iter->second->IsEfbCopy()) !iter->second->IsEfbCopy())
{ {
delete iter->second; FreeTexture(iter->second);
iter = textures.erase(iter); iter = textures.erase(iter);
} }
else else
@ -161,19 +161,22 @@ void TextureCache::Cleanup(int _frameCount)
} }
} }
for (size_t i = 0; i < render_target_pool.size();) TexPool::iterator iter2 = texture_pool.begin();
TexPool::iterator tcend2 = texture_pool.end();
while (iter2 != tcend2)
{ {
auto rt = render_target_pool[i]; if(iter2->second->frameCount == FRAMECOUNT_INVALID)
if (_frameCount > RENDER_TARGET_KILL_THRESHOLD + rt->frameCount)
{ {
delete rt; iter2->second->frameCount = _frameCount;
render_target_pool[i] = render_target_pool.back(); }
render_target_pool.pop_back(); if (_frameCount > TEXTURE_POOL_KILL_THRESHOLD + iter2->second->frameCount)
{
delete iter2->second;
iter2 = texture_pool.erase(iter2);
} }
else else
{ {
++i; ++iter2;
} }
} }
} }
@ -187,7 +190,7 @@ void TextureCache::InvalidateRange(u32 start_address, u32 size)
{ {
if (iter->second->OverlapsMemoryRange(start_address, size)) if (iter->second->OverlapsMemoryRange(start_address, size))
{ {
delete iter->second; FreeTexture(iter->second);
textures.erase(iter++); textures.erase(iter++);
} }
else else
@ -246,7 +249,7 @@ void TextureCache::ClearRenderTargets()
{ {
if (iter->second->type == TCET_EC_VRAM) if (iter->second->type == TCET_EC_VRAM)
{ {
delete iter->second; FreeTexture(iter->second);
textures.erase(iter++); textures.erase(iter++);
} }
else else
@ -323,7 +326,6 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
u64 tlut_hash = TEXHASH_INVALID; u64 tlut_hash = TEXHASH_INVALID;
u32 full_format = texformat; u32 full_format = texformat;
PC_TexFormat pcfmt = PC_TEX_FMT_NONE;
const bool isPaletteTexture = (texformat == GX_TF_C4 || texformat == GX_TF_C8 || texformat == GX_TF_C14X2); const bool isPaletteTexture = (texformat == GX_TF_C4 || texformat == GX_TF_C8 || texformat == GX_TF_C14X2);
if (isPaletteTexture) if (isPaletteTexture)
@ -362,7 +364,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
// e.g. 64x64 with 7 LODs would have the mipmap chain 64x64,32x32,16x16,8x8,4x4,2x2,1x1,0x0, so we limit the mipmap count to 6 there // e.g. 64x64 with 7 LODs would have the mipmap chain 64x64,32x32,16x16,8x8,4x4,2x2,1x1,0x0, so we limit the mipmap count to 6 there
tex_levels = std::min<u32>(IntLog2(std::max(width, height)) + 1, tex_levels); tex_levels = std::min<u32>(IntLog2(std::max(width, height)) + 1, tex_levels);
TCacheEntryBase *entry = textures[texID]; TCacheEntryBase*& entry = textures[texID];
if (entry) if (entry)
{ {
// 1. Calculate reference hash: // 1. Calculate reference hash:
@ -388,29 +390,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
return ReturnEntry(stage, entry); return ReturnEntry(stage, entry);
} }
// 3. If we reach this line, we'll have to upload the new texture data to VRAM. // pool this texture and make a new one later
// If we're lucky, the texture parameters didn't change and we can reuse the internal texture object instead of destroying and recreating it. FreeTexture(entry);
//
// TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies?
// TODO: Actually, it should be enough if the internal texture format matches...
if (((entry->type == TCET_NORMAL &&
width == entry->config.width &&
height == entry->config.height &&
full_format == entry->format &&
entry->config.levels >= tex_levels) ||
(entry->type == TCET_EC_DYNAMIC &&
entry->native_width == width &&
entry->native_height == height)) &&
entry->config.layers == 1)
{
// reuse the texture
}
else
{
// delete the texture and make a new one
delete entry;
entry = nullptr;
}
} }
std::unique_ptr<HiresTexture> hires_tex; std::unique_ptr<HiresTexture> hires_tex;
@ -430,19 +411,11 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
{ {
width = l.width; width = l.width;
height = l.height; height = l.height;
// If we thought we could reuse the texture before, make sure to pool it now!
if (entry)
{
delete entry;
entry = nullptr;
}
} }
expandedWidth = l.width; expandedWidth = l.width;
expandedHeight = l.height; expandedHeight = l.height;
CheckTempSize(l.data_size); CheckTempSize(l.data_size);
memcpy(temp, l.data, l.data_size); memcpy(temp, l.data, l.data_size);
pcfmt = PC_TEX_FMT_RGBA32;
} }
} }
@ -451,12 +424,12 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
if (!(texformat == GX_TF_RGBA8 && from_tmem)) if (!(texformat == GX_TF_RGBA8 && from_tmem))
{ {
const u8* tlut = &texMem[tlutaddr]; const u8* tlut = &texMem[tlutaddr];
pcfmt = TexDecoder_Decode(temp, src_data, expandedWidth, expandedHeight, texformat, tlut, (TlutFormat) tlutfmt); TexDecoder_Decode(temp, src_data, expandedWidth, expandedHeight, texformat, tlut, (TlutFormat) tlutfmt);
} }
else else
{ {
u8* src_data_gb = &texMem[bpmem.tex[stage/4].texImage2[stage%4].tmem_odd * TMEM_LINE_SIZE]; u8* src_data_gb = &texMem[bpmem.tex[stage/4].texImage2[stage%4].tmem_odd * TMEM_LINE_SIZE];
pcfmt = TexDecoder_DecodeRGBA8FromTmem(temp, src_data, src_data_gb, expandedWidth, expandedHeight); TexDecoder_DecodeRGBA8FromTmem(temp, src_data, src_data_gb, expandedWidth, expandedHeight);
} }
} }
@ -466,21 +439,14 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
const bool use_native_mips = use_mipmaps && !using_custom_lods && (width == nativeW && height == nativeH); const bool use_native_mips = use_mipmaps && !using_custom_lods && (width == nativeW && height == nativeH);
texLevels = (use_native_mips || using_custom_lods) ? texLevels : 1; // TODO: Should be forced to 1 for non-pow2 textures (e.g. efb copies with automatically adjusted IR) texLevels = (use_native_mips || using_custom_lods) ? texLevels : 1; // TODO: Should be forced to 1 for non-pow2 textures (e.g. efb copies with automatically adjusted IR)
if (entry && entry->config.levels != texLevels)
{
// delete the texture and make a new one
delete entry;
entry = nullptr;
}
// create the entry/texture // create the entry/texture
if (nullptr == entry) TCacheEntryConfig config;
{ config.width = width;
textures[texID] = entry = g_texture_cache->CreateTexture(width, height, texLevels, pcfmt); config.height = height;
entry->type = TCET_NORMAL; config.levels = texLevels;
entry = AllocateTexture(config);
GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); entry->type = TCET_NORMAL;
} GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true);
entry->SetGeneralParameters(address, texture_size, full_format); entry->SetGeneralParameters(address, texture_size, full_format);
entry->SetDimensions(nativeW, nativeH, tex_levels); entry->SetDimensions(nativeW, nativeH, tex_levels);
@ -508,53 +474,50 @@ TextureCache::TCacheEntryBase* TextureCache::Load(const u32 stage)
u32 level = 1; u32 level = 1;
// load mips - TODO: Loading mipmaps from tmem is untested! // load mips - TODO: Loading mipmaps from tmem is untested!
if (pcfmt != PC_TEX_FMT_NONE) if (use_native_mips)
{ {
if (use_native_mips) src_data += texture_size;
const u8* ptr_even = nullptr;
const u8* ptr_odd = nullptr;
if (from_tmem)
{ {
src_data += texture_size; ptr_even = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE + texture_size];
ptr_odd = &texMem[bpmem.tex[stage/4].texImage2[stage%4].tmem_odd * TMEM_LINE_SIZE];
const u8* ptr_even = nullptr;
const u8* ptr_odd = nullptr;
if (from_tmem)
{
ptr_even = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE + texture_size];
ptr_odd = &texMem[bpmem.tex[stage/4].texImage2[stage%4].tmem_odd * TMEM_LINE_SIZE];
}
for (; level != texLevels; ++level)
{
const u32 mip_width = CalculateLevelSize(width, level);
const u32 mip_height = CalculateLevelSize(height, level);
const u32 expanded_mip_width = (mip_width + bsw) & (~bsw);
const u32 expanded_mip_height = (mip_height + bsh) & (~bsh);
const u8*& mip_src_data = from_tmem
? ((level % 2) ? ptr_odd : ptr_even)
: src_data;
const u8* tlut = &texMem[tlutaddr];
TexDecoder_Decode(temp, mip_src_data, expanded_mip_width, expanded_mip_height, texformat, tlut, (TlutFormat) tlutfmt);
mip_src_data += TexDecoder_GetTextureSizeInBytes(expanded_mip_width, expanded_mip_height, texformat);
entry->Load(mip_width, mip_height, expanded_mip_width, level);
if (g_ActiveConfig.bDumpTextures)
DumpTexture(entry, basename, level);
}
} }
else if (using_custom_lods)
for (; level != texLevels; ++level)
{ {
for (; level != texLevels; ++level) const u32 mip_width = CalculateLevelSize(width, level);
{ const u32 mip_height = CalculateLevelSize(height, level);
auto& l = hires_tex->m_levels[level]; const u32 expanded_mip_width = (mip_width + bsw) & (~bsw);
CheckTempSize(l.data_size); const u32 expanded_mip_height = (mip_height + bsh) & (~bsh);
memcpy(temp, l.data, l.data_size);
entry->Load(l.width, l.height, l.width, level); const u8*& mip_src_data = from_tmem
} ? ((level % 2) ? ptr_odd : ptr_even)
: src_data;
const u8* tlut = &texMem[tlutaddr];
TexDecoder_Decode(temp, mip_src_data, expanded_mip_width, expanded_mip_height, texformat, tlut, (TlutFormat) tlutfmt);
mip_src_data += TexDecoder_GetTextureSizeInBytes(expanded_mip_width, expanded_mip_height, texformat);
entry->Load(mip_width, mip_height, expanded_mip_width, level);
if (g_ActiveConfig.bDumpTextures)
DumpTexture(entry, basename, level);
}
}
else if (using_custom_lods)
{
for (; level != texLevels; ++level)
{
auto& l = hires_tex->m_levels[level];
CheckTempSize(l.data_size);
memcpy(temp, l.data, l.data_size);
entry->Load(l.width, l.height, l.width, level);
} }
} }
INCSTAT(stats.numTexturesCreated); INCSTAT(stats.numTexturesUploaded);
SETSTAT(stats.numTexturesAlive, textures.size()); SETSTAT(stats.numTexturesAlive, textures.size());
return ReturnEntry(stage, entry); return ReturnEntry(stage, entry);
@ -846,69 +809,46 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat
unsigned int scaled_tex_w = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledX(tex_w) : tex_w; unsigned int scaled_tex_w = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledX(tex_w) : tex_w;
unsigned int scaled_tex_h = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledY(tex_h) : tex_h; unsigned int scaled_tex_h = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledY(tex_h) : tex_h;
const unsigned int efb_layers = FramebufferManagerBase::GetEFBLayers(); TCacheEntryBase*& entry = textures[dstAddr];
TCacheEntryBase *entry = textures[dstAddr];
if (entry) if (entry)
{ FreeTexture(entry);
if (entry->type == TCET_EC_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h && entry->config.layers == efb_layers)
{
scaled_tex_w = tex_w;
scaled_tex_h = tex_h;
}
else if (!(entry->type == TCET_EC_VRAM && entry->config.width == scaled_tex_w && entry->config.height == scaled_tex_h && entry->config.layers == efb_layers))
{
if (entry->type == TCET_EC_VRAM)
{
// try to re-use this render target later
FreeRenderTarget(entry);
}
else
{
// remove it and recreate it as a render target
delete entry;
}
entry = nullptr; // create the texture
} TCacheEntryConfig config;
} config.rendertarget = true;
config.width = scaled_tex_w;
config.height = scaled_tex_h;
config.layers = FramebufferManagerBase::GetEFBLayers();
if (nullptr == entry) entry = AllocateTexture(config);
{
// create the texture
textures[dstAddr] = entry = AllocateRenderTarget(scaled_tex_w, scaled_tex_h, FramebufferManagerBase::GetEFBLayers());
// TODO: Using the wrong dstFormat, dumb... // TODO: Using the wrong dstFormat, dumb...
entry->SetGeneralParameters(dstAddr, 0, dstFormat); entry->SetGeneralParameters(dstAddr, 0, dstFormat);
entry->SetDimensions(tex_w, tex_h, 1); entry->SetDimensions(tex_w, tex_h, 1);
entry->SetHashes(TEXHASH_INVALID); entry->SetHashes(TEXHASH_INVALID);
entry->type = TCET_EC_VRAM; entry->type = TCET_EC_VRAM;
}
entry->frameCount = FRAMECOUNT_INVALID; entry->frameCount = FRAMECOUNT_INVALID;
entry->FromRenderTarget(dstAddr, dstFormat, srcFormat, srcRect, isIntensity, scaleByHalf, cbufid, colmat); entry->FromRenderTarget(dstAddr, dstFormat, srcFormat, srcRect, isIntensity, scaleByHalf, cbufid, colmat);
} }
TextureCache::TCacheEntryBase* TextureCache::AllocateRenderTarget(unsigned int width, unsigned int height, unsigned int layers) TextureCache::TCacheEntryBase* TextureCache::AllocateTexture(const TCacheEntryConfig& config)
{ {
for (size_t i = 0; i < render_target_pool.size(); ++i) TexPool::iterator iter = texture_pool.find(config);
if (iter != texture_pool.end())
{ {
auto rt = render_target_pool[i]; TextureCache::TCacheEntryBase* entry = iter->second;
texture_pool.erase(iter);
if (rt->config.width != width || rt->config.height != height || rt->config.layers != layers) return entry;
continue;
render_target_pool[i] = render_target_pool.back();
render_target_pool.pop_back();
return rt;
} }
return g_texture_cache->CreateRenderTargetTexture(width, height, layers); INCSTAT(stats.numTexturesCreated);
return g_texture_cache->CreateTexture(config);
} }
void TextureCache::FreeRenderTarget(TCacheEntryBase* entry) void TextureCache::FreeTexture(TCacheEntryBase* entry)
{ {
render_target_pool.push_back(entry); entry->frameCount = FRAMECOUNT_INVALID;
texture_pool.insert(TexPool::value_type(entry->config, entry));
} }

View file

@ -4,7 +4,9 @@
#pragma once #pragma once
#include <functional>
#include <map> #include <map>
#include <unordered_map>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Thread.h" #include "Common/Thread.h"
@ -32,6 +34,21 @@ public:
u32 width, height; u32 width, height;
u32 levels, layers; u32 levels, layers;
bool rendertarget; bool rendertarget;
bool operator == (const TCacheEntryConfig& b) const
{
return width == b.width && height == b.height && levels == b.levels && layers == b.layers && rendertarget == b.rendertarget;
}
struct Hasher : std::hash<u64>
{
size_t operator()(const TextureCache::TCacheEntryConfig& c) const
{
u64 id = (u64)c.rendertarget << 63 | (u64)c.layers << 48 | (u64)c.levels << 32 | (u64)c.height << 16 | (u64)c.width;
return std::hash<u64>::operator()(id);
}
};
}; };
struct TCacheEntryBase struct TCacheEntryBase
@ -104,9 +121,7 @@ public:
static void ClearRenderTargets(); // currently only used by OGL static void ClearRenderTargets(); // currently only used by OGL
static bool Find(u32 start_address, u64 hash); static bool Find(u32 start_address, u64 hash);
virtual TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height, virtual TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) = 0;
unsigned int tex_levels, PC_TexFormat pcfmt) = 0;
virtual TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h, unsigned int layers) = 0;
virtual void CompileShaders() = 0; // currently only implemented by OGL virtual void CompileShaders() = 0; // currently only implemented by OGL
virtual void DeleteShaders() = 0; // currently only implemented by OGL virtual void DeleteShaders() = 0; // currently only implemented by OGL
@ -127,14 +142,14 @@ private:
static void DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level); static void DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level);
static void CheckTempSize(size_t required_size); static void CheckTempSize(size_t required_size);
static TCacheEntryBase* AllocateRenderTarget(unsigned int width, unsigned int height, unsigned int layers); static TCacheEntryBase* AllocateTexture(const TCacheEntryConfig& config);
static void FreeRenderTarget(TCacheEntryBase* entry); static void FreeTexture(TCacheEntryBase* entry);
typedef std::map<u32, TCacheEntryBase*> TexCache; typedef std::map<u32, TCacheEntryBase*> TexCache;
typedef std::vector<TCacheEntryBase*> RenderTargetPool; typedef std::unordered_multimap<TCacheEntryConfig, TCacheEntryBase*, TCacheEntryConfig::Hasher> TexPool;
static TexCache textures; static TexCache textures;
static RenderTargetPool render_target_pool; static TexPool texture_pool;
// Backup configuration values // Backup configuration values
static struct BackupConfig static struct BackupConfig

View file

@ -66,25 +66,12 @@ int TexDecoder_GetBlockWidthInTexels(u32 format);
int TexDecoder_GetBlockHeightInTexels(u32 format); int TexDecoder_GetBlockHeightInTexels(u32 format);
int TexDecoder_GetPaletteSize(int fmt); int TexDecoder_GetPaletteSize(int fmt);
enum PC_TexFormat void TexDecoder_Decode(u8 *dst, const u8 *src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt);
{ void TexDecoder_DecodeRGBA8FromTmem(u8* dst, const u8 *src_ar, const u8 *src_gb, int width, int height);
PC_TEX_FMT_NONE = 0,
PC_TEX_FMT_BGRA32,
PC_TEX_FMT_RGBA32,
PC_TEX_FMT_I4_AS_I8,
PC_TEX_FMT_IA4_AS_IA8,
PC_TEX_FMT_I8,
PC_TEX_FMT_IA8,
PC_TEX_FMT_RGB565,
PC_TEX_FMT_DXT1,
};
PC_TexFormat TexDecoder_Decode(u8 *dst, const u8 *src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt);
PC_TexFormat TexDecoder_DecodeRGBA8FromTmem(u8* dst, const u8 *src_ar, const u8 *src_gb, int width, int height);
void TexDecoder_DecodeTexel(u8 *dst, const u8 *src, int s, int t, int imageWidth, int texformat, const u8* tlut, TlutFormat tlutfmt); void TexDecoder_DecodeTexel(u8 *dst, const u8 *src, int s, int t, int imageWidth, int texformat, const u8* tlut, TlutFormat tlutfmt);
void TexDecoder_DecodeTexelRGBA8FromTmem(u8 *dst, const u8 *src_ar, const u8* src_gb, int s, int t, int imageWidth); void TexDecoder_DecodeTexelRGBA8FromTmem(u8 *dst, const u8 *src_ar, const u8* src_gb, int s, int t, int imageWidth);
void TexDecoder_SetTexFmtOverlayOptions(bool enable, bool center); void TexDecoder_SetTexFmtOverlayOptions(bool enable, bool center);
/* Internal method, implemented by TextureDecoder_Generic and TextureDecoder_x64. */ /* Internal method, implemented by TextureDecoder_Generic and TextureDecoder_x64. */
PC_TexFormat _TexDecoder_DecodeImpl(u32 * dst, const u8 * src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt); void _TexDecoder_DecodeImpl(u32 * dst, const u8 * src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt);

View file

@ -175,7 +175,7 @@ static const char* texfmt[] = {
"CZ16L", "0x3D", "0x3E", "0x3F", "CZ16L", "0x3D", "0x3E", "0x3F",
}; };
static void TexDecoder_DrawOverlay(u8 *dst, int width, int height, int texformat, PC_TexFormat pc_texformat) static void TexDecoder_DrawOverlay(u8 *dst, int width, int height, int texformat)
{ {
int w = std::min(width, 40); int w = std::min(width, 40);
int h = std::min(height, 10); int h = std::min(height, 10);
@ -208,36 +208,8 @@ static void TexDecoder_DrawOverlay(u8 *dst, int width, int height, int texformat
{ {
for (int x=0; x < xcnt; x++) for (int x=0; x < xcnt; x++)
{ {
switch (pc_texformat) int *dtp = (int*)dst;
{ dtp[(y + yoff) * width + x + xoff] = ptr[x] ? 0xFFFFFFFF : 0xFF000000;
case PC_TEX_FMT_I8:
{
// TODO: Is this an acceptable way to draw in I8?
u8 *dtp = (u8*)dst;
dtp[(y + yoff) * width + x + xoff] = ptr[x] ? 0xFF : 0x88;
break;
}
case PC_TEX_FMT_IA8:
case PC_TEX_FMT_IA4_AS_IA8:
{
u16 *dtp = (u16*)dst;
dtp[(y + yoff) * width + x + xoff] = ptr[x] ? 0xFFFF : 0xFF00;
break;
}
case PC_TEX_FMT_RGB565:
{
u16 *dtp = (u16*)dst;
dtp[(y + yoff)*width + x + xoff] = ptr[x] ? 0xFFFF : 0x0000;
break;
}
default:
case PC_TEX_FMT_BGRA32:
{
int *dtp = (int*)dst;
dtp[(y + yoff) * width + x + xoff] = ptr[x] ? 0xFFFFFFFF : 0xFF000000;
break;
}
}
} }
ptr += 9; ptr += 9;
} }
@ -246,14 +218,12 @@ static void TexDecoder_DrawOverlay(u8 *dst, int width, int height, int texformat
} }
} }
PC_TexFormat TexDecoder_Decode(u8 *dst, const u8 *src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt) void TexDecoder_Decode(u8 *dst, const u8 *src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt)
{ {
PC_TexFormat pc_texformat = _TexDecoder_DecodeImpl((u32*)dst, src, width, height, texformat, tlut, tlutfmt); _TexDecoder_DecodeImpl((u32*)dst, src, width, height, texformat, tlut, tlutfmt);
if (TexFmt_Overlay_Enable && pc_texformat != PC_TEX_FMT_NONE) if (TexFmt_Overlay_Enable)
TexDecoder_DrawOverlay(dst, width, height, texformat, pc_texformat); TexDecoder_DrawOverlay(dst, width, height, texformat);
return pc_texformat;
} }
static inline u32 DecodePixel_IA8(u16 val) static inline u32 DecodePixel_IA8(u16 val)
@ -604,15 +574,15 @@ void TexDecoder_DecodeTexelRGBA8FromTmem(u8 *dst, const u8 *src_ar, const u8* sr
dst[2] = val_addr_gb[1]; // B dst[2] = val_addr_gb[1]; // B
} }
PC_TexFormat TexDecoder_DecodeRGBA8FromTmem(u8* dst, const u8 *src_ar, const u8 *src_gb, int width, int height) void TexDecoder_DecodeRGBA8FromTmem(u8* dst, const u8 *src_ar, const u8 *src_gb, int width, int height)
{ {
// TODO for someone who cares: Make this less slow! // TODO for someone who cares: Make this less slow!
for (int y = 0; y < height; ++y) for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x) for (int x = 0; x < width; ++x)
{ {
TexDecoder_DecodeTexelRGBA8FromTmem(dst, src_ar, src_gb, x, y, width-1); TexDecoder_DecodeTexelRGBA8FromTmem(dst, src_ar, src_gb, x, y, width-1);
dst += 4; dst += 4;
} }
}
return PC_TEX_FMT_RGBA32;
} }

View file

@ -202,7 +202,7 @@ static void DecodeDXTBlock(u32 *dst, const DXTBlock *src, int pitch)
// TODO: complete SSE2 optimization of less often used texture formats. // TODO: complete SSE2 optimization of less often used texture formats.
// TODO: refactor algorithms using _mm_loadl_epi64 unaligned loads to prefer 128-bit aligned loads. // TODO: refactor algorithms using _mm_loadl_epi64 unaligned loads to prefer 128-bit aligned loads.
PC_TexFormat _TexDecoder_DecodeImpl(u32 * dst, const u8 * src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt) void _TexDecoder_DecodeImpl(u32 * dst, const u8 * src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt)
{ {
const int Wsteps4 = (width + 3) / 4; const int Wsteps4 = (width + 3) / 4;
const int Wsteps8 = (width + 7) / 8; const int Wsteps8 = (width + 7) / 8;
@ -344,7 +344,4 @@ PC_TexFormat _TexDecoder_DecodeImpl(u32 * dst, const u8 * src, int width, int he
break; break;
} }
} }
// The "copy" texture formats, too?
return PC_TEX_FMT_RGBA32;
} }

View file

@ -236,7 +236,7 @@ static void DecodeDXTBlock(u32 *dst, const DXTBlock *src, int pitch)
// TODO: complete SSE2 optimization of less often used texture formats. // TODO: complete SSE2 optimization of less often used texture formats.
// TODO: refactor algorithms using _mm_loadl_epi64 unaligned loads to prefer 128-bit aligned loads. // TODO: refactor algorithms using _mm_loadl_epi64 unaligned loads to prefer 128-bit aligned loads.
PC_TexFormat _TexDecoder_DecodeImpl(u32 * dst, const u8 * src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt) void _TexDecoder_DecodeImpl(u32 * dst, const u8 * src, int width, int height, int texformat, const u8* tlut, TlutFormat tlutfmt)
{ {
const int Wsteps4 = (width + 3) / 4; const int Wsteps4 = (width + 3) / 4;
const int Wsteps8 = (width + 7) / 8; const int Wsteps8 = (width + 7) / 8;
@ -1273,7 +1273,4 @@ PC_TexFormat _TexDecoder_DecodeImpl(u32 * dst, const u8 * src, int width, int he
break; break;
} }
} }
// The "copy" texture formats, too?
return PC_TEX_FMT_RGBA32;
} }