code: Better screen support

This commit is contained in:
GPUCode 2023-07-10 23:59:44 +03:00
parent d28796fd3f
commit f75a23b5a9
15 changed files with 305 additions and 66 deletions

View file

@ -450,6 +450,37 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
void RendererGL::display() {
gl.disableScissor();
gl.disableBlend();
gl.disableDepth();
gl.disableScissor();
gl.setColourMask(true, true, true, true);
gl.useProgram(displayProgram);
gl.bindVAO(dummyVAO);
OpenGL::disableClipPlane(0);
OpenGL::disableClipPlane(1);
using namespace PICA::ExternalRegs;
const u32 topScreenAddr = gpu.readExternalReg(Framebuffer0AFirstAddr);
const u32 bottomScreenAddr = gpu.readExternalReg(Framebuffer1AFirstAddr);
auto topScreen = colourBufferCache.findFromAddress(topScreenAddr);
auto bottomScreen = colourBufferCache.findFromAddress(bottomScreenAddr);
Helpers::warn("Top screen addr %08X\n", topScreenAddr);
screenFramebuffer.bind(OpenGL::DrawFramebuffer);
if (topScreen) {
topScreen->get().texture.bind();
OpenGL::setViewport(0, 240, 400, 240); // Top screen viewport
OpenGL::draw(OpenGL::TriangleStrip, 4); // Actually draw our 3DS screen
}
if (bottomScreen) {
bottomScreen->get().texture.bind();
OpenGL::setViewport(40, 0, 320, 240);
OpenGL::draw(OpenGL::TriangleStrip, 4);
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
screenFramebuffer.bind(OpenGL::ReadFramebuffer);
@ -550,42 +581,56 @@ OpenGL::Texture RendererGL::getTexture(Texture& tex) {
}
}
// NOTE: The GPU format has RGB5551 and RGB655 swapped compared to internal regs format
PICA::ColorFmt ToColorFmt(u32 format) {
switch (format) {
case 2: return PICA::ColorFmt::RGB565;
case 3: return PICA::ColorFmt::RGBA5551;
default: return static_cast<PICA::ColorFmt>(format);
}
}
void RendererGL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) {
const u32 inputWidth = inputSize & 0xffff;
const u32 inputGap = inputSize >> 16;
const u32 inputHeight = inputSize >> 16;
const auto inputFormat = ToColorFmt(Helpers::getBits<8, 3>(flags));
const auto outputFormat = ToColorFmt(Helpers::getBits<12, 3>(flags));
const PICA::Scaling scaling = static_cast<PICA::Scaling>(Helpers::getBits<24, 2>(flags));
const u32 outputWidth = outputSize & 0xffff;
const u32 outputGap = outputSize >> 16;
auto framebuffer = colourBufferCache.findFromAddress(inputAddr);
// If there's a framebuffer at this address, use it. Otherwise go back to our old hack and display framebuffer 0
// Displays are hard I really don't want to try implementing them because getting a fast solution is terrible
OpenGL::Texture& tex = framebuffer.has_value() ? framebuffer.value().get().texture : colourBufferCache[0].texture;
tex.bind();
screenFramebuffer.bind(OpenGL::DrawFramebuffer);
gl.disableBlend();
gl.disableLogicOp();
gl.disableDepth();
gl.disableScissor();
gl.disableStencil();
gl.setColourMask(true, true, true, true);
gl.useProgram(displayProgram);
gl.bindVAO(dummyVAO);
gl.disableClipPlane(0);
gl.disableClipPlane(1);
// Hack: Detect whether we are writing to the top or bottom screen by checking output gap and drawing to the proper part of the output texture
// We consider output gap == 320 to mean bottom, and anything else to mean top
if (outputGap == 320) {
OpenGL::setViewport(40, 0, 320, 240); // Bottom screen viewport
} else {
OpenGL::setViewport(0, 240, 400, 240); // Top screen viewport
u32 outputWidth = outputSize & 0xffff;
if (scaling == PICA::Scaling::X || scaling == PICA::Scaling::XY) {
outputWidth >>= 1;
}
u32 outputHeight = outputSize >> 16;
if (scaling == PICA::Scaling::XY) {
outputHeight >>= 1;
}
OpenGL::draw(OpenGL::TriangleStrip, 4); // Actually draw our 3DS screen
// If there's a framebuffer at this address, use it. Otherwise go back to our old hack and display framebuffer 0
// Displays are hard I really don't want to try implementing them because getting a fast solution is terrible
auto srcFramebuffer = getColourBuffer(inputAddr, inputFormat, inputWidth, inputHeight);
auto dstFramebuffer = getColourBuffer(outputAddr, outputFormat, outputWidth, outputHeight);
Helpers::warn("Display transfer with outputAddr %08X\n", outputAddr);
// Blit the framebuffers
srcFramebuffer.fbo.bind(OpenGL::ReadFramebuffer);
dstFramebuffer.fbo.bind(OpenGL::DrawFramebuffer);
glBlitFramebuffer(0, 0, inputWidth, inputHeight, 0, 0, outputWidth, outputHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
ColourBuffer RendererGL::getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height) {
// Try to find an already existing buffer that contains the provided address
// This is a more relaxed check compared to getColourFBO as display transfer/texcopy may refer to
// subrect of a surface and in case of texcopy we don't know the format of the surface.
auto buffer = colourBufferCache.findFromAddress(addr);
if (buffer.has_value()) {
return buffer.value().get();
}
// Otherwise create and cache a new buffer.
ColourBuffer sampleBuffer(addr, format, width, height);
return colourBufferCache.add(sampleBuffer);
}
void RendererGL::screenshot(const std::string& name) {