Detach textures and fbs using a range check.

This is more compatible with the attach logic, and might also be faster.
Although, framebuffer destroys should not be that common.
This commit is contained in:
Unknown W. Brackets 2013-07-31 23:19:15 -07:00
parent da39d80742
commit ab1c20f509
3 changed files with 60 additions and 57 deletions

View File

@ -408,7 +408,7 @@ void GuessDrawingSize(int &drawing_width, int &drawing_height) {
} }
void FramebufferManager::DestroyFramebuf(VirtualFramebuffer *v) { void FramebufferManager::DestroyFramebuf(VirtualFramebuffer *v) {
textureCache_->NotifyFramebufferDestroyed(v->fb_address, v); textureCache_->NotifyFramebuffer(v->fb_address, v, NOTIFY_FB_DESTROYED);
if (v->fbo) { if (v->fbo) {
fbo_destroy(v->fbo); fbo_destroy(v->fbo);
v->fbo = 0; v->fbo = 0;
@ -534,7 +534,7 @@ void FramebufferManager::SetRenderFrameBuffer() {
gstate_c.skipDrawReason |= SKIPDRAW_NON_DISPLAYED_FB; gstate_c.skipDrawReason |= SKIPDRAW_NON_DISPLAYED_FB;
} }
textureCache_->NotifyFramebuffer(vfb->fb_address, vfb); textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_CREATED);
vfb->last_frame_used = gpuStats.numFrames; vfb->last_frame_used = gpuStats.numFrames;
frameLastFramebufUsed = gpuStats.numFrames; frameLastFramebufUsed = gpuStats.numFrames;
@ -568,7 +568,7 @@ void FramebufferManager::SetRenderFrameBuffer() {
} else { } else {
if (vfb->fbo) { if (vfb->fbo) {
// wtf? This should only happen very briefly when toggling bBufferedRendering // wtf? This should only happen very briefly when toggling bBufferedRendering
textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb); textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_DESTROYED);
fbo_destroy(vfb->fbo); fbo_destroy(vfb->fbo);
vfb->fbo = 0; vfb->fbo = 0;
} }
@ -588,7 +588,7 @@ void FramebufferManager::SetRenderFrameBuffer() {
gstate_c.skipDrawReason |= ~SKIPDRAW_SKIPNONFB; gstate_c.skipDrawReason |= ~SKIPDRAW_SKIPNONFB;
}*/ }*/
} }
textureCache_->NotifyFramebuffer(vfb->fb_address, vfb); textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_UPDATED);
#ifdef USING_GLES2 #ifdef USING_GLES2
// Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering // Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering

View File

