Skip to content

Commit

Permalink
Support render-to-clut, at least in some cases.
Browse files Browse the repository at this point in the history
This is pretty much only tested with Brave Story.  See hrydgard#6754.

There may be other cases which are not handled yet.
  • Loading branch information
unknownbrackets committed Apr 28, 2015
1 parent 959118a commit 893c376
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 7 deletions.
17 changes: 16 additions & 1 deletion GPU/Common/FramebufferCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ void FramebufferManagerCommon::DoSetRenderFrameBuffer() {
vfb->last_frame_used = 0;
vfb->last_frame_attached = 0;
vfb->last_frame_displayed = 0;
vfb->last_frame_clut = 0;
frameLastFramebufUsed_ = gpuStats.numFlips;
vfbs_.push_back(vfb);
currentRenderVfb_ = vfb;
Expand Down Expand Up @@ -491,6 +492,13 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
srcBuffer = vfb;
srcY = yOffset;
srcH = 1;
} else if (yOffset == 0 && yOffset < srcY) {
// Okay, last try - it might be a clut.
if (vfb->usageFlags & FB_USAGE_CLUT) {
srcBuffer = vfb;
srcY = yOffset;
srcH = 1;
}
}
}
}
Expand Down Expand Up @@ -584,7 +592,13 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst
// Grand Knights History copies with a mismatching stride but a full line at a time.
// Makes it hard to detect the wrong transfers in e.g. God of War.
if (width != dstStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) {
match = false;
// However, some other games write cluts to framebuffers.
// Let's catch this and upload. Otherwise reject the match.
match = (vfb->usageFlags & FB_USAGE_CLUT) != 0;
if (match) {
dstWidth = byteStride * height / vfb_bpp;
dstHeight = 1;
}
} else {
dstWidth = byteStride * height / vfb_bpp;
dstHeight = 1;
Expand Down Expand Up @@ -786,4 +800,5 @@ void FramebufferManagerCommon::UpdateFramebufUsage(VirtualFramebuffer *vfb) {
checkFlag(FB_USAGE_DISPLAYED_FRAMEBUFFER, vfb->last_frame_displayed);
checkFlag(FB_USAGE_TEXTURE, vfb->last_frame_used);
checkFlag(FB_USAGE_RENDERTARGET, vfb->last_frame_render);
checkFlag(FB_USAGE_CLUT, vfb->last_frame_clut);
}
2 changes: 2 additions & 0 deletions GPU/Common/FramebufferCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ enum {
FB_USAGE_DISPLAYED_FRAMEBUFFER = 1,
FB_USAGE_RENDERTARGET = 2,
FB_USAGE_TEXTURE = 4,
FB_USAGE_CLUT = 8,
};

enum {
Expand All @@ -51,6 +52,7 @@ struct VirtualFramebuffer {
int last_frame_attached;
int last_frame_render;
int last_frame_displayed;
int last_frame_clut;
bool memoryUpdated;
bool depthUpdated;

Expand Down
24 changes: 20 additions & 4 deletions GPU/Directx9/TextureCacheDX9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -823,11 +823,27 @@ inline bool TextureCacheDX9::TexCacheEntry::Matches(u16 dim2, u8 format2, int ma
}

void TextureCacheDX9::LoadClut() {
u32 clutAddr = gstate.getClutAddress();
// Clear the uncached bit, etc. to match framebuffers.
const u32 clutAddr = gstate.getClutAddress() & 0x3FFFFFFF;
bool foundFramebuffer = false;

for (size_t i = 0, n = fbCache_.size(); i < n; ++i) {
auto framebuffer = fbCache_[i];
if ((framebuffer->fb_address | 0x04000000) == clutAddr) {
framebuffer->last_frame_clut = gpuStats.numFlips;
framebuffer->usageFlags |= FB_USAGE_CLUT;
foundFramebuffer = true;
}
}

clutTotalBytes_ = gstate.getClutLoadBytes();
if (Memory::IsValidAddress(clutAddr)) {
// It's possible for a game to (successfully) access outside valid memory.
u32 bytes = Memory::ValidSize(clutAddr, clutTotalBytes_);
if (foundFramebuffer) {
gpu->PerformMemoryDownload(clutAddr, bytes);
}

#ifdef _M_SSE
int numBlocks = bytes / 16;
if (bytes == clutTotalBytes_) {
Expand All @@ -842,17 +858,17 @@ void TextureCacheDX9::LoadClut() {
} else {
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
if (bytes < clutTotalBytes_) {
memset(clutBufRaw_ + bytes, 0x00, clutTotalBytes_ - bytes);
memset((u8 *)clutBufRaw_ + bytes, 0x00, clutTotalBytes_ - bytes);
}
}
#else
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
if (bytes < clutTotalBytes_) {
memset(clutBufRaw_ + bytes, 0x00, clutTotalBytes_ - bytes);
memset((u8 *)clutBufRaw_ + bytes, 0x00, clutTotalBytes_ - bytes);
}
#endif
} else {
memset(clutBufRaw_, 0x00, clutTotalBytes_);
memset((u8 *)clutBufRaw_, 0x00, clutTotalBytes_);
}
// Reload the clut next time.
clutLastFormat_ = 0xFFFFFFFF;
Expand Down
20 changes: 18 additions & 2 deletions GPU/GLES/TextureCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -950,11 +950,27 @@ inline bool TextureCache::TexCacheEntry::Matches(u16 dim2, u8 format2, int maxLe
}

void TextureCache::LoadClut() {
u32 clutAddr = gstate.getClutAddress();
// Clear the uncached bit, etc. to match framebuffers.
const u32 clutAddr = gstate.getClutAddress() & 0x3FFFFFFF;
bool foundFramebuffer = false;

for (size_t i = 0, n = fbCache_.size(); i < n; ++i) {
auto framebuffer = fbCache_[i];
if ((framebuffer->fb_address | 0x04000000) == clutAddr) {
framebuffer->last_frame_clut = gpuStats.numFlips;
framebuffer->usageFlags |= FB_USAGE_CLUT;
foundFramebuffer = true;
}
}

clutTotalBytes_ = gstate.getClutLoadBytes();
if (Memory::IsValidAddress(clutAddr)) {
// It's possible for a game to (successfully) access outside valid memory.
u32 bytes = Memory::ValidSize(clutAddr, clutTotalBytes_);
if (foundFramebuffer) {
gpu->PerformMemoryDownload(clutAddr, bytes);
}

#ifdef _M_SSE
int numBlocks = bytes / 16;
if (bytes == clutTotalBytes_) {
Expand All @@ -979,7 +995,7 @@ void TextureCache::LoadClut() {
}
#endif
} else {
memset(clutBufRaw_, 0x00, clutTotalBytes_);
memset((u8 *)clutBufRaw_, 0x00, clutTotalBytes_);
}
// Reload the clut next time.
clutLastFormat_ = 0xFFFFFFFF;
Expand Down

0 comments on commit 893c376

Please sign in to comment.