TextureCache: Rewrite EFB Copy control flow

This commit is contained in:
degasus 2015-10-29 18:08:04 +01:00
parent dcdf8fd3ce
commit 087ba5268a
9 changed files with 91 additions and 84 deletions

View File

@ -32,8 +32,8 @@ public:
void Init(); void Init();
void Shutdown(); void Shutdown();
void Encode(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, void Encode(u8* dst, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect, PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect,
bool isIntensity, bool scaleByHalf); bool isIntensity, bool scaleByHalf);
private: private:
bool m_ready; bool m_ready;

View File

@ -184,10 +184,8 @@ TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntry
} }
} }
void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, unsigned int dstFormat, u32 dstStride, void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect,
PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect, bool scaleByHalf, unsigned int cbufid, const float *colmat)
bool isIntensity, bool scaleByHalf, unsigned int cbufid,
const float *colmat)
{ {
g_renderer->ResetAPIState(); g_renderer->ResetAPIState();

View File

@ -34,10 +34,8 @@ private:
void Load(unsigned int width, unsigned int height, void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int levels) override; unsigned int expanded_width, unsigned int levels) override;
void FromRenderTarget(u8* dst, unsigned int dstFormat, u32 dstStride, void FromRenderTarget(u8* dst, PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect,
PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect, bool scaleByHalf, unsigned int cbufid, const float *colmat) override;
bool isIntensity, bool scaleByHalf, unsigned int cbufid,
const float *colmat) override;
void Bind(unsigned int stage) override; void Bind(unsigned int stage) override;
bool Save(const std::string& filename, unsigned int level) override; bool Save(const std::string& filename, unsigned int level) override;

View File

@ -215,10 +215,8 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
TextureCache::SetStage(); TextureCache::SetStage();
} }
void TextureCache::TCacheEntry::FromRenderTarget(u8* dstPointer, unsigned int dstFormat, u32 dstStride, void TextureCache::TCacheEntry::FromRenderTarget(u8* dstPointer, PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect,
PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect, bool scaleByHalf, unsigned int cbufid, const float *colmat)
bool isIntensity, bool scaleByHalf, unsigned int cbufid,
const float *colmat)
{ {
g_renderer->ResetAPIState(); // reset any game specific settings g_renderer->ResetAPIState(); // reset any game specific settings

View File

@ -42,10 +42,8 @@ private:
void Load(unsigned int width, unsigned int height, void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level) override; unsigned int expanded_width, unsigned int level) override;
void FromRenderTarget(u8 *dst, unsigned int dstFormat, u32 dstStride, void FromRenderTarget(u8 *dst, PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect,
PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect, bool scaleByHalf, unsigned int cbufid, const float *colmat) override;
bool isIntensity, bool scaleByHalf, unsigned int cbufid,
const float *colmat) override;
void Bind(unsigned int stage) override; void Bind(unsigned int stage) override;
bool Save(const std::string& filename, unsigned int level) override; bool Save(const std::string& filename, unsigned int level) override;

View File

@ -269,7 +269,7 @@ static void EncodeToRamUsingShader(GLuint srcTexture,
} }
} }
void EncodeToRamFromTexture(u8 *dest_ptr, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, void EncodeToRamFromTexture(u8* dest_ptr, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
PEControl::PixelFormat srcFormat, bool bIsIntensityFmt, int bScaleByHalf, const EFBRectangle& source) PEControl::PixelFormat srcFormat, bool bIsIntensityFmt, int bScaleByHalf, const EFBRectangle& source)
{ {
g_renderer->ResetAPIState(); g_renderer->ResetAPIState();

View File

@ -26,7 +26,7 @@ void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc,
void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture); void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture);
// returns size of the encoded data (in bytes) // returns size of the encoded data (in bytes)
void EncodeToRamFromTexture(u8 *dest_ptr, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, void EncodeToRamFromTexture(u8* dest_ptr, u32 format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
PEControl::PixelFormat srcFormat, bool bIsIntensityFmt, int bScaleByHalf, const EFBRectangle& source); PEControl::PixelFormat srcFormat, bool bIsIntensityFmt, int bScaleByHalf, const EFBRectangle& source);
} }

View File

@ -1032,80 +1032,109 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo
} }
} }
// create the texture u32 blockH = TexDecoder_GetBlockHeightInTexels(dstFormat);
TCacheEntryConfig config; const u32 blockW = TexDecoder_GetBlockWidthInTexels(dstFormat);
config.rendertarget = true;
config.width = scaled_tex_w;
config.height = scaled_tex_h;
config.layers = FramebufferManagerBase::GetEFBLayers();
TCacheEntryBase* entry = AllocateTexture(config); // Round up source height to multiple of block size
u32 actualHeight = ROUND_UP(tex_h, blockH);
const u32 actualWidth = ROUND_UP(tex_w, blockW);
entry->SetGeneralParameters(dstAddr, 0, dstFormat); u32 num_blocks_y = actualHeight / blockH;
entry->SetDimensions(tex_w, tex_h, 1); const u32 num_blocks_x = actualWidth / blockW;
entry->frameCount = FRAMECOUNT_INVALID; // RGBA takes two cache lines per block; all others take one
entry->SetEfbCopy(dstStride); const u32 bytes_per_block = dstFormat == GX_TF_RGBA8 ? 64 : 32;
entry->is_custom_tex = false;
entry->FromRenderTarget(dst, dstFormat, dstStride, srcFormat, srcRect, isIntensity, scaleByHalf, cbufid, colmat); u32 bytes_per_row = num_blocks_x * bytes_per_block;
if (g_ActiveConfig.bSkipEFBCopyToRam) bool copy_to_ram = !g_ActiveConfig.bSkipEFBCopyToRam;
{ bool copy_to_vram = true;
entry->Zero(dst);
} if (copy_to_ram)
else
{ {
g_texture_cache->CopyEFB( g_texture_cache->CopyEFB(
dst, dst,
entry->format, dstFormat,
entry->native_width, tex_w,
entry->BytesPerRow(), bytes_per_row,
entry->NumBlocksY(), num_blocks_y,
entry->memory_stride, dstStride,
srcFormat, srcFormat,
srcRect, srcRect,
isIntensity, isIntensity,
scaleByHalf); scaleByHalf);
} }
else
u64 hash = entry->CalculateHash();
entry->SetHashes(hash, hash);
// Invalidate all textures that overlap the range of our efb copy.
// Unless our efb copy has a weird stride, then we want avoid invalidating textures which
// we might be able to do a partial texture update on.
if (entry->memory_stride == entry->BytesPerRow())
{ {
TexCache::iterator iter = textures_by_address.begin(); // Hack: Most games don't actually need the correct texture data in RAM
while (iter != textures_by_address.end()) // and we can just keep a copy in VRAM. We zero the memory so we
// can check it hasn't changed before using our copy in VRAM.
u8* ptr = dst;
for (u32 i = 0; i < num_blocks_y; i++)
{ {
if (iter->second->OverlapsMemoryRange(dstAddr, entry->size_in_bytes)) memset(ptr, 0, bytes_per_row);
iter = FreeTexture(iter); ptr += dstStride;
else
++iter;
} }
} }
if (g_ActiveConfig.bDumpEFBTarget)
{
static int count = 0;
entry->Save(StringFromFormat("%sefb_frame_%i.png", File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(),
count++), 0);
}
if (g_bRecordFifoData) if (g_bRecordFifoData)
{ {
// Mark the memory behind this efb copy as dynamicly generated for the Fifo log // Mark the memory behind this efb copy as dynamicly generated for the Fifo log
u32 address = dstAddr; u32 address = dstAddr;
for (u32 i = 0; i < entry->NumBlocksY(); i++) for (u32 i = 0; i < num_blocks_y; i++)
{ {
FifoRecorder::GetInstance().UseMemory(address, entry->BytesPerRow(), MemoryUpdate::TEXTURE_MAP, true); FifoRecorder::GetInstance().UseMemory(address, bytes_per_row, MemoryUpdate::TEXTURE_MAP, true);
address += entry->memory_stride; address += dstStride;
} }
} }
textures_by_address.emplace((u64)dstAddr, entry); // Invalidate all textures that overlap the range of our efb copy.
// Unless our efb copy has a weird stride, then we want avoid invalidating textures which
// we might be able to do a partial texture update on.
if (dstStride == bytes_per_row || !copy_to_vram)
{
TexCache::iterator iter = textures_by_address.begin();
while (iter != textures_by_address.end())
{
if (iter->second->addr + iter->second->size_in_bytes <= dstAddr || iter->second->addr >= dstAddr + num_blocks_y * dstStride)
++iter;
else
iter = FreeTexture(iter);
}
}
if (copy_to_vram)
{
// create the texture
TCacheEntryConfig config;
config.rendertarget = true;
config.width = scaled_tex_w;
config.height = scaled_tex_h;
config.layers = FramebufferManagerBase::GetEFBLayers();
TCacheEntryBase* entry = AllocateTexture(config);
entry->SetGeneralParameters(dstAddr, 0, dstFormat);
entry->SetDimensions(tex_w, tex_h, 1);
entry->frameCount = FRAMECOUNT_INVALID;
entry->SetEfbCopy(dstStride);
entry->is_custom_tex = false;
entry->FromRenderTarget(dst, srcFormat, srcRect, scaleByHalf, cbufid, colmat);
u64 hash = entry->CalculateHash();
entry->SetHashes(hash, hash);
if (g_ActiveConfig.bDumpEFBTarget)
{
static int count = 0;
entry->Save(StringFromFormat("%sefb_frame_%i.png", File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(),
count++), 0);
}
textures_by_address.emplace((u64)dstAddr, entry);
}
} }
TextureCacheBase::TCacheEntryBase* TextureCacheBase::AllocateTexture(const TCacheEntryConfig& config) TextureCacheBase::TCacheEntryBase* TextureCacheBase::AllocateTexture(const TCacheEntryConfig& config)
@ -1177,16 +1206,6 @@ void TextureCacheBase::TCacheEntryBase::SetEfbCopy(u32 stride)
size_in_bytes = memory_stride * NumBlocksY(); size_in_bytes = memory_stride * NumBlocksY();
} }
// Fill gamecube memory backing this texture with zeros.
void TextureCacheBase::TCacheEntryBase::Zero(u8* ptr)
{
for (u32 i = 0; i < NumBlocksY(); i++)
{
memset(ptr, 0, BytesPerRow());
ptr += memory_stride;
}
}
u64 TextureCacheBase::TCacheEntryBase::CalculateHash() const u64 TextureCacheBase::TCacheEntryBase::CalculateHash() const
{ {
u8* ptr = Memory::GetPointer(addr); u8* ptr = Memory::GetPointer(addr);

View File

@ -101,10 +101,8 @@ public:
virtual void Load(unsigned int width, unsigned int height, virtual void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level) = 0; unsigned int expanded_width, unsigned int level) = 0;
virtual void FromRenderTarget(u8* dst, unsigned int dstFormat, u32 dstStride, virtual void FromRenderTarget(u8* dst, PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect,
PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect, bool scaleByHalf, unsigned int cbufid, const float *colmat) = 0;
bool isIntensity, bool scaleByHalf, unsigned int cbufid,
const float *colmat) = 0;
bool OverlapsMemoryRange(u32 range_address, u32 range_size) const; bool OverlapsMemoryRange(u32 range_address, u32 range_size) const;
@ -113,8 +111,6 @@ public:
u32 NumBlocksY() const; u32 NumBlocksY() const;
u32 BytesPerRow() const; u32 BytesPerRow() const;
void Zero(u8* ptr);
u64 CalculateHash() const; u64 CalculateHash() const;
}; };