wxGLCanvas always displaying previous render on Windows 10 Topic is solved

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
rjinman
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed Mar 13, 2019 5:08 pm

wxGLCanvas always displaying previous render on Windows 10

Post by rjinman » Thu Mar 14, 2019 3:29 pm

I've studied the pyramid sample, which doesn't exhibit the problem, so it's likely I'm doing something wrong, but I can't for the life of me see what I'm doing differently.

I'm making a fractal renderer, so I'm not rendering in a loop, like in a game; I'm rendering only when I want to redraw the fractal. The problem is that on Windows, I always see the last render, not the latest one. For example, if I click and drag on the canvas, so as to zoom in, nothing happens. Then I do something that forces a repaint, and only then do I see the render.

When I want a render to occur, I call refresh().

Code: Select all

void Canvas::refresh() {
  Refresh(false);
  Update();
}

void Canvas::onPaint(wxPaintEvent&) {
  wxPaintDC dc(this);
  render(dc);
}

void Canvas::render(wxDC&) {
  if (!IsShownOnScreen()) {
    return;
  }

  SetCurrent(*m_context);

  if (!m_renderer.isInitialised()) {
    auto sz = GetSize();
    m_renderer.initialise(sz.x, sz.y);

    PostSizeEvent();
  }

  m_renderer.clear(255, 255, 255);

  if (m_disabled) {
    m_renderer.finish();
    SwapBuffers();

    return;
  }

  // Disgusting hack to force out stale render on Windows
#ifdef WIN32
  m_renderer.drawMandelbrot(m_mouseDown || m_disableRender);

  if (!m_disableRender) {
    m_disableRender = true;
    CallAfter([this]() { refresh(); });
  }
  else {
    m_disableRender = false;
  }
#else
  m_renderer.drawMandelbrot(m_mouseDown);
#endif

  if (m_mouseDown) {
    int x = m_selectionRect.x;
    int y = m_selectionRect.y;
    int w = m_selectionRect.width;
    int h = m_selectionRect.height;

    m_renderer.drawSelectionRect(x, y, w, h);
  }

  m_renderer.finish();
  SwapBuffers();

  measureFrameRate();
  m_onRender();
}
As you can see I'm hacking it at the moment by forcing a repaint after every render. The bool passed to drawMandelbrot determines whether to recompute the fractal or just draw the last one from a texture (false to recompute).

User avatar
doublemax
Moderator
Moderator
Posts: 14098
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxGLCanvas always displaying previous render on Windows 10

Post by doublemax » Thu Mar 14, 2019 4:05 pm

For example, if I click and drag on the canvas, so as to zoom in, nothing happens.
If you do something that changes the view, you have to call Refresh() yourself. The system can't know that it changed and send a paint event.

E.g in the pyramid sample you see calls to Refresh() in the OnSize() event handler and the mouse event handler.
Use the source, Luke!

rjinman
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed Mar 13, 2019 5:08 pm

Re: wxGLCanvas always displaying previous render on Windows 10

Post by rjinman » Thu Mar 14, 2019 4:11 pm

Hi, yes, I do do that. Here is the relevant part of the code.

Code: Select all

void Canvas::onLeftMouseBtnDown(wxMouseEvent& e) {
  e.Skip();

  SetFocus();
  SetCurrent(*m_context);

  m_mouseDown = true;
  m_selectionRect = wxRect(e.GetPosition(), wxSize(0, 0));
}

void Canvas::onLeftMouseBtnUp(wxMouseEvent& e) {
  e.Skip();

  m_mouseDown = false;

  if (selectionSizeAboveThreshold(m_selectionRect.GetSize())) {
    int x0 = m_selectionRect.x;
    int y0 = m_selectionRect.y;
    int x1 = x0 + m_selectionRect.width;
    int y1 = y0 + m_selectionRect.height;

    m_renderer.screenSpaceZoom(x0, y0, x1, y1);
  }

  m_renderer.drawSelectionRect(0, 0, 0, 0);
  refresh();
}

void Canvas::onMouseMove(wxMouseEvent& e) {
  e.Skip();

  if (m_mouseDown) {
    wxPoint p = wxGetMousePosition() - GetScreenPosition();
    wxPoint sz_p = p - m_selectionRect.GetTopLeft();
    wxSize sz(sz_p.x, sz_p.y);

    wxSize winSz = GetClientSize();
    float aspect = static_cast<float>(winSz.x) / static_cast<float>(winSz.y);
    sz.y = sz.x / aspect;

    m_selectionRect.SetSize(sz);

    refresh();
  }
}
So on mouse up, I'm calling refresh, which I can confirm triggers a paint event and a render, but I don't see the render.

User avatar
doublemax
Moderator
Moderator
Posts: 14098
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxGLCanvas always displaying previous render on Windows 10

