mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #13322 from hrydgard/refactor-framebuffer-attach
Framebuffer manager refactor step 1: Split ApplyFramebuffer into Match and Apply steps.
This commit is contained in:
commit
a8059d53f9
2 changed files with 105 additions and 52 deletions
|
@ -530,7 +530,9 @@ void TextureCacheCommon::SetTexture(bool force) {
|
|||
entry->framebuffer = nullptr;
|
||||
for (size_t i = 0, n = fbCache_.size(); i < n; ++i) {
|
||||
auto framebuffer = fbCache_[i];
|
||||
AttachFramebuffer(entry, framebuffer->fb_address, framebuffer, 0, (entry->status & TexCacheEntry::STATUS_DEPTH) ? NOTIFY_FB_DEPTH : NOTIFY_FB_COLOR);
|
||||
auto notificationChannel = (entry->status & TexCacheEntry::STATUS_DEPTH) ? NOTIFY_FB_DEPTH : NOTIFY_FB_COLOR;
|
||||
FramebufferMatchInfo match = MatchFramebuffer(entry, framebuffer->fb_address, framebuffer, 0, notificationChannel);
|
||||
ApplyFramebufferMatch(match, entry, framebuffer->fb_address, framebuffer, notificationChannel);
|
||||
}
|
||||
|
||||
// If we ended up with a framebuffer, attach it - no texture decoding needed.
|
||||
|
@ -662,15 +664,22 @@ void TextureCacheCommon::NotifyFramebuffer(u32 address, VirtualFramebuffer *fram
|
|||
if (std::find(fbCache_.begin(), fbCache_.end(), framebuffer) == fbCache_.end()) {
|
||||
fbCache_.push_back(framebuffer);
|
||||
}
|
||||
|
||||
// TODO: Rework this to not try to "apply" all matches, only the best one.
|
||||
for (auto it = cache_.lower_bound(cacheKey), end = cache_.upper_bound(cacheKeyEnd); it != end; ++it) {
|
||||
AttachFramebuffer(it->second.get(), addr, framebuffer, 0, channel);
|
||||
TexCacheEntry *entry = it->second.get();
|
||||
FramebufferMatchInfo match = MatchFramebuffer(entry, addr, framebuffer, 0, channel);
|
||||
ApplyFramebufferMatch(match, entry, addr, framebuffer, channel);
|
||||
}
|
||||
// Let's assume anything in mirrors is fair game to check.
|
||||
// TODO: Only do this for depth?
|
||||
for (auto it = cache_.lower_bound(mirrorCacheKey), end = cache_.upper_bound(mirrorCacheKeyEnd); it != end; ++it) {
|
||||
const u64 mirrorlessKey = it->first & ~0x0060000000000000ULL;
|
||||
// Let's still make sure it's in the cache range.
|
||||
if (mirrorlessKey >= cacheKey && mirrorlessKey <= cacheKeyEnd) {
|
||||
AttachFramebuffer(it->second.get(), addr, framebuffer, 0, channel);
|
||||
TexCacheEntry *entry = it->second.get();
|
||||
FramebufferMatchInfo match = MatchFramebuffer(entry, addr, framebuffer, 0, channel);
|
||||
ApplyFramebufferMatch(match, entry, addr, framebuffer, channel);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -691,7 +700,8 @@ void TextureCacheCommon::NotifyFramebuffer(u32 address, VirtualFramebuffer *fram
|
|||
}
|
||||
}
|
||||
|
||||
void TextureCacheCommon::AttachFramebufferValid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const AttachedFramebufferInfo &fbInfo, FramebufferNotificationChannel channel) {
|
||||
void TextureCacheCommon::AttachFramebufferValid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const FramebufferMatchInfo &fbInfo, FramebufferNotificationChannel channel) {
|
||||
_dbg_assert_((fbInfo.match == FramebufferMatch::VALID) || (fbInfo.match == FramebufferMatch::VALID_DEPAL));
|
||||
const u64 cachekey = entry->CacheKey();
|
||||
const bool hasInvalidFramebuffer = entry->framebuffer == nullptr || entry->invalidHint == -1;
|
||||
const bool hasOlderFramebuffer = entry->framebuffer != nullptr && entry->framebuffer->last_frame_render < framebuffer->last_frame_render;
|
||||
|
@ -725,7 +735,8 @@ void TextureCacheCommon::AttachFramebufferValid(TexCacheEntry *entry, VirtualFra
|
|||
}
|
||||
}
|
||||
|
||||
void TextureCacheCommon::AttachFramebufferInvalid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const AttachedFramebufferInfo &fbInfo, FramebufferNotificationChannel channel) {
|
||||
void TextureCacheCommon::AttachFramebufferInvalid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const FramebufferMatchInfo &fbInfo, FramebufferNotificationChannel channel) {
|
||||
_dbg_assert_(fbInfo.match == FramebufferMatch::INVALID);
|
||||
const u64 cachekey = entry->CacheKey();
|
||||
|
||||
if (entry->framebuffer == nullptr || entry->framebuffer == framebuffer) {
|
||||
|
@ -757,10 +768,32 @@ void TextureCacheCommon::DetachFramebuffer(TexCacheEntry *entry, u32 address, Vi
|
|||
}
|
||||
}
|
||||
|
||||
bool TextureCacheCommon::AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset, FramebufferNotificationChannel channel) {
|
||||
static const u32 MAX_SUBAREA_Y_OFFSET_SAFE = 32;
|
||||
bool TextureCacheCommon::ApplyFramebufferMatch(FramebufferMatchInfo match, TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, FramebufferNotificationChannel channel) {
|
||||
// There were five possible outcomes of the old ApplyFramebuffer, these have been
|
||||
// mapped to the FramebufferMatch enum, and we handle them the same old ways here.
|
||||
switch (match.match) {
|
||||
case FramebufferMatch::VALID:
|
||||
AttachFramebufferValid(entry, framebuffer, match, channel);
|
||||
return true;
|
||||
case FramebufferMatch::VALID_DEPAL:
|
||||
AttachFramebufferValid(entry, framebuffer, match, channel);
|
||||
entry->status |= TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
return true;
|
||||
case FramebufferMatch::INVALID:
|
||||
AttachFramebufferInvalid(entry, framebuffer, match, channel);
|
||||
return true;
|
||||
case FramebufferMatch::NO_MATCH:
|
||||
DetachFramebuffer(entry, address, framebuffer, channel);
|
||||
return false;
|
||||
case FramebufferMatch::IGNORE:
|
||||
// The purpose of this seems to be to delay a decision to the next frame.
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AttachedFramebufferInfo fbInfo = { 0 };
|
||||
FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset, FramebufferNotificationChannel channel) const {
|
||||
static const u32 MAX_SUBAREA_Y_OFFSET_SAFE = 32;
|
||||
|
||||
const u32 mirrorMask = 0x00600000;
|
||||
|
||||
|
@ -778,14 +811,14 @@ bool TextureCacheCommon::AttachFramebuffer(TexCacheEntry *entry, u32 address, Vi
|
|||
// Don't match the depth channel with these addresses when texturing.
|
||||
if (channel == FramebufferNotificationChannel::NOTIFY_FB_DEPTH) {
|
||||
// God of War: If we actively detach here, the shadows disappear.
|
||||
return false;
|
||||
return FramebufferMatchInfo{ FramebufferMatch::IGNORE };
|
||||
}
|
||||
break;
|
||||
case 0x00200000:
|
||||
case 0x00600000:
|
||||
// Don't match the color channel with these addresses when texturing.
|
||||
if (channel == FramebufferNotificationChannel::NOTIFY_FB_COLOR) {
|
||||
return false;
|
||||
return FramebufferMatchInfo{ FramebufferMatch::IGNORE };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -811,16 +844,21 @@ bool TextureCacheCommon::AttachFramebuffer(TexCacheEntry *entry, u32 address, Vi
|
|||
// Let's avoid using it when we know the format is wrong. May be a video/etc. updating memory.
|
||||
// However, some games use a different format to clear the buffer.
|
||||
if (framebuffer->last_frame_attached + 1 < gpuStats.numFlips) {
|
||||
DetachFramebuffer(entry, address, framebuffer, channel);
|
||||
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
|
||||
} else {
|
||||
// TODO: This is a weird outcome. The purpose seems to be to delay
|
||||
// a decision to the next frame.
|
||||
// Should really try to map it to something else.
|
||||
return FramebufferMatchInfo{ FramebufferMatch::IGNORE };
|
||||
}
|
||||
} else {
|
||||
AttachFramebufferValid(entry, framebuffer, fbInfo, channel);
|
||||
return true;
|
||||
return FramebufferMatchInfo{ FramebufferMatch::VALID };
|
||||
}
|
||||
} else {
|
||||
// Apply to buffered mode only.
|
||||
if (!framebufferManager_->UseBufferedRendering())
|
||||
return false;
|
||||
if (!framebufferManager_->UseBufferedRendering()) {
|
||||
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
|
||||
}
|
||||
|
||||
// Check works for D16 too (???)
|
||||
const bool matchingClutFormat =
|
||||
|
@ -832,55 +870,53 @@ bool TextureCacheCommon::AttachFramebuffer(TexCacheEntry *entry, u32 address, Vi
|
|||
|
||||
const u32 bitOffset = (texaddr - addr) * 8;
|
||||
const u32 pixelOffset = bitOffset / std::max(1U, (u32)textureBitsPerPixel[entry->format]);
|
||||
|
||||
// To avoid ruining git blame, kept the same name as the old struct.
|
||||
FramebufferMatchInfo fbInfo{ FramebufferMatch::VALID };
|
||||
|
||||
fbInfo.yOffset = entry->bufw == 0 ? 0 : pixelOffset / entry->bufw;
|
||||
fbInfo.xOffset = entry->bufw == 0 ? 0 : pixelOffset % entry->bufw;
|
||||
|
||||
if (framebuffer->fb_stride != entry->bufw) {
|
||||
if (noOffset) {
|
||||
// Not actually sure why we even try here. There's no way it'll go well if the strides are different.
|
||||
WARN_LOG_ONCE(diffStrides2, G3D, "Texturing from framebuffer (matching_clut=%s) different strides %d != %d", matchingClutFormat ? "yes" : "no", entry->bufw, framebuffer->fb_stride);
|
||||
// Continue on with other checks.
|
||||
// Not actually sure why we even try here. There's no way it'll go well if the strides are different.
|
||||
} else {
|
||||
// Assume any render-to-tex with different bufw + offset is a render from ram.
|
||||
DetachFramebuffer(entry, address, framebuffer, channel);
|
||||
return false;
|
||||
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it's in bufferWidth (which might be higher than width and may indicate the framebuffer includes the data.)
|
||||
if (fbInfo.xOffset >= framebuffer->bufferWidth && fbInfo.xOffset + w <= (u32)framebuffer->fb_stride) {
|
||||
// This happens in Brave Story, see #10045 - the texture is in the space between strides, with matching stride.
|
||||
DetachFramebuffer(entry, address, framebuffer, channel);
|
||||
return false;
|
||||
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
|
||||
}
|
||||
|
||||
if (fbInfo.yOffset + minSubareaHeight >= framebuffer->height) {
|
||||
// Can't be inside the framebuffer then, ram. Detach to be safe.
|
||||
DetachFramebuffer(entry, address, framebuffer, channel);
|
||||
return false;
|
||||
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
|
||||
}
|
||||
|
||||
// Trying to play it safe. Below 0x04110000 is almost always framebuffers.
|
||||
// TODO: Maybe we can reduce this check and find a better way above 0x04110000?
|
||||
if (fbInfo.yOffset > MAX_SUBAREA_Y_OFFSET_SAFE && addr > 0x04110000) {
|
||||
WARN_LOG_REPORT_ONCE(subareaIgnored, G3D, "Ignoring possible texturing from framebuffer at %08x +%dx%d / %dx%d", address, fbInfo.xOffset, fbInfo.yOffset, framebuffer->width, framebuffer->height);
|
||||
DetachFramebuffer(entry, address, framebuffer, channel);
|
||||
return false;
|
||||
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
|
||||
}
|
||||
|
||||
// Check for CLUT. The framebuffer is always RGB, but it can be interpreted as a CLUT texture.
|
||||
// 3rd Birthday (and a bunch of other games) render to a 16 bit clut texture.
|
||||
if (matchingClutFormat) {
|
||||
if (!noOffset) {
|
||||
WARN_LOG_REPORT_ONCE(subareaClut, G3D, "Texturing from framebuffer using CLUT with offset at %08x +%dx%d", address, fbInfo.xOffset, fbInfo.yOffset);
|
||||
WARN_LOG_ONCE(subareaClut, G3D, "Texturing from framebuffer using CLUT with offset at %08x +%dx%d", address, fbInfo.xOffset, fbInfo.yOffset);
|
||||
}
|
||||
AttachFramebufferValid(entry, framebuffer, fbInfo, channel);
|
||||
entry->status |= TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
// We'll validate it compiles later.
|
||||
return true;
|
||||
fbInfo.match = FramebufferMatch::VALID_DEPAL;
|
||||
return fbInfo;
|
||||
} else if (IsClutFormat((GETextureFormat)(entry->format)) || IsDXTFormat((GETextureFormat)(entry->format))) {
|
||||
WARN_LOG_ONCE(fourEightBit, G3D, "%s format not supported when texturing from framebuffer of format %s", GeTextureFormatToString((GETextureFormat)entry->format), GeBufferFormatToString(framebuffer->format));
|
||||
DetachFramebuffer(entry, address, framebuffer, channel);
|
||||
return false;
|
||||
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
|
||||
}
|
||||
|
||||
// This is either normal or we failed to generate a shader to depalettize
|
||||
|
@ -888,23 +924,19 @@ bool TextureCacheCommon::AttachFramebuffer(TexCacheEntry *entry, u32 address, Vi
|
|||
if (framebuffer->format != entry->format) {
|
||||
WARN_LOG_ONCE(diffFormat2, G3D, "Texturing from framebuffer with different formats %s != %s at %08x",
|
||||
GeTextureFormatToString((GETextureFormat)entry->format), GeBufferFormatToString(framebuffer->format), address);
|
||||
AttachFramebufferValid(entry, framebuffer, fbInfo, channel);
|
||||
return true;
|
||||
return fbInfo; // Valid!
|
||||
} else {
|
||||
WARN_LOG_ONCE(subarea, G3D, "Render to area containing texture at %08x +%dx%d", address, fbInfo.xOffset, fbInfo.yOffset);
|
||||
// If "AttachFramebufferValid" , God of War Ghost of Sparta/Chains of Olympus will be missing special effect.
|
||||
AttachFramebufferInvalid(entry, framebuffer, fbInfo, channel);
|
||||
return true;
|
||||
// If we return VALID here, God of War Ghost of Sparta/Chains of Olympus will be missing some special effect according to an old comment.
|
||||
fbInfo.match = FramebufferMatch::INVALID;
|
||||
return fbInfo;
|
||||
}
|
||||
} else {
|
||||
WARN_LOG_REPORT_ONCE(diffFormat2, G3D, "Texturing from framebuffer with incompatible format %s != %s at %08x",
|
||||
WARN_LOG_ONCE(diffFormat2, G3D, "Texturing from framebuffer with incompatible format %s != %s at %08x",
|
||||
GeTextureFormatToString((GETextureFormat)entry->format), GeBufferFormatToString(framebuffer->format), address);
|
||||
DetachFramebuffer(entry, address, framebuffer, channel);
|
||||
return false;
|
||||
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextureCacheCommon::SetTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) {
|
||||
|
@ -976,7 +1008,9 @@ bool TextureCacheCommon::SetOffsetTexture(u32 yOffset) {
|
|||
bool success = false;
|
||||
for (size_t i = 0, n = fbCache_.size(); i < n; ++i) {
|
||||
auto framebuffer = fbCache_[i];
|
||||
if (AttachFramebuffer(entry, framebuffer->fb_address, framebuffer, texaddrOffset, (entry->status & TexCacheEntry::STATUS_DEPTH) ? NOTIFY_FB_DEPTH : NOTIFY_FB_COLOR)) {
|
||||
FramebufferNotificationChannel channel = (entry->status & TexCacheEntry::STATUS_DEPTH) ? NOTIFY_FB_DEPTH : NOTIFY_FB_COLOR;
|
||||
FramebufferMatchInfo match = MatchFramebuffer(entry, framebuffer->fb_address, framebuffer, texaddrOffset, channel);
|
||||
if (ApplyFramebufferMatch(match, entry, framebuffer->fb_address, framebuffer, channel)) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,6 +185,27 @@ class FramebufferManagerCommon;
|
|||
// Would really like to replace this with DenseHashMap but can't as long as we need lower_bound.
|
||||
typedef std::map<u64, std::unique_ptr<TexCacheEntry>> TexCache;
|
||||
|
||||
// Urgh.
|
||||
#ifdef IGNORE
|
||||
#undef IGNORE
|
||||
#endif
|
||||
|
||||
// TODO: Try to get rid of IGNORE, it doesn't match what we want to do
|
||||
enum class FramebufferMatch {
|
||||
VALID = 0,
|
||||
VALID_DEPAL,
|
||||
INVALID,
|
||||
NO_MATCH,
|
||||
IGNORE,
|
||||
};
|
||||
|
||||
// Separate to keep main texture cache size down.
|
||||
struct FramebufferMatchInfo {
|
||||
FramebufferMatch match;
|
||||
u32 xOffset;
|
||||
u32 yOffset;
|
||||
};
|
||||
|
||||
class TextureCacheCommon {
|
||||
public:
|
||||
TextureCacheCommon(Draw::DrawContext *draw);
|
||||
|
@ -236,12 +257,6 @@ protected:
|
|||
virtual void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) = 0;
|
||||
bool CheckFullHash(TexCacheEntry *entry, bool &doDelete);
|
||||
|
||||
// Separate to keep main texture cache size down.
|
||||
struct AttachedFramebufferInfo {
|
||||
u32 xOffset;
|
||||
u32 yOffset;
|
||||
};
|
||||
|
||||
void DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, bool reverseColors, bool useBGRA, bool expandTo32Bit);
|
||||
void UnswizzleFromMem(u32 *dest, u32 destPitch, const u8 *texptr, u32 bufw, u32 height, u32 bytesPerPixel);
|
||||
void ReadIndexedTex(u8 *out, int outPitch, int level, const u8 *texptr, int bytesPerIndex, int bufw, bool expandTo32Bit);
|
||||
|
@ -256,9 +271,13 @@ protected:
|
|||
void UpdateSamplingParams(TexCacheEntry &entry, SamplerCacheKey &key); // Used by D3D11 and Vulkan.
|
||||
void UpdateMaxSeenV(TexCacheEntry *entry, bool throughMode);
|
||||
|
||||
bool AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset, FramebufferNotificationChannel channel);
|
||||
void AttachFramebufferValid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const AttachedFramebufferInfo &fbInfo, FramebufferNotificationChannel channel);
|
||||
void AttachFramebufferInvalid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const AttachedFramebufferInfo &fbInfo, FramebufferNotificationChannel channel);
|
||||
FramebufferMatchInfo MatchFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, u32 texaddrOffset, FramebufferNotificationChannel channel) const;
|
||||
|
||||
// Temporary utility during conversion
|
||||
bool ApplyFramebufferMatch(FramebufferMatchInfo match, TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, FramebufferNotificationChannel channel);
|
||||
|
||||
void AttachFramebufferValid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const FramebufferMatchInfo &fbInfo, FramebufferNotificationChannel channel);
|
||||
void AttachFramebufferInvalid(TexCacheEntry *entry, VirtualFramebuffer *framebuffer, const FramebufferMatchInfo &fbInfo, FramebufferNotificationChannel channel);
|
||||
void DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, FramebufferNotificationChannel channel);
|
||||
|
||||
void SetTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer);
|
||||
|
@ -306,7 +325,7 @@ protected:
|
|||
u32 secondCacheSizeEstimate_;
|
||||
|
||||
std::vector<VirtualFramebuffer *> fbCache_;
|
||||
std::map<u64, AttachedFramebufferInfo> fbTexInfo_;
|
||||
std::map<u64, FramebufferMatchInfo> fbTexInfo_;
|
||||
|
||||
std::map<u32, int> videos_;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue