mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-08 00:02:58 +12:00
Initial screen layout implementation
This commit is contained in:
parent
d06f600b3a
commit
62748eef47
11 changed files with 335 additions and 126 deletions
|
@ -11,6 +11,7 @@
|
|||
#include "PICA/pica_hash.hpp"
|
||||
#include "PICA/pica_simd.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "PICA/screen_layout.hpp"
|
||||
#include "PICA/shader_decompiler.hpp"
|
||||
#include "config.hpp"
|
||||
#include "math_util.hpp"
|
||||
|
@ -134,8 +135,8 @@ void RendererGL::initGraphicsContextInternal() {
|
|||
|
||||
auto prevTexture = OpenGL::getTex2D();
|
||||
|
||||
// Create a plain black texture for when a game reads an invalid texture. It is common for games to configure the PICA to read texture info from NULL.
|
||||
// Some games that do this are Pokemon X, Cars 2, Tomodachi Life, and more. We bind the texture to an FBO, clear it, and free the FBO
|
||||
// Create a plain black texture for when a game reads an invalid texture. It is common for games to configure the PICA to read texture info from
|
||||
// NULL. Some games that do this are Pokemon X, Cars 2, Tomodachi Life, and more. We bind the texture to an FBO, clear it, and free the FBO
|
||||
blankTexture.create(8, 8, GL_RGBA8);
|
||||
blankTexture.bind();
|
||||
blankTexture.setMinFilter(OpenGL::Linear);
|
||||
|
@ -228,7 +229,7 @@ void RendererGL::setupBlending() {
|
|||
// Shows if blending is enabled. If it is not enabled, then logic ops are enabled instead
|
||||
const bool blendingEnabled = (regs[PICA::InternalRegs::ColourOperation] & (1 << 8)) != 0;
|
||||
|
||||
if (!blendingEnabled) { // Logic ops are enabled
|
||||
if (!blendingEnabled) { // Logic ops are enabled
|
||||
const u32 logicOp = getBits<0, 4>(regs[PICA::InternalRegs::LogicOp]);
|
||||
gl.setLogicOp(logicOps[logicOp]);
|
||||
|
||||
|
@ -268,21 +269,12 @@ void RendererGL::setupStencilTest(bool stencilEnable) {
|
|||
return;
|
||||
}
|
||||
|
||||
static constexpr std::array<GLenum, 8> stencilFuncs = {
|
||||
GL_NEVER,
|
||||
GL_ALWAYS,
|
||||
GL_EQUAL,
|
||||
GL_NOTEQUAL,
|
||||
GL_LESS,
|
||||
GL_LEQUAL,
|
||||
GL_GREATER,
|
||||
GL_GEQUAL
|
||||
};
|
||||
static constexpr std::array<GLenum, 8> stencilFuncs = {GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL};
|
||||
gl.enableStencil();
|
||||
|
||||
const u32 stencilConfig = regs[PICA::InternalRegs::StencilTest];
|
||||
const u32 stencilFunc = getBits<4, 3>(stencilConfig);
|
||||
const s32 reference = s8(getBits<16, 8>(stencilConfig)); // Signed reference value
|
||||
const s32 reference = s8(getBits<16, 8>(stencilConfig)); // Signed reference value
|
||||
const u32 stencilRefMask = getBits<24, 8>(stencilConfig);
|
||||
|
||||
const bool stencilWrite = regs[PICA::InternalRegs::DepthBufferWrite];
|
||||
|
@ -293,15 +285,9 @@ void RendererGL::setupStencilTest(bool stencilEnable) {
|
|||
gl.setStencilMask(stencilBufferMask);
|
||||
|
||||
static constexpr std::array<GLenum, 8> stencilOps = {
|
||||
GL_KEEP,
|
||||
GL_ZERO,
|
||||
GL_REPLACE,
|
||||
GL_INCR,
|
||||
GL_DECR,
|
||||
GL_INVERT,
|
||||
GL_INCR_WRAP,
|
||||
GL_DECR_WRAP
|
||||
GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP,
|
||||
};
|
||||
|
||||
const u32 stencilOpConfig = regs[PICA::InternalRegs::StencilOp];
|
||||
const u32 stencilFailOp = getBits<0, 3>(stencilOpConfig);
|
||||
const u32 depthFailOp = getBits<4, 3>(stencilOpConfig);
|
||||
|
@ -468,7 +454,10 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
|
|||
const int depthFunc = getBits<4, 3>(depthControl);
|
||||
const int colourMask = getBits<8, 4>(depthControl);
|
||||
gl.setColourMask(colourMask & 1, colourMask & 2, colourMask & 4, colourMask & 8);
|
||||
static constexpr std::array<GLenum, 8> depthModes = {GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL};
|
||||
|
||||
static constexpr std::array<GLenum, 8> depthModes = {
|
||||
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL,
|
||||
};
|
||||
|
||||
bindTexturesToSlots();
|
||||
if (gpu.fogLUTDirty) {
|
||||
|
@ -565,14 +554,14 @@ void RendererGL::display() {
|
|||
|
||||
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
|
||||
OpenGL::setViewport(0, 240, 400, 240); // Top screen viewport
|
||||
OpenGL::draw(OpenGL::TriangleStrip, 4); // Actually draw our 3DS screen
|
||||
}
|
||||
|
||||
const u32 bottomActiveFb = externalRegs[Framebuffer1Select] & 1;
|
||||
const u32 bottomScreenAddr = externalRegs[bottomActiveFb == 0 ? Framebuffer1AFirstAddr : Framebuffer1ASecondAddr];
|
||||
auto bottomScreen = colourBufferCache.findFromAddress(bottomScreenAddr);
|
||||
|
||||
|
||||
if (bottomScreen) {
|
||||
bottomScreen->get().texture.bind();
|
||||
OpenGL::setViewport(40, 0, 320, 240);
|
||||
|
@ -583,32 +572,62 @@ void RendererGL::display() {
|
|||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
screenFramebuffer.bind(OpenGL::ReadFramebuffer);
|
||||
|
||||
constexpr auto layout = ScreenLayout::Layout::Default;
|
||||
if (outputSizeChanged) {
|
||||
outputSizeChanged = false;
|
||||
|
||||
const float srcAspect = 400.0f / 480.0f; // 3DS aspect ratio
|
||||
const float dstAspect = float(outputWindowWidth) / float(outputWindowHeight);
|
||||
// Get information about our new screen layout to use for blitting the output
|
||||
ScreenLayout::WindowCoordinates windowCoords;
|
||||
ScreenLayout::calculateCoordinates(windowCoords, outputWindowWidth, outputWindowHeight, layout);
|
||||
|
||||
blitInfo.destWidth = outputWindowWidth;
|
||||
blitInfo.destHeight = outputWindowHeight;
|
||||
blitInfo.destX = 0;
|
||||
blitInfo.destY = 0;
|
||||
blitInfo.topScreenX = windowCoords.topScreenX;
|
||||
blitInfo.topScreenY = windowCoords.topScreenY;
|
||||
blitInfo.topScreenWidth = windowCoords.topScreenWidth;
|
||||
blitInfo.topScreenHeight = windowCoords.topScreenHeight;
|
||||
|
||||
if (dstAspect > srcAspect) {
|
||||
// Window is wider than source
|
||||
blitInfo.destWidth = int(outputWindowHeight * srcAspect + 0.5f);
|
||||
blitInfo.destX = (outputWindowWidth - blitInfo.destWidth) / 2;
|
||||
} else {
|
||||
// Window is taller than source
|
||||
blitInfo.destHeight = int(outputWindowWidth / srcAspect + 0.5f);
|
||||
blitInfo.destY = (outputWindowHeight - blitInfo.destHeight) / 2;
|
||||
}
|
||||
blitInfo.bottomScreenX = windowCoords.bottomScreenX;
|
||||
blitInfo.bottomScreenY = windowCoords.bottomScreenY;
|
||||
blitInfo.bottomScreenWidth = windowCoords.bottomScreenWidth;
|
||||
blitInfo.bottomScreenHeight = windowCoords.bottomScreenHeight;
|
||||
|
||||
// Flip topScreenY and bottomScreenY because glBlitFramebuffer uses bottom-left origin
|
||||
blitInfo.topScreenY = outputWindowHeight - (blitInfo.topScreenY + blitInfo.topScreenHeight);
|
||||
blitInfo.topScreenY = outputWindowHeight - (blitInfo.bottomScreenY + blitInfo.bottomScreenHeight);
|
||||
|
||||
// Used for optimizing the screen blit into a single blit
|
||||
blitInfo.destX = windowCoords.singleBlitInfo.destX;
|
||||
blitInfo.destY = windowCoords.singleBlitInfo.destY;
|
||||
blitInfo.destWidth = windowCoords.singleBlitInfo.destWidth;
|
||||
blitInfo.destHeight = windowCoords.singleBlitInfo.destHeight;
|
||||
|
||||
// Check if we can blit the screens in 1 blit. If not, we'll break it into two.
|
||||
blitInfo.canDoSingleBlit =
|
||||
windowCoords.topScreenY + windowCoords.topScreenHeight == windowCoords.bottomScreenY &&
|
||||
windowCoords.bottomScreenX == windowCoords.topScreenX + int(ScreenLayout::BOTTOM_SCREEN_X_OFFSET * windowCoords.scale) &&
|
||||
windowCoords.topScreenWidth == u32(ScreenLayout::TOP_SCREEN_WIDTH * windowCoords.scale) &&
|
||||
windowCoords.bottomScreenWidth == u32(ScreenLayout::BOTTOM_SCREEN_WIDTH * windowCoords.scale) &&
|
||||
windowCoords.topScreenHeight == u32(ScreenLayout::TOP_SCREEN_HEIGHT * windowCoords.scale) &&
|
||||
windowCoords.bottomScreenHeight == u32(ScreenLayout::BOTTOM_SCREEN_HEIGHT * windowCoords.scale);
|
||||
}
|
||||
|
||||
glBlitFramebuffer(
|
||||
0, 0, 400, 480, blitInfo.destX, blitInfo.destY, blitInfo.destX + blitInfo.destWidth, blitInfo.destY + blitInfo.destHeight,
|
||||
GL_COLOR_BUFFER_BIT, GL_LINEAR
|
||||
);
|
||||
if (blitInfo.canDoSingleBlit) {
|
||||
glBlitFramebuffer(
|
||||
0, 0, 400, 480, blitInfo.destX, blitInfo.destY, blitInfo.destX + blitInfo.destWidth, blitInfo.destY + blitInfo.destHeight,
|
||||
GL_COLOR_BUFFER_BIT, GL_LINEAR
|
||||
);
|
||||
} else {
|
||||
// Blit top screen
|
||||
glBlitFramebuffer(
|
||||
0, 240, 400, 480, blitInfo.topScreenX, blitInfo.topScreenY, blitInfo.topScreenX + blitInfo.topScreenWidth,
|
||||
blitInfo.topScreenY + blitInfo.topScreenHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR
|
||||
);
|
||||
|
||||
// Blit bottom screen
|
||||
glBlitFramebuffer(
|
||||
40, 0, 360, 240, blitInfo.bottomScreenX, blitInfo.bottomScreenY, blitInfo.bottomScreenX + blitInfo.bottomScreenWidth,
|
||||
blitInfo.bottomScreenY + blitInfo.bottomScreenHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -735,8 +754,10 @@ void RendererGL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u
|
|||
u32 outputWidth = outputSize & 0xffff;
|
||||
u32 outputHeight = outputSize >> 16;
|
||||
|
||||
OpenGL::DebugScope scope("DisplayTransfer inputAddr 0x%08X outputAddr 0x%08X inputWidth %d outputWidth %d inputHeight %d outputHeight %d",
|
||||
inputAddr, outputAddr, inputWidth, outputWidth, inputHeight, outputHeight);
|
||||
OpenGL::DebugScope scope(
|
||||
"DisplayTransfer inputAddr 0x%08X outputAddr 0x%08X inputWidth %d outputWidth %d inputHeight %d outputHeight %d", inputAddr, outputAddr,
|
||||
inputWidth, outputWidth, inputHeight, outputHeight
|
||||
);
|
||||
|
||||
auto srcFramebuffer = getColourBuffer(inputAddr, inputFormat, inputWidth, outputHeight);
|
||||
Math::Rect<u32> srcRect = srcFramebuffer->getSubRect(inputAddr, outputWidth, outputHeight);
|
||||
|
@ -786,8 +807,10 @@ void RendererGL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
|
|||
const u32 outputWidth = (outputSize & 0xffff) << 4;
|
||||
const u32 outputGap = (outputSize >> 16) << 4;
|
||||
|
||||
OpenGL::DebugScope scope("TextureCopy inputAddr 0x%08X outputAddr 0x%08X totalBytes %d inputWidth %d inputGap %d outputWidth %d outputGap %d",
|
||||
inputAddr, outputAddr, totalBytes, inputWidth, inputGap, outputWidth, outputGap);
|
||||
OpenGL::DebugScope scope(
|
||||
"TextureCopy inputAddr 0x%08X outputAddr 0x%08X totalBytes %d inputWidth %d inputGap %d outputWidth %d outputGap %d", inputAddr, outputAddr,
|
||||
totalBytes, inputWidth, inputGap, outputWidth, outputGap
|
||||
);
|
||||
|
||||
if (inputGap != 0 || outputGap != 0) {
|
||||
// Helpers::warn("Strided texture copy\n");
|
||||
|
@ -825,7 +848,7 @@ void RendererGL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
|
|||
// Find the source surface.
|
||||
auto srcFramebuffer = getColourBuffer(inputAddr, PICA::ColorFmt::RGBA8, copyStride, copyHeight, false);
|
||||
if (!srcFramebuffer) {
|
||||
static int shutUpCounter = 0; // Don't want to spam the console too much, so shut up after 5 times
|
||||
static int shutUpCounter = 0; // Don't want to spam the console too much, so shut up after 5 times
|
||||
|
||||
if (shutUpCounter < 5) {
|
||||
shutUpCounter++;
|
||||
|
@ -1041,8 +1064,8 @@ bool RendererGL::prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration*
|
|||
driverInfo.usingGLES ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL
|
||||
);
|
||||
|
||||
// Empty source means compilation error, if the source is not empty then we convert the recompiled PICA code into a valid shader and upload
|
||||
// it to the GPU
|
||||
// Empty source means compilation error, if the source is not empty then we convert the recompiled PICA code into a valid shader and
|
||||
// upload it to the GPU
|
||||
if (!picaShaderSource.empty()) {
|
||||
std::string vertexShaderSource = fragShaderGen.getVertexShaderAccelerated(picaShaderSource, vertexConfig, usingUbershader);
|
||||
shader->create({vertexShaderSource}, OpenGL::Vertex);
|
||||
|
@ -1073,7 +1096,7 @@ bool RendererGL::prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration*
|
|||
if (!usingUbershader) {
|
||||
OpenGL::Program& program = getSpecializedShader();
|
||||
gl.useProgram(program);
|
||||
} else { // Bind ubershader & load ubershader uniforms
|
||||
} else { // Bind ubershader & load ubershader uniforms
|
||||
gl.useProgram(triangleProgram);
|
||||
|
||||
const float depthScale = f24::fromRaw(regs[PICA::InternalRegs::DepthScale] & 0xffffff).toFloat32();
|
||||
|
@ -1243,7 +1266,7 @@ void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAccele
|
|||
const u32 currentAttributeMask = accel->enabledAttributeMask;
|
||||
// Use bitwise xor to calculate which attributes changed
|
||||
u32 attributeMaskDiff = currentAttributeMask ^ previousAttributeMask;
|
||||
|
||||
|
||||
while (attributeMaskDiff != 0) {
|
||||
// Get index of next different attribute and turn it off
|
||||
const u32 index = 31 - std::countl_zero<u32>(attributeMaskDiff);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue