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:
parent
1aaf22d181
commit
db963aa002
|
@ -330,6 +330,7 @@ void DeInit()
|
|||
ARMJIT_Memory::DeInit();
|
||||
|
||||
delete JITCompiler;
|
||||
JITCompiler = nullptr;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
|
|
|
@ -308,7 +308,7 @@ HANDLE MemoryFile;
|
|||
LPVOID ExceptionHandlerHandle;
|
||||
#else
|
||||
u8* MemoryBase;
|
||||
int MemoryFile;
|
||||
int MemoryFile = -1;
|
||||
#endif
|
||||
|
||||
bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size)
|
||||
|
@ -811,25 +811,58 @@ void DeInit()
|
|||
{
|
||||
#if defined(__SWITCH__)
|
||||
virtmemLock();
|
||||
virtmemRemoveReservation(FastMem9Reservation);
|
||||
virtmemRemoveReservation(FastMem7Reservation);
|
||||
if (FastMem9Reservation)
|
||||
virtmemRemoveReservation(FastMem9Reservation);
|
||||
|
||||
if (FastMem7Reservation)
|
||||
virtmemRemoveReservation(FastMem7Reservation);
|
||||
|
||||
FastMem9Reservation = nullptr;
|
||||
FastMem7Reservation = nullptr;
|
||||
virtmemUnlock();
|
||||
|
||||
svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem, (u64)MemoryBase, MemoryTotalSize);
|
||||
free(MemoryBase);
|
||||
MemoryBase = nullptr;
|
||||
#elif defined(_WIN32)
|
||||
assert(UnmapViewOfFile(MemoryBase));
|
||||
CloseHandle(MemoryFile);
|
||||
if (MemoryBase)
|
||||
{
|
||||
bool viewUnmapped = UnmapViewOfFile(MemoryBase);
|
||||
assert(viewUnmapped);
|
||||
MemoryBase = nullptr;
|
||||
FastMem9Start = nullptr;
|
||||
FastMem7Start = nullptr;
|
||||
}
|
||||
|
||||
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
|
||||
if (MemoryFile)
|
||||
{
|
||||
CloseHandle(MemoryFile);
|
||||
MemoryFile = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (ExceptionHandlerHandle)
|
||||
{
|
||||
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
|
||||
ExceptionHandlerHandle = nullptr;
|
||||
}
|
||||
#else
|
||||
sigaction(SIGSEGV, &OldSaSegv, nullptr);
|
||||
#ifdef __APPLE__
|
||||
sigaction(SIGBUS, &OldSaBus, nullptr);
|
||||
#endif
|
||||
if (MemoryBase)
|
||||
{
|
||||
munmap(MemoryBase, MemoryTotalSize);
|
||||
MemoryBase = nullptr;
|
||||
FastMem9Start = nullptr;
|
||||
FastMem7Start = nullptr;
|
||||
}
|
||||
|
||||
munmap(MemoryBase, MemoryTotalSize);
|
||||
close(MemoryFile);
|
||||
if (MemoryFile >= 0)
|
||||
{
|
||||
close(MemoryFile);
|
||||
MemoryFile = -1;
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
if (Libandroid)
|
||||
|
|
12
src/DSi.cpp
12
src/DSi.cpp
|
@ -128,6 +128,10 @@ void DeInit()
|
|||
delete[] NWRAM_A;
|
||||
delete[] NWRAM_B;
|
||||
delete[] NWRAM_C;
|
||||
|
||||
NWRAM_A = nullptr;
|
||||
NWRAM_B = nullptr;
|
||||
NWRAM_C = nullptr;
|
||||
#endif
|
||||
|
||||
DSi_I2C::DeInit();
|
||||
|
@ -135,10 +139,16 @@ void DeInit()
|
|||
DSi_AES::DeInit();
|
||||
DSi_DSP::DeInit();
|
||||
|
||||
for (int i = 0; i < 8; i++) delete NDMAs[i];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
delete NDMAs[i];
|
||||
NDMAs[i] = nullptr;
|
||||
}
|
||||
|
||||
delete SDMMC;
|
||||
SDMMC = nullptr;
|
||||
delete SDIO;
|
||||
SDIO = nullptr;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
|
|
|
@ -63,6 +63,9 @@ void DeInit()
|
|||
{
|
||||
delete Camera0;
|
||||
delete Camera1;
|
||||
|
||||
Camera0 = nullptr;
|
||||
Camera1 = nullptr;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
|
|
28
src/GPU.cpp
28
src/GPU.cpp
|
@ -171,6 +171,11 @@ void DeInit()
|
|||
if (Framebuffer[0][1]) delete[] Framebuffer[0][1];
|
||||
if (Framebuffer[1][0]) delete[] Framebuffer[1][0];
|
||||
if (Framebuffer[1][1]) delete[] Framebuffer[1][1];
|
||||
|
||||
Framebuffer[0][0] = nullptr;
|
||||
Framebuffer[0][1] = nullptr;
|
||||
Framebuffer[1][0] = nullptr;
|
||||
Framebuffer[1][1] = nullptr;
|
||||
}
|
||||
|
||||
void ResetVRAMCache()
|
||||
|
@ -388,20 +393,18 @@ void InitRenderer(int renderer)
|
|||
#ifdef OGLRENDERER_ENABLED
|
||||
if (renderer == 1)
|
||||
{
|
||||
CurGLCompositor = std::make_unique<GLCompositor>();
|
||||
// Create opengl rendrerer
|
||||
if (!CurGLCompositor->Init())
|
||||
CurGLCompositor = GLCompositor::New();
|
||||
// Create opengl renderer
|
||||
if (!CurGLCompositor)
|
||||
{
|
||||
// Fallback on software renderer
|
||||
renderer = 0;
|
||||
GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>();
|
||||
GPU3D::CurrentRenderer->Init();
|
||||
}
|
||||
GPU3D::CurrentRenderer = std::make_unique<GPU3D::GLRenderer>();
|
||||
if (!GPU3D::CurrentRenderer->Init())
|
||||
GPU3D::CurrentRenderer = GPU3D::GLRenderer::New();
|
||||
if (!GPU3D::CurrentRenderer)
|
||||
{
|
||||
// Fallback on software renderer
|
||||
CurGLCompositor->DeInit();
|
||||
CurGLCompositor.reset();
|
||||
renderer = 0;
|
||||
GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>();
|
||||
|
@ -411,7 +414,6 @@ void InitRenderer(int renderer)
|
|||
#endif
|
||||
{
|
||||
GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>();
|
||||
GPU3D::CurrentRenderer->Init();
|
||||
}
|
||||
|
||||
Renderer = renderer;
|
||||
|
@ -419,12 +421,12 @@ void InitRenderer(int renderer)
|
|||
|
||||
void DeInitRenderer()
|
||||
{
|
||||
GPU3D::CurrentRenderer->DeInit();
|
||||
// Delete the 3D renderer, if it exists
|
||||
GPU3D::CurrentRenderer.reset();
|
||||
|
||||
#ifdef OGLRENDERER_ENABLED
|
||||
if (Renderer == 1)
|
||||
{
|
||||
CurGLCompositor->DeInit();
|
||||
}
|
||||
// Delete the compositor, if one exists
|
||||
CurGLCompositor.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -137,14 +137,11 @@ void Write32(u32 addr, u32 val);
|
|||
class Renderer3D
|
||||
{
|
||||
public:
|
||||
Renderer3D(bool Accelerated);
|
||||
virtual ~Renderer3D() {};
|
||||
virtual ~Renderer3D() = default;
|
||||
|
||||
Renderer3D(const Renderer3D&) = delete;
|
||||
Renderer3D& operator=(const Renderer3D&) = delete;
|
||||
|
||||
virtual bool Init() = 0;
|
||||
virtual void DeInit() = 0;
|
||||
virtual void Reset() = 0;
|
||||
|
||||
// This "Accelerated" flag currently communicates if the framebuffer should
|
||||
|
@ -159,6 +156,8 @@ public:
|
|||
virtual void RenderFrame() = 0;
|
||||
virtual void RestartFrame() {};
|
||||
virtual u32* GetLine(int line) = 0;
|
||||
protected:
|
||||
Renderer3D(bool Accelerated);
|
||||
};
|
||||
|
||||
extern int Renderer;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -28,10 +28,7 @@ namespace GPU3D
|
|||
class GLRenderer : public Renderer3D
|
||||
{
|
||||
public:
|
||||
GLRenderer();
|
||||
virtual ~GLRenderer() override {};
|
||||
virtual bool Init() override;
|
||||
virtual void DeInit() override;
|
||||
virtual ~GLRenderer() override;
|
||||
virtual void Reset() override;
|
||||
|
||||
virtual void SetRenderSettings(GPU::RenderSettings& settings) override;
|
||||
|
@ -42,7 +39,11 @@ public:
|
|||
|
||||
void SetupAccelFrame();
|
||||
void PrepareCaptureFrame();
|
||||
|
||||
static std::unique_ptr<GLRenderer> New() noexcept;
|
||||
private:
|
||||
// Used by New()
|
||||
GLRenderer() noexcept;
|
||||
|
||||
// GL version requirements
|
||||
// * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS)
|
||||
|
@ -62,7 +63,7 @@ private:
|
|||
u32 RenderKey;
|
||||
};
|
||||
|
||||
RendererPolygon PolygonList[2048];
|
||||
RendererPolygon PolygonList[2048] {};
|
||||
|
||||
bool BuildRenderShader(u32 flags, const char* vs, const char* fs);
|
||||
void UseRenderShader(u32 flags);
|
||||
|
@ -83,13 +84,13 @@ private:
|
|||
};
|
||||
|
||||
|
||||
GLuint ClearShaderPlain[3];
|
||||
GLuint ClearShaderPlain[3] {};
|
||||
|
||||
GLuint RenderShader[16][3];
|
||||
GLuint RenderShader[16][3] {};
|
||||
GLuint CurShaderID = -1;
|
||||
|
||||
GLuint FinalPassEdgeShader[3];
|
||||
GLuint FinalPassFogShader[3];
|
||||
GLuint FinalPassEdgeShader[3] {};
|
||||
GLuint FinalPassFogShader[3] {};
|
||||
|
||||
// std140 compliant structure
|
||||
struct
|
||||
|
@ -104,13 +105,13 @@ private:
|
|||
u32 uFogOffset; // int 304 / 1
|
||||
u32 uFogShift; // int 305 / 1
|
||||
u32 _pad1[2]; // int 306 / 2
|
||||
} ShaderConfig;
|
||||
} ShaderConfig {};
|
||||
|
||||
GLuint ShaderConfigUBO;
|
||||
int NumFinalPolys, NumOpaqueFinalPolys;
|
||||
GLuint ShaderConfigUBO {};
|
||||
int NumFinalPolys {}, NumOpaqueFinalPolys {};
|
||||
|
||||
GLuint ClearVertexBufferID, ClearVertexArrayID;
|
||||
GLint ClearUniformLoc[4];
|
||||
GLuint ClearVertexBufferID = 0, ClearVertexArrayID {};
|
||||
GLint ClearUniformLoc[4] {};
|
||||
|
||||
// vertex buffer
|
||||
// * XYZW: 4x16bit
|
||||
|
@ -124,28 +125,28 @@ private:
|
|||
// * bit8: front-facing (?)
|
||||
// * bit9: W-buffering (?)
|
||||
|
||||
GLuint VertexBufferID;
|
||||
u32 VertexBuffer[10240 * 7];
|
||||
u32 NumVertices;
|
||||
GLuint VertexBufferID {};
|
||||
u32 VertexBuffer[10240 * 7] {};
|
||||
u32 NumVertices {};
|
||||
|
||||
GLuint VertexArrayID;
|
||||
GLuint IndexBufferID;
|
||||
u16 IndexBuffer[2048 * 40];
|
||||
u32 NumIndices, NumEdgeIndices;
|
||||
GLuint VertexArrayID {};
|
||||
GLuint IndexBufferID {};
|
||||
u16 IndexBuffer[2048 * 40] {};
|
||||
u32 NumIndices {}, NumEdgeIndices {};
|
||||
|
||||
const u32 EdgeIndicesOffset = 2048 * 30;
|
||||
|
||||
GLuint TexMemID;
|
||||
GLuint TexPalMemID;
|
||||
GLuint TexMemID {};
|
||||
GLuint TexPalMemID {};
|
||||
|
||||
int ScaleFactor;
|
||||
bool BetterPolygons;
|
||||
int ScreenW, ScreenH;
|
||||
int ScaleFactor {};
|
||||
bool BetterPolygons {};
|
||||
int ScreenW {}, ScreenH {};
|
||||
|
||||
GLuint FramebufferTex[8];
|
||||
int FrontBuffer;
|
||||
GLuint FramebufferID[4], PixelbufferID;
|
||||
u32 Framebuffer[256*192];
|
||||
GLuint FramebufferTex[8] {};
|
||||
int FrontBuffer {};
|
||||
GLuint FramebufferID[4] {}, PixelbufferID {};
|
||||
u32 Framebuffer[256*192] {};
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -71,13 +71,8 @@ void SoftRenderer::SetupRenderThread()
|
|||
}
|
||||
|
||||
|
||||
SoftRenderer::SoftRenderer()
|
||||
SoftRenderer::SoftRenderer() noexcept
|
||||
: Renderer3D(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool SoftRenderer::Init()
|
||||
{
|
||||
Sema_RenderStart = Platform::Semaphore_Create();
|
||||
Sema_RenderDone = Platform::Semaphore_Create();
|
||||
|
@ -86,11 +81,9 @@ bool SoftRenderer::Init()
|
|||
Threaded = false;
|
||||
RenderThreadRunning = false;
|
||||
RenderThreadRendering = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoftRenderer::DeInit()
|
||||
SoftRenderer::~SoftRenderer()
|
||||
{
|
||||
StopRenderThread();
|
||||
|
||||
|
|
|
@ -28,10 +28,8 @@ namespace GPU3D
|
|||
class SoftRenderer : public Renderer3D
|
||||
{
|
||||
public:
|
||||
SoftRenderer();
|
||||
virtual ~SoftRenderer() override {};
|
||||
virtual bool Init() override;
|
||||
virtual void DeInit() override;
|
||||
SoftRenderer() noexcept;
|
||||
virtual ~SoftRenderer() override;
|
||||
virtual void Reset() override;
|
||||
|
||||
virtual void SetRenderSettings(GPU::RenderSettings& settings) override;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "GPU_OpenGL.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
|
@ -32,33 +33,36 @@ namespace GPU
|
|||
|
||||
using namespace OpenGL;
|
||||
|
||||
bool GLCompositor::Init()
|
||||
std::unique_ptr<GLCompositor> GLCompositor::New() noexcept
|
||||
{
|
||||
if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, CompShader[0], "CompositorShader"))
|
||||
//if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Linear, CompShader[0], "CompositorShader"))
|
||||
//if (!OpenGL::BuildShaderProgram(kCompositorVS_xBRZ, kCompositorFS_xBRZ, CompShader[0], "CompositorShader"))
|
||||
return false;
|
||||
assert(glBindAttribLocation != nullptr);
|
||||
|
||||
for (int i = 0; i < 1; i++)
|
||||
{
|
||||
GLint uni_id;
|
||||
std::array<GLuint, 3> CompShader {};
|
||||
if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, &CompShader[0], "CompositorShader"))
|
||||
return nullptr;
|
||||
|
||||
glBindAttribLocation(CompShader[i][2], 0, "vPosition");
|
||||
glBindAttribLocation(CompShader[i][2], 1, "vTexcoord");
|
||||
glBindFragDataLocation(CompShader[i][2], 0, "oColor");
|
||||
glBindAttribLocation(CompShader[2], 0, "vPosition");
|
||||
glBindAttribLocation(CompShader[2], 1, "vTexcoord");
|
||||
glBindFragDataLocation(CompShader[2], 0, "oColor");
|
||||
|
||||
if (!OpenGL::LinkShaderProgram(CompShader[i]))
|
||||
return false;
|
||||
if (!OpenGL::LinkShaderProgram(CompShader.data()))
|
||||
// OpenGL::LinkShaderProgram already deletes the shader program object
|
||||
// if linking the shaders together failed.
|
||||
return nullptr;
|
||||
|
||||
CompScaleLoc[i] = glGetUniformLocation(CompShader[i][2], "u3DScale");
|
||||
Comp3DXPosLoc[i] = glGetUniformLocation(CompShader[i][2], "u3DXPos");
|
||||
return std::unique_ptr<GLCompositor>(new GLCompositor(CompShader));
|
||||
}
|
||||
|
||||
glUseProgram(CompShader[i][2]);
|
||||
uni_id = glGetUniformLocation(CompShader[i][2], "ScreenTex");
|
||||
glUniform1i(uni_id, 0);
|
||||
uni_id = glGetUniformLocation(CompShader[i][2], "_3DTex");
|
||||
glUniform1i(uni_id, 1);
|
||||
}
|
||||
GLCompositor::GLCompositor(std::array<GLuint, 3> compShader) noexcept : CompShader(compShader)
|
||||
{
|
||||
CompScaleLoc = glGetUniformLocation(CompShader[2], "u3DScale");
|
||||
Comp3DXPosLoc = glGetUniformLocation(CompShader[2], "u3DXPos");
|
||||
|
||||
glUseProgram(CompShader[2]);
|
||||
GLuint screenTextureUniform = glGetUniformLocation(CompShader[2], "ScreenTex");
|
||||
glUniform1i(screenTextureUniform, 0);
|
||||
GLuint _3dTextureUniform = glGetUniformLocation(CompShader[2], "_3DTex");
|
||||
glUniform1i(_3dTextureUniform, 1);
|
||||
|
||||
// all this mess is to prevent bleeding
|
||||
#define SETVERTEX(i, x, y, offset) \
|
||||
|
@ -119,12 +123,12 @@ bool GLCompositor::Init()
|
|||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLCompositor::DeInit()
|
||||
GLCompositor::~GLCompositor()
|
||||
{
|
||||
assert(glDeleteFramebuffers != nullptr);
|
||||
|
||||
glDeleteFramebuffers(2, CompScreenOutputFB);
|
||||
glDeleteTextures(1, &CompScreenInputTex);
|
||||
glDeleteTextures(2, CompScreenOutputTex);
|
||||
|
@ -132,8 +136,7 @@ void GLCompositor::DeInit()
|
|||
glDeleteVertexArrays(1, &CompVertexArrayID);
|
||||
glDeleteBuffers(1, &CompVertexBufferID);
|
||||
|
||||
for (int i = 0; i < 1; i++)
|
||||
OpenGL::DeleteShaderProgram(CompShader[i]);
|
||||
OpenGL::DeleteShaderProgram(CompShader.data());
|
||||
}
|
||||
|
||||
void GLCompositor::Reset()
|
||||
|
@ -197,11 +200,11 @@ void GLCompositor::RenderFrame()
|
|||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// TODO: select more shaders (filtering, etc)
|
||||
OpenGL::UseShaderProgram(CompShader[0]);
|
||||
glUniform1ui(CompScaleLoc[0], Scale);
|
||||
OpenGL::UseShaderProgram(CompShader.data());
|
||||
glUniform1ui(CompScaleLoc, Scale);
|
||||
|
||||
// TODO: support setting this midframe, if ever needed
|
||||
glUniform1i(Comp3DXPosLoc[0], ((int)GPU3D::RenderXPos << 23) >> 23);
|
||||
glUniform1i(Comp3DXPosLoc, ((int)GPU3D::RenderXPos << 23) >> 23);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, CompScreenInputTex);
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
|
||||
#include "OpenGLSupport.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
namespace GPU
|
||||
{
|
||||
|
||||
|
@ -28,12 +31,11 @@ struct RenderSettings;
|
|||
class GLCompositor
|
||||
{
|
||||
public:
|
||||
GLCompositor() = default;
|
||||
static std::unique_ptr<GLCompositor> New() noexcept;
|
||||
GLCompositor(const GLCompositor&) = delete;
|
||||
GLCompositor& operator=(const GLCompositor&) = delete;
|
||||
~GLCompositor();
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
void Reset();
|
||||
|
||||
void SetRenderSettings(RenderSettings& settings);
|
||||
|
@ -42,13 +44,14 @@ public:
|
|||
void RenderFrame();
|
||||
void BindOutputTexture(int buf);
|
||||
private:
|
||||
GLCompositor(std::array<GLuint, 3> CompShader) noexcept;
|
||||
|
||||
int Scale;
|
||||
int ScreenH, ScreenW;
|
||||
|
||||
GLuint CompShader[1][3];
|
||||
GLuint CompScaleLoc[1];
|
||||
GLuint Comp3DXPosLoc[1];
|
||||
std::array<GLuint, 3> CompShader;
|
||||
GLuint CompScaleLoc;
|
||||
GLuint Comp3DXPosLoc;
|
||||
|
||||
GLuint CompVertexBufferID;
|
||||
GLuint CompVertexArrayID;
|
||||
|
|
|
@ -226,10 +226,16 @@ void DeInit()
|
|||
#endif
|
||||
|
||||
delete ARM9;
|
||||
ARM9 = nullptr;
|
||||
|
||||
delete ARM7;
|
||||
ARM7 = nullptr;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
delete DMAs[i];
|
||||
DMAs[i] = nullptr;
|
||||
}
|
||||
|
||||
NDSCart::DeInit();
|
||||
GBACart::DeInit();
|
||||
|
|
|
@ -29,6 +29,12 @@ bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char*
|
|||
int len;
|
||||
int res;
|
||||
|
||||
if (!glCreateShader)
|
||||
{
|
||||
Log(LogLevel::Error, "OpenGL: Cannot build shader program, OpenGL hasn't been loaded\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ids[0] = glCreateShader(GL_VERTEX_SHADER);
|
||||
len = strlen(vs);
|
||||
glShaderSource(ids[0], 1, &vs, &len);
|
||||
|
@ -87,6 +93,12 @@ bool LinkShaderProgram(GLuint* ids)
|
|||
{
|
||||
int res;
|
||||
|
||||
if (!glLinkProgram)
|
||||
{
|
||||
Log(LogLevel::Error, "OpenGL: Cannot link shader program, OpenGL hasn't been loaded\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
glLinkProgram(ids[2]);
|
||||
|
||||
glDetachShader(ids[2], ids[0]);
|
||||
|
@ -115,12 +127,18 @@ bool LinkShaderProgram(GLuint* ids)
|
|||
|
||||
void DeleteShaderProgram(GLuint* ids)
|
||||
{
|
||||
glDeleteProgram(ids[2]);
|
||||
if (glDeleteProgram)
|
||||
{ // If OpenGL isn't loaded, then there's no shader program to delete
|
||||
glDeleteProgram(ids[2]);
|
||||
}
|
||||
}
|
||||
|
||||
void UseShaderProgram(GLuint* ids)
|
||||
{
|
||||
glUseProgram(ids[2]);
|
||||
if (glUseProgram)
|
||||
{ // If OpenGL isn't loaded, then there's no shader program to use
|
||||
glUseProgram(ids[2]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ bool Init()
|
|||
void DeInit()
|
||||
{
|
||||
if (Firmware) delete[] Firmware;
|
||||
Firmware = nullptr;
|
||||
}
|
||||
|
||||
u32 FixFirmwareLength(u32 originalLength)
|
||||
|
|
|
@ -135,12 +135,18 @@ bool Init()
|
|||
void DeInit()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
delete Channels[i];
|
||||
Channels[i] = nullptr;
|
||||
}
|
||||
|
||||
delete Capture[0];
|
||||
delete Capture[1];
|
||||
Capture[0] = nullptr;
|
||||
Capture[1] = nullptr;
|
||||
|
||||
Platform::Mutex_Free(AudioLock);
|
||||
AudioLock = nullptr;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
|
|
|
@ -334,12 +334,17 @@ void Init()
|
|||
void DeInit()
|
||||
{
|
||||
if (audioDevice) SDL_CloseAudioDevice(audioDevice);
|
||||
audioDevice = 0;
|
||||
MicClose();
|
||||
|
||||
SDL_DestroyCond(audioSync);
|
||||
SDL_DestroyMutex(audioSyncLock);
|
||||
if (audioSync) SDL_DestroyCond(audioSync);
|
||||
audioSync = nullptr;
|
||||
|
||||
if (audioSyncLock) SDL_DestroyMutex(audioSyncLock);
|
||||
audioSyncLock = nullptr;
|
||||
|
||||
if (micWavBuffer) delete[] micWavBuffer;
|
||||
micWavBuffer = nullptr;
|
||||
}
|
||||
|
||||
void AudioSync()
|
||||
|
|
|
@ -297,16 +297,21 @@ bool Init()
|
|||
|
||||
void DeInit()
|
||||
{
|
||||
MPQueue->lock();
|
||||
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
|
||||
header->ConnectedBitmask &= ~(1 << InstanceID);
|
||||
header->InstanceBitmask &= ~(1 << InstanceID);
|
||||
header->NumInstances--;
|
||||
MPQueue->unlock();
|
||||
if (MPQueue)
|
||||
{
|
||||
MPQueue->lock();
|
||||
MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
|
||||
header->ConnectedBitmask &= ~(1 << InstanceID);
|
||||
header->InstanceBitmask &= ~(1 << InstanceID);
|
||||
header->NumInstances--;
|
||||
MPQueue->unlock();
|
||||
|
||||
SemPoolDeinit();
|
||||
SemPoolDeinit();
|
||||
|
||||
MPQueue->detach();
|
||||
MPQueue->detach();
|
||||
}
|
||||
|
||||
MPQueue = nullptr;
|
||||
delete MPQueue;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue