Fixed crash when resources in use by the Renderer are released

Former-commit-id: 98eedb556f0387f0a5c1cafde2fc74645d1d0457
This commit is contained in:
Lynix 2013-08-26 00:40:45 +02:00
parent f1dc7b98e5
commit 72a57fbf4c
11 changed files with 228 additions and 42 deletions

View File

@ -42,10 +42,14 @@ class NAZARA_API NzResource
void NotifyDestroy(); void NotifyDestroy();
private: private:
typedef std::unordered_map<NzResourceListener*, std::pair<int, unsigned int>> ResourceListenerMap;
void RemoveResourceListenerIterator(ResourceListenerMap::iterator iterator) const;
NazaraMutexAttrib(m_mutex, mutable) NazaraMutexAttrib(m_mutex, mutable)
// Je fais précéder le nom par 'resource' pour éviter les éventuels conflits de noms // Je fais précéder le nom par 'resource' pour éviter les éventuels conflits de noms
mutable std::unordered_map<NzResourceListener*, int> m_resourceListeners; mutable ResourceListenerMap m_resourceListeners;
std::atomic_bool m_resourcePersistent; std::atomic_bool m_resourcePersistent;
mutable std::atomic_uint m_resourceReferenceCount; mutable std::atomic_uint m_resourceReferenceCount;
bool m_resourceListenersLocked; bool m_resourceListenersLocked;

View File

@ -32,13 +32,13 @@ class NAZARA_API NzContext : public NzResource
const NzContextParameters& GetParameters() const; const NzContextParameters& GetParameters() const;
bool IsActive() const; bool IsActive() const;
bool SetActive(bool active); bool SetActive(bool active) const;
void SwapBuffers(); void SwapBuffers();
static bool EnsureContext(); static bool EnsureContext();
static NzContext* GetCurrent(); static const NzContext* GetCurrent();
static NzContext* GetReference(); static const NzContext* GetReference();
static NzContext* GetThreadContext(); static const NzContext* GetThreadContext();
static bool Initialize(); static bool Initialize();
static void Uninitialize(); static void Uninitialize();

View File

@ -133,8 +133,8 @@ class NAZARA_API NzOpenGL
static GLenum TextureTargetProxy[nzImageType_Max+1]; static GLenum TextureTargetProxy[nzImageType_Max+1];
private: private:
static void OnContextDestruction(NzContext* context); static void OnContextDestruction(const NzContext* context);
static void OnContextChange(NzContext* newContext); static void OnContextChange(const NzContext* newContext);
}; };
NAZARA_API extern PFNGLACTIVETEXTUREPROC glActiveTexture; NAZARA_API extern PFNGLACTIVETEXTUREPROC glActiveTexture;

View File

@ -23,10 +23,14 @@ class NzIndexBuffer;
class NzMaterial; class NzMaterial;
class NzRenderTarget; class NzRenderTarget;
class NzShaderProgram; class NzShaderProgram;
class NzTexture;
class NzVertexBuffer; class NzVertexBuffer;
class NAZARA_API NzRenderer class NAZARA_API NzRenderer
{ {
friend NzShaderProgram;
friend NzTexture;
public: public:
NzRenderer() = delete; NzRenderer() = delete;
~NzRenderer() = delete; ~NzRenderer() = delete;
@ -96,6 +100,8 @@ class NAZARA_API NzRenderer
private: private:
static void EnableInstancing(bool instancing); static void EnableInstancing(bool instancing);
static bool EnsureStateUpdate(); static bool EnsureStateUpdate();
static void OnProgramReleased(const NzShaderProgram* program);
static void OnTextureReleased(const NzTexture* texture);
static void UpdateMatrix(nzMatrixType type); static void UpdateMatrix(nzMatrixType type);
static unsigned int s_moduleReferenceCounter; static unsigned int s_moduleReferenceCounter;

View File

@ -6,6 +6,7 @@
#include <Nazara/Core/Config.hpp> #include <Nazara/Core/Config.hpp>
#include <Nazara/Core/Error.hpp> #include <Nazara/Core/Error.hpp>
#include <Nazara/Core/ResourceListener.hpp> #include <Nazara/Core/ResourceListener.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <Nazara/Utility/VertexDeclaration.hpp> #include <Nazara/Utility/VertexDeclaration.hpp>
#include <typeinfo> #include <typeinfo>
#include <Nazara/Core/Debug.hpp> #include <Nazara/Core/Debug.hpp>
@ -21,7 +22,7 @@ NzResource::~NzResource()
{ {
m_resourceListenersLocked = true; m_resourceListenersLocked = true;
for (auto& pair : m_resourceListeners) for (auto& pair : m_resourceListeners)
pair.first->OnResourceReleased(this, pair.second); pair.first->OnResourceReleased(this, pair.second.first);
#if NAZARA_CORE_SAFE #if NAZARA_CORE_SAFE
if (m_resourceReferenceCount > 0) if (m_resourceReferenceCount > 0)
@ -35,7 +36,11 @@ void NzResource::AddResourceListener(NzResourceListener* listener, int index) co
NazaraLock(m_mutex) NazaraLock(m_mutex)
if (!m_resourceListenersLocked) if (!m_resourceListenersLocked)
m_resourceListeners.insert(std::make_pair(listener, index)); {
auto pair = m_resourceListeners.insert(std::make_pair(listener, std::make_pair(index, 1U)));
if (!pair.second)
pair.first->second.second++;
}
} }
void NzResource::AddResourceReference() const void NzResource::AddResourceReference() const
@ -59,7 +64,11 @@ void NzResource::RemoveResourceListener(NzResourceListener* listener) const
NazaraLock(m_mutex); NazaraLock(m_mutex);
if (!m_resourceListenersLocked) if (!m_resourceListenersLocked)
m_resourceListeners.erase(listener); {
ResourceListenerMap::iterator it = m_resourceListeners.find(listener);
if (it != m_resourceListeners.end())
RemoveResourceListenerIterator(it);
}
} }
bool NzResource::RemoveResourceReference() const bool NzResource::RemoveResourceReference() const
@ -105,8 +114,9 @@ void NzResource::NotifyCreated()
auto it = m_resourceListeners.begin(); auto it = m_resourceListeners.begin();
while (it != m_resourceListeners.end()) while (it != m_resourceListeners.end())
{ {
if (!it->first->OnResourceCreated(this, it->second)) ResourceListenerMap::iterator iterator = it++;
m_resourceListeners.erase(it++); if (!it->first->OnResourceCreated(this, it->second.first))
RemoveResourceListenerIterator(it++);
else else
++it; ++it;
} }
@ -123,11 +133,20 @@ void NzResource::NotifyDestroy()
auto it = m_resourceListeners.begin(); auto it = m_resourceListeners.begin();
while (it != m_resourceListeners.end()) while (it != m_resourceListeners.end())
{ {
if (!it->first->OnResourceDestroy(this, it->second)) if (!it->first->OnResourceDestroy(this, it->second.first))
m_resourceListeners.erase(it++); RemoveResourceListenerIterator(it++);
else else
++it; ++it;
} }
m_resourceListenersLocked = false; m_resourceListenersLocked = false;
} }
void NzResource::RemoveResourceListenerIterator(ResourceListenerMap::iterator iterator) const
{
unsigned int& referenceCount = iterator->second.second;
if (referenceCount == 1)
m_resourceListeners.erase(iterator);
else
referenceCount--;
}

View File

@ -22,8 +22,8 @@
namespace namespace
{ {
thread_local NzContext* currentContext = nullptr; thread_local const NzContext* currentContext = nullptr;
thread_local NzContext* threadContext = nullptr; thread_local const NzContext* threadContext = nullptr;
std::vector<NzContext*> contexts; std::vector<NzContext*> contexts;
@ -219,7 +219,7 @@ bool NzContext::IsActive() const
return currentContext == this; return currentContext == this;
} }
bool NzContext::SetActive(bool active) bool NzContext::SetActive(bool active) const
{ {
#ifdef NAZARA_RENDERER_SAFE #ifdef NAZARA_RENDERER_SAFE
if (!m_impl) if (!m_impl)
@ -302,17 +302,17 @@ bool NzContext::EnsureContext()
return true; return true;
} }
NzContext* NzContext::GetCurrent() const NzContext* NzContext::GetCurrent()
{ {
return currentContext; return currentContext;
} }
NzContext* NzContext::GetReference() const NzContext* NzContext::GetReference()
{ {
return s_reference; return s_reference;
} }
NzContext* NzContext::GetThreadContext() const NzContext* NzContext::GetThreadContext()
{ {
EnsureContext(); EnsureContext();

View File

@ -71,7 +71,7 @@ namespace
}; };
std::set<NzString> s_openGLextensionSet; std::set<NzString> s_openGLextensionSet;
std::unordered_map<NzContext*, ContextStates> s_contexts; std::unordered_map<const NzContext*, ContextStates> s_contexts;
thread_local ContextStates* s_contextStates = nullptr; thread_local ContextStates* s_contextStates = nullptr;
NzString s_rendererName; NzString s_rendererName;
NzString s_vendorName; NzString s_vendorName;
@ -1305,12 +1305,12 @@ void NzOpenGL::Uninitialize()
} }
} }
void NzOpenGL::OnContextDestruction(NzContext* context) void NzOpenGL::OnContextDestruction(const NzContext* context)
{ {
s_contexts.erase(context); s_contexts.erase(context);
} }
void NzOpenGL::OnContextChange(NzContext* newContext) void NzOpenGL::OnContextChange(const NzContext* newContext)
{ {
s_contextStates = (newContext) ? &s_contexts[newContext] : nullptr; s_contextStates = (newContext) ? &s_contexts[newContext] : nullptr;
} }

View File

