How to setup wxGLContext properly? Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 466
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: How to setup wxGLContext properly?

Post by New Pagodi »

I've tried pulling everything out into a separate class like you did. This works for me on both windows and linux

Code: Select all

#include "wx/wx.h"

#include <wx/glcanvas.h>
#include "glhelper.h"

class WaveformViewer:public wxPanel
{
public:
    WaveformViewer(wxWindow* window);
    ~WaveformViewer();

private:
    void OnCanvasPaint(wxPaintEvent&);
    void OnCanvasSize(wxSizeEvent&);

    void InitGL();

    wxGLCanvas* m_canvas;
    wxGLContext* m_context;
    GLHelper m_helper;
};

WaveformViewer::WaveformViewer(wxWindow* window)
               :wxPanel(window, wxID_ANY, wxDefaultPosition, wxDefaultSize,
                        wxTAB_TRAVERSAL | wxNO_BORDER |
                        wxFULL_REPAINT_ON_RESIZE)
{
    // Create the canvas and context.
    #if wxCHECK_VERSION(3,1,0)
        // These settings should work with any GPU from the last 10 years.
        wxGLAttributes dispAttrs;
        dispAttrs.PlatformDefaults().RGBA().DoubleBuffer().EndList();

        wxGLContextAttrs cxtAttrs;
        cxtAttrs.PlatformDefaults().CoreProfile().OGLVersion(3, 3).EndList();

        m_canvas = new wxGLCanvas(this, dispAttrs);
        m_context = new wxGLContext(m_canvas, NULL, &cxtAttrs);

        if ( !m_context->IsOK() )
        {
            wxLogDebug("Failed to create context.");
            return;

        }
    #else
        int dispAttrs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_CORE_PROFILE,
                            WX_GL_MAJOR_VERSION ,3, WX_GL_MINOR_VERSION, 3, 0 };

        m_canvas = new wxGLCanvas(this, wxID_ANY, dispAttrs);
        m_context = new wxGLContext(m_canvas, NULL);

        // Unfortunately, there doesn't seem to be any way to check if the
        // context is ok prior to wxWidgets 3.1.0.
    #endif // wxCHECK_VERSION

    wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
    szr->Add(m_canvas, wxSizerFlags(1).Expand());
    SetSizer(szr);

    // On Linux, we must delay delay initialization until the canvas has
    // been full created.  On windows, we can finish now.
    #ifdef __WXMSW__
        InitGL();
    #elif defined(__WXGTK__)
        m_canvas->Bind(wxEVT_CREATE, [this](wxWindowCreateEvent&){InitGL();});
    #endif // defined
}

WaveformViewer::~WaveformViewer()
{
    delete m_context;
}

void WaveformViewer::OnCanvasSize(wxSizeEvent& event)
{
    wxSize sz = event.GetSize();
    m_helper.SetSize(sz.GetWidth(), sz.GetHeight());
    event.Skip();
}

void WaveformViewer::OnCanvasPaint(wxPaintEvent&)
{
    wxPaintDC dc(m_canvas);

    m_helper.Render();
    m_canvas->SwapBuffers();
}

void WaveformViewer::InitGL()
{
    // First call SetCurrent or GL initialization will fail.
    m_context->SetCurrent(*m_canvas);

    // Initialize GLEW.
    bool glewInialized = m_helper.InitGlew();

    if ( !glewInialized )
    {
        wxLogDebug("Failed it initialize GLEW.");
        return;
    }

    // Bind event handlers for the canvas. Binding was delayed until OpenGL was
    // initialized because these handlers will need to call OpenGL functions.
    m_canvas->Bind(wxEVT_SIZE, &WaveformViewer::OnCanvasSize, this);
    m_canvas->Bind(wxEVT_PAINT, &WaveformViewer::OnCanvasPaint, this);
}

class wxGlewFrame: public wxFrame
{
    public:
        wxGlewFrame(wxWindow*);
};

wxGlewFrame::wxGlewFrame(wxWindow* parent)
            :wxFrame(parent,  wxID_ANY, wxString())
{
    new WaveformViewer(this);
}

class MyApp : public wxApp
{
    public:
        virtual bool OnInit()
        {
            wxGlewFrame* frame = new wxGlewFrame(NULL);
            frame->Show();
            return true;
        }
};

wxIMPLEMENT_APP(MyApp);
Can you see if it works for you. If it works, I guess start adding the extra items from your class and see if any of them cause glew initialization to fail.

Otherwise, I guess you could try debugging into the glewInit call to see where it fails or try the second answer from here to get a string description of why initialization is failing.

Edit: another thing to check is to see if the SetCurrent call is failing. Maybe add something like this to the InitGL method

