Skip to content

Commit

Permalink
Merge pull request #15857 from hrydgard/sequence-based-texture-matching
Browse files Browse the repository at this point in the history
More flexible texture matching, bias matching by the bind sequence counters
  • Loading branch information
unknownbrackets committed Aug 18, 2022
2 parents 6cc8ca5 + 76333f9 commit 0749f14
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 96 deletions.
145 changes: 59 additions & 86 deletions GPU/Common/TextureCacheCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,35 +625,27 @@ std::vector<AttachCandidate> TextureCacheCommon::GetFramebufferCandidates(const

std::vector<AttachCandidate> candidates;

RasterChannel channel = Memory::IsDepthTexVRAMAddress(entry.addr) ? RasterChannel::RASTER_DEPTH : RasterChannel::RASTER_COLOR;
if (channel == RasterChannel::RASTER_DEPTH && !gstate_c.Supports(GPU_SUPPORTS_DEPTH_TEXTURE)) {
// Depth texture not supported. Don't try to match it, fall back to the memory behind..
return std::vector<AttachCandidate>();
}

const std::vector<VirtualFramebuffer *> &framebuffers = framebufferManager_->Framebuffers();

for (VirtualFramebuffer *framebuffer : framebuffers) {
FramebufferMatchInfo match = MatchFramebuffer(entry, framebuffer, texAddrOffset, channel);
switch (match.match) {
case FramebufferMatch::VALID:
candidates.push_back(AttachCandidate{ match, entry, framebuffer, channel });
break;
default:
break;
FramebufferMatchInfo match{};
if (MatchFramebuffer(entry, framebuffer, texAddrOffset, RASTER_COLOR, &match)) {
candidates.push_back(AttachCandidate{ match, entry, framebuffer, RASTER_COLOR, framebuffer->colorBindSeq });
}
match = {};
if (gstate_c.Supports(GPU_SUPPORTS_DEPTH_TEXTURE) && MatchFramebuffer(entry, framebuffer, texAddrOffset, RASTER_DEPTH, &match)) {
candidates.push_back(AttachCandidate{ match, entry, framebuffer, RASTER_DEPTH, framebuffer->depthBindSeq });
}
}

if (candidates.size() > 1) {
bool depth = channel == RasterChannel::RASTER_DEPTH;

std::string cands;
for (auto &candidate : candidates) {
cands += candidate.ToString() + " ";
}

WARN_LOG_REPORT_ONCE(multifbcandidate, G3D, "GetFramebufferCandidates(%s): Multiple (%d) candidate framebuffers. First will be chosen. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s",
depth ? "DEPTH" : "COLOR", (int)candidates.size(),
WARN_LOG_REPORT_ONCE(multifbcandidate, G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s",
(int)candidates.size(),
entry.addr, texAddrOffset, dimWidth(entry.dim), dimHeight(entry.dim), entry.bufw, GeTextureFormatToString(entry.format),
cands.c_str()
);
Expand All @@ -677,29 +669,22 @@ int TextureCacheCommon::GetBestCandidateIndex(const std::vector<AttachCandidate>
// a comparison function.
for (int i = 0; i < (int)candidates.size(); i++) {
const AttachCandidate &candidate = candidates[i];
int relevancy = 0;
switch (candidate.match.match) {
case FramebufferMatch::VALID:
relevancy += 1000;
break;
default:
break;
}
int relevancy = candidate.seqCount;

// Bonus point for matching stride.
if (candidate.channel == RASTER_COLOR && candidate.fb->fb_stride == candidate.entry.bufw) {
relevancy += 100;
relevancy += 1000;
}

// Bonus points for no offset.
if (candidate.match.xOffset == 0 && candidate.match.yOffset == 0) {
relevancy += 10;
relevancy += 100;
}

if (candidate.channel == RASTER_COLOR && candidate.fb->last_frame_render == gpuStats.numFlips) {
relevancy += 5;
relevancy += 50;
} else if (candidate.channel == RASTER_DEPTH && candidate.fb->last_frame_depth_render == gpuStats.numFlips) {
relevancy += 5;
relevancy += 50;
}

if (relevancy > bestRelevancy) {
Expand Down Expand Up @@ -876,12 +861,25 @@ void TextureCacheCommon::NotifyFramebuffer(VirtualFramebuffer *framebuffer, Fram
}
}

FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer(
bool TextureCacheCommon::MatchFramebuffer(
const TextureDefinition &entry,
VirtualFramebuffer *framebuffer, u32 texaddrOffset, RasterChannel channel) const {
VirtualFramebuffer *framebuffer, u32 texaddrOffset, RasterChannel channel, FramebufferMatchInfo *matchInfo) const {
static const u32 MAX_SUBAREA_Y_OFFSET_SAFE = 32;

uint32_t fb_address = channel == RASTER_DEPTH ? framebuffer->z_address : framebuffer->fb_address;
uint32_t fb_stride = channel == RASTER_DEPTH ? framebuffer->z_stride : framebuffer->fb_stride;

if (channel == RASTER_DEPTH && framebuffer->z_address == framebuffer->fb_address) {
// Try to avoid silly matches to somewhat malformed buffers.
return false;
}

switch (entry.format) {
case GE_TFMT_DXT1:
case GE_TFMT_DXT3:
case GE_TFMT_DXT5:
return false;
}

u32 addr = fb_address & 0x3FFFFFFF;
u32 texaddr = entry.addr + texaddrOffset;
Expand All @@ -891,34 +889,12 @@ FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer(

if (texInVRAM != fbInVRAM) {
// Shortcut. Cannot possibly be a match.
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
return false;
}

if (texInVRAM) {
const u32 mirrorMask = 0x00600000;

// This bit controls swizzle. The swizzles at 0x00200000 and 0x00600000 are designed
// to perfectly match reading depth as color (which one to use I think might be related
// to the bpp of the color format used when rendering to it).
// It's fairly unlikely that games would screw this up since the result will be garbage so
// we use it to filter out unlikely matches.
switch (entry.addr & mirrorMask) {
case 0x00000000:
case 0x00400000:
// Don't match the depth channel with these addresses when texturing.
if (channel == RasterChannel::RASTER_DEPTH) {
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
}
break;
case 0x00200000:
case 0x00600000:
// Don't match the color channel with these addresses when texturing.
if (channel == RasterChannel::RASTER_COLOR) {
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
}
break;
}

addr &= ~mirrorMask;
texaddr &= ~mirrorMask;
}
Expand All @@ -932,28 +908,29 @@ FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer(

// If they match "exactly", it's non-CLUT and from the top left.
if (exactMatch) {
if (framebuffer->fb_stride != entry.bufw) {
WARN_LOG_ONCE(diffStrides1, G3D, "Texturing from framebuffer with different strides %d != %d", entry.bufw, framebuffer->fb_stride);
if (fb_stride != entry.bufw) {
WARN_LOG_ONCE(diffStrides1, G3D, "Texturing from framebuffer with different strides %d != %d", entry.bufw, (int)fb_stride);
}
// NOTE: This check is okay because the first texture formats are the same as the buffer formats.
if (IsTextureFormatBufferCompatible(entry.format)) {
if (TextureFormatMatchesBufferFormat(entry.format, framebuffer->format) || (framebuffer->usageFlags & FB_USAGE_BLUE_TO_ALPHA)) {
return FramebufferMatchInfo{ FramebufferMatch::VALID };
return true;
} else if (IsTextureFormat16Bit(entry.format) && IsBufferFormat16Bit(framebuffer->format)) {
WARN_LOG_ONCE(diffFormat1, G3D, "Texturing from framebuffer with reinterpretable format: %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format));
return FramebufferMatchInfo{ FramebufferMatch::VALID, 0, 0, true, TextureFormatToBufferFormat(entry.format) };
*matchInfo = FramebufferMatchInfo{ 0, 0, true, TextureFormatToBufferFormat(entry.format) };
return true;
} else {
WARN_LOG_ONCE(diffFormat2, G3D, "Texturing from framebuffer with incompatible formats %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format));
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
return false;
}
} else {
// Format incompatible, ignoring without comment. (maybe some really gnarly hacks will end up here...)
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
return false;
}
} else {
// Apply to buffered mode only.
if (!framebufferManager_->UseBufferedRendering()) {
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
return false;
}

// Check works for D16 too (???)
Expand All @@ -962,73 +939,69 @@ FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer(
(channel == RASTER_COLOR && framebuffer->format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT32) ||
(channel == RASTER_COLOR && framebuffer->format != GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT16);

// To avoid ruining git blame, kept the same name as the old struct.
FramebufferMatchInfo fbInfo{ FramebufferMatch::VALID };

const u32 bitOffset = (texaddr - addr) * 8;
if (bitOffset != 0) {
const u32 pixelOffset = bitOffset / std::max(1U, (u32)textureBitsPerPixel[entry.format]);

fbInfo.yOffset = entry.bufw == 0 ? 0 : pixelOffset / entry.bufw;
fbInfo.xOffset = entry.bufw == 0 ? 0 : pixelOffset % entry.bufw;
matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / entry.bufw;
matchInfo->xOffset = entry.bufw == 0 ? 0 : pixelOffset % entry.bufw;
}

if (fbInfo.yOffset + minSubareaHeight >= framebuffer->height) {
if (matchInfo->yOffset + minSubareaHeight >= framebuffer->height) {
// Can't be inside the framebuffer.
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
return false;
}

if (framebuffer->fb_stride != entry.bufw) {
if (fb_stride != entry.bufw) {
if (noOffset) {
WARN_LOG_ONCE(diffStrides2, G3D, "Texturing from framebuffer (matching_clut=%s) different strides %d != %d", matchingClutFormat ? "yes" : "no", entry.bufw, framebuffer->fb_stride);
WARN_LOG_ONCE(diffStrides2, G3D, "Texturing from framebuffer (matching_clut=%s) different strides %d != %d", matchingClutFormat ? "yes" : "no", entry.bufw, 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.
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
return false;
}
}

// 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) {
if (matchInfo->xOffset >= framebuffer->bufferWidth && matchInfo->xOffset + w <= (u32)fb_stride) {
// This happens in Brave Story, see #10045 - the texture is in the space between strides, with matching stride.
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
return false;
}

// 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 && !PSP_CoreParameter().compat.flags().AllowLargeFBTextureOffsets) {
WARN_LOG_REPORT_ONCE(subareaIgnored, G3D, "Ignoring possible texturing from framebuffer at %08x +%dx%d / %dx%d", fb_address, fbInfo.xOffset, fbInfo.yOffset, framebuffer->width, framebuffer->height);
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
if (matchInfo->yOffset > MAX_SUBAREA_Y_OFFSET_SAFE && addr > 0x04110000 && !PSP_CoreParameter().compat.flags().AllowLargeFBTextureOffsets) {
WARN_LOG_REPORT_ONCE(subareaIgnored, G3D, "Ignoring possible texturing from framebuffer at %08x +%dx%d / %dx%d", fb_address, matchInfo->xOffset, matchInfo->yOffset, framebuffer->width, framebuffer->height);
return false;
}

// 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_ONCE(subareaClut, G3D, "Texturing from framebuffer using CLUT with offset at %08x +%dx%d", fb_address, fbInfo.xOffset, fbInfo.yOffset);
WARN_LOG_ONCE(subareaClut, G3D, "Texturing from framebuffer using CLUT with offset at %08x +%dx%d", fb_address, matchInfo->xOffset, matchInfo->yOffset);
}
fbInfo.match = FramebufferMatch::VALID; // We check the format again later, no need to return a special value here.
return fbInfo;
return true;
} 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(entry.format), GeBufferFormatToString(framebuffer->format));
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
return false;
}

// This is either normal or we failed to generate a shader to depalettize
if ((int)framebuffer->format == (int)entry.format || matchingClutFormat) {
if ((int)framebuffer->format != (int)entry.format) {
WARN_LOG_ONCE(diffFormat2, G3D, "Texturing from framebuffer with different formats %s != %s at %08x",
GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format), fb_address);
return fbInfo;
return true;
} else {
WARN_LOG_ONCE(subarea, G3D, "Texturing from framebuffer at %08x +%dx%d", fb_address, fbInfo.xOffset, fbInfo.yOffset);
return fbInfo;
WARN_LOG_ONCE(subarea, G3D, "Texturing from framebuffer at %08x +%dx%d", fb_address, matchInfo->xOffset, matchInfo->yOffset);
return true;
}
} else {
WARN_LOG_ONCE(diffFormat2, G3D, "Texturing from framebuffer with incompatible format %s != %s at %08x",
GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format), fb_address);
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
return false;
}
}
}
Expand Down Expand Up @@ -2158,8 +2131,8 @@ void TextureCacheCommon::ClearNextFrame() {
clearCacheNextFrame_ = true;
}

std::string AttachCandidate::ToString() {
return StringFromFormat("[C:%08x/%d Z:%08x/%d X:%d Y:%d reint: %s]", this->fb->fb_address, this->fb->fb_stride, this->fb->z_address, this->fb->z_stride, this->match.xOffset, this->match.yOffset, this->match.reinterpret ? "true" : "false");
std::string AttachCandidate::ToString() const {
return StringFromFormat("[%s seq:%d C:%08x/%d Z:%08x/%d X:%d Y:%d reint: %s]", this->channel == RASTER_COLOR ? "COLOR" : "DEPTH", this->seqCount, this->fb->fb_address, this->fb->fb_stride, this->fb->z_address, this->fb->z_stride, this->match.xOffset, this->match.yOffset, this->match.reinterpret ? "true" : "false");
}

bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEntry *entry) {
Expand Down
13 changes: 3 additions & 10 deletions GPU/Common/TextureCacheCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,7 @@ typedef std::map<u64, std::unique_ptr<TexCacheEntry>> TexCache;
#undef IGNORE
#endif

enum class FramebufferMatch {
// Valid, exact match.
VALID = 0,
// Not a match, remove if currently attached.
NO_MATCH,
};

struct FramebufferMatchInfo {
FramebufferMatch match;
u32 xOffset;
u32 yOffset;
bool reinterpret;
Expand All @@ -226,8 +218,9 @@ struct AttachCandidate {
TextureDefinition entry;
VirtualFramebuffer *fb;
RasterChannel channel;
int seqCount;

std::string ToString();
std::string ToString() const;
};

class FramebufferManagerCommon;
Expand Down Expand Up @@ -365,7 +358,7 @@ class TextureCacheCommon {
SamplerCacheKey GetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight);
void UpdateMaxSeenV(TexCacheEntry *entry, bool throughMode);

FramebufferMatchInfo MatchFramebuffer(const TextureDefinition &entry, VirtualFramebuffer *framebuffer, u32 texaddrOffset, RasterChannel channel) const;
bool MatchFramebuffer(const TextureDefinition &entry, VirtualFramebuffer *framebuffer, u32 texaddrOffset, RasterChannel channel, FramebufferMatchInfo *matchInfo) const;

std::vector<AttachCandidate> GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset);
int GetBestCandidateIndex(const std::vector<AttachCandidate> &candidates);
Expand Down

0 comments on commit 0749f14

Please sign in to comment.