351 lines
9.4 KiB
C++
351 lines
9.4 KiB
C++
#include "GPU.h"
|
|
#include "OpenGLSupport.h"
|
|
#include "frontend/FrontendUtil.h"
|
|
|
|
#include "BizGLPresenter.h"
|
|
|
|
#include <emulibc.h>
|
|
|
|
// half of this is taken from melonDS/src/frontend/qt_sdl/main.cpp
|
|
|
|
namespace Frontend
|
|
{
|
|
extern void M23_Transform(float* m, float& x, float& y);
|
|
}
|
|
|
|
namespace GLPresenter
|
|
{
|
|
|
|
constexpr u32 NDS_WIDTH = 256;
|
|
constexpr u32 NDS_HEIGHT = 384;
|
|
|
|
static const char* ScreenVS = R"(#version 140
|
|
|
|
uniform vec2 uScreenSize;
|
|
uniform mat2x3 uScreenTransform;
|
|
|
|
in vec2 vPosition;
|
|
in vec2 vTexcoord;
|
|
|
|
smooth out vec2 fTexcoord;
|
|
|
|
void main()
|
|
{
|
|
vec4 fpos;
|
|
|
|
fpos.xy = vec3(vPosition, 1.0) * uScreenTransform;
|
|
|
|
fpos.xy = ((fpos.xy * 2.0) / uScreenSize) - 1.0;
|
|
fpos.y *= -1;
|
|
fpos.z = 0.0;
|
|
fpos.w = 1.0;
|
|
|
|
gl_Position = fpos;
|
|
fTexcoord = vTexcoord;
|
|
}
|
|
)";
|
|
|
|
static const char* ScreenFS = R"(#version 140
|
|
|
|
uniform sampler2D ScreenTex;
|
|
|
|
smooth in vec2 fTexcoord;
|
|
|
|
out vec4 oColor;
|
|
|
|
void main()
|
|
{
|
|
vec4 pixel = texture(ScreenTex, fTexcoord);
|
|
|
|
oColor = vec4(pixel.bgr, 1.0);
|
|
}
|
|
)";
|
|
|
|
ECL_INVISIBLE static GLuint ScreenShaderProgram[3];
|
|
ECL_INVISIBLE static GLuint ScreenShaderTransformULoc, ScreenShaderSizeULoc;
|
|
|
|
ECL_INVISIBLE static GLuint VertexBuffer, VertexArray;
|
|
|
|
ECL_INVISIBLE static float ScreenMatrix[2 * 6];
|
|
ECL_INVISIBLE static int ScreenKinds[2];
|
|
ECL_INVISIBLE static int NumScreens;
|
|
|
|
ECL_INVISIBLE static u32 Width, Height;
|
|
ECL_INVISIBLE static u32 GLScale;
|
|
|
|
ECL_INVISIBLE static GLuint InputTextureID;
|
|
ECL_INVISIBLE static GLuint OutputTextureID;
|
|
ECL_INVISIBLE static GLuint OutputFboID;
|
|
ECL_INVISIBLE static GLuint OutputPboID;
|
|
|
|
void Init(u32 scale)
|
|
{
|
|
OpenGL::BuildShaderProgram(ScreenVS, ScreenFS, ScreenShaderProgram, "GLPresenterShader");
|
|
|
|
GLuint pid = ScreenShaderProgram[2];
|
|
glBindAttribLocation(pid, 0, "vPosition");
|
|
glBindAttribLocation(pid, 1, "vTexcoord");
|
|
glBindFragDataLocation(pid, 0, "oColor");
|
|
|
|
OpenGL::LinkShaderProgram(ScreenShaderProgram);
|
|
|
|
glUseProgram(pid);
|
|
glUniform1i(glGetUniformLocation(pid, "ScreenTex"), 0);
|
|
|
|
ScreenShaderSizeULoc = glGetUniformLocation(pid, "uScreenSize");
|
|
ScreenShaderTransformULoc = glGetUniformLocation(pid, "uScreenTransform");
|
|
|
|
constexpr int paddedHeight = NDS_HEIGHT + 2;
|
|
constexpr float padPixels = 1.f / paddedHeight;
|
|
|
|
static const float vertices[] =
|
|
{
|
|
0.f, 0.f, 0.f, 0.f,
|
|
0.f, 192.f, 0.f, 0.5f - padPixels,
|
|
256.f, 192.f, 1.f, 0.5f - padPixels,
|
|
0.f, 0.f, 0.f, 0.f,
|
|
256.f, 192.f, 1.f, 0.5f - padPixels,
|
|
256.f, 0.f, 1.f, 0.f,
|
|
|
|
0.f, 0.f, 0.f, 0.5f + padPixels,
|
|
0.f, 192.f, 0.f, 1.f,
|
|
256.f, 192.f, 1.f, 1.f,
|
|
0.f, 0.f, 0.f, 0.5f + padPixels,
|
|
256.f, 192.f, 1.f, 1.f,
|
|
256.f, 0.f, 1.f, 0.5f + padPixels
|
|
};
|
|
|
|
glGenBuffers(1, &VertexBuffer);
|
|
glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
|
|
|
glGenVertexArrays(1, &VertexArray);
|
|
glBindVertexArray(VertexArray);
|
|
glEnableVertexAttribArray(0); // position
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * 4, (void*)(0));
|
|
glEnableVertexAttribArray(1); // texcoord
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * 4, (void*)(2 * 4));
|
|
|
|
glGenTextures(1, &InputTextureID);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, InputTextureID);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, NDS_WIDTH, paddedHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
|
|
static u8 zeroData[NDS_WIDTH * 4 * 4]{};
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, NDS_HEIGHT / 2, NDS_WIDTH, 2, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, zeroData);
|
|
|
|
glGenBuffers(1, &OutputPboID);
|
|
|
|
GLScale = scale;
|
|
}
|
|
|
|
std::pair<u32, u32> Present()
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, OutputFboID);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthMask(false);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_SCISSOR_TEST);
|
|
glDisable(GL_STENCIL_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glViewport(0, 0, Width, Height);
|
|
|
|
glUseProgram(ScreenShaderProgram[2]);
|
|
glUniform2f(ScreenShaderSizeULoc, Width, Height);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
if (GPU3D::CurrentRenderer->Accelerated)
|
|
{
|
|
GPU::CurGLCompositor->BindOutputTexture(GPU::FrontBuffer);
|
|
}
|
|
else
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, InputTextureID);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, NDS_WIDTH, NDS_HEIGHT / 2, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, GPU::Framebuffer[GPU::FrontBuffer][0]);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, NDS_HEIGHT / 2 + 2, NDS_WIDTH, NDS_HEIGHT / 2, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, GPU::Framebuffer[GPU::FrontBuffer][1]);
|
|
}
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer);
|
|
glBindVertexArray(VertexArray);
|
|
|
|
for (int i = 0; i < NumScreens; i++)
|
|
{
|
|
glUniformMatrix2x3fv(ScreenShaderTransformULoc, 1, GL_TRUE, &ScreenMatrix[i * 6]);
|
|
glDrawArrays(GL_TRIANGLES, ScreenKinds[i] == 0 ? 0 : 6, 6);
|
|
}
|
|
|
|
glFlush();
|
|
|
|
GLint oldPbo;
|
|
glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &oldPbo);
|
|
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, OutputPboID);
|
|
glBufferData(GL_PIXEL_PACK_BUFFER, Width * Height * sizeof(u32), nullptr, GL_STREAM_READ);
|
|
glReadPixels(0, 0, Width, Height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (void*)(0));
|
|
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, oldPbo);
|
|
|
|
return std::make_pair(Width, Height);
|
|
}
|
|
|
|
ECL_EXPORT u32 GetGLTexture()
|
|
{
|
|
return OutputTextureID;
|
|
}
|
|
|
|
ECL_EXPORT void ReadFrameBuffer(u32* buffer)
|
|
{
|
|
GLint oldPbo;
|
|
glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &oldPbo);
|
|
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, OutputPboID);
|
|
const auto p = static_cast<const u32*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY));
|
|
if (p)
|
|
{
|
|
// FBOs render upside down, so flip vertically to counteract that
|
|
buffer += Width * (Height - 1);
|
|
const int w = Width;
|
|
const int h = Height;
|
|
for (int i = 0; i < h; i++)
|
|
{
|
|
std::memcpy(&buffer[-i * w], &p[i * w], Width * sizeof(u32));
|
|
}
|
|
|
|
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
|
}
|
|
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, oldPbo);
|
|
}
|
|
|
|
struct ScreenSettings
|
|
{
|
|
Frontend::ScreenLayout ScreenLayout;
|
|
Frontend::ScreenRotation ScreenRotation;
|
|
Frontend::ScreenSizing ScreenSizing;
|
|
int ScreenGap;
|
|
bool ScreenSwap;
|
|
};
|
|
|
|
static std::pair<u32, u32> GetScreenSize(const ScreenSettings* screenSettings, u32 scale)
|
|
{
|
|
bool isHori = screenSettings->ScreenRotation == Frontend::screenRot_90Deg
|
|
|| screenSettings->ScreenRotation == Frontend::screenRot_270Deg;
|
|
int gap = screenSettings->ScreenGap * scale;
|
|
|
|
int w = NDS_WIDTH * scale;
|
|
int h = (NDS_HEIGHT / 2) * scale;
|
|
|
|
if (screenSettings->ScreenSizing == Frontend::screenSizing_TopOnly
|
|
|| screenSettings->ScreenSizing == Frontend::screenSizing_BotOnly)
|
|
{
|
|
return isHori
|
|
? std::make_pair(h, w)
|
|
: std::make_pair(w, h);
|
|
}
|
|
|
|
switch (screenSettings->ScreenLayout)
|
|
{
|
|
case Frontend::screenLayout_Natural:
|
|
return isHori
|
|
? std::make_pair(h * 2 + gap, w)
|
|
: std::make_pair(w, h * 2 + gap);
|
|
case Frontend::screenLayout_Vertical:
|
|
return isHori
|
|
? std::make_pair(h, w * 2 + gap)
|
|
: std::make_pair(w, h * 2 + gap);
|
|
case Frontend::screenLayout_Horizontal:
|
|
return isHori
|
|
? std::make_pair(h * 2 + gap, w)
|
|
: std::make_pair(w * 2 + gap, h);
|
|
default:
|
|
__builtin_unreachable();
|
|
}
|
|
}
|
|
|
|
ECL_EXPORT void SetScreenSettings(const ScreenSettings* screenSettings, u32* width, u32* height, u32* vwidth, u32* vheight)
|
|
{
|
|
auto [w, h] = GetScreenSize(screenSettings, GLScale);
|
|
if (w != Width || h != Height)
|
|
{
|
|
Width = w;
|
|
Height = h;
|
|
|
|
glDeleteTextures(1, &OutputTextureID);
|
|
glGenTextures(1, &OutputTextureID);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, OutputTextureID);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
|
|
|
|
glDeleteFramebuffers(1, &OutputFboID);
|
|
glGenFramebuffers(1, &OutputFboID);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, OutputFboID);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, OutputTextureID, 0);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
}
|
|
|
|
Frontend::SetupScreenLayout(w, h,
|
|
screenSettings->ScreenLayout,
|
|
screenSettings->ScreenRotation,
|
|
screenSettings->ScreenSizing,
|
|
screenSettings->ScreenGap,
|
|
true, // Integer Scaling
|
|
screenSettings->ScreenSwap,
|
|
1, 1); // Aspect Ratio
|
|
|
|
NumScreens = Frontend::GetScreenTransforms(ScreenMatrix, ScreenKinds);
|
|
|
|
Present();
|
|
|
|
*width = w;
|
|
*height = h;
|
|
|
|
if (GLScale > 1)
|
|
{
|
|
auto [vw, vh] = GetScreenSize(screenSettings, 1);
|
|
*vwidth = vw;
|
|
*vheight = vh;
|
|
}
|
|
else
|
|
{
|
|
*vwidth = w;
|
|
*vheight = h;
|
|
}
|
|
}
|
|
|
|
ECL_EXPORT void GetTouchCoords(int* x, int* y)
|
|
{
|
|
if (!Frontend::GetTouchCoords(*x, *y, true))
|
|
{
|
|
*x = 0;
|
|
*y = 0;
|
|
}
|
|
}
|
|
|
|
ECL_EXPORT void GetScreenCoords(float* x, float* y)
|
|
{
|
|
for (int i = 0; i < NumScreens; i++)
|
|
{
|
|
// bottom screen
|
|
if (ScreenKinds[i] == 1)
|
|
{
|
|
Frontend::M23_Transform(&ScreenMatrix[i * 6], *x, *y);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// bottom screen not visible
|
|
*x = 0;
|
|
*y = 0;
|
|
}
|
|
|
|
}
|