Code: Select all

    
    bool current = m_context->SetCurrent(*m_canvas);
    
    if ( !current )
    {
        wxLogDebug("Unable to set context current.");
        return;
    }
    }
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to setup wxGLContext properly?

Post by apoorv569 »

New Pagodi wrote: Sun Oct 17, 2021 8:16 pm I've tried pulling everything out into a separate class like you did. This works for me on both windows and linux

Code: Select all

#include "wx/wx.h"

#include <wx/glcanvas.h>
#include "glhelper.h"

class WaveformViewer:public wxPanel
{
public:
    WaveformViewer(wxWindow* window);
    ~WaveformViewer();

private:
    void OnCanvasPaint(wxPaintEvent&);
    void OnCanvasSize(wxSizeEvent&);

    void InitGL();

    wxGLCanvas* m_canvas;
    wxGLContext* m_context;
    GLHelper m_helper;
};

WaveformViewer::WaveformViewer(wxWindow* window)
               :wxPanel(window, wxID_ANY, wxDefaultPosition, wxDefaultSize,
                        wxTAB_TRAVERSAL | wxNO_BORDER |
                        wxFULL_REPAINT_ON_RESIZE)
{
    // Create the canvas and context.
    #if wxCHECK_VERSION(3,1,0)
        // These settings should work with any GPU from the last 10 years.
        wxGLAttributes dispAttrs;
        dispAttrs.PlatformDefaults().RGBA().DoubleBuffer().EndList();

        wxGLContextAttrs cxtAttrs;
        cxtAttrs.PlatformDefaults().CoreProfile().OGLVersion(3, 3).EndList();

        m_canvas = new wxGLCanvas(this, dispAttrs);
        m_context = new wxGLContext(m_canvas, NULL, &cxtAttrs);

        if ( !m_context->IsOK() )
        {
            wxLogDebug("Failed to create context.");
            return;

        }
    #else
        int dispAttrs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_CORE_PROFILE,
                            WX_GL_MAJOR_VERSION ,3, WX_GL_MINOR_VERSION, 3, 0 };

        m_canvas = new wxGLCanvas(this, wxID_ANY, dispAttrs);
        m_context = new wxGLContext(m_canvas, NULL);

        // Unfortunately, there doesn't seem to be any way to check if the
        // context is ok prior to wxWidgets 3.1.0.
    #endif // wxCHECK_VERSION

    wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
    szr->Add(m_canvas, wxSizerFlags(1).Expand());
    SetSizer(szr);

    // On Linux, we must delay delay initialization until the canvas has
    // been full created.  On windows, we can finish now.
    #ifdef __WXMSW__
        InitGL();
    #elif defined(__WXGTK__)
        m_canvas->Bind(wxEVT_CREATE, [this](wxWindowCreateEvent&){InitGL();});
    #endif // defined
}

WaveformViewer::~WaveformViewer()
{
    delete m_context;
}

void WaveformViewer::OnCanvasSize(wxSizeEvent& event)
{
    wxSize sz = event.GetSize();
    m_helper.SetSize(sz.GetWidth(), sz.GetHeight());
    event.Skip();
}

void WaveformViewer::OnCanvasPaint(wxPaintEvent&)
{
    wxPaintDC dc(m_canvas);

    m_helper.Render();
    m_canvas->SwapBuffers();
}

void WaveformViewer::InitGL()
{
    // First call SetCurrent or GL initialization will fail.
    m_context->SetCurrent(*m_canvas);

    // Initialize GLEW.
    bool glewInialized = m_helper.InitGlew();

    if ( !glewInialized )
    {
        wxLogDebug("Failed it initialize GLEW.");
        return;
    }

    // Bind event handlers for the canvas. Binding was delayed until OpenGL was
    // initialized because these handlers will need to call OpenGL functions.
    m_canvas->Bind(wxEVT_SIZE, &WaveformViewer::OnCanvasSize, this);
    m_canvas->Bind(wxEVT_PAINT, &WaveformViewer::OnCanvasPaint, this);
}

class wxGlewFrame: public wxFrame
{
    public:
        wxGlewFrame(wxWindow*);
};

wxGlewFrame::wxGlewFrame(wxWindow* parent)
            :wxFrame(parent,  wxID_ANY, wxString())
{
    new WaveformViewer(this);
}

class MyApp : public wxApp
{
    public:
        virtual bool OnInit()
        {
            wxGlewFrame* frame = new wxGlewFrame(NULL);
            frame->Show();
            return true;
        }
};

wxIMPLEMENT_APP(MyApp);
Can you see if it works for you. If it works, I guess start adding the extra items from your class and see if any of them cause glew initialization to fail.

Otherwise, I guess you could try debugging into the glewInit call to see where it fails or try the second answer from here to get a string description of why initialization is failing.

Edit: another thing to check is to see if the SetCurrent call is failing. Maybe add something like this to the InitGL method

Code: Select all

    
    bool current = m_context->SetCurrent(*m_canvas);
    
    if ( !current )
    {
        wxLogDebug("Unable to set context current.");
        return;
    }
    }
Ok I tried commenting out all the code except related to GL/GLEW, I am still getting same error, I also added the check for current context, this seems to throw error,

Code: Select all

    if (!m_Context->SetCurrent(*m_Canvas))
        wxLogDebug("Error! Unable to set context current.");
I also added.

Code: Select all

bool GLHelper::InitGlew()
{
    GLenum initStatus = glewInit();

    wxLogDebug("Error: %s", (wxString)glewGetErrorString(initStatus));

    return initStatus == GLEW_OK;
}
So I get these errors,

Code: Select all

10:32:53: Debug: Error! Unable to set context current.
10:32:53: Debug: Error: Missing GL version
10:32:53: Debug: Failed it initialize GLEW.
However the code you shared above with the WaveformViewer class works for me as well. I guess there is something in between my app that is causing the error.
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 466
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: How to setup wxGLContext properly?

Post by New Pagodi »

I'm not sure why calling 'SetCurrent' can fail. Can you try to debug into that call and see why it's returning false?

As a guess, instead of calling InitGlew() in WaveformViewer's constructor, you'll have to wait and have the frame class call it when the WaveformViewer panel is visible and ready to begin drawing. I'm not sure how your frame can detect when that is the case. To keep things from looking ugly until OpenGL is initialized, you can initially attach a temporary paint handler for the canvas that will draw a blank wave from and then after GL is initialized, detach that paint handler and bind the one that calls the Render method.
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to setup wxGLContext properly?

Post by apoorv569 »

New Pagodi wrote: Mon Oct 18, 2021 4:43 pm I'm not sure why calling 'SetCurrent' can fail. Can you try to debug into that call and see why it's returning false?

As a guess, instead of calling InitGlew() in WaveformViewer's constructor, you'll have to wait and have the frame class call it when the WaveformViewer panel is visible and ready to begin drawing. I'm not sure how your frame can detect when that is the case. To keep things from looking ugly until OpenGL is initialized, you can initially attach a temporary paint handler for the canvas that will draw a blank wave from and then after GL is initialized, detach that paint handler and bind the one that calls the Render method.
I tried calling InitGL() from the MainFrame, but I get the same error. I also tried making another paint handler and binded 1 in the constructor like this,

Code: Select all

    // On Linux, we must delay delay initialization until the canvas has
    // been full created.  On windows, we can finish now.
    #ifdef __WXMSW__
        InitGL();
    #elif defined(__WXGTK__)
        m_Canvas->Bind(wxEVT_CREATE, [this](wxWindowCreateEvent&){InitGL();});
    #endif // defined

    // this->SetDoubleBuffered(true);

    if (!m_Helper.InitGlew())
        m_Canvas->Bind(wxEVT_PAINT, &WaveformViewer::OnPaint, this);

Code: Select all

void WaveformViewer::OnPaint(wxPaintEvent& event)
{
    wxPaintDC dc(m_Canvas);

    dc.SetPen(wxPen(wxColour(255, 0, 0, 255), 10, wxPENSTYLE_SOLID));
    dc.SetBrush(wxBrush(wxColour(255, 0, 0, 255), wxBRUSHSTYLE_SOLID));
    dc.DrawPoint(0, 0);
}
and the other one in the InitGL(),

Code: Select all

void WaveformViewer::InitGL()
{
    // First call SetCurrent or GL initialization will fail.
    if (!m_Context->SetCurrent(*m_Canvas))
        wxLogDebug("Error! Unable to set context current.");

    // Initialize GLEW.
    bool glewInialized = m_Helper.InitGlew();

    if ( !glewInialized )
    {
        wxLogDebug("Failed it initialize GLEW.");
        return;
    }
    else
    {
        m_Canvas->Unbind(wxEVT_PAINT, &WaveformViewer::OnPaint, this);

        // Bind event handlers for the canvas. Binding was delayed until OpenGL was
        // initialized because these handlers will need to call OpenGL functions.
        m_Canvas->Bind(wxEVT_SIZE, &WaveformViewer::OnSize, this);
        m_Canvas->Bind(wxEVT_PAINT, &WaveformViewer::OnGLPaint, this);
    }

Code: Select all

void WaveformViewer::OnGLPaint(wxPaintEvent& event)
{
    wxPaintDC dc(m_Canvas);

    m_Helper.Render();
    m_Canvas->SwapBuffers();
}
but I still get the same error.

However I switched to wx version 3.0.5, and the error goes away, and I can see the canvas but it appears very small on top left corner, here is a screenshot,

Image

I did not change any other code. I guess then there is something wrong in the construction of the canvas or context for the wx 3.1.x version, or maybe I'm missing some library or wx module? that I also need to link?
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 466
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: How to setup wxGLContext properly?

Post by New Pagodi »

I think that's the default minimum size for a window. Since the sizers should resize the canvas to fill up the whole space, maybe there is a missing layout call for the waveform viewer panel.
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to setup wxGLContext properly?

Post by apoorv569 »

New Pagodi wrote: Mon Oct 18, 2021 7:48 pm I think that's the default minimum size for a window. Since the sizers should resize the canvas to fill up the whole space, maybe there is a missing layout call for the waveform viewer panel.
Calling the Layout makes the canvas even small, don't know if you can see it, maybe you have zoom in to see its a very tiny pixel,

Image

Code: Select all

    m_CanvasSizer->Add(m_Canvas, wxSizerFlags(1).Expand().Border(wxALL));

    this->SetSizer(m_CanvasSizer);
    m_CanvasSizer->Layout();
but if I add these 2 Layout calls,

Code: Select all

    m_Canvas->Layout();
    this->Layout();
then I still see the tiny square on top left corner,

Image

Also I don't want to use wx 3.0.5 version, I only recently switched to the wx 3.1.x and I want to use that only.
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 466
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: How to setup wxGLContext properly?

Post by New Pagodi »

My guess was that a wxGLCanvas can not be set current if it is to small, but I was able to get both the context and GLEW to initialize with small sizes like 2x2 and even 0x0 on both windows and linux. So it doesn't seem like the size is the issue after all (although that may depend on the system and GPU).

Regardless, it seems that there are some layout issues in the app. Maybe try temporarily replacing the canvas with something distinct like a lime green panel. Then fix the layout so that the replacement is sized correctly. Then bring the canvas back and see if GLEW will initialize when the canvas is sized correctly. If that still doesn't work, I'm out of ideas.
Manolo
Can't get richer than this
Can't get richer than this
Posts: 827
Joined: Mon Apr 30, 2012 11:07 pm

Re: How to setup wxGLContext properly?

Post by Manolo »

My guess was that a wxGLCanvas can not be set current if it is to small
No, that's not the issue.

The problem is that in Linux you can't call SetCurrent() before the window is realized.
There's a "realized-event" in wxGTK, but it makes no sense for MSW. So we need another way, multiplatform available.
And it's the size of the window, plus the IsShownOnScreen() check.

In Linux some size must be set before some other initializations, so 10x10 pixels is used (or any other tiny size that doesn't need many kBs).
When the window is realized then the true size is set, and a size-event is posted.
And in LInux, if I'm not wrong or alike, a hidden window can't comunicate with OGL hardware; so we need the IsShownOnScreen() check.

This way is what opengl/pyramid sample uses. See https://github.com/wxWidgets/wxWidgets/ ... yramid.cpp
Pay attention MyGLCanvas::OnSize, MyGLCanvas::OnPaint and MyGLCanvas::oglInit()
oglInit() can't work if we don't have a valid context (which you can create at your window ctor). And it calls SetCurrent(), and then tries to init the OGL stuff (glew, in your case).
This oglInit() is called only once from OnSize.

The work at OnSize is not just initilizate OGL, but also setting the glViewport: the OGL area to draw in, normally same as the phisical size of the window (that's why GetContentScaleFactor() is used, a DPI-scale matter).

SetCurrent() is used several times. Well, normally calling it only once is enough. The reason for the sample to procceed so is more a good-style way than a necessity.

Summarizing:
Check your scrren attributes before creating a wxGLCanvas.
Create a wxGLContext at yourwindow constructor. For example, see MyGLCanvas ctor. Don't forget to check mycontex.IsOk().
Handle size-event and paint-event (New Pagodi: In my opinion create-event is not enough in Linux)
At you size-event handler check for any size >0 and check IsShownOnScreen(). When both conditions are true, call your ogl-init (glew stuff)
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to setup wxGLContext properly?

Post by apoorv569 »

New Pagodi wrote: Tue Oct 19, 2021 6:06 pm My guess was that a wxGLCanvas can not be set current if it is to small, but I was able to get both the context and GLEW to initialize with small sizes like 2x2 and even 0x0 on both windows and linux. So it doesn't seem like the size is the issue after all (although that may depend on the system and GPU).

Regardless, it seems that there are some layout issues in the app. Maybe try temporarily replacing the canvas with something distinct like a lime green panel. Then fix the layout so that the replacement is sized correctly. Then bring the canvas back and see if GLEW will initialize when the canvas is sized correctly. If that still doesn't work, I'm out of ideas.
I tried making another panel and setting color of different widgets differently, it didn't help and I could not get it to stretch to full panel size.

Code: Select all

    wxPanel* panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);

    this->SetBackgroundColour(wxColour(0, 255, 0, 255));
    // this->SetBackgroundColour(wxColour(0, 0, 255, 255));
    panel->SetBackgroundColour(wxColour(0, 0, 255, 255));

    wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);

    sizer->Add(panel, 1, wxEXPAND | wxALL, 50);

    this->SetSizer(sizer);
    sizer->Fit(this);
    sizer->SetSizeHints(this);
    this->Layout();

    sizer->Layout();
Manolo wrote: Tue Oct 19, 2021 6:53 pm
My guess was that a wxGLCanvas can not be set current if it is to small
No, that's not the issue.

The problem is that in Linux you can't call SetCurrent() before the window is realized.
There's a "realized-event" in wxGTK, but it makes no sense for MSW. So we need another way, multiplatform available.
And it's the size of the window, plus the IsShownOnScreen() check.

In Linux some size must be set before some other initializations, so 10x10 pixels is used (or any other tiny size that doesn't need many kBs).
When the window is realized then the true size is set, and a size-event is posted.
And in LInux, if I'm not wrong or alike, a hidden window can't comunicate with OGL hardware; so we need the IsShownOnScreen() check.

This way is what opengl/pyramid sample uses. See https://github.com/wxWidgets/wxWidgets/ ... yramid.cpp
Pay attention MyGLCanvas::OnSize, MyGLCanvas::OnPaint and MyGLCanvas::oglInit()
oglInit() can't work if we don't have a valid context (which you can create at your window ctor). And it calls SetCurrent(), and then tries to init the OGL stuff (glew, in your case).
This oglInit() is called only once from OnSize.

The work at OnSize is not just initilizate OGL, but also setting the glViewport: the OGL area to draw in, normally same as the phisical size of the window (that's why GetContentScaleFactor() is used, a DPI-scale matter).

SetCurrent() is used several times. Well, normally calling it only once is enough. The reason for the sample to procceed so is more a good-style way than a necessity.

Summarizing:
Check your scrren attributes before creating a wxGLCanvas.
Create a wxGLContext at yourwindow constructor. For example, see MyGLCanvas ctor. Don't forget to check mycontex.IsOk().
Handle size-event and paint-event (New Pagodi: In my opinion create-event is not enough in Linux)
At you size-event handler check for any size >0 and check IsShownOnScreen(). When both conditions are true, call your ogl-init (glew stuff)
I could not get the canvas to stretch to full parent window size no matter what I tried. So I ended up changing my WaveformViewer class inherit from wxGLCanvas it self rather then wxPanel, and made these changes,

In the constructor I moved the Bind() for OnPaint and OnSize here,

Code: Select all

WaveformViewer::WaveformViewer(wxWindow* parentFrame, wxWindow* window, const wxGLAttributes& attrs,
                               wxDataViewListCtrl& library, wxMediaCtrl& mediaCtrl, Database& database,
                               const std::string& configFilepath, const std::string& databaseFilepath)
    : wxGLCanvas(window, attrs),
    // : wxPanel(window, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxFULL_REPAINT_ON_RESIZE),
      m_ParentFrame(parentFrame), m_Window(window), m_Database(database), m_Library(library), m_MediaCtrl(mediaCtrl),
      m_ConfigFilepath(configFilepath), m_DatabaseFilepath(databaseFilepath)
{
    // Create the canvas and context.
    #if wxCHECK_VERSION(3, 1, 0)
        // These settings should work with any GPU from the last 10 years.
        // wxGLAttributes display_attributes;
        // display_attributes.PlatformDefaults().RGBA().DoubleBuffer().EndList();

        wxGLContextAttrs context_attributes;
        context_attributes.PlatformDefaults().CoreProfile().OGLVersion(3, 3).EndList();

        // this = new wxGLCanvas(this, display_attributes);
        m_Context = new wxGLContext(this, NULL, &context_attributes);

        if (!m_Context->IsOK())
        {
            wxLogDebug("Failed to create context.");
            return;
        }
    #else
        // int display_attributes[] =
        //     { WX_GL_RGBA,
        //       WX_GL_DOUBLEBUFFER,
        //       WX_GL_CORE_PROFILE,
        //       WX_GL_MAJOR_VERSION ,3,
        //       WX_GL_MINOR_VERSION, 3,
        //       0 };

        // this = new wxGLCanvas(this, wxID_ANY, display_attributes);
        m_Context = new wxGLContext(this, NULL);

        // Unfortunately, there doesn't seem to be any way to check if the
        // context is ok prior to wxWidgets 3.1.0.
    #endif // wxCHECK_VERSION

    // On Linux, we must delay delay initialization until the canvas has
    // been fully created.  On windows, we can finish now.
    #ifdef __WXMSW__
        InitGL();
    #elif defined(__WXGTK__)
        this->Bind(wxEVT_CREATE, [this](wxWindowCreateEvent&){ InitGL(); });
    #endif // defined

    this->Bind(wxEVT_SIZE, &WaveformViewer::OnSize, this);
    this->Bind(wxEVT_PAINT, &WaveformViewer::OnPaint, this);

    Bind(wxEVT_MOTION, &WaveformViewer::OnMouseMotion, this);
    Bind(wxEVT_LEFT_DOWN, &WaveformViewer::OnMouseLeftButtonDown, this);
    Bind(wxEVT_LEFT_UP, &WaveformViewer::OnMouseLeftButtonUp, this);
    Bind(wxEVT_KEY_UP, &WaveformViewer::OnControlKeyUp, this);
}
the OnSize event handler,

Code: Select all

void WaveformViewer::OnSize(wxSizeEvent &event)
{
    event.Skip();

    // If this window is not fully initialized, dismiss this event
    if (!IsShownOnScreen())
    {
        wxLogDebug("Error! Not shown on screen");
        return;
    }

    //Now we have a context, retrieve pointers to OGL functions
    if (!m_Helper.InitGlew())
    {
        wxLogDebug("Error! GLEW not initialized");
        return;
    }

    PostSizeEvent();

    // First call SetCurrent or GL initialization will fail.
    if (!m_Context->SetCurrent(*this))
        wxLogDebug("Error from %s! Unable to set the context", __FUNCTION__);

    wxSize sz = event.GetSize();
    m_Helper.SetSize(sz.x, sz.y);

    Refresh(false);
}
the OnPaint event handler,

Code: Select all

void WaveformViewer::OnPaint(wxPaintEvent& event)
{
    wxPaintDC dc(this);

    if (!m_Context->SetCurrent(*this))
        wxLogDebug("Error from %s! Unable to set the context", __FUNCTION__);

    m_Helper.Render();
    this->SwapBuffers();
}
and the InitGL(),

Code: Select all

void WaveformViewer::InitGL()
{
    // First call SetCurrent or GL initialization will fail.
    // if (!m_Context->SetCurrent(*this))
    //     wxLogDebug("Error! Unable to set the context");

    // Initialize GLEW.
    bool glewInialized = m_Helper.InitGlew();

    if (!glewInialized)
    {
        wxLogDebug("Error from %s! Failed to initialize GLEW.", __FUNCTION__);
        return;
    }

    wxLogDebug("Context and GLEW initialized.");

    // Bind event handlers for the canvas. Binding was delayed until OpenGL was
    // initialized because these handlers will need to call OpenGL functions.
    // this->Bind(wxEVT_SIZE, &WaveformViewer::OnSize, this);
    // this->Bind(wxEVT_PAINT, &WaveformViewer::OnPaint, this);
}
now when I open the app I do get the green color appear from the GL Render function,

Image

but I also get these errors,

Code: Select all

18:15:34: Debug: Error! Not shown on screen
18:15:34: Debug: Error! Not shown on screen
18:15:34: Debug: Error! Not shown on screen
18:15:34: Debug: Error! Not shown on screen
18:15:34: Debug: Error from InitGL! Failed to initialize GLEW.
18:15:34: Debug: Error! GLEW not initialized
I'm not a 100% sure this will be stable enough to use though. There is definitely room for improvement here.

BTW I also got the same code working with wx 3.1.x.
Manolo
Can't get richer than this
Can't get richer than this
Posts: 827
Joined: Mon Apr 30, 2012 11:07 pm

Re: How to setup wxGLContext properly?

Post by Manolo »

I think you are mixing some concepts.
If you wish you GL drawing to be shown in the whole client part of the main frame (or other frame) then no intermediate window (eg.wxPanel) is needed. Just use a wxGLCanvas as child of that frame:

Code: Select all

class MyViewer: public wxGLCanvas
{...}

MyViewer::MyViewer(MyFrame* parent,  ...)
{
  check display attributes
  create context, check it's ok
  bind handlers for size/paint events
  bind others
...
}
If instead of an only child you want more windows (or controls, because a control is a window) in the client area of a frame then use a wxPanel as child of the frame. Now MyViewer is still of type wxGLCanvas, but has that wxPanel as parent (instead of the frame); the other controls are also child of the panel.
You better use wxSizers to manage the layout of all childs of the panel.

To make the GL window fill its available area you need to use that size-handler you bound at MyViewer ctor.

Code: Select all

void MyViewer ::OnSize(wxSizeEvent& event)
{
    event.Skip();
   ...

  // call your glew initalization helper
  isok= InitGL();
  ...
  
  //handle new size of this window
  const wxSize size = event.GetSize() * GetContentScaleFactor();
  m_Handler->SetViewport(0, 0, size.x, size.y); // m_Handler just call glViewPort()

}
GetContentScaleFactor() is available since wx 3.1.4

I notice two marks in your last code:
A) You have two functions for GL initialization: WaveformViewer::InitGL() and Helpr::InitGlew()
This is going to get messy. I'd move InitGlew() inside InitGL() function.

B) For GL context > 2.1 (better use CoreProfile and GL ver >= 3.2) Glew needs glewExperimental

Code: Select all

bool MyViewer::InitGL()
{
    if ( !m_oglContext )
        return false;

    // The current context must be set before we get OGL pointers
    SetCurrent(*m_oglContext);

    //Glew initialization
    glewExperimental = GL_TRUE;
    if ( GLEW_OK != glewInit() )
    {
        // Problem: glewInit failed, something is seriously wrong.
        return false;
    }
   ....
}
apoorv569
Super wx Problem Solver
Super wx Problem Solver
Posts: 426
Joined: Tue Oct 20, 2020 3:35 pm

Re: How to setup wxGLContext properly?

Post by apoorv569 »

Manolo wrote: Wed Oct 20, 2021 7:31 pm I think you are mixing some concepts.
If you wish you GL drawing to be shown in the whole client part of the main frame (or other frame) then no intermediate window (eg.wxPanel) is needed. Just use a wxGLCanvas as child of that frame:

Code: Select all

class MyViewer: public wxGLCanvas
{...}

MyViewer::MyViewer(MyFrame* parent,  ...)
{
  check display attributes
  create context, check it's ok
  bind handlers for size/paint events
  bind others
...
}
If instead of an only child you want more windows (or controls, because a control is a window) in the client area of a frame then use a wxPanel as child of the frame. Now MyViewer is still of type wxGLCanvas, but has that wxPanel as parent (instead of the frame); the other controls are also child of the panel.
You better use wxSizers to manage the layout of all childs of the panel.

To make the GL window fill its available area you need to use that size-handler you bound at MyViewer ctor.

Code: Select all

void MyViewer ::OnSize(wxSizeEvent& event)
{
    event.Skip();
   ...

  // call your glew initalization helper
  isok= InitGL();
  ...
  
  //handle new size of this window
  const wxSize size = event.GetSize() * GetContentScaleFactor();
  m_Handler->SetViewport(0, 0, size.x, size.y); // m_Handler just call glViewPort()

}
GetContentScaleFactor() is available since wx 3.1.4

I notice two marks in your last code:
A) You have two functions for GL initialization: WaveformViewer::InitGL() and Helpr::InitGlew()
This is going to get messy. I'd move InitGlew() inside InitGL() function.

