mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-12 18:28:30 +12:00
Better screen layout support
This commit is contained in:
parent
1c0f65c740
commit
cf321b1ed8
17 changed files with 328 additions and 186 deletions
|
@ -11,7 +11,7 @@
|
|||
#include "PICA/pica_hash.hpp"
|
||||
#include "PICA/pica_simd.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "PICA/screen_layout.hpp"
|
||||
#include "screen_layout.hpp"
|
||||
#include "PICA/shader_decompiler.hpp"
|
||||
#include "config.hpp"
|
||||
#include "math_util.hpp"
|
||||
|
@ -570,15 +570,18 @@ void RendererGL::display() {
|
|||
|
||||
if constexpr (!Helpers::isHydraCore()) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
OpenGL::clearColor();
|
||||
|
||||
screenFramebuffer.bind(OpenGL::ReadFramebuffer);
|
||||
|
||||
constexpr auto layout = ScreenLayout::Layout::Default;
|
||||
if (outputSizeChanged) {
|
||||
outputSizeChanged = false;
|
||||
|
||||
auto layout = emulatorConfig->screenLayout;
|
||||
|
||||
// Get information about our new screen layout to use for blitting the output
|
||||
ScreenLayout::WindowCoordinates windowCoords;
|
||||
ScreenLayout::calculateCoordinates(windowCoords, outputWindowWidth, outputWindowHeight, layout);
|
||||
ScreenLayout::calculateCoordinates(windowCoords, outputWindowWidth, outputWindowHeight, emulatorConfig->topScreenSize, layout);
|
||||
|
||||
blitInfo.topScreenX = windowCoords.topScreenX;
|
||||
blitInfo.topScreenY = windowCoords.topScreenY;
|
||||
|
@ -590,20 +593,16 @@ void RendererGL::display() {
|
|||
blitInfo.bottomScreenWidth = windowCoords.bottomScreenWidth;
|
||||
blitInfo.bottomScreenHeight = windowCoords.bottomScreenHeight;
|
||||
|
||||
// Flip topScreenY and bottomScreenY because glBlitFramebuffer uses bottom-left origin
|
||||
// Flip topScreenY and bottomScreenY because glBlitFramebuffer uses bottom-left origin
|
||||
blitInfo.topScreenY = outputWindowHeight - (blitInfo.topScreenY + blitInfo.topScreenHeight);
|
||||
blitInfo.bottomScreenY = outputWindowHeight - (blitInfo.bottomScreenY + blitInfo.bottomScreenHeight);
|
||||
|
||||
// Used for optimizing the screen blit into a single blit
|
||||
blitInfo.canDoSingleBlit = windowCoords.singleBlitInfo.canDoSingleBlit;
|
||||
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.
|
||||
// TODO: Maybe add some size-related checks too.
|
||||
blitInfo.canDoSingleBlit =
|
||||
windowCoords.topScreenY + windowCoords.topScreenHeight == windowCoords.bottomScreenY && layout == ScreenLayout::Layout::Default;
|
||||
}
|
||||
|
||||
if (blitInfo.canDoSingleBlit) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include "PICA/gpu.hpp"
|
||||
#include "PICA/pica_hash.hpp"
|
||||
#include "PICA/screen_layout.hpp"
|
||||
#include "screen_layout.hpp"
|
||||
#include "SDL_metal.h"
|
||||
|
||||
using namespace PICA;
|
||||
|
@ -107,7 +107,9 @@ void RendererMTL::display() {
|
|||
if (outputSizeChanged) {
|
||||
outputSizeChanged = false;
|
||||
ScreenLayout::WindowCoordinates windowCoords;
|
||||
ScreenLayout::calculateCoordinates(windowCoords, outputWindowWidth, outputWindowHeight, ScreenLayout::Layout::Default);
|
||||
ScreenLayout::calculateCoordinates(
|
||||
windowCoords, outputWindowWidth, outputWindowHeight, emulatorConfig->topScreenSize, emulatorConfig->screenLayout
|
||||
);
|
||||
|
||||
blitInfo.topScreenX = float(windowCoords.topScreenX);
|
||||
blitInfo.topScreenY = float(windowCoords.topScreenY);
|
||||
|
|
155
src/core/screen_layout.cpp
Normal file
155
src/core/screen_layout.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
#include "screen_layout.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace ScreenLayout;
|
||||
|
||||
// Calculate screen coordinates on the screen for a given layout & a given size for the output window
|
||||
// Used in both the renderers and in the frontends (To eg calculate touch screen boundaries)
|
||||
void ScreenLayout::calculateCoordinates(
|
||||
WindowCoordinates& coordinates, u32 outputWindowWidth, u32 outputWindowHeight, float topScreenPercentage, Layout layout
|
||||
) {
|
||||
const float destAspect = float(outputWindowWidth) / float(outputWindowHeight);
|
||||
|
||||
if (layout == Layout::Default || layout == Layout::DefaultFlipped) {
|
||||
// Calculate available height for each screen based on split
|
||||
int availableTopHeight = int(outputWindowHeight * topScreenPercentage + 0.5f);
|
||||
int availableBottomHeight = outputWindowHeight - availableTopHeight;
|
||||
|
||||
// Calculate scales for top and bottom screens, and then the actual sizes
|
||||
float scaleTopX = float(outputWindowWidth) / float(TOP_SCREEN_WIDTH);
|
||||
float scaleTopY = float(availableTopHeight) / float(TOP_SCREEN_HEIGHT);
|
||||
float scaleTop = std::min(scaleTopX, scaleTopY);
|
||||
|
||||
float scaleBottomX = float(outputWindowWidth) / float(BOTTOM_SCREEN_WIDTH);
|
||||
float scaleBottomY = float(availableBottomHeight) / float(BOTTOM_SCREEN_HEIGHT);
|
||||
float scaleBottom = std::min(scaleBottomX, scaleBottomY);
|
||||
|
||||
int topScreenWidth = int(TOP_SCREEN_WIDTH * scaleTop + 0.5f);
|
||||
int topScreenHeight = int(TOP_SCREEN_HEIGHT * scaleTop + 0.5f);
|
||||
int bottomScreenWidth = int(BOTTOM_SCREEN_WIDTH * scaleBottom + 0.5f);
|
||||
int bottomScreenHeight = int(BOTTOM_SCREEN_HEIGHT * scaleBottom + 0.5f);
|
||||
|
||||
// Center screens horizontally
|
||||
int topScreenX = (outputWindowWidth - topScreenWidth) / 2;
|
||||
int bottomScreenX = (outputWindowWidth - bottomScreenWidth) / 2;
|
||||
|
||||
coordinates.topScreenWidth = topScreenWidth;
|
||||
coordinates.topScreenHeight = topScreenHeight;
|
||||
coordinates.bottomScreenWidth = bottomScreenWidth;
|
||||
coordinates.bottomScreenHeight = bottomScreenHeight;
|
||||
|
||||
coordinates.topScreenX = topScreenX;
|
||||
coordinates.bottomScreenX = bottomScreenX;
|
||||
|
||||
if (layout == Layout::Default) {
|
||||
coordinates.topScreenY = 0;
|
||||
coordinates.bottomScreenY = topScreenHeight;
|
||||
} else {
|
||||
coordinates.bottomScreenY = 0;
|
||||
coordinates.topScreenY = bottomScreenHeight;
|
||||
}
|
||||
|
||||
coordinates.windowWidth = outputWindowWidth;
|
||||
coordinates.windowHeight = outputWindowHeight;
|
||||
|
||||
// Default layout can be rendered using a single blit, flipped layout can't
|
||||
if (layout == Layout::Default) {
|
||||
coordinates.singleBlitInfo.destX = coordinates.topScreenX;
|
||||
coordinates.singleBlitInfo.destY = coordinates.topScreenY;
|
||||
coordinates.singleBlitInfo.destWidth = coordinates.topScreenWidth;
|
||||
coordinates.singleBlitInfo.destHeight = coordinates.topScreenHeight + coordinates.bottomScreenHeight;
|
||||
} else {
|
||||
// Dummy data for the single blit info, as we can't render the screen in 1 blit when the screens are side-by-sid
|
||||
coordinates.singleBlitInfo.destX = 0;
|
||||
coordinates.singleBlitInfo.destY = 0;
|
||||
coordinates.singleBlitInfo.destWidth = 0;
|
||||
coordinates.singleBlitInfo.destHeight = 0;
|
||||
}
|
||||
|
||||
// Check if we can blit the screens in 1 blit. If not, we'll break it into two.
|
||||
// TODO: Maybe add some more size-related checks too.
|
||||
coordinates.singleBlitInfo.canDoSingleBlit = layout == Layout::Default && topScreenPercentage == 0.5 &&
|
||||
coordinates.topScreenY + coordinates.topScreenHeight == coordinates.bottomScreenY;
|
||||
} else if (layout == Layout::SideBySide || layout == Layout::SideBySideFlipped) {
|
||||
// Calculate available width for each screen based on split
|
||||
int availableTopWidth = int(outputWindowWidth * topScreenPercentage + 0.5f);
|
||||
int availableBottomWidth = outputWindowWidth - availableTopWidth;
|
||||
|
||||
// Calculate scales for top and bottom screens, and then the actual sizes
|
||||
float scaleTop = std::min(float(availableTopWidth) / float(TOP_SCREEN_WIDTH), float(outputWindowHeight) / float(TOP_SCREEN_HEIGHT));
|
||||
float scaleBottom =
|
||||
std::min(float(availableBottomWidth) / float(BOTTOM_SCREEN_WIDTH), float(outputWindowHeight) / float(BOTTOM_SCREEN_HEIGHT));
|
||||
|
||||
int topScreenWidth = int(TOP_SCREEN_WIDTH * scaleTop + 0.5f);
|
||||
int topScreenHeight = int(TOP_SCREEN_HEIGHT * scaleTop + 0.5f);
|
||||
int bottomScreenWidth = int(BOTTOM_SCREEN_WIDTH * scaleBottom + 0.5f);
|
||||
int bottomScreenHeight = int(BOTTOM_SCREEN_HEIGHT * scaleBottom + 0.5f);
|
||||
|
||||
// Vertically center the tallest screen
|
||||
int maxHeight = std::max(topScreenHeight, bottomScreenHeight);
|
||||
int baseY = (outputWindowHeight - maxHeight) / 2;
|
||||
|
||||
int topScreenY = baseY + (maxHeight - topScreenHeight) / 2;
|
||||
int bottomScreenY = baseY + (maxHeight - bottomScreenHeight) / 2;
|
||||
|
||||
if (layout == Layout::SideBySide) {
|
||||
coordinates.topScreenX = (outputWindowWidth - (topScreenWidth + bottomScreenWidth)) / 2;
|
||||
coordinates.bottomScreenX = coordinates.topScreenX + topScreenWidth;
|
||||
} else {
|
||||
coordinates.bottomScreenX = (outputWindowWidth - (topScreenWidth + bottomScreenWidth)) / 2;
|
||||
coordinates.topScreenX = coordinates.bottomScreenX + bottomScreenWidth;
|
||||
}
|
||||
|
||||
coordinates.topScreenY = topScreenY;
|
||||
coordinates.topScreenWidth = topScreenWidth;
|
||||
coordinates.topScreenHeight = topScreenHeight;
|
||||
|
||||
coordinates.bottomScreenY = bottomScreenY;
|
||||
coordinates.bottomScreenWidth = bottomScreenWidth;
|
||||
coordinates.bottomScreenHeight = bottomScreenHeight;
|
||||
|
||||
coordinates.windowWidth = outputWindowWidth;
|
||||
coordinates.windowHeight = outputWindowHeight;
|
||||
|
||||
// Dummy data for the single blit info, as we can't render the screen in 1 blit when the screens are side-by-side
|
||||
coordinates.singleBlitInfo.canDoSingleBlit = false;
|
||||
coordinates.singleBlitInfo.destX = 0;
|
||||
coordinates.singleBlitInfo.destY = 0;
|
||||
coordinates.singleBlitInfo.destWidth = 0;
|
||||
coordinates.singleBlitInfo.destHeight = 0;
|
||||
} else {
|
||||
Helpers::panic("Unimplemented screen layout");
|
||||
}
|
||||
}
|
||||
|
||||
Layout ScreenLayout::layoutFromString(std::string inString) {
|
||||
// Transform to lower-case to make the setting case-insensitive
|
||||
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
|
||||
static const std::unordered_map<std::string, Layout> map = {
|
||||
{"default", Layout::Default},
|
||||
{"defaultflipped", Layout::DefaultFlipped},
|
||||
{"sidebyside", Layout::SideBySide},
|
||||
{"sidebysideflipped", Layout::SideBySideFlipped},
|
||||
};
|
||||
|
||||
if (auto search = map.find(inString); search != map.end()) {
|
||||
return search->second;
|
||||
}
|
||||
|
||||
printf("Invalid screen layout. Defaulting to Default\n");
|
||||
return Layout::Default;
|
||||
}
|
||||
|
||||
const char* ScreenLayout::layoutToString(Layout layout) {
|
||||
switch (layout) {
|
||||
case Layout::Default: return "default";
|
||||
case Layout::DefaultFlipped: return "defaultFlipped";
|
||||
case Layout::SideBySide: return "sideBySide";
|
||||
case Layout::SideBySideFlipped: return "sideBySideFlipped";
|
||||
default: return "invalid";
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue