mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-22 05:15:51 +12:00
code: Better screen support
This commit is contained in:
parent
d28796fd3f
commit
f75a23b5a9
15 changed files with 305 additions and 66 deletions
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue