Implement resource cleanup for all resource types

This commit is contained in:
Magnus Norddahl 2019-12-22 23:44:58 +01:00
parent 11aa31452b
commit e1ff5fc0da
8 changed files with 220 additions and 66 deletions

View file

@ -1,14 +1,15 @@
#include "Precomp.h"
#include "IndexBuffer.h"
IndexBuffer::IndexBuffer()
{
}
#include "RenderDevice.h"
IndexBuffer::~IndexBuffer()
{
// To do: move mBuffer to a delete list as this might be called by a finalizer in a different thread
if (Device && mBuffer != 0)
{
glDeleteBuffers(1, &mBuffer);
mBuffer = 0;
}
}
GLuint IndexBuffer::GetBuffer()
@ -30,7 +31,7 @@ IndexBuffer* IndexBuffer_New()
void IndexBuffer_Delete(IndexBuffer* buffer)
{
//delete buffer;
RenderDevice::DeleteObject(buffer);
}
}

View file

@ -1,13 +1,16 @@
#pragma once
class RenderDevice;
class IndexBuffer
{
public:
IndexBuffer();
~IndexBuffer();
GLuint GetBuffer();
RenderDevice* Device = nullptr;
private:
GLuint mBuffer = 0;
};

View file

@ -61,15 +61,23 @@ RenderDevice::~RenderDevice()
if (Context)
{
Context->MakeCurrent();
ProcessDeleteList();
glDeleteBuffers(1, &mStreamVertexBuffer);
glDeleteVertexArrays(1, &mStreamVAO);
for (auto& sharedbuf : mSharedVertexBuffers)
{
for (VertexBuffer* buf : sharedbuf->VertexBuffers)
buf->Device = nullptr;
GLuint handle = sharedbuf->GetBuffer();
glDeleteBuffers(1, &handle);
handle = sharedbuf->GetVAO();
glDeleteVertexArrays(1, &handle);
}
for (auto& it : mSamplers)
{
for (GLuint handle : it.second.WrapModes)
@ -78,6 +86,7 @@ RenderDevice::~RenderDevice()
glDeleteSamplers(1, &handle);
}
}
mShaderManager->ReleaseResources();
Context->ClearCurrent();
}
@ -330,7 +339,7 @@ bool RenderDevice::StartRendering(bool clear, int backcolor, Texture* target, bo
GLuint framebuffer = 0;
try
{
framebuffer = target->GetFramebuffer(usedepthbuffer);
framebuffer = target->GetFramebuffer(this, usedepthbuffer);
}
catch (std::runtime_error& e)
{
@ -385,7 +394,9 @@ bool RenderDevice::FinishRendering()
bool RenderDevice::Present()
{
Context->MakeCurrent();
Context->SwapBuffers();
ProcessDeleteList();
return CheckGLError();
}
@ -410,7 +421,7 @@ bool RenderDevice::CopyTexture(Texture* dst, CubeMapFace face)
GLint oldTexture = 0;
glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &oldTexture);
glBindTexture(GL_TEXTURE_CUBE_MAP, dst->GetTexture());
glBindTexture(GL_TEXTURE_CUBE_MAP, dst->GetTexture(this));
glCopyTexSubImage2D(facegl[(int)face], 0, 0, 0, 0, 0, dst->GetWidth(), dst->GetHeight());
if (face == CubeMapFace::NegativeZ)
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
@ -420,40 +431,94 @@ bool RenderDevice::CopyTexture(Texture* dst, CubeMapFace face)
return result;
}
void RenderDevice::GarbageCollectBuffer(int size, VertexFormat format)
{
auto& sharedbuf = mSharedVertexBuffers[(int)format];
if (sharedbuf->NextPos + size <= sharedbuf->Size)
return;
GLint oldarray = 0, oldvao = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &oldarray);
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldvao);
int totalSize = size;
for (VertexBuffer* buf : sharedbuf->VertexBuffers)
totalSize += buf->Size;
// If buffer is only half full we only need to GC. Otherwise we also need to expand the buffer size.
int newSize = std::max(totalSize, sharedbuf->Size);
if (newSize < totalSize * 2) newSize *= 2;
std::unique_ptr<SharedVertexBuffer> old = std::move(sharedbuf);
sharedbuf.reset(new SharedVertexBuffer(format, newSize));
glBindBuffer(GL_ARRAY_BUFFER, sharedbuf->GetBuffer());
glBufferData(GL_ARRAY_BUFFER, sharedbuf->Size, nullptr, GL_STATIC_DRAW);
glBindBuffer(GL_COPY_READ_BUFFER, old->GetBuffer());
// Copy all ranges still in use to the new buffer
int stride = (format == VertexFormat::Flat ? SharedVertexBuffer::FlatStride : SharedVertexBuffer::WorldStride);
int readPos = 0;
int writePos = 0;
int copySize = 0;
for (VertexBuffer* buf : old->VertexBuffers)
{
if (buf->BufferOffset != readPos + copySize)
{
if (copySize != 0)
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_ARRAY_BUFFER, readPos, writePos, copySize);
readPos = buf->BufferOffset;
writePos += copySize;
copySize = 0;
}
buf->BufferOffset = sharedbuf->NextPos;
buf->BufferStartIndex = buf->BufferOffset / stride;
sharedbuf->NextPos += buf->Size;
copySize += buf->Size;
}
if (copySize != 0)
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_ARRAY_BUFFER, readPos, writePos, copySize);
sharedbuf->VertexBuffers.swap(old->VertexBuffers);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
GLuint handle = old->GetVAO();
glDeleteVertexArrays(1, &handle);
if (handle == oldvao) oldvao = sharedbuf->GetVAO();
handle = old->GetBuffer();
glDeleteBuffers(1, &handle);
if (handle == oldarray) oldarray = sharedbuf->GetBuffer();
glBindBuffer(GL_ARRAY_BUFFER, oldarray);
glBindVertexArray(oldvao);
mVertexBufferChanged = true;
mNeedApply = true;
}
bool RenderDevice::SetVertexBufferData(VertexBuffer* buffer, void* data, int64_t size, VertexFormat format)
{
if (!mContextIsCurrent) Context->MakeCurrent();
GLint oldbinding = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &oldbinding);
if (buffer->Device)
{
buffer->Device->mSharedVertexBuffers[(int)buffer->Format]->VertexBuffers.erase(buffer->ListIt);
buffer->Device = nullptr;
}
GarbageCollectBuffer(size, format);
auto& sharedbuf = mSharedVertexBuffers[(int)format];
if (sharedbuf->NextPos + size > sharedbuf->Size)
{
std::unique_ptr<SharedVertexBuffer> old = std::move(sharedbuf);
sharedbuf.reset(new SharedVertexBuffer(format, old->Size * 2));
sharedbuf->NextPos = old->NextPos;
glBindBuffer(GL_ARRAY_BUFFER, sharedbuf->GetBuffer());
glBufferData(GL_ARRAY_BUFFER, sharedbuf->Size, nullptr, GL_STATIC_DRAW);
glBindBuffer(GL_COPY_READ_BUFFER, old->GetBuffer());
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_ARRAY_BUFFER, 0, 0, old->Size);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
GLuint handle = old->GetBuffer();
glDeleteBuffers(1, &handle);
handle = old->GetVAO();
glDeleteVertexArrays(1, &handle);
mVertexBufferChanged = true;
mNeedApply = true;
}
else
{
glBindBuffer(GL_ARRAY_BUFFER, sharedbuf->GetBuffer());
}
GLint oldbinding = 0;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &oldbinding);
glBindBuffer(GL_ARRAY_BUFFER, sharedbuf->GetBuffer());
buffer->ListIt = sharedbuf->VertexBuffers.insert(sharedbuf->VertexBuffers.end(), buffer);
buffer->Device = this;
buffer->Size = size;
buffer->Format = format;
buffer->BufferOffset = sharedbuf->NextPos;
buffer->BufferStartIndex = buffer->BufferOffset / (format == VertexFormat::Flat ? SharedVertexBuffer::FlatStride : SharedVertexBuffer::WorldStride);
@ -504,7 +569,7 @@ bool RenderDevice::SetCubePixels(Texture* texture, CubeMapFace face, const void*
void* RenderDevice::MapPBO(Texture* texture)
{
if (!mContextIsCurrent) Context->MakeCurrent();
GLint pbo = texture->GetPBO();
GLint pbo = texture->GetPBO(this);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
void* buf = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
bool result = CheckGLError();
@ -519,10 +584,10 @@ void* RenderDevice::MapPBO(Texture* texture)
bool RenderDevice::UnmapPBO(Texture* texture)
{
if (!mContextIsCurrent) Context->MakeCurrent();
GLint pbo = texture->GetPBO();
GLint pbo = texture->GetPBO(this);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindTexture(GL_TEXTURE_2D, texture->GetTexture());
glBindTexture(GL_TEXTURE_2D, texture->GetTexture(this));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texture->GetWidth(), texture->GetHeight(), 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
bool result = CheckGLError();
mNeedApply = true;
@ -784,7 +849,7 @@ bool RenderDevice::ApplyTextures()
{
GLenum target = mTextureUnit.Tex->IsCubeTexture() ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
glBindTexture(target, mTextureUnit.Tex->GetTexture());
glBindTexture(target, mTextureUnit.Tex->GetTexture(this));
GLuint& samplerHandle = mSamplerFilter->WrapModes[(int)mTextureUnit.WrapMode];
if (samplerHandle == 0)
@ -815,6 +880,50 @@ bool RenderDevice::ApplyTextures()
return CheckGLError();
}
std::mutex& RenderDevice::GetMutex()
{
static std::mutex m;
return m;
}
void RenderDevice::DeleteObject(VertexBuffer* buffer)
{
std::unique_lock<std::mutex> lock(RenderDevice::GetMutex());
if (buffer->Device)
buffer->Device->mDeleteList.VertexBuffers.push_back(buffer);
else
delete buffer;
}
void RenderDevice::DeleteObject(IndexBuffer* buffer)
{
std::unique_lock<std::mutex> lock(RenderDevice::GetMutex());
if (buffer->Device)
buffer->Device->mDeleteList.IndexBuffers.push_back(buffer);
else
delete buffer;
}
void RenderDevice::DeleteObject(Texture* texture)
{
std::unique_lock<std::mutex> lock(RenderDevice::GetMutex());
if (texture->Device)
texture->Device->mDeleteList.Textures.push_back(texture);
else
delete texture;
}
void RenderDevice::ProcessDeleteList()
{
std::unique_lock<std::mutex> lock(RenderDevice::GetMutex());
for (auto buffer : mDeleteList.IndexBuffers) delete buffer;
for (auto buffer : mDeleteList.VertexBuffers) delete buffer;
for (auto texture : mDeleteList.Textures) delete texture;
mDeleteList.IndexBuffers.clear();
mDeleteList.VertexBuffers.clear();
mDeleteList.Textures.clear();
}
/////////////////////////////////////////////////////////////////////////////
extern "C"

View file

@ -2,6 +2,7 @@
#include "OpenGLContext.h"
#include <string>
#include <mutex>
class SharedVertexBuffer;
class VertexBuffer;
@ -78,6 +79,8 @@ public:
bool InvalidateTexture(Texture* texture);
void GarbageCollectBuffer(int size, VertexFormat format);
bool ApplyViewport();
bool ApplyChanges();
bool ApplyVertexBuffer();
@ -97,8 +100,22 @@ public:
GLint GetGLMinFilter(TextureFilter filter, TextureFilter mipfilter);
static std::mutex& GetMutex();
static void DeleteObject(VertexBuffer* buffer);
static void DeleteObject(IndexBuffer* buffer);
static void DeleteObject(Texture* texture);
void ProcessDeleteList();
std::unique_ptr<IOpenGLContext> Context;
struct DeleteList
{
std::vector<VertexBuffer*> VertexBuffers;
std::vector<IndexBuffer*> IndexBuffers;
std::vector<Texture*> Textures;
} mDeleteList;
struct TextureUnit
{
Texture* Tex = nullptr;

View file

@ -1,6 +1,7 @@
#include "Precomp.h"
#include "Texture.h"
#include "RenderDevice.h"
#include <stdexcept>
Texture::Texture()
@ -9,7 +10,8 @@ Texture::Texture()
Texture::~Texture()
{
// To do: move mTexture to a delete list as this might be called by a finalizer in a different thread
if (Device)
Invalidate();
}
void Texture::Set2DImage(int width, int height)
@ -48,12 +50,15 @@ void Texture::Invalidate()
mFramebuffer = 0;
mTexture = 0;
mPBO = 0;
Device = nullptr;
}
GLuint Texture::GetTexture()
GLuint Texture::GetTexture(RenderDevice* device)
{
if (mTexture == 0)
{
Device = device;
GLint oldActiveTex = GL_TEXTURE0;
glGetIntegerv(GL_ACTIVE_TEXTURE, &oldActiveTex);
glActiveTexture(GL_TEXTURE0);
@ -97,13 +102,13 @@ GLuint Texture::GetTexture()
return mTexture;
}
GLuint Texture::GetFramebuffer(bool usedepthbuffer)
GLuint Texture::GetFramebuffer(RenderDevice* device, bool usedepthbuffer)
{
if (!usedepthbuffer)
{
if (mFramebuffer == 0)
{
GLuint texture = GetTexture();
GLuint texture = GetTexture(Device);
glGenFramebuffers(1, &mFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
@ -116,6 +121,8 @@ GLuint Texture::GetFramebuffer(bool usedepthbuffer)
{
if (mDepthRenderbuffer == 0)
{
Device = device;
glGenRenderbuffers(1, &mDepthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, mDepthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWidth, mHeight);
@ -124,7 +131,7 @@ GLuint Texture::GetFramebuffer(bool usedepthbuffer)
if (mFramebuffer == 0)
{
GLuint texture = GetTexture();
GLuint texture = GetTexture(Device);
glGenFramebuffers(1, &mFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
@ -137,10 +144,12 @@ GLuint Texture::GetFramebuffer(bool usedepthbuffer)
}
}
GLuint Texture::GetPBO()
GLuint Texture::GetPBO(RenderDevice* device)
{
if (mPBO == 0)
{
Device = device;
glGenBuffers(1, &mPBO);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mPBO);
glBufferData(GL_PIXEL_UNPACK_BUFFER, mWidth*mHeight * 4, NULL, GL_STREAM_DRAW);
@ -161,7 +170,7 @@ Texture* Texture_New()
void Texture_Delete(Texture* tex)
{
//delete tex;
RenderDevice::DeleteObject(tex);
}
void Texture_Set2DImage(Texture* handle, int width, int height)

View file

@ -10,6 +10,8 @@ enum class CubeMapFace : int
NegativeZ
};
class RenderDevice;
class Texture
{
public:
@ -29,9 +31,11 @@ public:
bool IsTextureCreated() const { return mTexture; }
void Invalidate();
GLuint GetTexture();
GLuint GetFramebuffer(bool usedepthbuffer);
GLuint GetPBO();
GLuint GetTexture(RenderDevice* device);
GLuint GetFramebuffer(RenderDevice* device, bool usedepthbuffer);
GLuint GetPBO(RenderDevice* device);
RenderDevice* Device = nullptr;
private:
int mWidth = 0;

View file

@ -2,19 +2,9 @@
#include "Precomp.h"
#include "VertexBuffer.h"
#include "Shader.h"
#include "RenderDevice.h"
VertexBuffer::VertexBuffer()
{
}
VertexBuffer::~VertexBuffer()
{
// To do: release its slot in RenderDevice (note: this might be called by a finalizer in a different thread)
}
/////////////////////////////////////////////////////////////////////////////
SharedVertexBuffer::SharedVertexBuffer(VertexFormat format, int64_t size) : Format(format), Size(size)
SharedVertexBuffer::SharedVertexBuffer(VertexFormat format, int size) : Format(format), Size(size)
{
}
@ -65,6 +55,17 @@ void SharedVertexBuffer::SetupWorldVAO()
/////////////////////////////////////////////////////////////////////////////
VertexBuffer::~VertexBuffer()
{
if (Device)
{
Device->mSharedVertexBuffers[(int)Format]->VertexBuffers.erase(ListIt);
Device = nullptr;
}
}
/////////////////////////////////////////////////////////////////////////////
extern "C"
{
@ -75,7 +76,7 @@ VertexBuffer* VertexBuffer_New()
void VertexBuffer_Delete(VertexBuffer* buffer)
{
//delete buffer;
RenderDevice::DeleteObject(buffer);
}
}

View file

@ -1,19 +1,26 @@
#pragma once
#include <list>
enum class VertexFormat : int32_t { Flat, World };
class RenderDevice;
class VertexBuffer;
class SharedVertexBuffer
{
public:
SharedVertexBuffer(VertexFormat format, int64_t size);
SharedVertexBuffer(VertexFormat format, int size);
GLuint GetBuffer();
GLuint GetVAO();
VertexFormat Format = VertexFormat::Flat;
int64_t NextPos = 0;
int64_t Size = 0;
int NextPos = 0;
int Size = 0;
std::list<VertexBuffer*> VertexBuffers;
static const int FlatStride = 24;
static const int WorldStride = 36;
@ -29,11 +36,14 @@ private:
class VertexBuffer
{
public:
VertexBuffer();
~VertexBuffer();
VertexFormat Format = VertexFormat::Flat;
RenderDevice* Device = nullptr;
std::list<VertexBuffer*>::iterator ListIt;
int BufferOffset = 0;
int BufferStartIndex = 0;
int Size = 0;
};