Skip to content

Commit

Permalink
Persist CLUT framebuffer to GS memory
Browse files Browse the repository at this point in the history
Required by DBZ Budokai Tenkaichi 2 for CLUT rendering of characters
  • Loading branch information
kernle32dll committed Jun 4, 2021
1 parent ac76638 commit ee616e0
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 0 deletions.
116 changes: 116 additions & 0 deletions Source/gs/GSH_OpenGL/GSH_OpenGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,32 @@ CGSH_OpenGL::BitmapPtr CGSH_OpenGL::FindOrCreateBitmap(const FramebufferPtr& fra
return sharedPtr;
}

CGSH_OpenGL::FramebufferPtr CGSH_OpenGL::FindSmallestFramebufferAtPtr(uint32 ptr, uint32 psm) const
{
uint32 smallestSize = 0;
FramebufferPtr framebufferPtr = nullptr;

for(const auto& framebuffer : m_framebuffers)
{
if(framebuffer->m_psm != psm)
{
continue;
}

if(framebuffer->m_basePtr <= ptr && framebuffer->m_basePtr >= ptr)
{
uint32 size = framebuffer->m_width * framebuffer->m_height;
if(size < smallestSize || smallestSize == 0)
{
smallestSize = size;
framebufferPtr = framebuffer;
}
}
}

return framebufferPtr;
}

/////////////////////////////////////////////////////////////
// Individual Primitives Implementations
/////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1643,6 +1669,15 @@ void CGSH_OpenGL::FlushVertexBuffer()
}
DoRenderPass();
m_vertexBuffer.clear();

auto handle = m_renderState.framebufferHandle;
auto framebufferIterator = std::find_if(m_framebuffers.begin(), m_framebuffers.end(),
[handle](const FramebufferPtr& framebuffer) {
return (framebuffer->m_framebuffer == handle);
});
const auto& framebuffer = (*framebufferIterator);

framebuffer->copiedToRam = false;
}

void CGSH_OpenGL::DoRenderPass()
Expand Down Expand Up @@ -1897,6 +1932,87 @@ void CGSH_OpenGL::WriteRegisterImpl(uint8 nRegister, uint64 nData)
}
}

void CGSH_OpenGL::SyncCLUT(const TEX0& tex0)
{
if(!ProcessCLD(tex0)) return;

// If we use a CLUT based texture, its possible that the memory
// referenced is inside a framebuffer. In that case, we need
// to persist the framebuffer content into the GS RAM.

const uint32 ptr = tex0.GetCLUTPtr();

FramebufferPtr framebuffer = FindSmallestFramebufferAtPtr(ptr, PSMCT32);
if(framebuffer)
{
WriteFramebufferToMemory(framebuffer, true);
}

CGSHandler::SyncCLUT(tex0);
}

void CGSH_OpenGL::WriteFramebufferToMemory(const FramebufferPtr& framebuffer, bool downSample)
{
// If the framebuffer has already been persisted to RAM,
// we don't need to do that again.
if(framebuffer->copiedToRam)
{
return;
}

FramebufferPtr targetFramebuffer = framebuffer;
uint32 scale = m_fbScale;

if(downSample && m_fbScale != 1)
{
auto dstFramebuffer = FramebufferPtr(new CFramebuffer(
framebuffer->m_basePtr,
framebuffer->m_width,
framebuffer->m_height,
framebuffer->m_psm,
1,
m_multisampleEnabled));
PopulateFramebuffer(dstFramebuffer);

glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer->m_framebuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer->m_framebuffer);

//Copy buffers
glBlitFramebuffer(
0, 0, framebuffer->m_width * m_fbScale, framebuffer->m_height * m_fbScale,
0, 0, dstFramebuffer->m_width, dstFramebuffer->m_height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
CHECKGLERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);

targetFramebuffer = dstFramebuffer;
scale = 1;
}

glBindFramebuffer(GL_FRAMEBUFFER, targetFramebuffer->m_framebuffer);

// ----------------------

// Read data into ram

auto imgbuffer = FindOrCreateBitmap(targetFramebuffer, scale);
glReadPixels(0, 0, targetFramebuffer->m_width * scale, targetFramebuffer->m_height * scale, GL_RGBA, GL_UNSIGNED_BYTE, imgbuffer->GetPixels());

CGsPixelFormats::CPixelIndexorPSMCT32 indexor(m_pRAM, targetFramebuffer->m_basePtr, targetFramebuffer->m_width / 64);
for(uint32 y = 0; y < targetFramebuffer->m_height; y++)
{
for(uint32 x = 0; x < targetFramebuffer->m_width; x++)
{
auto pixel = imgbuffer->GetPixel(x * scale, y * scale);
indexor.SetPixel(x, y, MakeColor(pixel.r, pixel.g, pixel.b, pixel.a));
}
}

framebuffer->copiedToRam = true;

glBindFramebuffer(GL_FRAMEBUFFER, m_renderState.framebufferHandle);
}

void CGSH_OpenGL::VertexKick(uint8 nRegister, uint64 nValue)
{
if(m_nVtxCount == 0) return;
Expand Down
5 changes: 5 additions & 0 deletions Source/gs/GSH_OpenGL/GSH_OpenGL.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ class CGSH_OpenGL : public CGSHandler
bool m_resolveNeeded = false;
GLuint m_colorBufferMs = 0;

bool copiedToRam = false;

CGsCachedArea m_cachedArea;
};
typedef std::shared_ptr<CFramebuffer> FramebufferPtr;
Expand Down Expand Up @@ -312,6 +314,7 @@ class CGSH_OpenGL : public CGSHandler
typedef std::vector<PRIM_VERTEX> VertexBuffer;

void WriteRegisterImpl(uint8, uint64) override;
void SyncCLUT(const TEX0&) override;

void InitializeRC();
void CheckExtensions();
Expand Down Expand Up @@ -373,6 +376,8 @@ class CGSH_OpenGL : public CGSHandler
FramebufferPtr FindFramebuffer(const FRAME&) const;
DepthbufferPtr FindDepthbuffer(const ZBUF&, const FRAME&) const;
BitmapPtr FindOrCreateBitmap(const FramebufferPtr&, uint32);
FramebufferPtr FindSmallestFramebufferAtPtr(uint32, uint32) const;
void WriteFramebufferToMemory(const FramebufferPtr&, bool);

void DumpTexture(unsigned int, unsigned int, uint32);

Expand Down

0 comments on commit ee616e0

Please sign in to comment.