@ -59,7 +59,7 @@ bool NzRenderWindow::CopyToImage(NzImage* image) const
} }
#endif #endif
NzContext* currentContext = NzContext::GetCurrent(); const NzContext* currentContext = NzContext::GetCurrent();
if (m_context != currentContext) if (m_context != currentContext)
{ {
if (!m_context->SetActive(true)) if (!m_context->SetActive(true))
@ -112,7 +112,7 @@ bool NzRenderWindow::CopyToTexture(NzTexture* texture) const
} }
#endif #endif
NzContext* currentContext = NzContext::GetCurrent(); const NzContext* currentContext = NzContext::GetCurrent();
if (m_context != currentContext) if (m_context != currentContext)
{ {
if (!m_context->SetActive(true)) if (!m_context->SetActive(true))

View File

@ -33,6 +33,14 @@
namespace namespace
{ {
enum ResourceType
{
ResourceType_Context,
ResourceType_IndexBuffer,
ResourceType_VertexBuffer,
ResourceType_VertexDeclaration
};
enum UpdateFlags enum UpdateFlags
{ {
Update_None = 0, Update_None = 0,
@ -64,8 +72,9 @@ namespace
} }
using VAO_Key = std::tuple<const NzIndexBuffer*, const NzVertexBuffer*, const NzVertexDeclaration*, const NzVertexDeclaration*>; using VAO_Key = std::tuple<const NzIndexBuffer*, const NzVertexBuffer*, const NzVertexDeclaration*, const NzVertexDeclaration*>;
using VAO_Map = std::unordered_map<const NzContext*, std::map<VAO_Key, unsigned int>>;
std::unordered_map<NzContext*, std::map<VAO_Key, unsigned int>> s_vaos; VAO_Map s_vaos;
std::set<unsigned int> s_dirtyTextureUnits; std::set<unsigned int> s_dirtyTextureUnits;
std::vector<TextureUnit> s_textureUnits; std::vector<TextureUnit> s_textureUnits;
GLuint s_currentVAO = 0; GLuint s_currentVAO = 0;
@ -89,6 +98,90 @@ namespace
unsigned int s_maxRenderTarget; unsigned int s_maxRenderTarget;
unsigned int s_maxTextureUnit; unsigned int s_maxTextureUnit;
unsigned int s_maxVertexAttribs; unsigned int s_maxVertexAttribs;
class ResourceListener : public NzResourceListener
{
public:
void OnResourceReleased(const NzResource* resource, int index) override
{
switch (index)
{
case ResourceType_Context:
{
const NzContext* context = static_cast<const NzContext*>(resource);
s_vaos.erase(context);
break;
}
case ResourceType_IndexBuffer:
{
const NzIndexBuffer* indexBuffer = static_cast<const NzIndexBuffer*>(resource);
for (auto& pair : s_vaos)
{
auto it = pair.second.begin();
while (it != pair.second.end())
{
const VAO_Key& key = it->first;
const NzIndexBuffer* vaoIndexBuffer = std::get<0>(key);
if (vaoIndexBuffer == indexBuffer)
pair.second.erase(it++);
else
++it;
}
}
break;
}
case ResourceType_VertexBuffer:
{
const NzVertexBuffer* vertexBuffer = static_cast<const NzVertexBuffer*>(resource);
for (auto& pair : s_vaos)
{
auto it = pair.second.begin();
while (it != pair.second.end())
{
const VAO_Key& key = it->first;
const NzVertexBuffer* vaoVertexBuffer = std::get<1>(key);
if (vaoVertexBuffer == vertexBuffer)
pair.second.erase(it++);
else
++it;
}
}
break;
}
case ResourceType_VertexDeclaration:
{
const NzVertexDeclaration* vertexDeclaration = static_cast<const NzVertexDeclaration*>(resource);
for (auto& pair : s_vaos)
{
auto it = pair.second.begin();
while (it != pair.second.end())
{
const VAO_Key& key = it->first;
const NzVertexDeclaration* vaoVertexDeclaration = std::get<2>(key);
const NzVertexDeclaration* vaoInstancingDeclaration = std::get<3>(key);
if (vaoVertexDeclaration == vertexDeclaration || vaoInstancingDeclaration == vertexDeclaration)
pair.second.erase(it++);
else
++it;
}
}
break;
}
default:
NazaraInternalError("Unknown resource type");
break;
}
}
};
ResourceListener s_listener;
} }
void NzRenderer::Clear(nzUInt32 flags) void NzRenderer::Clear(nzUInt32 flags)
@ -1164,12 +1257,33 @@ void NzRenderer::Uninitialize()
// Libération des VAOs // Libération des VAOs
for (auto& pair : s_vaos) for (auto& pair : s_vaos)
{ {
const NzContext* context = pair.first;
context->SetActive(true);
for (auto& pair2 : pair.second) for (auto& pair2 : pair.second)
{ {
const VAO_Key& key = pair2.first;
const NzIndexBuffer* indexBuffer = std::get<0>(key);
const NzVertexBuffer* vertexBuffer = std::get<1>(key);
const NzVertexDeclaration* vertexDeclaration = std::get<2>(key);
const NzVertexDeclaration* instancingDeclaration = std::get<3>(key);
if (indexBuffer)
indexBuffer->RemoveResourceListener(&s_listener);
vertexBuffer->RemoveResourceListener(&s_listener);
vertexDeclaration->RemoveResourceListener(&s_listener);
if (instancingDeclaration)
instancingDeclaration->RemoveResourceListener(&s_listener);
GLuint vao = static_cast<GLuint>(pair2.second); GLuint vao = static_cast<GLuint>(pair2.second);
glDeleteVertexArrays(1, &vao); glDeleteVertexArrays(1, &vao);
} }
context->SetActive(false);
} }
s_vaos.clear(); s_vaos.clear();
NzOpenGL::Uninitialize(); NzOpenGL::Uninitialize();
@ -1256,18 +1370,21 @@ bool NzRenderer::EnsureStateUpdate()
{ {
TextureUnit& unit = s_textureUnits[i]; TextureUnit& unit = s_textureUnits[i];
if (!unit.textureUpdated) if (unit.texture)
{ {
NzOpenGL::BindTextureUnit(i); if (!unit.textureUpdated)
unit.texture->Bind(); {
NzOpenGL::BindTextureUnit(i);
unit.texture->Bind();
unit.textureUpdated = true; unit.textureUpdated = true;
} }
if (!unit.samplerUpdated) if (!unit.samplerUpdated)
{ {
unit.sampler.Bind(i); unit.sampler.Bind(i);
unit.samplerUpdated = true; unit.samplerUpdated = true;
}
} }
} }
} }
@ -1277,13 +1394,16 @@ bool NzRenderer::EnsureStateUpdate()
{ {
TextureUnit& unit = s_textureUnits[i]; TextureUnit& unit = s_textureUnits[i];
NzOpenGL::BindTextureUnit(i); if (unit.texture)
{
NzOpenGL::BindTextureUnit(i);
unit.texture->Bind(); unit.texture->Bind();
unit.textureUpdated = true; unit.textureUpdated = true;
unit.sampler.Apply(unit.texture); unit.sampler.Apply(unit.texture);
unit.samplerUpdated = true; unit.samplerUpdated = true;
}
} }
} }
@ -1324,10 +1444,18 @@ bool NzRenderer::EnsureStateUpdate()
if (s_useVertexArrayObjects) if (s_useVertexArrayObjects)
{ {
// Note: Les VAOs ne sont pas partagés entre les contextes, nous avons donc un tableau de VAOs par contexte // Note: Les VAOs ne sont pas partagés entre les contextes, nous avons donc un tableau de VAOs par contexte
auto& vaos = s_vaos[NzContext::GetCurrent()]; const NzContext* context = NzContext::GetCurrent();
auto pair = s_vaos.insert(std::make_pair(context, VAO_Map::mapped_type()));
if (pair.second)
context->AddResourceListener(&s_listener, ResourceType_Context);
auto& vaos = pair.first->second;
// Notre clé est composée de ce qui définit un VAO // Notre clé est composée de ce qui définit un VAO
VAO_Key key(s_indexBuffer, s_vertexBuffer, s_vertexBuffer->GetVertexDeclaration(), (s_instancing) ? s_instancingDeclaration : nullptr); const NzVertexDeclaration* vertexDeclaration = s_vertexBuffer->GetVertexDeclaration();
const NzVertexDeclaration* instancingDeclaration = (s_instancing) ? s_instancingDeclaration : nullptr;
VAO_Key key(s_indexBuffer, s_vertexBuffer, vertexDeclaration, instancingDeclaration);
// On recherche un VAO existant avec notre configuration // On recherche un VAO existant avec notre configuration
auto it = vaos.find(key); auto it = vaos.find(key);
@ -1339,6 +1467,14 @@ bool NzRenderer::EnsureStateUpdate()
// On l'ajoute à notre liste // On l'ajoute à notre liste
vaos.insert(std::make_pair(key, static_cast<unsigned int>(s_currentVAO))); vaos.insert(std::make_pair(key, static_cast<unsigned int>(s_currentVAO)));
if (s_indexBuffer)
s_indexBuffer->AddResourceListener(&s_listener, ResourceType_IndexBuffer);
s_vertexBuffer->AddResourceListener(&s_listener, ResourceType_VertexBuffer);
vertexDeclaration->AddResourceListener(&s_listener, ResourceType_VertexDeclaration);
if (instancingDeclaration)
instancingDeclaration->AddResourceListener(&s_listener, ResourceType_VertexDeclaration);
// Et on indique qu'on veut le programmer // Et on indique qu'on veut le programmer
update = true; update = true;
@ -1474,6 +1610,25 @@ bool NzRenderer::EnsureStateUpdate()
return true; return true;
} }
void NzRenderer::OnProgramReleased(const NzShaderProgram* program)
{
if (s_program == program)
{
s_program = nullptr;
s_updateFlags |= Update_Program;
}
}
void NzRenderer::OnTextureReleased(const NzTexture* texture)
{
for (TextureUnit& unit : s_textureUnits)
{
if (unit.texture == texture)
unit.texture = nullptr;
// Inutile de changer le flag pour une texture désactivée
}
}
void NzRenderer::UpdateMatrix(nzMatrixType type) void NzRenderer::UpdateMatrix(nzMatrixType type)
{ {
#ifdef NAZARA_DEBUG #ifdef NAZARA_DEBUG

View File

@ -40,6 +40,7 @@ m_compiled(program.m_compiled)
NzShaderProgram::~NzShaderProgram() NzShaderProgram::~NzShaderProgram()
{ {
Destroy(); Destroy();
NzRenderer::OnProgramReleased(this);
} }
bool NzShaderProgram::Create(nzShaderLanguage language) bool NzShaderProgram::Create(nzShaderLanguage language)

View File

@ -164,6 +164,7 @@ NzTexture::NzTexture(const NzImage& image)
NzTexture::~NzTexture() NzTexture::~NzTexture()
{ {
Destroy(); Destroy();
NzRenderer::OnTextureReleased(this);
} }
bool NzTexture::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount) bool NzTexture::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount)