fix opengl context checks

This commit is contained in:
Natsirt867
2025-12-14 08:13:43 -06:00
parent 2fe2a2644d
commit 6a4fafae4c
25 changed files with 383 additions and 46 deletions

View File

@@ -2822,6 +2822,8 @@ void MapView::paintGL()
OpenGL::context::scoped_setter const _(::gl, context()); OpenGL::context::scoped_setter const _(::gl, context());
makeCurrent(); makeCurrent();
gl.processCleanup();
gl.clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); gl.clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
{ {
@@ -2944,27 +2946,34 @@ void MapView::resizeGL (int width, int height)
MapView::~MapView() MapView::~MapView()
{ {
makeCurrent(); //makeCurrent();
_destroying = true; _destroying = true;
_main_window->removeToolBar(_main_window->_app_toolbar); _main_window->removeToolBar(_main_window->_app_toolbar);
OpenGL::context::scoped_setter const _ (::gl, context()); //OpenGL::context::scoped_setter const _ (::gl, context());
delete _texBrush;
delete _viewport_overlay_ui; //::gl.processCleanup();
//delete _texBrush;
//delete _viewport_overlay_ui;
// when the uid fix fail the UI isn't created // when the uid fix fail the UI isn't created
if (!_uid_fix_failed) if (!_uid_fix_failed)
{ {
// delete TexturePicker; // explicitly delete this here to avoid opengl context related crash // delete TexturePicker; // explicitly delete this here to avoid opengl context related crash
// delete objectEditor; // delete objectEditor;
// since the ground effect tool preview renderer got added, this causes crashing on exit to menu. // since the ground effect tool preview renderer got added, this causes crashing on exit to menu.
// Now it crashes in application exit. // Now it crashes in application exit.
// delete texturingTool; // delete texturingTool;
if (_tools[static_cast<int>(editing_mode::area_trigger)])
{
_tools[static_cast<int>(editing_mode::area_trigger)]->unload();
}
_tools[static_cast<int>(editing_mode::paint)].reset(); _tools[static_cast<int>(editing_mode::paint)].reset();
_tools[static_cast<int>(editing_mode::object)].reset(); _tools[static_cast<int>(editing_mode::object)].reset();
_tools[static_cast<int>(editing_mode::area_trigger)].reset();
} }
if (_force_uid_check) if (_force_uid_check)
@@ -2978,14 +2987,22 @@ MapView::~MapView()
Noggit::Ui::selected_texture::texture.reset(); Noggit::Ui::selected_texture::texture.reset();
_buffers.unload();
makeCurrent();
OpenGL::context::scoped_setter const _(::gl, context());
::gl.processCleanup();
delete _texBrush;
delete _viewport_overlay_ui;
ModelManager::report(); ModelManager::report();
TextureManager::report(); TextureManager::report();
WMOManager::report(); WMOManager::report();
NOGGIT_ACTION_MGR->disconnect(); NOGGIT_ACTION_MGR->disconnect();
_buffers.unload();
} }
void MapView::tick (float dt) void MapView::tick (float dt)

View File

@@ -322,6 +322,7 @@ private:
QOpenGLContext* _last_opengl_context; QOpenGLContext* _last_opengl_context;
private:
virtual void tabletEvent(QTabletEvent* event) override; virtual void tabletEvent(QTabletEvent* event) override;
virtual void initializeGL() override; virtual void initializeGL() override;
virtual void paintGL() override; virtual void paintGL() override;

View File

@@ -42,8 +42,29 @@ void TextureManager::unload_all(Noggit::NoggitRenderContext context)
for (auto& pair : arrays_for_context) for (auto& pair : arrays_for_context)
{ {
gl.deleteTextures(static_cast<GLuint>(pair.second.arrays.size()), pair.second.arrays.data()); std::vector<GLuint> ids_to_delete = std::move(pair.second.arrays);
GLsizei count = static_cast<GLsizei>(ids_to_delete.size());
if (count == 0)
{
continue;
}
if (QOpenGLContext::currentContext())
{
gl.deleteTextures(count, ids_to_delete.data());
}
else
{
gl.scheduleCleanup([count, ids_to_delete = std::move(ids_to_delete)]() mutable
{
gl.deleteTextures(count, ids_to_delete.data());
}
);
}
} }
arrays_for_context.clear();
} }
TexArrayParams& TextureManager::get_tex_array(int width, int height, int mip_level, TexArrayParams& TextureManager::get_tex_array(int width, int height, int mip_level,

View File

@@ -198,6 +198,8 @@ namespace Noggit
// Save tool-specific settings to disk // Save tool-specific settings to disk
virtual void saveSettings(); virtual void saveSettings();
virtual void unload() {};
protected: protected:
void addHotkey(StringHash name, Hotkey hotkey); void addHotkey(StringHash name, Hotkey hotkey);

View File

@@ -191,8 +191,23 @@ void LiquidRender::updateLayerData(LiquidTextureManager* tex_manager)
{ {
auto& layer_params = _render_layers.back(); auto& layer_params = _render_layers.back();
gl.deleteBuffers(1, &layer_params.chunk_data_buf); GLuint buffer_id = layer_params.chunk_data_buf;
gl.deleteTextures(1, &layer_params.vertex_data_tex); GLuint texture_id = layer_params.vertex_data_tex;
if (QOpenGLContext::currentContext())
{
gl.deleteBuffers(1, &buffer_id);
gl.deleteTextures(1, &texture_id);
}
else
{
gl.scheduleCleanup([buffer_id, texture_id]() mutable
{
gl.deleteBuffers(1, &buffer_id);
gl.deleteTextures(1, &texture_id);
}
);
}
_render_layers.pop_back(); _render_layers.pop_back();
} }
@@ -226,8 +241,24 @@ void LiquidRender::unload()
for (auto& render_layer : _render_layers) for (auto& render_layer : _render_layers)
{ {
gl.deleteBuffers(1, &render_layer.chunk_data_buf); GLuint buffer_id = render_layer.chunk_data_buf;
gl.deleteTextures(1, &render_layer.vertex_data_tex); GLuint texture_id = render_layer.vertex_data_tex;
if (QOpenGLContext::currentContext())
{
gl.deleteBuffers(1, &buffer_id);
gl.deleteTextures(1, &texture_id);
}
else
{
gl.scheduleCleanup([buffer_id, texture_id]() mutable
{
gl.deleteBuffers(1, &buffer_id);
gl.deleteTextures(1, &texture_id);
}
);
}
} }
_render_layers.clear(); _render_layers.clear();

View File

@@ -137,7 +137,19 @@ void LiquidTextureManager::unload()
for (auto& pair : _texture_frames_map) for (auto& pair : _texture_frames_map)
{ {
GLuint array = std::get<0>(pair.second); GLuint array = std::get<0>(pair.second);
gl.deleteTextures(1, &array);
if (QOpenGLContext::currentContext())
{
gl.deleteTextures(1, &array);
}
else
{
gl.scheduleCleanup([array]() mutable
{
gl.deleteTextures(1, &array);
}
);
}
} }
_texture_frames_map.clear(); _texture_frames_map.clear();

View File

@@ -74,7 +74,22 @@ void ModelRender::unload()
_vertex_arrays.unload(); _vertex_arrays.unload();
if (_bone_matrices_buf_tex) if (_bone_matrices_buf_tex)
gl.deleteTextures(1, &_bone_matrices_buf_tex); {
GLuint texture_id = _bone_matrices_buf_tex;
if (QOpenGLContext::currentContext())
{
gl.deleteTextures(1, &texture_id);
}
else
{
gl.scheduleCleanup([texture_id]() mutable
{
gl.deleteTextures(1, &texture_id);
}
);
}
}
for (auto& particle : _model->_particles) for (auto& particle : _model->_particles)
{ {

View File

@@ -50,6 +50,24 @@ void TileRender::unload()
_buffers.unload(); _buffers.unload();
_uploaded = false; _uploaded = false;
gl.deleteQueries(1, &_tile_occlusion_query); gl.deleteQueries(1, &_tile_occlusion_query);
if (_tile_occlusion_query)
{
GLuint query_id = _tile_occlusion_query;
if (QOpenGLContext::currentContext())
{
gl.deleteQueries(1, &query_id);
}
else
{
gl.scheduleCleanup([query_id]() mutable
{
gl.deleteQueries(1, &query_id);
}
);
}
}
} }

View File

@@ -251,7 +251,23 @@ void WMOGroupRender::unload()
_vertex_array.unload(); _vertex_array.unload();
_buffers.unload(); _buffers.unload();
gl.deleteTextures(1, &_render_batch_tex); if (_render_batch_tex)
{
GLuint texture_id = _render_batch_tex;
if (QOpenGLContext::currentContext())
{
gl.deleteTextures(1, &texture_id);
}
else
{
gl.scheduleCleanup([texture_id]() mutable
{
gl.deleteTextures(1, &texture_id);
}
);
}
}
_uploaded = false; _uploaded = false;
_vao_is_setup = false; _vao_is_setup = false;

View File

@@ -56,6 +56,17 @@ namespace Noggit
{ {
} }
void AreaTriggerTool::unload()
{
_boxRenderer.unload();
_sphereRenderer.unload();
if (_editor) {
delete _editor;
_editor = nullptr;
}
}
char const* AreaTriggerTool::name() const char const* AreaTriggerTool::name() const
{ {
return "Area Trigger"; return "Area Trigger";

View File

@@ -52,6 +52,8 @@ namespace Noggit
void saveSettings() override; void saveSettings() override;
void unload() override;
private: private:
Ui::Tools::AreaTriggerEditor* _editor = nullptr; Ui::Tools::AreaTriggerEditor* _editor = nullptr;
Noggit::Rendering::Primitives::WireBox _boxRenderer; Noggit::Rendering::Primitives::WireBox _boxRenderer;

View File

@@ -170,6 +170,16 @@ namespace Noggit
setupTexturePicker(mv); setupTexturePicker(mv);
} }
void TexturingTool::unload()
{
if (_texturingTool)
{
_texturingTool->unload();
}
Tool::unload();
}
void TexturingTool::setupTextureBrowser(MapView* mv) void TexturingTool::setupTextureBrowser(MapView* mv)
{ {
// Dock // Dock

View File

@@ -23,6 +23,8 @@ namespace Noggit
TexturingTool(MapView* mapView); TexturingTool(MapView* mapView);
~TexturingTool(); ~TexturingTool();
void unload() override;
[[nodiscard]] [[nodiscard]]
char const* name() const override; char const* name() const override;

View File

@@ -742,6 +742,12 @@ namespace Noggit
void GroundEffectsTool::delete_renderer() void GroundEffectsTool::delete_renderer()
{ {
delete _preview_renderer; delete _preview_renderer;
_preview_renderer = nullptr;
}
void GroundEffectsTool::unload()
{
delete_renderer();
} }
void GroundEffectsTool::showEvent(QShowEvent* event) void GroundEffectsTool::showEvent(QShowEvent* event)

View File

@@ -82,6 +82,7 @@ namespace Noggit
void updateTerrainUniformParams(); void updateTerrainUniformParams();
// Delete renderer. // Delete renderer.
~GroundEffectsTool(); ~GroundEffectsTool();
void unload();
float radius() const; float radius() const;
ground_effect_brush_mode brush_mode() const; ground_effect_brush_mode brush_mode() const;
bool render_mode() const; bool render_mode() const;

View File

@@ -1025,5 +1025,13 @@ namespace Noggit
style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, this); style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, this);
*/ */
} }
void texturing_tool::unload()
{
if (_ground_effect_tool)
{
_ground_effect_tool->unload();
}
}
} }
} }

View File

@@ -78,6 +78,7 @@ namespace Noggit
~texturing_tool(); // { _ground_effect_tool->deleteLater(); }; // { delete _ground_effect_tool; }; ~texturing_tool(); // { _ground_effect_tool->deleteLater(); }; // { delete _ground_effect_tool; };
void unload();
float brush_radius() const; float brush_radius() const;
float hardness() const; float hardness() const;
bool show_unpaintable_chunks() const; bool show_unpaintable_chunks() const;

View File

@@ -701,9 +701,9 @@ void PreviewRenderer::unloadOpenglData()
return; return;
} }
assert(context() != nullptr); //assert(context() != nullptr);
makeCurrent(); //makeCurrent();
OpenGL::context::scoped_setter const _ (::gl, context()); //OpenGL::context::scoped_setter const _ (::gl, context());
ModelManager::unload_all(_context); ModelManager::unload_all(_context);
WMOManager::unload_all(_context); WMOManager::unload_all(_context);

View File

@@ -600,6 +600,8 @@ namespace Noggit::Ui::Windows
case QMessageBox::AcceptRole: case QMessageBox::AcceptRole:
_stack_widget->setCurrentIndex(0); _stack_widget->setCurrentIndex(0);
_stack_widget->removeLast(); _stack_widget->removeLast();
Noggit::Ui::Tools::ViewportManager::ViewportManager::unloadAll();
delete _map_view; delete _map_view;
_map_view = nullptr; _map_view = nullptr;
_minimap->world(nullptr); _minimap->world(nullptr);
@@ -807,8 +809,16 @@ namespace Noggit::Ui::Windows
progress_box->repaint(); progress_box->repaint();
qApp->processEvents(); qApp->processEvents();
try try
{ {
//if (!clientData->openArchiveForWriting(*archive))
//{
// QMessageBox::warning(this, "Error", "Failed to switch archive to writable mode.");
// progress_box->close();
// return;
//}
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
std::array<int, 2> result = clientData->saveLocalFilesToArchive(archive.value(), mpq_compress_files_chk->isChecked(), mpq_compact_chk->isChecked()); std::array<int, 2> result = clientData->saveLocalFilesToArchive(archive.value(), mpq_compress_files_chk->isChecked(), mpq_compact_chk->isChecked());
@@ -820,9 +830,11 @@ namespace Noggit::Ui::Windows
std::ostringstream oss; // duration in seconds with 1 digit std::ostringstream oss; // duration in seconds with 1 digit
oss << std::fixed << std::setprecision(1) << duration.count(); oss << std::fixed << std::setprecision(1) << duration.count();
// clientData->closeArchiveToReadOnly(*archive);
// if no file was processed, archive was most likely opened and not accessible // if no file was processed, archive was most likely opened and not accessible
// TODO : we can throw an error message in saveLocalFilesToArchive if (!archive->openForWritting()) instead // TODO : we can throw an error message in saveLocalFilesToArchive if (!archive->openForWritting()) instead
if (!processed_files) if (processed_files <= 0)
{ {
QMessageBox::warning(this, "Error", "Project Folder is not a valid directory or client MPQ is not accessible.\ QMessageBox::warning(this, "Error", "Project Folder is not a valid directory or client MPQ is not accessible.\
\nMake sure it isn't opened by Wow or MPQ editor"); \nMake sure it isn't opened by Wow or MPQ editor");
@@ -833,6 +845,26 @@ namespace Noggit::Ui::Windows
} }
/*
catch (const BlizzardArchive::Exceptions::Archive::ArchiveOpenError& e)
{
QMessageBox::critical(this, "Archive Access Error",
QString("Failed to open/close the archive for writing.\nDetails: %1").arg(e.what()));
}
catch (const BlizzardArchive::Exceptions::Archive::FileWriteFailedError& e)
{
QMessageBox::critical(this, "File Write Error",
QString("A file operation failed during saving.\nDetails: %1").arg(e.what()));
}
catch (const std::exception& e)
{
QMessageBox::critical(this, "Standard Exception",
QString("An unexpected standard error occurred: %1").arg(e.what()));
}*/
catch (...) catch (...)
{ {
QMessageBox::critical(nullptr, "Error", "unhandled exception"); QMessageBox::critical(nullptr, "Error", "unhandled exception");

View File

@@ -401,7 +401,23 @@ namespace Noggit
if (!_transform_storage_uploaded) if (!_transform_storage_uploaded)
return; return;
gl.deleteTextures(1, &_m2_instances_transform_buf_tex); if (_m2_instances_transform_buf_tex)
{
GLuint texture_id = _m2_instances_transform_buf_tex;
if (QOpenGLContext::currentContext())
{
gl.deleteTextures(1, &texture_id);
}
else
{
gl.scheduleCleanup([texture_id]() mutable
{
gl.deleteTextures(1, &texture_id);
}
);
}
}
_buffers.unload(); _buffers.unload();
_transform_storage_uploaded = false; _transform_storage_uploaded = false;

View File

@@ -53,4 +53,32 @@ namespace OpenGL
{ {
return _current_context; return _current_context;
} }
void context::scheduleCleanup(std::function<void()> task)
{
QMutexLocker lock(&_cleanup_mutex);
_cleanup_queue.append(task);
}
void context::processCleanup()
{
if (QOpenGLContext::currentContext() != _current_context)
{
// must be called after the context is made current
assert(QOpenGLContext::currentContext() == _current_context);
return;
}
QList<std::function<void()>> tasks_to_process;
{
QMutexLocker lock(&_cleanup_mutex);
_cleanup_queue.swap(tasks_to_process);
}
for (const auto& task : tasks_to_process)
{
task();
}
}
} }

View File

@@ -3,6 +3,9 @@
#pragma once #pragma once
#include <opengl/types.hpp> #include <opengl/types.hpp>
#include <QtGui/QOpenGLFunctions_4_1_Core> #include <QtGui/QOpenGLFunctions_4_1_Core>
#include <QMutex>
#include <QList>
#include <functional>
// NOGGIT_FORCEINLINE ---------------------------------------------// // NOGGIT_FORCEINLINE ---------------------------------------------//
// Macro to use in place of 'inline' to force a function to be inline // Macro to use in place of 'inline' to force a function to be inline
@@ -55,6 +58,12 @@ namespace OpenGL
QOpenGLContext* _current_context = nullptr; QOpenGLContext* _current_context = nullptr;
QOpenGLFunctions_4_1_Core* _4_1_core_func = nullptr; QOpenGLFunctions_4_1_Core* _4_1_core_func = nullptr;
QMutex _cleanup_mutex;
QList<std::function<void()>> _cleanup_queue;
void scheduleCleanup(std::function<void()> task);
void processCleanup();
NOGGIT_FORCEINLINE bool has_extension(std::string const& name); NOGGIT_FORCEINLINE bool has_extension(std::string const& name);
NOGGIT_FORCEINLINE void enable (GLenum); NOGGIT_FORCEINLINE void enable (GLenum);

View File

@@ -101,7 +101,7 @@ namespace OpenGL
private: private:
bool _buffer_generated = false; bool _buffer_generated = false;
GLuint _buffers[count]; GLuint _buffers[count] = { 0 };
}; };
template<std::size_t count> template<std::size_t count>
@@ -139,7 +139,7 @@ namespace OpenGL
private: private:
bool _buffer_generated = false; bool _buffer_generated = false;
GLuint _vertex_arrays[count]; GLuint _vertex_arrays[count] = { 0 };
}; };
template<std::size_t count> template<std::size_t count>
@@ -160,7 +160,7 @@ namespace OpenGL
private: private:
bool _texture_generated = false; bool _texture_generated = false;
GLuint _textures[count]; GLuint _textures[count] = { 0 };
}; };
template<std::size_t count> template<std::size_t count>

View File

@@ -139,17 +139,38 @@ namespace OpenGL
template<std::size_t count> template<std::size_t count>
void deferred_upload_buffers<count>::unload() void deferred_upload_buffers<count>::unload()
{ {
gl.deleteBuffers(count, _buffers); for (int i = 0; i < count; ++i)
_buffer_generated = false; {
GLuint& buffer_id = _buffers[i];
if (buffer_id == 0)
{
continue;
}
if (QOpenGLContext::currentContext() && ::gl.getCurrentContext() != nullptr)
{
gl.deleteBuffers(1, &buffer_id);
}
else
{
gl.scheduleCleanup([buffer_id]() mutable
{
gl.deleteBuffers(1, &buffer_id);
}
);
}
buffer_id = 0;
}
_buffer_generated = false;
} }
template<std::size_t count> template<std::size_t count>
deferred_upload_buffers<count>::~deferred_upload_buffers() deferred_upload_buffers<count>::~deferred_upload_buffers()
{ {
if (_buffer_generated) unload();
{
gl.deleteBuffers (count, _buffers);
}
} }
template<std::size_t count> template<std::size_t count>
@@ -186,8 +207,31 @@ namespace OpenGL
template<std::size_t count> template<std::size_t count>
void deferred_upload_vertex_arrays<count>::unload() void deferred_upload_vertex_arrays<count>::unload()
{ {
gl.deleteVertexArray(count, _vertex_arrays); for (int i = 0; i < count; ++i)
_buffer_generated = false; {
GLuint& vertex_array_id = _vertex_arrays[i];
if (vertex_array_id == 0)
{
continue;
}
if (QOpenGLContext::currentContext() && ::gl.getCurrentContext() != nullptr)
{
gl.deleteVertexArray(1, &vertex_array_id);
}
else
{
gl.scheduleCleanup([vertex_array_id]() mutable
{
gl.deleteVertexArray(1, &vertex_array_id);
}
);
}
vertex_array_id = 0;
}
_buffer_generated = false;
} }
template<std::size_t count> template<std::size_t count>
@@ -195,7 +239,7 @@ namespace OpenGL
{ {
if (_buffer_generated) if (_buffer_generated)
{ {
gl.deleteVertexArray (count, _vertex_arrays); unload();
} }
} }
@@ -222,17 +266,38 @@ namespace OpenGL
template<std::size_t count> template<std::size_t count>
void deferred_upload_textures<count>::unload() void deferred_upload_textures<count>::unload()
{ {
gl.deleteTextures(count, _textures); for (int i = 0; i < count; ++i)
_texture_generated = false; {
GLuint& texture_id = _textures[i];
if (texture_id == 0)
{
continue;
}
if (QOpenGLContext::currentContext() && ::gl.getCurrentContext() != nullptr)
{
gl.deleteTextures(1, &texture_id);
}
else
{
gl.scheduleCleanup([texture_id]() mutable
{
gl.deleteTextures(1, &texture_id);
}
);
}
texture_id = 0;
}
_texture_generated = false;
} }
template<std::size_t count> template<std::size_t count>
deferred_upload_textures<count>::~deferred_upload_textures() deferred_upload_textures<count>::~deferred_upload_textures()
{ {
if (_texture_generated) unload();
{
gl.deleteTextures(count, _textures);
}
} }
template<std::size_t count> template<std::size_t count>

View File

@@ -121,7 +121,20 @@ namespace OpenGL
{ {
if (_handle) if (_handle)
{ {
gl.deleteProgram (*_handle); if (QOpenGLContext::currentContext() && ::gl.getCurrentContext() != nullptr)
{
gl.deleteProgram(*_handle);
}
/*else
{
GLuint id = *_handle;
gl.scheduleCleanup([id]()
{
gl.deleteProgram(id);
}
);
}*/
} }
} }