mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2024-11-14 21:37:42 -07:00
289 lines
9.4 KiB
C++
289 lines
9.4 KiB
C++
/*
|
|
Copyright 2016-2024 melonDS team
|
|
|
|
This file is part of melonDS.
|
|
|
|
melonDS is free software: you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation, either version 3 of the License, or (at your option)
|
|
any later version.
|
|
|
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
|
*/
|
|
|
|
#include "GPU_OpenGL.h"
|
|
|
|
#include <assert.h>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#include "NDS.h"
|
|
#include "GPU.h"
|
|
#include "GPU3D_OpenGL.h"
|
|
#include "OpenGLSupport.h"
|
|
#include "GPU_OpenGL_shaders.h"
|
|
|
|
namespace melonDS
|
|
{
|
|
|
|
using namespace OpenGL;
|
|
|
|
std::optional<GLCompositor> GLCompositor::New() noexcept
|
|
{
|
|
assert(glBindAttribLocation != nullptr);
|
|
GLuint CompShader {};
|
|
|
|
if (!OpenGL::CompileVertexFragmentProgram(CompShader,
|
|
kCompositorVS, kCompositorFS_Nearest,
|
|
"CompositorShader",
|
|
{{"vPosition", 0}, {"vTexcoord", 1}},
|
|
{{"oColor", 0}}))
|
|
return std::nullopt;
|
|
|
|
return { GLCompositor(CompShader) };
|
|
}
|
|
|
|
GLCompositor::GLCompositor(GLuint compShader) noexcept : CompShader(compShader)
|
|
{
|
|
CompScaleLoc = glGetUniformLocation(CompShader, "u3DScale");
|
|
Comp3DXPosLoc = glGetUniformLocation(CompShader, "u3DXPos");
|
|
|
|
glUseProgram(CompShader);
|
|
GLuint screenTextureUniform = glGetUniformLocation(CompShader, "ScreenTex");
|
|
glUniform1i(screenTextureUniform, 0);
|
|
GLuint _3dTextureUniform = glGetUniformLocation(CompShader, "_3DTex");
|
|
glUniform1i(_3dTextureUniform, 1);
|
|
|
|
// all this mess is to prevent bleeding
|
|
#define SETVERTEX(i, x, y, offset) \
|
|
CompVertices[i].Position[0] = x; \
|
|
CompVertices[i].Position[1] = y + offset; \
|
|
CompVertices[i].Texcoord[0] = (x + 1.f) * (256.f / 2.f); \
|
|
CompVertices[i].Texcoord[1] = (y + 1.f) * (384.f / 2.f)
|
|
|
|
const float padOffset = 1.f/(192*2.f+2.f)*2.f;
|
|
// top screen
|
|
SETVERTEX(0, -1, 1, 0);
|
|
SETVERTEX(1, 1, 0, padOffset);
|
|
SETVERTEX(2, 1, 1, 0);
|
|
SETVERTEX(3, -1, 1, 0);
|
|
SETVERTEX(4, -1, 0, padOffset);
|
|
SETVERTEX(5, 1, 0, padOffset);
|
|
|
|
// bottom screen
|
|
SETVERTEX(6, -1, 0, -padOffset);
|
|
SETVERTEX(7, 1, -1, 0);
|
|
SETVERTEX(8, 1, 0, -padOffset);
|
|
SETVERTEX(9, -1, 0, -padOffset);
|
|
SETVERTEX(10, -1, -1, 0);
|
|
SETVERTEX(11, 1, -1, 0);
|
|
|
|
#undef SETVERTEX
|
|
|
|
glGenBuffers(1, &CompVertexBufferID);
|
|
glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(CompVertices), &CompVertices[0], GL_STATIC_DRAW);
|
|
|
|
glGenVertexArrays(1, &CompVertexArrayID);
|
|
glBindVertexArray(CompVertexArrayID);
|
|
glEnableVertexAttribArray(0); // position
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(CompVertex), (void*)(offsetof(CompVertex, Position)));
|
|
glEnableVertexAttribArray(1); // texcoord
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CompVertex), (void*)(offsetof(CompVertex, Texcoord)));
|
|
|
|
glGenFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]);
|
|
|
|
glGenTextures(1, &CompScreenInputTex);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, CompScreenInputTex);
|
|
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_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glGenTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]);
|
|
for (GLuint i : CompScreenOutputTex)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, i);
|
|
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);
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
GLCompositor::~GLCompositor()
|
|
{
|
|
assert(glDeleteFramebuffers != nullptr);
|
|
|
|
glDeleteFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]);
|
|
glDeleteTextures(1, &CompScreenInputTex);
|
|
glDeleteTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]);
|
|
|
|
glDeleteVertexArrays(1, &CompVertexArrayID);
|
|
glDeleteBuffers(1, &CompVertexBufferID);
|
|
|
|
glDeleteProgram(CompShader);
|
|
}
|
|
|
|
|
|
GLCompositor::GLCompositor(GLCompositor&& other) noexcept :
|
|
Scale(other.Scale),
|
|
ScreenH(other.ScreenH),
|
|
ScreenW(other.ScreenW),
|
|
CompScaleLoc(other.CompScaleLoc),
|
|
Comp3DXPosLoc(other.Comp3DXPosLoc),
|
|
CompVertices(other.CompVertices),
|
|
CompShader(other.CompShader),
|
|
CompVertexBufferID(other.CompVertexBufferID),
|
|
CompVertexArrayID(other.CompVertexArrayID),
|
|
CompScreenInputTex(other.CompScreenInputTex),
|
|
CompScreenOutputTex(other.CompScreenOutputTex),
|
|
CompScreenOutputFB(other.CompScreenOutputFB)
|
|
{
|
|
other.CompScreenOutputFB = {};
|
|
other.CompScreenInputTex = {};
|
|
other.CompScreenOutputTex = {};
|
|
other.CompVertexArrayID = {};
|
|
other.CompVertexBufferID = {};
|
|
other.CompShader = {};
|
|
}
|
|
|
|
GLCompositor& GLCompositor::operator=(GLCompositor&& other) noexcept
|
|
{
|
|
if (this != &other)
|
|
{
|
|
Scale = other.Scale;
|
|
ScreenH = other.ScreenH;
|
|
ScreenW = other.ScreenW;
|
|
CompScaleLoc = other.CompScaleLoc;
|
|
Comp3DXPosLoc = other.Comp3DXPosLoc;
|
|
CompVertices = other.CompVertices;
|
|
|
|
// Clean up these resources before overwriting them
|
|
glDeleteProgram(CompShader);
|
|
CompShader = other.CompShader;
|
|
|
|
glDeleteBuffers(1, &CompVertexBufferID);
|
|
CompVertexBufferID = other.CompVertexBufferID;
|
|
|
|
glDeleteVertexArrays(1, &CompVertexArrayID);
|
|
CompVertexArrayID = other.CompVertexArrayID;
|
|
|
|
glDeleteTextures(1, &CompScreenInputTex);
|
|
CompScreenInputTex = other.CompScreenInputTex;
|
|
|
|
glDeleteTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]);
|
|
CompScreenOutputTex = other.CompScreenOutputTex;
|
|
|
|
glDeleteFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]);
|
|
CompScreenOutputFB = other.CompScreenOutputFB;
|
|
|
|
other.CompScreenOutputFB = {};
|
|
other.CompScreenInputTex = {};
|
|
other.CompScreenOutputTex = {};
|
|
other.CompVertexArrayID = {};
|
|
other.CompVertexBufferID = {};
|
|
other.CompShader = {};
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
void GLCompositor::SetScaleFactor(int scale) noexcept
|
|
{
|
|
if (scale == Scale)
|
|
return;
|
|
|
|
Scale = scale;
|
|
ScreenW = 256 * scale;
|
|
ScreenH = (384+2) * scale;
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[i]);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ScreenW, ScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
// fill the padding
|
|
u8* zeroPixels = (u8*) calloc(1, ScreenW*2*scale*4);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192*scale, ScreenW, 2*scale, GL_RGBA, GL_UNSIGNED_BYTE, zeroPixels);
|
|
|
|
GLenum fbassign[] = {GL_COLOR_ATTACHMENT0};
|
|
glBindFramebuffer(GL_FRAMEBUFFER, CompScreenOutputFB[i]);
|
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, CompScreenOutputTex[i], 0);
|
|
glDrawBuffers(1, fbassign);
|
|
free(zeroPixels);
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
void GLCompositor::Stop(const GPU& gpu) noexcept
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[gpu.FrontBuffer]);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
void GLCompositor::RenderFrame(const GPU& gpu, Renderer3D& renderer) noexcept
|
|
{
|
|
int backbuf = gpu.FrontBuffer ^ 1;
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[backbuf]);
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_STENCIL_TEST);
|
|
glDisable(GL_BLEND);
|
|
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
glViewport(0, 0, ScreenW, ScreenH);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
// TODO: select more shaders (filtering, etc)
|
|
glUseProgram(CompShader);
|
|
glUniform1ui(CompScaleLoc, Scale);
|
|
|
|
// TODO: support setting this midframe, if ever needed
|
|
glUniform1i(Comp3DXPosLoc, ((int)gpu.GPU3D.GetRenderXPos() << 23) >> 23);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, CompScreenInputTex);
|
|
|
|
if (gpu.Framebuffer[backbuf][0] && gpu.Framebuffer[backbuf][1])
|
|
{
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER,
|
|
GL_UNSIGNED_BYTE, gpu.Framebuffer[backbuf][0].get());
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER,
|
|
GL_UNSIGNED_BYTE, gpu.Framebuffer[backbuf][1].get());
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE1);
|
|
renderer.SetupAccelFrame();
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID);
|
|
glBindVertexArray(CompVertexArrayID);
|
|
glDrawArrays(GL_TRIANGLES, 0, 4*3);
|
|
}
|
|
|
|
void GLCompositor::BindOutputTexture(int buf)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[buf]);
|
|
}
|
|
|
|
}
|