mirror of
https://github.com/jpd002/Play-.git
synced 2025-04-28 13:47:57 +03:00

Some checks failed
Build macOS / build_macos (push) Has been cancelled
Build Android / build_android (apk) (push) Has been cancelled
Build Android / build_android (libretro) (push) Has been cancelled
Build Linux ARM32 / build_linux_arm32 (push) Has been cancelled
Build Linux ARM64 / build_linux_arm64 (push) Has been cancelled
Build Windows Psf / build_windows_psf (off, x86_64, Visual Studio 16 2019, installer64.nsi, x64) (push) Has been cancelled
Build Windows Psf / build_windows_psf (on, x86_64, Visual Studio 16 2019, installer64.nsi, x64) (push) Has been cancelled
Build Windows / build_windows (x86_32, Visual Studio 16 2019, installer32.nsi, win32_msvc2019, Win32) (push) Has been cancelled
Build Windows / build_windows (x86_64, Visual Studio 16 2019, installer64.nsi, win64_msvc2019_64, x64) (push) Has been cancelled
Check Format / run_clangformat (push) Has been cancelled
Build iOS / build_ios (push) Has been cancelled
Build JavaScript / build_js (push) Has been cancelled
Build Linux / build_linux (push) Has been cancelled
338 lines
9.7 KiB
C++
338 lines
9.7 KiB
C++
#include "GsContextView.h"
|
|
#include "GsStateUtils.h"
|
|
#include "gs/GsDebuggerInterface.h"
|
|
#include "gs/GsPixelFormats.h"
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
enum TAB_IDS
|
|
{
|
|
TAB_ID_FRAMEBUFFER,
|
|
TAB_ID_DEPTHBUFFER,
|
|
TAB_ID_TEXTURE_BASE,
|
|
TAB_ID_TEXTURE_MIP1,
|
|
TAB_ID_TEXTURE_MIP2,
|
|
TAB_ID_TEXTURE_MIP3,
|
|
TAB_ID_TEXTURE_MIP4,
|
|
TAB_ID_TEXTURE_MIP5,
|
|
TAB_ID_TEXTURE_MIP6
|
|
};
|
|
|
|
CGsContextView::CGsContextView(QWidget* parent, QComboBox* contextBuffer, QPushButton* fitButton, QPushButton* saveButton, const GsHandlerPtr& gs, unsigned int contextId)
|
|
: QWidget(parent)
|
|
, m_contextId(contextId)
|
|
, m_gs(gs)
|
|
{
|
|
|
|
m_bufferView = std::make_unique<CPixelBufferView>(this, contextBuffer);
|
|
auto layout = new QVBoxLayout;
|
|
setLayout(layout);
|
|
layout->addWidget(m_bufferView.get());
|
|
|
|
connect(fitButton, &QPushButton::clicked, [&]() {
|
|
m_bufferView->FitBitmap();
|
|
});
|
|
connect(saveButton, &QPushButton::clicked, [&]() {
|
|
m_bufferView->OnSaveBitmap();
|
|
});
|
|
}
|
|
|
|
void CGsContextView::SetFbDisplayMode(FB_DISPLAY_MODE fbDisplayMode)
|
|
{
|
|
m_fbDisplayMode = fbDisplayMode;
|
|
UpdateBufferView();
|
|
}
|
|
|
|
void CGsContextView::UpdateState(CGsPacketMetadata*, DRAWINGKICK_INFO* drawingKick)
|
|
{
|
|
m_drawingKick = (*drawingKick);
|
|
UpdateBufferView();
|
|
}
|
|
|
|
void CGsContextView::SetSelection(int selected)
|
|
{
|
|
m_selected = selected;
|
|
UpdateBufferView();
|
|
}
|
|
|
|
void CGsContextView::UpdateBufferView()
|
|
{
|
|
uint32 selectedId = m_selected;
|
|
switch(selectedId)
|
|
{
|
|
case TAB_ID_FRAMEBUFFER:
|
|
UpdateFramebufferView();
|
|
break;
|
|
case TAB_ID_DEPTHBUFFER:
|
|
UpdateDepthbufferView();
|
|
break;
|
|
case TAB_ID_TEXTURE_BASE:
|
|
case TAB_ID_TEXTURE_MIP1:
|
|
case TAB_ID_TEXTURE_MIP2:
|
|
case TAB_ID_TEXTURE_MIP3:
|
|
case TAB_ID_TEXTURE_MIP4:
|
|
case TAB_ID_TEXTURE_MIP5:
|
|
case TAB_ID_TEXTURE_MIP6:
|
|
{
|
|
uint64 tex0Reg = m_gs->GetRegisters()[GS_REG_TEX0_1 + m_contextId];
|
|
uint64 tex1Reg = m_gs->GetRegisters()[GS_REG_TEX1_1 + m_contextId];
|
|
uint64 miptbp1Reg = m_gs->GetRegisters()[GS_REG_MIPTBP1_1 + m_contextId];
|
|
uint64 miptbp2Reg = m_gs->GetRegisters()[GS_REG_MIPTBP2_1 + m_contextId];
|
|
auto tex0 = make_convertible<CGSHandler::TEX0>(tex0Reg);
|
|
auto tex1 = make_convertible<CGSHandler::TEX1>(tex1Reg);
|
|
|
|
uint32 mipLevel = selectedId - TAB_ID_TEXTURE_BASE;
|
|
Framework::CBitmap texture, clutTexture, alphaTexture;
|
|
|
|
if(mipLevel <= tex1.nMaxMip)
|
|
{
|
|
if(auto debuggerInterface = dynamic_cast<CGsDebuggerInterface*>(m_gs.get()))
|
|
{
|
|
texture = debuggerInterface->GetTexture(tex0Reg, tex1.nMaxMip, miptbp1Reg, miptbp2Reg, mipLevel);
|
|
}
|
|
}
|
|
|
|
if(!texture.IsEmpty() && CGsPixelFormats::IsPsmIDTEX(tex0.nPsm))
|
|
{
|
|
ColorArray convertedClut;
|
|
m_gs->MakeLinearCLUT(tex0, convertedClut);
|
|
clutTexture = LookupBitmap(texture, convertedClut);
|
|
|
|
if(tex0.nCPSM == CGSHandler::PSMCT32)
|
|
{
|
|
alphaTexture = ExtractAlpha32(clutTexture);
|
|
}
|
|
}
|
|
|
|
if(!texture.IsEmpty() && CGsPixelFormats::IsPsmIDTEX4(tex0.nPsm))
|
|
{
|
|
//Too hard to see if pixel brightness is not boosted
|
|
BrightenBitmap(texture);
|
|
}
|
|
|
|
if(!texture.IsEmpty() && tex0.nPsm == CGSHandler::PSMCT32)
|
|
{
|
|
alphaTexture = ExtractAlpha32(texture);
|
|
}
|
|
|
|
CPixelBufferView::PixelBufferArray pixelBuffers;
|
|
pixelBuffers.emplace_back("Raw", std::move(texture));
|
|
if(!clutTexture.IsEmpty())
|
|
{
|
|
pixelBuffers.emplace_back("+ CLUT", std::move(clutTexture));
|
|
}
|
|
if(!alphaTexture.IsEmpty())
|
|
{
|
|
pixelBuffers.emplace_back("Alpha", std::move(alphaTexture));
|
|
}
|
|
m_bufferView->SetPixelBuffers(std::move(pixelBuffers));
|
|
}
|
|
break;
|
|
}
|
|
m_bufferView->repaint();
|
|
}
|
|
|
|
void CGsContextView::UpdateFramebufferView()
|
|
{
|
|
uint64 frameReg = m_gs->GetRegisters()[GS_REG_FRAME_1 + m_contextId];
|
|
auto frame = make_convertible<CGSHandler::FRAME>(frameReg);
|
|
|
|
Framework::CBitmap framebuffer;
|
|
int fbScale = 1;
|
|
if(auto debuggerInterface = dynamic_cast<CGsDebuggerInterface*>(m_gs.get()))
|
|
{
|
|
framebuffer = debuggerInterface->GetFramebuffer(frame);
|
|
fbScale = debuggerInterface->GetFramebufferScale();
|
|
}
|
|
if(framebuffer.IsEmpty())
|
|
{
|
|
m_bufferView->SetPixelBuffers(CPixelBufferView::PixelBufferArray());
|
|
return;
|
|
}
|
|
|
|
framebuffer = ClipRenderbuffer(std::move(framebuffer), fbScale);
|
|
|
|
Framework::CBitmap alphaFramebuffer;
|
|
if(frame.nPsm == CGSHandler::PSMCT32)
|
|
{
|
|
assert(framebuffer.GetBitsPerPixel() == 32);
|
|
alphaFramebuffer = ExtractAlpha32(framebuffer);
|
|
}
|
|
|
|
framebuffer = PostProcessRenderbuffer(std::move(framebuffer), fbScale);
|
|
alphaFramebuffer = PostProcessRenderbuffer(std::move(alphaFramebuffer), fbScale);
|
|
|
|
CPixelBufferView::PixelBufferArray pixelBuffers;
|
|
pixelBuffers.emplace_back("Raw", std::move(framebuffer));
|
|
if(!alphaFramebuffer.IsEmpty())
|
|
{
|
|
pixelBuffers.emplace_back("Alpha", std::move(alphaFramebuffer));
|
|
}
|
|
m_bufferView->SetPixelBuffers(std::move(pixelBuffers));
|
|
}
|
|
|
|
void CGsContextView::UpdateDepthbufferView()
|
|
{
|
|
uint64 frameReg = m_gs->GetRegisters()[GS_REG_FRAME_1 + m_contextId];
|
|
uint64 zbufReg = m_gs->GetRegisters()[GS_REG_ZBUF_1 + m_contextId];
|
|
|
|
auto frame = make_convertible<CGSHandler::FRAME>(frameReg);
|
|
auto zbuf = make_convertible<CGSHandler::ZBUF>(zbufReg);
|
|
|
|
Framework::CBitmap depthbuffer;
|
|
int fbScale = 1;
|
|
if(auto debuggerInterface = dynamic_cast<CGsDebuggerInterface*>(m_gs.get()))
|
|
{
|
|
depthbuffer = debuggerInterface->GetDepthbuffer(frame, zbuf);
|
|
fbScale = debuggerInterface->GetFramebufferScale();
|
|
}
|
|
if(depthbuffer.IsEmpty())
|
|
{
|
|
m_bufferView->SetPixelBuffers(CPixelBufferView::PixelBufferArray());
|
|
return;
|
|
}
|
|
|
|
depthbuffer = ClipRenderbuffer(std::move(depthbuffer), fbScale);
|
|
depthbuffer = PostProcessRenderbuffer(std::move(depthbuffer), fbScale);
|
|
|
|
CPixelBufferView::PixelBufferArray pixelBuffers;
|
|
pixelBuffers.emplace_back("Raw", std::move(depthbuffer));
|
|
m_bufferView->SetPixelBuffers(std::move(pixelBuffers));
|
|
}
|
|
|
|
Framework::CBitmap CGsContextView::ClipRenderbuffer(Framework::CBitmap buffer, float scale)
|
|
{
|
|
if(m_fbDisplayMode == FB_DISPLAY_MODE_448P)
|
|
{
|
|
buffer = buffer.ResizeCanvas(640 * scale, 448 * scale);
|
|
}
|
|
else if(m_fbDisplayMode == FB_DISPLAY_MODE_448I)
|
|
{
|
|
buffer = buffer.ResizeCanvas(640 * scale, 224 * scale);
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
Framework::CBitmap CGsContextView::PostProcessRenderbuffer(Framework::CBitmap buffer, float scale)
|
|
{
|
|
if(!buffer.IsEmpty())
|
|
{
|
|
RenderDrawKick(buffer);
|
|
if(m_fbDisplayMode == FB_DISPLAY_MODE_448I)
|
|
{
|
|
buffer = buffer.Resize(640 * scale, 448 * scale);
|
|
}
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
void CGsContextView::RenderDrawKick(Framework::CBitmap& bitmap)
|
|
{
|
|
if(m_drawingKick.primType == CGSHandler::PRIM_INVALID) return;
|
|
if(m_drawingKick.context != m_contextId) return;
|
|
|
|
int fbScale = 1;
|
|
if(auto debuggerInterface = dynamic_cast<CGsDebuggerInterface*>(m_gs.get()))
|
|
{
|
|
fbScale = debuggerInterface->GetFramebufferScale();
|
|
}
|
|
|
|
auto primHighlightColor = Framework::CColor(0, 0xFF, 0, 0xFF);
|
|
switch(m_drawingKick.primType)
|
|
{
|
|
case CGSHandler::PRIM_TRIANGLE:
|
|
case CGSHandler::PRIM_TRIANGLESTRIP:
|
|
case CGSHandler::PRIM_TRIANGLEFAN:
|
|
{
|
|
int x1 = (m_drawingKick.vertex[0].x / 16) * fbScale;
|
|
int y1 = (m_drawingKick.vertex[0].y / 16) * fbScale;
|
|
int x2 = (m_drawingKick.vertex[1].x / 16) * fbScale;
|
|
int y2 = (m_drawingKick.vertex[1].y / 16) * fbScale;
|
|
int x3 = (m_drawingKick.vertex[2].x / 16) * fbScale;
|
|
int y3 = (m_drawingKick.vertex[2].y / 16) * fbScale;
|
|
bitmap.DrawLine(x1, y1, x2, y2, primHighlightColor);
|
|
bitmap.DrawLine(x1, y1, x3, y3, primHighlightColor);
|
|
bitmap.DrawLine(x2, y2, x3, y3, primHighlightColor);
|
|
}
|
|
break;
|
|
case CGSHandler::PRIM_SPRITE:
|
|
{
|
|
int x1 = (m_drawingKick.vertex[0].x / 16) * fbScale;
|
|
int y1 = (m_drawingKick.vertex[0].y / 16) * fbScale;
|
|
int x2 = (m_drawingKick.vertex[1].x / 16) * fbScale;
|
|
int y2 = (m_drawingKick.vertex[1].y / 16) * fbScale;
|
|
bitmap.DrawLine(x1, y1, x1, y2, primHighlightColor);
|
|
bitmap.DrawLine(x1, y2, x2, y2, primHighlightColor);
|
|
bitmap.DrawLine(x2, y2, x2, y1, primHighlightColor);
|
|
bitmap.DrawLine(x2, y1, x1, y1, primHighlightColor);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CGsContextView::BrightenBitmap(Framework::CBitmap& bitmap)
|
|
{
|
|
assert(!bitmap.IsEmpty());
|
|
assert(bitmap.GetBitsPerPixel() == 8);
|
|
auto pixels = reinterpret_cast<uint8*>(bitmap.GetPixels());
|
|
for(uint32 y = 0; y < bitmap.GetHeight(); y++)
|
|
{
|
|
for(uint32 x = 0; x < bitmap.GetWidth(); x++)
|
|
{
|
|
pixels[x] <<= 4;
|
|
}
|
|
pixels += bitmap.GetPitch();
|
|
}
|
|
}
|
|
|
|
uint32 CGsContextView::Color_Ps2ToRGBA(uint32 color)
|
|
{
|
|
uint8 r = (color & 0xFF);
|
|
uint8 b = (color & 0xFF00) >> 8;
|
|
uint8 g = (color & 0xFF0000) >> 16;
|
|
uint8 a = (color & 0xFF000000) >> 24;
|
|
return g << 0 | b << 8 | r << 16 | a << 24;
|
|
}
|
|
|
|
Framework::CBitmap CGsContextView::LookupBitmap(const Framework::CBitmap& srcBitmap, const ColorArray& clut)
|
|
{
|
|
assert(!srcBitmap.IsEmpty());
|
|
assert(srcBitmap.GetBitsPerPixel() == 8);
|
|
auto dstBitmap = Framework::CBitmap(srcBitmap.GetWidth(), srcBitmap.GetHeight(), 32);
|
|
auto srcPixels = reinterpret_cast<uint8*>(srcBitmap.GetPixels());
|
|
auto dstPixels = reinterpret_cast<uint32*>(dstBitmap.GetPixels());
|
|
for(uint32 y = 0; y < srcBitmap.GetHeight(); y++)
|
|
{
|
|
for(uint32 x = 0; x < srcBitmap.GetWidth(); x++)
|
|
{
|
|
uint8 index = srcPixels[x];
|
|
uint32 color = clut[index];
|
|
uint32 newColor = Color_Ps2ToRGBA(color);
|
|
dstPixels[x] = newColor;
|
|
}
|
|
srcPixels += srcBitmap.GetPitch();
|
|
dstPixels += dstBitmap.GetPitch() / 4;
|
|
}
|
|
return std::move(dstBitmap);
|
|
}
|
|
|
|
Framework::CBitmap CGsContextView::ExtractAlpha32(const Framework::CBitmap& srcBitmap)
|
|
{
|
|
assert(!srcBitmap.IsEmpty());
|
|
assert(srcBitmap.GetBitsPerPixel() == 32);
|
|
auto dstBitmap = Framework::CBitmap(srcBitmap.GetWidth(), srcBitmap.GetHeight(), 32);
|
|
auto srcPixels = reinterpret_cast<uint32*>(srcBitmap.GetPixels());
|
|
auto dstPixels = reinterpret_cast<uint32*>(dstBitmap.GetPixels());
|
|
for(uint32 y = 0; y < srcBitmap.GetHeight(); y++)
|
|
{
|
|
for(uint32 x = 0; x < srcBitmap.GetWidth(); x++)
|
|
{
|
|
uint32 color = srcPixels[x];
|
|
uint32 alpha = color >> 24;
|
|
dstPixels[x] = (alpha) | (alpha << 8) | (alpha << 16) | 0xFF000000;
|
|
}
|
|
srcPixels += srcBitmap.GetPitch() / 4;
|
|
dstPixels += dstBitmap.GetPitch() / 4;
|
|
}
|
|
return std::move(dstBitmap);
|
|
}
|