Request help when encountering strange problems about magnum-bootstrap-base-wxwidgets
fenggy opened this issue · 3 comments
I found a strange problem while using the wxwidgets project.When using the default empty project, the program normally has an empty window.
But when I add any Magnum variable (such as GL: Mesh _mesh), An assertion error will occur. as follows
Context& Context::current() {
// An error occurred
CORRADE_ASSERT(currentContext, "GL::Context::current(): no current context", *currentContext);
return *currentContext;
}
I debugged the code and found class Mesh has Constructor function call.
Mesh::Mesh(const MeshPrimitive primitive): _primitive{primitive}, _flags{ObjectFlag::DeleteOnDestruction} {
Context::current().state().mesh.createImplementation(*this);
}
So I moved the create of _glContexts from MainFrame to MyApplication, but still encountered a new initialization error with the following error code
bool Context::tryCreate(const Configuration& configuration) {
//Omit code ......
#ifndef MAGNUM_TARGET_GLES2
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
const auto versionNumberError = Renderer::error();
if(versionNumberError == Renderer::Error::NoError) //If _glContext.create () is called in MainFrame, the logic is normal.
glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
else
#endif
{
#ifndef MAGNUM_TARGET_GLES2
//If _glContext. create() is called in MyApplication, an error occurs here
CORRADE_ASSERT(versionNumberError == Renderer::Error::InvalidEnum,
"GL::Context: cannot retrieve OpenGL version:" << versionNumberError, false);
#endif
//Omit code ......
}
When strange, if _glContext.create (); The code is functioning normally when called in MainFrame.
Novices need your help, thank you
The test code is as follows
#include <Magnum/GL/DefaultFramebuffer.h>
#include <Magnum/Platform/GLContext.h>
#include <Magnum/GL/Buffer.h>
#include <Magnum/GL/Mesh.h>
#include <Magnum/GL/Renderer.h>
#include <Magnum/Math/Color.h>
#include <Magnum/MeshTools/Compile.h>
#include <Magnum/Primitives/Cube.h>
#include <Magnum/SceneGraph/Camera.h>
#include <Magnum/SceneGraph/Drawable.h>
#include <Magnum/SceneGraph/MatrixTransformation3D.h>
#include <Magnum/SceneGraph/Scene.h>
#include <Magnum/Shaders/PhongGL.h>
#include <Magnum/Trade/MeshData.h>
#include <Magnum/Shaders/VertexColorGL.h>
#include <wx/app.h>
#include <wx/frame.h>
#include <wx/glcanvas.h>
#include <wx/sizer.h>
#include <wx/version.h>
using namespace Magnum;
class MyApplication: public wxApp {
public:
MyApplication();
Platform::GLContext _glContext;
bool OnInit();
};
class MainFrame: public wxFrame {
public:
explicit MainFrame(int argc, char** argv);
~MainFrame();
private:
void OnPaint(wxPaintEvent& event);
wxGLCanvas* _wxGlCanvas;
wxGLContext* _wxGlContext;
Platform::GLContext _glContext;
private:
GL::Mesh _mesh;
// Shaders::VertexColorGL2D _shader1;
};
wxIMPLEMENT_APP(MyApplication);
MyApplication::MyApplication():_glContext { NoCreate, argc, argv }
{
}
bool MyApplication::OnInit() {
//_glContext.create();
MainFrame *frame = new MainFrame{argc, argv};
frame->Show(true);
return true;
}
MainFrame::MainFrame(int argc, char** argv): wxFrame{nullptr, wxID_ANY, "Magnum wxWidgets Application"}, _glContext{NoCreate, argc, argv} {
wxBoxSizer* bSizer;
bSizer = new wxBoxSizer{wxVERTICAL};
#if (wxMAJOR_VERSION == 3) && (wxMINOR_VERSION >= 1)
wxGLAttributes attributes;
attributes.PlatformDefaults()
.BufferSize(24)
.MinRGBA(8, 8, 8, 0)
.Depth(24)
.Stencil(0)
.DoubleBuffer()
.EndList();
_wxGlCanvas = new wxGLCanvas{this, attributes, wxID_ANY, wxDefaultPosition, wxSize{800, 600}};
#elif (wxMAJOR_VERSION == 3) && (wxMINOR_VERSION == 0)
int attributes[] = { WX_GL_RGBA,
WX_GL_DOUBLEBUFFER,
WX_GL_DEPTH_SIZE, 24,
WX_GL_STENCIL_SIZE, 0,
0 };
_wxGlCanvas = new wxGLCanvas{this, wxID_ANY, &attributes[0], wxDefaultPosition, wxSize{800, 600}};
#else
#error You need wxWidgets version 3.0 or later.
#endif
_wxGlContext = new wxGLContext{_wxGlCanvas};
Show();
_wxGlCanvas->SetCurrent(*_wxGlContext);
_glContext.create();
bSizer->Add(_wxGlCanvas, 1, wxALL|wxEXPAND, 5);
SetSizer(bSizer);
Layout();
bSizer->Fit(this);
Centre(wxBOTH);
_wxGlCanvas->Connect(wxEVT_PAINT, wxPaintEventHandler(MainFrame::OnPaint), nullptr, this);
/* TODO: Add your initialization code here */
//using namespace Math::Literals;
//struct TriangleVertex {
// Vector2 position;
// Color3 color;
//};
//const TriangleVertex vertices[]{
// {{-0.5f, -0.5f}, 0xff0000_rgbf}, /* Left vertex, red color */
// {{ 0.5f, -0.5f}, 0x00ff00_rgbf}, /* Right vertex, green color */
// {{ 0.0f, 0.5f}, 0x0000ff_rgbf} /* Top vertex, blue color */
//};
//_mesh.setCount(Containers::arraySize(vertices))
// .addVertexBuffer(GL::Buffer{ vertices }, 0,
// Shaders::VertexColorGL2D::Position{},
// Shaders::VertexColorGL2D::Color3{});
}
MainFrame::~MainFrame() {
_wxGlCanvas->Disconnect(wxEVT_PAINT, wxPaintEventHandler(MainFrame::OnPaint), nullptr, this);
}
void MainFrame::OnPaint(wxPaintEvent& event) {
GL::defaultFramebuffer.clear(GL::FramebufferClear::Color);
/* TODO: Add your drawing code here */
// _shader1.draw(_mesh);
_wxGlCanvas->SwapBuffers();
}
`environment
window11 vs2022
Hello!
The root cause here (besides the empty MyApplication constructor and second _glContext instance, which causes another assertion), boils down to the GL::Mesh constructed right during MainFrame construction, while the GL context is only created later, when _glContext.create(); is called. Thus you get an assertion, and the backtrace reveals that it indeed gets accessed too early. Same would happen if you'd uncomment _shader1.
The solution for this is to defer creation of all GL objects until after _glContext.create() is called. One option is to construct the members with NoCreate:
private:
...
Platform::GLContext _glContext;
GL::Mesh _mesh{NoCreate}; // note the NoCreate
Shaders::VertexColorGL2D _shader1{NoCreate}; // here alsoand then explicitly creating them after the GL context is created inside MainFrame::MainFrame():
_glContext.create();
_mesh = GL::Mesh{}; // move actually created objects over these
_shader1 = Shaders::VertexColorGL2D{}; // here alsoBut this gets tedious and error-prone with more GL objects (if you forget to create the NoCreate'd instances, you may get either broken rendering, or a crash, or just anything), so instead I suggest having something like this, putting the objects into a dedicated struct wrapped in an Optional object (or std::optional, if you want) that you construct after _glContext creation:
private:
...
Platform::GLContext _glContext;
struct GLObjects {
GL::Mesh mesh;
Shaders::VertexColorGL2D shader1;
};
Containers::Optional<GLObjects> _glObjects;and then in MainFrame::MainFrame():
_glContext.create();
_glObjects.emplace(); // create all GL objectsThis also gives you an assertion if you accidentally forget to construct the contents, or attempt to access the contents too early, making it easier to discover mistakes.
I'm converting this issue to a discussion in case you have more questions.