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:
Jesse Talavera-Greenberg
2023-09-15 09:31:05 -04:00
committed by GitHub
parent 1aaf22d181
commit db963aa002
18 changed files with 309 additions and 206 deletions

View File

@ -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);