Reinterpret the data when binding a framebuffer with a different 16-bit format.

Car reflections in Outrun are better (see #11358) but have some
blue/yellow color garbage that will need a different fix.
This commit is contained in:
Henrik Rydgård 2020-11-06 11:54:57 +01:00
parent 1ccc8c129c
commit 981d0a2abe
5 changed files with 53 additions and 12 deletions

View File

@ -584,7 +584,7 @@ void FramebufferManagerCommon::ReformatFramebufferFrom(VirtualFramebuffer *vfb,
}
// Copy to a temp framebuffer.
Draw::Framebuffer *temp = GetTempFBO(TempFBO::COPY, vfb->renderWidth, vfb->renderHeight);
Draw::Framebuffer *temp = GetTempFBO(TempFBO::REINTERPRET, vfb->renderWidth, vfb->renderHeight);
draw_->CopyFramebufferImage(vfb->fbo, 0, 0, 0, 0, temp, 0, 0, 0, 0, vfb->renderWidth, vfb->renderHeight, 1, Draw::FBChannel::FB_COLOR_BIT, "reinterpret_prep");
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "reinterpret");

View File

@ -158,6 +158,8 @@ enum class TempFBO {
BLIT,
// For copies of framebuffers (e.g. shader blending.)
COPY,
// For another type of framebuffers that can happen together with COPY (see Outrun)
REINTERPRET,
// Used to copy stencil data, means we need a stencil backing.
STENCIL,
};
@ -320,6 +322,7 @@ public:
const std::vector<VirtualFramebuffer *> &Framebuffers() {
return vfbs_;
}
void ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old);
protected:
virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
@ -344,7 +347,6 @@ protected:
void NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbFormatChanged);
void NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth);
void ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old);
void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst);
void ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, int h, bool force = false, bool skipCopy = false);

View File

@ -486,9 +486,10 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
DeleteTexture(entryIter);
}
const AttachCandidate &candidate = candidates[index];
nextTexture_ = nullptr;
nextNeedsRebuild_ = false;
SetTextureFramebuffer(candidates[index]);
SetTextureFramebuffer(candidate);
return nullptr;
}
}
@ -849,11 +850,19 @@ FramebufferMatchInfo TextureCacheCommon::MatchFramebuffer(
WARN_LOG_ONCE(diffStrides1, G3D, "Texturing from framebuffer with different strides %d != %d", entry.bufw, framebuffer->fb_stride);
}
// NOTE: This check is okay because the first texture formats are the same as the buffer formats.
if (entry.format != (GETextureFormat)framebuffer->format) {
WARN_LOG_ONCE(diffFormat1, G3D, "Texturing from framebuffer with different formats %s != %s", GeTextureFormatToString(entry.format), GeBufferFormatToString(framebuffer->format));
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
} else {
if (IsTextureFormatBufferCompatible(entry.format)) {
if (TextureFormatMatchesBufferFormat(entry.format, framebuffer->format)) {
return FramebufferMatchInfo{ FramebufferMatch::VALID };
} 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) };
} 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 };
}
} else {
// Format incompatible, ignoring without comment. (maybe some really gnarly hacks will end up here...)
return FramebufferMatchInfo{ FramebufferMatch::NO_MATCH };
}
} else {
// Apply to buffered mode only.
@ -944,6 +953,13 @@ void TextureCacheCommon::SetTextureFramebuffer(const AttachCandidate &candidate)
VirtualFramebuffer *framebuffer = candidate.fb;
FramebufferMatchInfo fbInfo = candidate.match;
if (candidate.match.reinterpret) {
// TODO: Kinda ugly, maybe switch direction of the call?
GEBufferFormat oldFormat = candidate.fb->format;
candidate.fb->format = candidate.match.reinterpretTo;
framebufferManager_->ReformatFramebufferFrom(candidate.fb, oldFormat);
}
_dbg_assert_msg_(framebuffer != nullptr, "Framebuffer must not be null.");
framebuffer->usageFlags |= FB_USAGE_TEXTURE;

View File

@ -212,6 +212,8 @@ struct FramebufferMatchInfo {
FramebufferMatch match;
u32 xOffset;
u32 yOffset;
bool reinterpret;
GEBufferFormat reinterpretTo;
};
struct AttachCandidate {

View File

@ -419,12 +419,33 @@ enum GETextureFormat
GE_TFMT_DXT5 = 10,
};
const char *GeTextureFormatToString(GETextureFormat fmt);
inline bool IsClutFormat(GETextureFormat fmt) {
return fmt == GE_TFMT_CLUT4 || fmt == GE_TFMT_CLUT8 || fmt == GE_TFMT_CLUT16 || fmt == GE_TFMT_CLUT32;
const char *GeTextureFormatToString(GETextureFormat tfmt);
inline bool IsClutFormat(GETextureFormat tfmt) {
return tfmt == GE_TFMT_CLUT4 || tfmt == GE_TFMT_CLUT8 || tfmt == GE_TFMT_CLUT16 || tfmt == GE_TFMT_CLUT32;
}
inline bool IsDXTFormat(GETextureFormat fmt) {
return fmt == GE_TFMT_DXT1 || fmt == GE_TFMT_DXT3 || fmt == GE_TFMT_DXT5;
inline bool IsDXTFormat(GETextureFormat tfmt) {
return tfmt == GE_TFMT_DXT1 || tfmt == GE_TFMT_DXT3 || tfmt == GE_TFMT_DXT5;
}
inline bool IsTextureFormatBufferCompatible(GETextureFormat tfmt) {
return (int)tfmt < 4;
}
inline bool IsBufferFormat16Bit(GEBufferFormat bfmt) {
return (int)bfmt < 3;
}
inline bool IsTextureFormat16Bit(GETextureFormat tfmt) {
return (int)tfmt < 3;
}
inline bool TextureFormatMatchesBufferFormat(GETextureFormat fmt, GEBufferFormat bfmt) {
// First four matches perfectly.
if ((int)fmt < 4) {
return (int)fmt == (int)bfmt;
} else {
return false;
}
}
// only applicable if IsTextureFormatBufferCompatible(fmt)
inline GEBufferFormat TextureFormatToBufferFormat(GETextureFormat bfmt) {
return (GEBufferFormat)(int)bfmt;
}
enum GETexLevelMode {