Post by doublemax » Thu Mar 14, 2019 4:35 pm

This combination - render works if you force a refresh, render does not work during mouse down, but event handler gets called - should not be possible.

Are you sure the error is not in the m_renderer.drawMandelbrot(m_mouseDown) call, when m_mouseDown is true?

If that's not it, i'm out of ideas.
Use the source, Luke!

rjinman
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed Mar 13, 2019 5:08 pm

Re: wxGLCanvas always displaying previous render on Windows 10

Post by rjinman » Tue Mar 19, 2019 1:47 pm

I've simplified the code in an attempt to isolate the problem. You can see the code in the screenshot. As before, it's always displaying the last result.

Image

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 3469
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: wxGLCanvas always displaying previous render on Windows 10

Post by ONEEYEMAN » Tue Mar 19, 2019 2:35 pm

Hi,
It is better to put the code directly here in the <code></code> tags.
And it is better to do as minimal compilable example, so that anybody can grab it, compile it and test it.

Thank you.

rjinman
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed Mar 13, 2019 5:08 pm

Re: wxGLCanvas always displaying previous render on Windows 10

Post by rjinman » Thu Mar 21, 2019 6:28 pm

Thanks for your help so far. Here is a fairly minimal example. It requires GLEW.

Code: Select all

#include <stdexcept>
#include <sstream>
#include <wx/wx.h>
#include <GL/glew.h>
#include <wx/glcanvas.h>

const int WINDOW_W = 1000;
const int WINDOW_H = 600;
const int GL_VERSION_MAJOR = 3;
const int GL_VERSION_MINOR = 3;
const char* GL_VERSION_STRING = "GL_VERSION_3_3";

#define EXCEPTION(msg) \
  { \
    std::stringstream ss; \
    ss << msg << " (FILE: " << __FILE__ << ", LINE: " << __LINE__ << ")" \
       << std::endl; \
    throw std::runtime_error(ss.str()); \
  }

#define GL_CHECK(x) \
  x; \
  { \
    GLenum glError = glGetError(); \
    if (glError != GL_NO_ERROR) { \
      std::stringstream ss; \
      ss << "OpenGL error, code=0x" << std::hex << glError << std::dec \
        << " (FILE: " << __FILE__ << ", LINE: " << __LINE__ << ")" \
        << std::endl; \
      throw OpenGlException(ss.str(), glError); \
    } \
  }

class OpenGlException : public std::runtime_error {
public:
  OpenGlException(const std::string& msg, GLenum code)
    : runtime_error(msg),
      code(code) {}

  const GLenum code;
};

class Application : public wxApp {
public:
  virtual bool OnInit() override;
  virtual void HandleEvent(wxEvtHandler* handler, wxEventFunction func,
    wxEvent& event) const override;
};

class Canvas : public wxGLCanvas {
public:
  Canvas(wxWindow* parent, const wxGLAttributes& glAttrs);

  void refresh();

private:
  void initialise(int w, int h);
  void onLeftMouseBtnDown(wxMouseEvent& e);
  void onPaint(wxPaintEvent& e);

  bool m_initialised = false;
  std::unique_ptr<wxGLContext> m_context;

  wxDECLARE_EVENT_TABLE();
};

class MainWindow : public wxFrame {
public:
  MainWindow(const wxString& title, const wxSize& size);
};

wxBEGIN_EVENT_TABLE(Canvas, wxGLCanvas)
EVT_PAINT(Canvas::onPaint)
EVT_LEFT_DOWN(Canvas::onLeftMouseBtnDown)
wxEND_EVENT_TABLE()

Canvas::Canvas(wxWindow* parent, const wxGLAttributes& glAttrs)
  : wxGLCanvas(parent, glAttrs, wxID_ANY, wxDefaultPosition, wxDefaultSize,
    wxFULL_REPAINT_ON_RESIZE) {

  wxGLContextAttrs attrs;
  attrs.MajorVersion(GL_VERSION_MAJOR)
    .MinorVersion(GL_VERSION_MINOR)
    .ForwardCompatible()
    .EndList();

  m_context.reset(new wxGLContext(this, nullptr, &attrs));
}

void Canvas::onLeftMouseBtnDown(wxMouseEvent& e) {
  e.Skip();
  refresh();
}

void Canvas::refresh() {
  Refresh();
  Update();
}

void Canvas::initialise(int w, int h) {
  glewExperimental = GL_TRUE;
  GLenum result = glewInit();
  if (result != GLEW_OK) {
    EXCEPTION("Failed to initialize GLEW: " << glewGetErrorString(result));
  }

  if (!glewIsSupported(GL_VERSION_STRING)) {
    EXCEPTION("OpenGL " << GL_VERSION_MAJOR << "." << GL_VERSION_MINOR
              << " not supported");
  }

  m_initialised = true;
}

void Canvas::onPaint(wxPaintEvent&) {
  wxPaintDC dc(this);

  SetCurrent(*m_context);

  if (!m_initialised) {
    auto sz = GetSize();
    initialise(sz.x, sz.y);
  }

  static int i = 0;
  ++i;

  float c = 1.0 * (i % 2);
  GL_CHECK(glClearColor(c, c, c, 1.f));
  GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));

  std::cout << "Brightness = " << c << std::endl;

  GL_CHECK(glFlush());
  SwapBuffers();
}

MainWindow::MainWindow(const wxString& title, const wxSize& size)
  : wxFrame(nullptr, wxID_ANY, title, wxDefaultPosition, size) {

  auto vbox = new wxBoxSizer(wxVERTICAL);
  SetSizer(vbox);

  SetAutoLayout(true);

  wxGLAttributes glAttrs;
  glAttrs.PlatformDefaults().Defaults().EndList();

  auto canvas = new Canvas(this, glAttrs);
  vbox->Add(canvas, 1, wxEXPAND);
}

bool Application::OnInit() {
  MainWindow* frame = new MainWindow("Test App", wxSize(WINDOW_W, WINDOW_H));
  frame->Show();

  return true;
}

void Application::HandleEvent(wxEvtHandler* handler, wxEventFunction func,
                          wxEvent& event) const {
  try {
    wxApp::HandleEvent(handler, func, event);
  }
  catch (const std::runtime_error& e) {
    std::cerr << "A fatal exception occurred: " << std::endl;
    std::cerr << e.what() << std::endl;
    exit(1);
  }
}

wxIMPLEMENT_APP(Application);


User avatar
doublemax
Moderator
Moderator
Posts: 14098
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxGLCanvas always displaying previous render on Windows 10

Post by doublemax » Thu Mar 21, 2019 7:41 pm

On each mouse click, i get a new refresh. I think that's the expected and correct behavior?

Or what is the sample code supposed to show?
Use the source, Luke!

rjinman
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed Mar 13, 2019 5:08 pm

Re: wxGLCanvas always displaying previous render on Windows 10

Post by rjinman » Thu Mar 21, 2019 7:47 pm

If you run it from the console, and look at what's printed you'll see it's always showing the previous render. i.e. it looks black when it should look white, and vice versa.

User avatar
doublemax
Moderator
Moderator
Posts: 14098
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxGLCanvas always displaying previous render on Windows 10

Post by doublemax » Thu Mar 21, 2019 7:56 pm

rjinman wrote:
Thu Mar 21, 2019 7:47 pm
If you run it from the console, and look at what's printed you'll see it's always showing the previous render. i.e. it looks black when it should look white, and vice versa.
I don't see that either. 0 = Black, 1 = White
Use the source, Luke!

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 3469
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: wxGLCanvas always displaying previous render on Windows 10

Post by ONEEYEMAN » Thu Mar 21, 2019 8:03 pm

Hi,
Which video card/driver do you have?
Maybe its just a timing bug with the driver?

Thank you.

rjinman
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed Mar 13, 2019 5:08 pm

Re: wxGLCanvas always displaying previous render on Windows 10

Post by rjinman » Thu Mar 21, 2019 10:58 pm

Just tried it on a different Windows machine and didn't observe the problem, so I guess it's a problem with the OGL implementation on that machine. I'll have to wait till tomorrow to tell you the specs as it's a work machine and I'm at home now.

rjinman
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed Mar 13, 2019 5:08 pm

Re: wxGLCanvas always displaying previous render on Windows 10

Post by rjinman » Thu Mar 21, 2019 11:05 pm

It's strange that I didn't see the problem on that machine with the Pyramid sample when I modified it to do a single render on click. I could tell it was always showing the latest render by how the pyramid rotated in response to where I clicked.

rjinman
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed Mar 13, 2019 5:08 pm

Re: wxGLCanvas always displaying previous render on Windows 10

Post by rjinman » Fri Mar 22, 2019 12:13 pm

ONEEYEMAN wrote:
Thu Mar 21, 2019 8:03 pm
Hi,
Which video card/driver do you have?
Maybe its just a timing bug with the driver?

Thank you.
Intel(R) UHD Graphics 620
Driver version: 24.20.100.6170

rjinman
Earned a small fee
Earned a small fee
Posts: 14
Joined: Wed Mar 13, 2019 5:08 pm

Re: wxGLCanvas always displaying previous render on Windows 10

Post by rjinman » Fri Mar 22, 2019 12:57 pm

OK, by systematically deleting stuff from the pyramid sample until it exhibited the problem I've found the one line that fixes it - and I have no idea why.

Here it is:

Code: Select all

glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
If I call that before doing any rendering, the problem goes away.

Post Reply