Page 1 of 2
wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 14, 2019 3:29 pm
by rjinman
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).
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 14, 2019 4:05 pm
by doublemax
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.
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 14, 2019 4:11 pm
by rjinman
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.
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 14, 2019 4:35 pm
by doublemax
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.
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Tue Mar 19, 2019 1:47 pm
by rjinman
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.
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Tue Mar 19, 2019 2:35 pm
by ONEEYEMAN
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.
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 21, 2019 6:28 pm
by rjinman
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);
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 21, 2019 7:41 pm
by doublemax
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?
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 21, 2019 7:47 pm
by rjinman
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.
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 21, 2019 7:56 pm
by doublemax
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
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 21, 2019 8:03 pm
by ONEEYEMAN
Hi,
Which video card/driver do you have?
Maybe its just a timing bug with the driver?
Thank you.
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 21, 2019 10:58 pm
by rjinman
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.
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Thu Mar 21, 2019 11:05 pm
by rjinman
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.
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Fri Mar 22, 2019 12:13 pm
by rjinman
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
Re: wxGLCanvas always displaying previous render on Windows 10
Posted: Fri Mar 22, 2019 12:57 pm
by rjinman
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.