@ -172,16 +172,45 @@ void TextureCache::ClearNextFrame() {
} }
TextureCache::TexCacheEntry *TextureCache::GetEntryAt(u32 texaddr) { inline void TextureCache::AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, bool exactMatch) {
// If no CLUT, as in framebuffer textures, cache key is simply texaddr shifted up. // If they match exactly, it's non-CLUT and from the top left.
auto iter = cache.find((u64)texaddr << 32); if (exactMatch) {
if (iter != cache.end() && iter->second.addr == texaddr) DEBUG_LOG(HLE, "Render to texture detected at %08x!", address);
return &iter->second; if (!entry->framebuffer) {
else if (entry->format != framebuffer->format) {
return 0; WARN_LOG_REPORT_ONCE(diffFormat1, HLE, "Render to texture with different formats %d != %d", entry->format, framebuffer->format);
}
entry->framebuffer = framebuffer;
// TODO: Delete the original non-fbo texture too.
}
} else if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE || g_Config.iRenderingMode == FB_BUFFERED_MODE) {
// 3rd Birthday (and possibly other games) render to a 16 bit clut texture.
const bool compatFormat = framebuffer->format == entry->format
|| (framebuffer->format == GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT32)
|| (framebuffer->format != GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT16);
// Is it at least the right stride?
if (framebuffer->fb_stride == entry->bufw && compatFormat) {
if (framebuffer->format != entry->format) {
WARN_LOG_REPORT_ONCE(diffFormat2, HLE, "Render to texture with different formats %d != %d at %08x", entry->format, framebuffer->format, address);
// TODO: Use an FBO to translate the palette?
entry->framebuffer = framebuffer;
} else if ((entry->addr - address) / entry->bufw < framebuffer->height) {
WARN_LOG_REPORT_ONCE(subarea, HLE, "Render to area containing texture at %08x", address);
// TODO: Keep track of the y offset.
entry->framebuffer = framebuffer;
}
}
}
} }
void TextureCache::NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffer) { inline void TextureCache::DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer) {
if (entry->framebuffer == framebuffer) {
entry->framebuffer = 0;
}
}
void TextureCache::NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffer, FramebufferNotification msg) {
// This is a rough heuristic, because sometimes our framebuffers are too tall. // This is a rough heuristic, because sometimes our framebuffers are too tall.
static const u32 MAX_SUBAREA_Y_OFFSET = 32; static const u32 MAX_SUBAREA_Y_OFFSET = 32;
@ -191,52 +220,19 @@ void TextureCache::NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffe
// Also, if it's a subsample of the buffer, it'll also be within the FBO. // Also, if it's a subsample of the buffer, it'll also be within the FBO.
const u64 cacheKeyEnd = cacheKey + ((u64)(framebuffer->fb_stride * MAX_SUBAREA_Y_OFFSET) << 32); const u64 cacheKeyEnd = cacheKey + ((u64)(framebuffer->fb_stride * MAX_SUBAREA_Y_OFFSET) << 32);
for (auto it = cache.lower_bound(cacheKey), end = cache.upper_bound(cacheKeyEnd); it != end; ++it) { switch (msg) {
auto entry = &it->second; case NOTIFY_FB_CREATED:
case NOTIFY_FB_UPDATED:
// If they match exactly, it's non-CLUT and from the top left. for (auto it = cache.lower_bound(cacheKey), end = cache.upper_bound(cacheKeyEnd); it != end; ++it) {
if (it->first == cacheKey) { AttachFramebuffer(&it->second, address, framebuffer, it->first == cacheKey);
DEBUG_LOG(HLE, "Render to texture detected at %08x!", address);
if (!entry->framebuffer) {
if (entry->format != framebuffer->format) {
WARN_LOG_REPORT_ONCE(diffFormat1, HLE, "Render to texture with different formats %d != %d", entry->format, framebuffer->format);
}
entry->framebuffer = framebuffer;
// TODO: Delete the original non-fbo texture too.
}
} else if (g_Config.iRenderingMode == FB_NON_BUFFERED_MODE || g_Config.iRenderingMode == FB_BUFFERED_MODE) {
// 3rd Birthday (and possibly other games) render to a 16 bit clut texture.
const bool compatFormat = framebuffer->format == entry->format
|| (framebuffer->format == GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT32)
|| (framebuffer->format != GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT16);
// Is it at least the right stride?
if (framebuffer->fb_stride == entry->bufw && compatFormat) {
if (framebuffer->format != entry->format) {
WARN_LOG_REPORT_ONCE(diffFormat2, HLE, "Render to texture with different formats %d != %d at %08x", entry->format, framebuffer->format, address);
// TODO: Use an FBO to translate the palette?
entry->framebuffer = framebuffer;
} else if ((entry->addr - address) / entry->bufw < framebuffer->height) {
WARN_LOG_REPORT_ONCE(subarea, HLE, "Render to area containing texture at %08x", address);
// TODO: Keep track of the y offset.
entry->framebuffer = framebuffer;
}
}
} }
} break;
}
void TextureCache::NotifyFramebufferDestroyed(u32 address, VirtualFramebuffer *framebuffer) { case NOTIFY_FB_DESTROYED:
TexCacheEntry *entry = GetEntryAt(address | 0x04000000); for (auto it = cache.lower_bound(cacheKey), end = cache.upper_bound(cacheKeyEnd); it != end; ++it) {
if (entry && entry->framebuffer == framebuffer) { DetachFramebuffer(&it->second, address, framebuffer);
// There's at least one. We're going to have to loop through all textures unfortunately to be
// 100% safe.
for (TexCache::iterator iter = cache.begin(); iter != cache.end(); ++iter) {
if (iter->second.framebuffer == framebuffer) {
iter->second.framebuffer = 0;
}
} }
// entry->framebuffer = 0; break;
} }
} }

View File

@ -32,6 +32,12 @@ enum TextureFiltering {
LINEARFMV = 4, LINEARFMV = 4,
}; };
enum FramebufferNotification {
NOTIFY_FB_CREATED,
NOTIFY_FB_UPDATED,
NOTIFY_FB_DESTROYED,
};
class TextureCache class TextureCache
{ {
public: public:
@ -49,8 +55,7 @@ public:
// FramebufferManager keeps TextureCache updated about what regions of memory // FramebufferManager keeps TextureCache updated about what regions of memory
// are being rendered to. This is barebones so far. // are being rendered to. This is barebones so far.
void NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffer); void NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffer, FramebufferNotification msg);
void NotifyFramebufferDestroyed(u32 address, VirtualFramebuffer *framebuffer);
size_t NumLoadedTextures() const { size_t NumLoadedTextures() const {
return cache.size(); return cache.size();
@ -119,6 +124,8 @@ private:
const T *GetCurrentClut(); const T *GetCurrentClut();
u32 GetCurrentClutHash(); u32 GetCurrentClutHash();
void UpdateCurrentClut(); void UpdateCurrentClut();
void AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, bool exactMatch);
void DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer);
TexCacheEntry *GetEntryAt(u32 texaddr); TexCacheEntry *GetEntryAt(u32 texaddr);