mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-01 19:39:44 -06:00
Make the NDS teardown more robust (#1798)
* Make cleanup a little more robust to mitigate undefined behavior - Add some null checks before cleaning up the GPU3D renderer - Make sure that all deleted objects are null - Move cleanup logic out of an assert call - Note that deleting a null pointer is a no-op, so there's no need to check for null beforehand - Use RAII for GLCompositor instead of Init/DeInit methods * Replace a DeInit call that I missed * Make ARMJIT_Memory less likely to generate errors - Set FastMem7/9Start to nullptr at the end - Only close and unmap the file if it's initialized * Make Renderer3D manage its resources with RAII * Don't try to deallocate frontend resources that aren't loaded * Make ARMJIT_Memory::DeInit more robust on the Switch * Reset MemoryFile on Windows to INVALID_HANDLE_VALUE, not nullptr - There is a difference * Don't explicitly store a Valid state in GLCompositor or the 3D renderers - Instead, create them with static methods while making the actual constructors private * Make initialization of OpenGL resources fail if OpenGL isn't loaded * assert that OpenGL is loaded instead of returning failure
This commit is contained in:

committed by
GitHub

parent
1aaf22d181
commit
db963aa002
@ -18,6 +18,7 @@
|
||||
|
||||
#include "GPU3D_OpenGL.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "NDS.h"
|
||||
@ -96,14 +97,20 @@ void SetupDefaultTexParams(GLuint tex)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
GLRenderer::GLRenderer()
|
||||
: Renderer3D(true)
|
||||
GLRenderer::GLRenderer() noexcept : Renderer3D(true)
|
||||
{
|
||||
// GLRenderer::New() will be used to actually initialize the renderer;
|
||||
// The various glDelete* functions silently ignore invalid IDs,
|
||||
// so we can just let the destructor clean up a half-initialized renderer.
|
||||
}
|
||||
|
||||
bool GLRenderer::Init()
|
||||
std::unique_ptr<GLRenderer> GLRenderer::New() noexcept
|
||||
{
|
||||
GLint uni_id;
|
||||
assert(glEnable != nullptr);
|
||||
|
||||
// Will be returned if the initialization succeeds,
|
||||
// or cleaned up via RAII if it fails.
|
||||
std::unique_ptr<GLRenderer> result = std::unique_ptr<GLRenderer>(new GLRenderer());
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
@ -112,86 +119,93 @@ bool GLRenderer::Init()
|
||||
glClearDepth(1.0);
|
||||
|
||||
|
||||
if (!OpenGL::BuildShaderProgram(kClearVS, kClearFS, ClearShaderPlain, "ClearShader"))
|
||||
return false;
|
||||
if (!OpenGL::BuildShaderProgram(kClearVS, kClearFS, result->ClearShaderPlain, "ClearShader"))
|
||||
return nullptr;
|
||||
|
||||
glBindAttribLocation(ClearShaderPlain[2], 0, "vPosition");
|
||||
glBindFragDataLocation(ClearShaderPlain[2], 0, "oColor");
|
||||
glBindFragDataLocation(ClearShaderPlain[2], 1, "oAttr");
|
||||
glBindAttribLocation(result->ClearShaderPlain[2], 0, "vPosition");
|
||||
glBindFragDataLocation(result->ClearShaderPlain[2], 0, "oColor");
|
||||
glBindFragDataLocation(result->ClearShaderPlain[2], 1, "oAttr");
|
||||
|
||||
if (!OpenGL::LinkShaderProgram(ClearShaderPlain))
|
||||
return false;
|
||||
if (!OpenGL::LinkShaderProgram(result->ClearShaderPlain))
|
||||
return nullptr;
|
||||
|
||||
ClearUniformLoc[0] = glGetUniformLocation(ClearShaderPlain[2], "uColor");
|
||||
ClearUniformLoc[1] = glGetUniformLocation(ClearShaderPlain[2], "uDepth");
|
||||
ClearUniformLoc[2] = glGetUniformLocation(ClearShaderPlain[2], "uOpaquePolyID");
|
||||
ClearUniformLoc[3] = glGetUniformLocation(ClearShaderPlain[2], "uFogFlag");
|
||||
result->ClearUniformLoc[0] = glGetUniformLocation(result->ClearShaderPlain[2], "uColor");
|
||||
result->ClearUniformLoc[1] = glGetUniformLocation(result->ClearShaderPlain[2], "uDepth");
|
||||
result->ClearUniformLoc[2] = glGetUniformLocation(result->ClearShaderPlain[2], "uOpaquePolyID");
|
||||
result->ClearUniformLoc[3] = glGetUniformLocation(result->ClearShaderPlain[2], "uFogFlag");
|
||||
|
||||
memset(RenderShader, 0, sizeof(RenderShader));
|
||||
memset(result->RenderShader, 0, sizeof(RenderShader));
|
||||
|
||||
if (!BuildRenderShader(0,
|
||||
kRenderVS_Z, kRenderFS_ZO)) return false;
|
||||
if (!BuildRenderShader(RenderFlag_WBuffer,
|
||||
kRenderVS_W, kRenderFS_WO)) return false;
|
||||
if (!BuildRenderShader(RenderFlag_Edge,
|
||||
kRenderVS_Z, kRenderFS_ZE)) return false;
|
||||
if (!BuildRenderShader(RenderFlag_Edge | RenderFlag_WBuffer,
|
||||
kRenderVS_W, kRenderFS_WE)) return false;
|
||||
if (!BuildRenderShader(RenderFlag_Trans,
|
||||
kRenderVS_Z, kRenderFS_ZT)) return false;
|
||||
if (!BuildRenderShader(RenderFlag_Trans | RenderFlag_WBuffer,
|
||||
kRenderVS_W, kRenderFS_WT)) return false;
|
||||
if (!BuildRenderShader(RenderFlag_ShadowMask,
|
||||
kRenderVS_Z, kRenderFS_ZSM)) return false;
|
||||
if (!BuildRenderShader(RenderFlag_ShadowMask | RenderFlag_WBuffer,
|
||||
kRenderVS_W, kRenderFS_WSM)) return false;
|
||||
if (!result->BuildRenderShader(0, kRenderVS_Z, kRenderFS_ZO))
|
||||
return nullptr;
|
||||
|
||||
if (!result->BuildRenderShader(RenderFlag_WBuffer, kRenderVS_W, kRenderFS_WO))
|
||||
return nullptr;
|
||||
|
||||
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, FinalPassEdgeShader, "FinalPassEdgeShader"))
|
||||
return false;
|
||||
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, FinalPassFogShader, "FinalPassFogShader"))
|
||||
return false;
|
||||
if (!result->BuildRenderShader(RenderFlag_Edge, kRenderVS_Z, kRenderFS_ZE))
|
||||
return nullptr;
|
||||
|
||||
glBindAttribLocation(FinalPassEdgeShader[2], 0, "vPosition");
|
||||
glBindFragDataLocation(FinalPassEdgeShader[2], 0, "oColor");
|
||||
if (!result->BuildRenderShader(RenderFlag_Edge | RenderFlag_WBuffer, kRenderVS_W, kRenderFS_WE))
|
||||
return nullptr;
|
||||
|
||||
if (!OpenGL::LinkShaderProgram(FinalPassEdgeShader))
|
||||
return false;
|
||||
if (!result->BuildRenderShader(RenderFlag_Trans, kRenderVS_Z, kRenderFS_ZT))
|
||||
return nullptr;
|
||||
|
||||
uni_id = glGetUniformBlockIndex(FinalPassEdgeShader[2], "uConfig");
|
||||
glUniformBlockBinding(FinalPassEdgeShader[2], uni_id, 0);
|
||||
if (!result->BuildRenderShader(RenderFlag_Trans | RenderFlag_WBuffer, kRenderVS_W, kRenderFS_WT))
|
||||
return nullptr;
|
||||
|
||||
glUseProgram(FinalPassEdgeShader[2]);
|
||||
if (!result->BuildRenderShader(RenderFlag_ShadowMask, kRenderVS_Z, kRenderFS_ZSM))
|
||||
return nullptr;
|
||||
|
||||
uni_id = glGetUniformLocation(FinalPassEdgeShader[2], "DepthBuffer");
|
||||
if (!result->BuildRenderShader(RenderFlag_ShadowMask | RenderFlag_WBuffer, kRenderVS_W, kRenderFS_WSM))
|
||||
return nullptr;
|
||||
|
||||
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, result->FinalPassEdgeShader, "FinalPassEdgeShader"))
|
||||
return nullptr;
|
||||
|
||||
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, result->FinalPassFogShader, "FinalPassFogShader"))
|
||||
return nullptr;
|
||||
|
||||
glBindAttribLocation(result->FinalPassEdgeShader[2], 0, "vPosition");
|
||||
glBindFragDataLocation(result->FinalPassEdgeShader[2], 0, "oColor");
|
||||
|
||||
if (!OpenGL::LinkShaderProgram(result->FinalPassEdgeShader))
|
||||
return nullptr;
|
||||
|
||||
GLint uni_id = glGetUniformBlockIndex(result->FinalPassEdgeShader[2], "uConfig");
|
||||
glUniformBlockBinding(result->FinalPassEdgeShader[2], uni_id, 0);
|
||||
|
||||
glUseProgram(result->FinalPassEdgeShader[2]);
|
||||
|
||||
uni_id = glGetUniformLocation(result->FinalPassEdgeShader[2], "DepthBuffer");
|
||||
glUniform1i(uni_id, 0);
|
||||
uni_id = glGetUniformLocation(FinalPassEdgeShader[2], "AttrBuffer");
|
||||
uni_id = glGetUniformLocation(result->FinalPassEdgeShader[2], "AttrBuffer");
|
||||
glUniform1i(uni_id, 1);
|
||||
|
||||
glBindAttribLocation(FinalPassFogShader[2], 0, "vPosition");
|
||||
glBindFragDataLocation(FinalPassFogShader[2], 0, "oColor");
|
||||
glBindAttribLocation(result->FinalPassFogShader[2], 0, "vPosition");
|
||||
glBindFragDataLocation(result->FinalPassFogShader[2], 0, "oColor");
|
||||
|
||||
if (!OpenGL::LinkShaderProgram(FinalPassFogShader))
|
||||
return false;
|
||||
if (!OpenGL::LinkShaderProgram(result->FinalPassFogShader))
|
||||
return nullptr;
|
||||
|
||||
uni_id = glGetUniformBlockIndex(FinalPassFogShader[2], "uConfig");
|
||||
glUniformBlockBinding(FinalPassFogShader[2], uni_id, 0);
|
||||
uni_id = glGetUniformBlockIndex(result->FinalPassFogShader[2], "uConfig");
|
||||
glUniformBlockBinding(result->FinalPassFogShader[2], uni_id, 0);
|
||||
|
||||
glUseProgram(FinalPassFogShader[2]);
|
||||
glUseProgram(result->FinalPassFogShader[2]);
|
||||
|
||||
uni_id = glGetUniformLocation(FinalPassFogShader[2], "DepthBuffer");
|
||||
uni_id = glGetUniformLocation(result->FinalPassFogShader[2], "DepthBuffer");
|
||||
glUniform1i(uni_id, 0);
|
||||
uni_id = glGetUniformLocation(FinalPassFogShader[2], "AttrBuffer");
|
||||
uni_id = glGetUniformLocation(result->FinalPassFogShader[2], "AttrBuffer");
|
||||
glUniform1i(uni_id, 1);
|
||||
|
||||
|
||||
memset(&ShaderConfig, 0, sizeof(ShaderConfig));
|
||||
memset(&result->ShaderConfig, 0, sizeof(ShaderConfig));
|
||||
|
||||
glGenBuffers(1, &ShaderConfigUBO);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, ShaderConfigUBO);
|
||||
static_assert((sizeof(ShaderConfig) & 15) == 0, "");
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(ShaderConfig), &ShaderConfig, GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ShaderConfigUBO);
|
||||
glGenBuffers(1, &result->ShaderConfigUBO);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, result->ShaderConfigUBO);
|
||||
static_assert((sizeof(ShaderConfig) & 15) == 0);
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(ShaderConfig), &result->ShaderConfig, GL_STATIC_DRAW);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, result->ShaderConfigUBO);
|
||||
|
||||
|
||||
float clearvtx[6*2] =
|
||||
@ -205,22 +219,22 @@ bool GLRenderer::Init()
|
||||
1.0, 1.0
|
||||
};
|
||||
|
||||
glGenBuffers(1, &ClearVertexBufferID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, ClearVertexBufferID);
|
||||
glGenBuffers(1, &result->ClearVertexBufferID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result->ClearVertexBufferID);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(clearvtx), clearvtx, GL_STATIC_DRAW);
|
||||
|
||||
glGenVertexArrays(1, &ClearVertexArrayID);
|
||||
glBindVertexArray(ClearVertexArrayID);
|
||||
glGenVertexArrays(1, &result->ClearVertexArrayID);
|
||||
glBindVertexArray(result->ClearVertexArrayID);
|
||||
glEnableVertexAttribArray(0); // position
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0));
|
||||
|
||||
|
||||
glGenBuffers(1, &VertexBufferID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexBuffer), NULL, GL_DYNAMIC_DRAW);
|
||||
glGenBuffers(1, &result->VertexBufferID);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, result->VertexBufferID);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexBuffer), nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
glGenVertexArrays(1, &VertexArrayID);
|
||||
glBindVertexArray(VertexArrayID);
|
||||
glGenVertexArrays(1, &result->VertexArrayID);
|
||||
glBindVertexArray(result->VertexArrayID);
|
||||
glEnableVertexAttribArray(0); // position
|
||||
glVertexAttribIPointer(0, 4, GL_UNSIGNED_SHORT, 7*4, (void*)(0));
|
||||
glEnableVertexAttribArray(1); // color
|
||||
@ -230,43 +244,43 @@ bool GLRenderer::Init()
|
||||
glEnableVertexAttribArray(3); // attrib
|
||||
glVertexAttribIPointer(3, 3, GL_UNSIGNED_INT, 7*4, (void*)(4*4));
|
||||
|
||||
glGenBuffers(1, &IndexBufferID);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(IndexBuffer), NULL, GL_DYNAMIC_DRAW);
|
||||
glGenBuffers(1, &result->IndexBufferID);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result->IndexBufferID);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(IndexBuffer), nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
glGenFramebuffers(4, &FramebufferID[0]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[0]);
|
||||
glGenFramebuffers(4, &result->FramebufferID[0]);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, result->FramebufferID[0]);
|
||||
|
||||
glGenTextures(8, &FramebufferTex[0]);
|
||||
FrontBuffer = 0;
|
||||
glGenTextures(8, &result->FramebufferTex[0]);
|
||||
result->FrontBuffer = 0;
|
||||
|
||||
// color buffers
|
||||
SetupDefaultTexParams(FramebufferTex[0]);
|
||||
SetupDefaultTexParams(FramebufferTex[1]);
|
||||
SetupDefaultTexParams(result->FramebufferTex[0]);
|
||||
SetupDefaultTexParams(result->FramebufferTex[1]);
|
||||
|
||||
// depth/stencil buffer
|
||||
SetupDefaultTexParams(FramebufferTex[4]);
|
||||
SetupDefaultTexParams(FramebufferTex[6]);
|
||||
SetupDefaultTexParams(result->FramebufferTex[4]);
|
||||
SetupDefaultTexParams(result->FramebufferTex[6]);
|
||||
|
||||
// attribute buffer
|
||||
// R: opaque polyID (for edgemarking)
|
||||
// G: edge flag
|
||||
// B: fog flag
|
||||
SetupDefaultTexParams(FramebufferTex[5]);
|
||||
SetupDefaultTexParams(FramebufferTex[7]);
|
||||
SetupDefaultTexParams(result->FramebufferTex[5]);
|
||||
SetupDefaultTexParams(result->FramebufferTex[7]);
|
||||
|
||||
// downscale framebuffer for display capture (always 256x192)
|
||||
SetupDefaultTexParams(FramebufferTex[3]);
|
||||
SetupDefaultTexParams(result->FramebufferTex[3]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 192, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
|
||||
|
||||
glGenBuffers(1, &PixelbufferID);
|
||||
glGenBuffers(1, &result->PixelbufferID);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glGenTextures(1, &TexMemID);
|
||||
glBindTexture(GL_TEXTURE_2D, TexMemID);
|
||||
glGenTextures(1, &result->TexMemID);
|
||||
glBindTexture(GL_TEXTURE_2D, result->TexMemID);
|
||||
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);
|
||||
@ -274,8 +288,8 @@ bool GLRenderer::Init()
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, 1024, 512, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glGenTextures(1, &TexPalMemID);
|
||||
glBindTexture(GL_TEXTURE_2D, TexPalMemID);
|
||||
glGenTextures(1, &result->TexPalMemID);
|
||||
glBindTexture(GL_TEXTURE_2D, result->TexPalMemID);
|
||||
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);
|
||||
@ -284,11 +298,13 @@ bool GLRenderer::Init()
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void GLRenderer::DeInit()
|
||||
GLRenderer::~GLRenderer()
|
||||
{
|
||||
assert(glDeleteTextures != nullptr);
|
||||
|
||||
glDeleteTextures(1, &TexMemID);
|
||||
glDeleteTextures(1, &TexPalMemID);
|
||||
|
||||
|
Reference in New Issue
Block a user