B) For GL context > 2.1 (better use CoreProfile and GL ver >= 3.2) Glew needs glewExperimental

Code: Select all

bool MyViewer::InitGL()
{
    if ( !m_oglContext )
        return false;

    // The current context must be set before we get OGL pointers
    SetCurrent(*m_oglContext);

    //Glew initialization
    glewExperimental = GL_TRUE;
    if ( GLEW_OK != glewInit() )
    {
        // Problem: glewInit failed, something is seriously wrong.
        return false;
    }
   ....
}
I pretty much did exactly as you, I'm still getting the same error, however I do get the green color by the render function. I think it maybe because I have too many levels of panels and all between the wxFrame and the GLCanvas. Here is some code that will give you an idea of many layers I have ("this" is wxFrame here),

Code: Select all

    // Main panel of the app
    m_MainPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);

    // Creating top splitter window
    m_TopSplitter = new wxSplitterWindow(m_MainPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
                                         wxSP_NOBORDER | wxSP_LIVE_UPDATE | wxSP_THIN_SASH);
    m_TopSplitter->SetMinimumPaneSize(200);
    m_TopSplitter->SetSashGravity(0);

    // Top half of TopSplitter window
    m_TopPanel = new wxPanel(m_TopSplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);

    m_TopSplitter->SplitHorizontally(m_TopPanel, m_BottomSplitter);
    m_BottomSplitter->SplitVertically(m_BottomLeftPanel, m_BottomRightPanel);

    m_TopControlsPanel = new wxPanel(m_TopPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER);

    m_TopWaveformPanel = new WaveformViewer(this, m_TopPanel, display_attributes, *m_Library, *m_MediaCtrl, *m_Database,
                                            m_ConfigFilepath, m_DatabaseFilepath);
This is how I get this 2 panels (red and blue) and 2 splitters (pink arrows)

Image

In the ctor of WaveformView inheriting from wxGLCanvas,

Code: Select all

WaveformViewer::WaveformViewer(wxWindow* parentFrame, wxWindow* window, const wxGLAttributes& attrs,
                               wxDataViewListCtrl& library, wxMediaCtrl& mediaCtrl, Database& database,
                               const std::string& configFilepath, const std::string& databaseFilepath)
    : wxGLCanvas(window, attrs),
      m_ParentFrame(parentFrame), m_Window(window), m_Database(database), m_Library(library), m_MediaCtrl(mediaCtrl),
      m_ConfigFilepath(configFilepath), m_DatabaseFilepath(databaseFilepath)
{
    // Create the canvas and context.
    #if wxCHECK_VERSION(3, 1, 0)
        // These settings should work with any GPU from the last 10 years.
        // wxGLAttributes display_attributes;
        // display_attributes.PlatformDefaults().RGBA().DoubleBuffer().EndList();

        wxGLContextAttrs context_attributes;
        context_attributes.PlatformDefaults().CoreProfile().OGLVersion(3, 3).EndList();

        // this = new wxGLCanvas(this, display_attributes);
        m_Context = new wxGLContext(this, NULL, &context_attributes);

        if (!m_Context->IsOK())
        {
            wxLogDebug("Failed to create context.");
            return;
        }
    #else
        // int display_attributes[] =
        //     { WX_GL_RGBA,
        //       WX_GL_DOUBLEBUFFER,
        //       WX_GL_CORE_PROFILE,
        //       WX_GL_MAJOR_VERSION ,3,
        //       WX_GL_MINOR_VERSION, 3,
        //       0 };

        // this = new wxGLCanvas(this, wxID_ANY, display_attributes);
        m_Context = new wxGLContext(this, NULL);

        // Unfortunately, there doesn't seem to be any way to check if the
        // context is ok prior to wxWidgets 3.1.0.
    #endif // wxCHECK_VERSION

    this->Bind(wxEVT_SIZE, &WaveformViewer::OnSize, this);
    this->Bind(wxEVT_PAINT, &WaveformViewer::OnPaint, this);

    Bind(wxEVT_MOTION, &WaveformViewer::OnMouseMotion, this);
    Bind(wxEVT_LEFT_DOWN, &WaveformViewer::OnMouseLeftButtonDown, this);
    Bind(wxEVT_LEFT_UP, &WaveformViewer::OnMouseLeftButtonUp, this);
    Bind(wxEVT_KEY_UP, &WaveformViewer::OnControlKeyUp, this);
}
InitGL()

Code: Select all

bool WaveformViewer::InitGL()
{
    if (!m_Context)
        return false;

    // The current context must be set before we get OGL pointers
    SetCurrent(*m_Context);

    // Initialize GLEW.
    if (!m_GLHelper.InitGlew())
    {
        wxLogDebug("Error %s()! Failed to initialize GLEW.", __FUNCTION__);
        return false;
    }

    wxLogDebug("Context and GLEW initialized.");

    return true;
}
OnSize()

Code: Select all

void WaveformViewer::OnSize(wxSizeEvent &event)
{
    event.Skip();

    if (!InitGL())
        wxLogDebug("Failed to initialize GL");
    else
        wxLogDebug("Canvas ready, initializing GL..");

    wxSize size = event.GetSize() * GetContentScaleFactor();
    m_GLHelper.SetViewport(0, 0, size.x, size.y);
}
GLHelper::InitGlew()

Code: Select all

bool GLHelper::InitGlew()
{
    // glewExperimental = GL_TRUE;

    GLenum init_status = glewInit();

    if (init_status != GLEW_OK)
    {
        std::cout << "GLEW Error: " << glewGetErrorString(init_status) << std::endl;
        return false;
    }
    else
        return true;
}
and in the MainFrame class I initialize the WaveformViewer class as,

Code: Select all

    wxGLAttributes display_attributes;
    display_attributes.PlatformDefaults().RGBA().DoubleBuffer().EndList();

    m_TopWaveformPanel = new WaveformViewer(this, m_TopPanel, display_attributes, *m_Library, *m_MediaCtrl, *m_Database,
                                            m_ConfigFilepath, m_DatabaseFilepath);
and here are the errors that I'm getting,

Code: Select all

GLEW Error: Missing GL version
16:46:09: Debug: Error InitGL()! Failed to initialize GLEW.
16:46:09: Debug: Failed to initialize GL
GLEW Error: Missing GL version
16:46:09: Debug: Error InitGL()! Failed to initialize GLEW.
16:46:09: Debug: Failed to initialize GL
GLEW Error: Missing GL version
16:46:10: Debug: Error InitGL()! Failed to initialize GLEW.
16:46:10: Debug: Failed to initialize GL
GLEW Error: Missing GL version
16:46:10: Debug: Error InitGL()! Failed to initialize GLEW.
16:46:10: Debug: Failed to initialize GL
GLEW Error: Unknown error
16:46:10: Debug: Error InitGL()! Failed to initialize GLEW.
16:46:10: Debug: Failed to initialize GL
Post Reply