Discussion:
Draw OpenGLScene without WM_PAINT
(too old to reply)
Albe
2008-05-19 07:12:21 UTC
Permalink
Hi all,
I'm writing a software that receive data to USB port and draw same
graphics in same Windows TAB ( I'm using Visual Studio 2003 with
MFC ).
I have to draw the new scene only when the new data on USB port is
available and this data changing is not very fast (400 Hz - 400 sample
per second), on this I have an issue with the extra work to CPU, is
always upper than 90% probably because the WM_PAINT is so fast and the
software draws continuosly the same scene between two sample to USB
port.

To solve this issue I tried to eliminate the WM_PAINT in OpenGL and
call the OpenGL::OnPaint only when the new data occours but the
window's device context is 0 and I don't understand why.

Cau yuo help me?

Thank you

Alberto
Wolfgang Draxinger
2008-05-19 07:47:36 UTC
Permalink
Post by Albe
Hi all,
I'm writing a software that receive data to USB port and draw
same graphics in same Windows TAB ( I'm using Visual Studio
2003 with MFC ).
I have to draw the new scene only when the new data on USB port
is available and this data changing is not very fast (400 Hz -
400 sample per second), on this I have an issue with the extra
work to CPU, is always upper than 90% probably because the
WM_PAINT is so fast and the software draws continuosly the same
scene between two sample to USB port.
To solve this issue I tried to eliminate the WM_PAINT in OpenGL
and call the OpenGL::OnPaint only when the new data occours but
the window's device context is 0 and I don't understand why.
Cau yuo help me?
I don't know (anymore) why the DC in a manually issuied WM_PAINT
may be 0, however rest assured, that it doesn't matter to you:

Once created, the OpenGL context is independent of any DC, you
just have to make sure it's been made current (wglMakeCurrent).
Then you may use the context (i.e. render stuff) whenever you
see fit.

Wolfgang Draxinger
--
E-Mail address works, Jabber: ***@jabber.org, ICQ: 134682867
Albe
2008-05-19 08:47:07 UTC
Permalink
Hi Wolfgang, thank you for reply but I'm sorry I don't understand your
message, I'm a newbie in OpenGL and I'm no expert in context ecc
ecc...
Just to be clear, I'm using this code

OGLPlo = new COpenGLControl();
...
CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW |
CS_OWNDC,NULL,(HBRUSH)StockObject(BLACK_BRUSH),NULL);

CreateEx( 0,className,"OpenGL",WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS
| WS_CLIPCHILDREN,
rect,parent,0);
...
dc = new CClientDC(this);
...
openGLDevice.create(dc->m_hDC);

bool OpenGLDevice::create(HDC& deviceContext,int stencil)
{
if (!deviceContext)
{
return false;
}

if (!setDCPixelFormat(deviceContext,stencil))
{
return false;
}

renderContext = wglCreateContext(deviceContext);
wglMakeCurrent(deviceContext, renderContext);

OpenGLDevice::deviceContext = deviceContext;

return true;
}
...
"COpenGLControl::InitGL"
...
"COpenGLControl::OnSize"
... <---- ERROR dc=0
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
...
Disegna_Barre();
...
SwapBuffers(dc->m_hDC);

Here there are the main instruction used to create the OpenGL windows,
it works fine in normal way (when the OnPaint is called from WM_PAINT)
but when I try to call OnPaint without WM_PAINT (I'm calling OnPaint
from a function inside the ogl class each time a new data is
available) the dc is ok before the OnPaint calling, after the dc=0 and
an error occours in

SwapBuffers(dc->m_hDC);

I hope make clear, thank you in advance

Alberto
aku ankka
2008-05-19 10:21:22 UTC
Permalink
Post by Albe
Hi Wolfgang, thank you for reply but I'm sorry I don't understand your
message, I'm a newbie in OpenGL and I'm no expert in context ecc
ecc...
Just to be clear, I'm using this code
OGLPlo = new COpenGLControl();
...
CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW |
CS_OWNDC,NULL,(HBRUSH)StockObject(BLACK_BRUSH),NULL);
CreateEx(       0,className,"OpenGL",WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS
| WS_CLIPCHILDREN,
rect,parent,0);
...
dc = new CClientDC(this);
...
openGLDevice.create(dc->m_hDC);
bool OpenGLDevice::create(HDC& deviceContext,int stencil)
{
        if (!deviceContext)
        {
                return false;
        }
        if (!setDCPixelFormat(deviceContext,stencil))
        {
                return false;
        }
        renderContext = wglCreateContext(deviceContext);
        wglMakeCurrent(deviceContext, renderContext);
        OpenGLDevice::deviceContext = deviceContext;
        return true;}
...
"COpenGLControl::InitGL"
...
"COpenGLControl::OnSize"
...                                                     <----  ERROR dc=0
glClear(GL_COLOR_BUFFER_BIT      |  GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
...
Disegna_Barre();
...
SwapBuffers(dc->m_hDC);
Here there are the main instruction used to create the OpenGL windows,
it works fine in normal way (when the OnPaint is called from WM_PAINT)
but when I try to call OnPaint without WM_PAINT (I'm calling OnPaint
from a function inside the ogl class each time a new data is
available) the dc is ok before the OnPaint calling, after the dc=0 and
an error occours in
SwapBuffers(dc->m_hDC);
I hope make clear, thank you in advance
Alberto
Don't sweat over the paint messages, they are just artifact of
Microsoft's aged window manager. The window manager doesn't keep a
copy of the canvas contents so any time new portions of window are
revealed the window manager has to ask the application to
(re-)generate the content.

This is done much better on modern window managers, but that doesn't
help you much since it appears you are using the GDI (not a bad choise
since it caters for more flavours of windows than just the latest
Vista incarnations).

What you want to do is to beginpaint, do your rendering, endpaint and
repeat that as often as required. If you use FBO's and render-to-
texture, you can use the framebuffer object for the paint messages.
This would go into the category of useless optimizations as you need a
good performance (or so I would be inclined to guess) to begin with.
Might help with lower performance systems if you don't update the
image too frequently, go figure, could depend on how complex your
dataset is.

I didn't catch what you meant by 400 Hz, you want to update the scene
every time a new sample arrives, or batch updates? It will be rather
difficult to display your "signal" at 400 Hz, so it would make sense
to display snapshots of the data after specific time has elapsed for
example. This is kind of embarassingly trivial stuff but since you are
asking I'm forced to assume that there is something unstated in your
question. Care to be more specific what kind of trouble you are
experiencing again?
Albe
2008-05-19 12:57:37 UTC
Permalink
Hi aku ankka,
my problem is that when the software shows the OpenGL the CPU works
around at 100% and I have to decrease this usage. I saw that if I
insert in the cycle the instruction

Sleep(1)

the CPU-usage decrease around 30% and it's right but the issue is that
the time to draw a scene is longer than the time between two sample
and, in this case, the software buffer the data.

My idea was to call the OnPaint only when a new data occours.
Jonno
2008-05-19 13:23:06 UTC
Permalink
Post by Albe
Hi aku ankka,
my problem is that when the software shows the OpenGL the CPU works
around at 100% and I have to decrease this usage. I saw that if I
insert in the cycle the instruction
Sleep(1)
the CPU-usage decrease around 30% and it's right but the issue is that
the time to draw a scene is longer than the time between two sample
and, in this case, the software buffer the data.
My idea was to call the OnPaint only when a new data occours.
You shouldn't call OnPaint directly (or send WM_PAINT).
Instead, call InvalidateRect to tell Windows that you window needs
redrawing. Then Windows will post a WM_PAINT message. Call UpdateWindow
if you want the window to update immediately.
Albe
2008-05-19 14:27:09 UTC
Permalink
Ok Jonno,
in the message manage I have deleted the WM_PAINT -> OnPaint in opengl
class and for each new data I call UpdateWindows, unfortunatly the
software don't draw the opengl windows.

:(
Jonno
2008-05-19 14:54:39 UTC
Permalink
Post by Albe
Ok Jonno,
in the message manage I have deleted the WM_PAINT -> OnPaint in opengl
class and for each new data I call UpdateWindows, unfortunatly the
software don't draw the opengl windows.
:(
You still need the WM_PAINT message handler.
Post by Albe
I'm calling OnPaint from a function inside the ogl class each time a new data is
available
Don't call OnPaint, call InvalidateRect (and optionally UpdateWindow)
instead.
Albe
2008-05-19 15:47:09 UTC
Permalink
Hi Jonno,
probably owing to my bad english I did't explain correctly my problem:
I have an application named EKXXX with 3 classes

CEKXXXApp
CEKXXXDlg
COpenGLControl

in CEKXXXDlg there is a function named OnPaint associate at WM_PAINT
message and is

void CEKXXXDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
if (IsIconic())
{
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);

CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}

in COpenGLControl there is a function named OnPaint associate at
WM_PAINT message and is

void COpenGLControl::OnPaint()
{
if(*datoOK)
{
/** OpenGL section **/

openGLDevice.makeCurrent();
DrawGLScene();
*datoOK=false;

temp_stato_old=tab;
}
}

When the software don't show opengl (there are two tab, in the first
there is a text box with the program description, in the second there
is an opengl windows that draw the data received from USB) the CPU
usage is about 10%, when the opengl is showed the CPU usage is about
98%, MORE.

I think that WM_PAINT is the same windows message and when it comes
the software runs OnPaint in CEKXXXDlg and OnPaint in COpenGLControl.

I can't use Sleep(x) becuse the system became too late, how I can do
to decrease CPU usage when the software shows OpenGL windows???

Sorry and thank you

Alberto
Jonno
2008-05-19 16:00:35 UTC
Permalink
Post by Albe
void COpenGLControl::OnPaint()
{
if(*datoOK)
{
/** OpenGL section **/
openGLDevice.makeCurrent();
DrawGLScene();
*datoOK=false;
temp_stato_old=tab;
}
}
You're not validating your window here.
You need this line:

CPaintDC dc(this);

Even though you don't use the DC, the CPaintDC constructor calls
BeginPaint which tells Windows you have handled the WM_PAINT message.
Otherwise it will just keep sending more WM_PAINTs.
aku ankka
2008-05-20 06:29:33 UTC
Permalink
Post by Albe
Hi aku ankka,
my problem is that when the software shows the OpenGL the CPU works
around at 100% and I have to decrease this usage. I saw that if I
insert in the cycle the instruction
Sleep(1)
the CPU-usage decrease around 30% and it's right but the issue is that
the time to draw a scene is longer than the time between two sample
and, in this case,  the software buffer the data.
It doesn't benefit you to render faster than the refresh rate of the
display; rendering snapshots of the data shouldn't be a problem.

You got me hooked already, what's the data?
Albe
2008-05-20 07:25:21 UTC
Permalink
I agree aku ankka, it doesn't benefit render faster than the refresh
rate of the display, I have to refresh only when a new data occours
but the WM_PAINT message is faster than the data. I have associated
the high CPU usage with the faster WM_PAINT message, I'm not sure that
it is correct, I tried to eliminate the WM_PAINT function in
OpenGLControl::OnPaint and the CPU usage decrease to 15-25%, I tried
to comment the code in OpenGLControl::OnPaint (I create the
openglwindows but I don't draw aniting) the CPU usage stay to 95-98%.

To USB I read a serial data composed to 10 bytes, the time betwenn two
samples is settable at 50 Hz, 100Hz, 400Hz and 1000Hz. My driver
maximum speed is 600 Hz (I have same problem when the device write at
1000 Hz but is not my priority now).

Thank you for support
Diego
2008-05-21 16:33:15 UTC
Permalink
Post by Albe
I agree aku ankka, it doesn't benefit render faster than the refresh
rate of the display, I have to refresh only when a new data occours
but the WM_PAINT message is faster than the data. I have associated
the high CPU usage with the faster WM_PAINT message, I'm not sure that
it is correct, I tried to eliminate the WM_PAINT function in
OpenGLControl::OnPaint and the CPU usage decrease to 15-25%, I tried
to comment the code in OpenGLControl::OnPaint (I create the
openglwindows but I don't draw aniting) the CPU usage stay to 95-98%.
To USB I read a serial data composed to 10 bytes, the time betwenn two
samples is settable at 50 Hz, 100Hz, 400Hz and 1000Hz. My driver
maximum speed is 600 Hz (I have same problem when the device write at
1000 Hz but is not my priority now).
Thank you for support
you must be pooling when reading from USB
can you switch to a blocking I/O ? this will save CPU resources a lot
Albe
2008-05-22 09:33:00 UTC
Permalink
Post by Diego
Post by Albe
I agree aku ankka, it doesn't benefit render faster than the refresh
rate of the display, I have to refresh only when a new data occours
but the WM_PAINT message is faster than the data. I have associated
the high CPU usage with the faster WM_PAINT message, I'm not sure that
it is correct, I tried to eliminate the WM_PAINT function in
OpenGLControl::OnPaint and the CPU usage decrease to 15-25%, I tried
to comment the code in OpenGLControl::OnPaint (I create the
openglwindows but I don't draw aniting) the CPU usage stay to 95-98%.
To USB I read a serial data composed to 10 bytes, the time betwenn two
samples is settable at 50 Hz, 100Hz, 400Hz and 1000Hz. My driver
maximum speed is 600 Hz (I have same problem when the device write at
1000 Hz but is not my priority now).
Thank you for support
you must be pooling when reading from USB
can you switch to a blocking I/O ? this will save CPU resources a lot
My problem is to control the WM_PAINT, this message arrives also when
the data is not available and this probably overload the CPU
Jonno
2008-05-22 10:24:50 UTC
Permalink
Post by Albe
My problem is to control the WM_PAINT, this message arrives also when
the data is not available and this probably overload the CPU
I already told you how to fix this. Add this line to
COpenGLControl::OnPaint()...

CPaintDC dc(this);

Otherwise Windows will keep sending more WM_PAINT messages.
Clemens
2008-05-24 07:56:44 UTC
Permalink
Post by Albe
Hi all,
I'm writing a software that receive data to USB port and draw same
graphics in same Windows TAB ( I'm using Visual Studio 2003 with
MFC ).
I have to draw the new scene only when the new data on USB port is
available and this data changing is not very fast (400 Hz - 400 sample
per second), on this I have an issue with the extra work to CPU, is
always upper than 90% probably because the WM_PAINT is so fast and the
software draws continuosly the same scene between two sample to USB
port.
The problem with 90% CPU power and unneeded draws is that a bug in command
sequence.
Use one of these
- ValidateRect()
- DefWindowProc()
- [MFC] CWnd::OnPaint()

to stop WM_PAINT sending until new content.

Use InvalidateRect() to refresh area. Do not send manual WM_PAINT with
SendEvent() !!
Post by Albe
To solve this issue I tried to eliminate the WM_PAINT in OpenGL and
call the OpenGL::OnPaint only when the new data occours but the
window's device context is 0 and I don't understand why.
Cau yuo help me?
Thank you
Alberto
Hope this helps.

Clemens
Albe
2008-05-26 09:43:17 UTC
Permalink
Hi Clemens,
thak you very much for reply, probably I understand your suggestion, I
have implemented new instruction in the code

void COpenGLControl::OnPaint()
{
if(*datoOK)
{
InvalidateRect(Prova, true);
openGLDevice.makeCurrent();
DrawGLScene();
*datoOK=false;
temp_stato_old=tab;
}
else
{
ValidateRect(Prova);
}

}

"Prova" is CRect ogl windows where i draw with "DrawGLScene"

My idea based on Clemens's suggestion is that when the new data is
available I use InvalidateRect and when windows call WM_PAINT but the
data is not availabe I use ValidateRect.

Unfortunally, with this code the ogl is freezed, probably I don't use
correctly InvalidateRect.
Jonno
2008-05-27 08:09:26 UTC
Permalink
Post by Albe
Hi Clemens,
thak you very much for reply, probably I understand your suggestion, I
have implemented new instruction in the code
void COpenGLControl::OnPaint()
{
if(*datoOK)
{
InvalidateRect(Prova, true);
openGLDevice.makeCurrent();
DrawGLScene();
*datoOK=false;
temp_stato_old=tab;
}
else
{
ValidateRect(Prova);
}
}
Try this:
void COpenGLControl::OnPaint()
{
openGLDevice.makeCurrent();
DrawGLScene();
ValidateRect(Prova);
}

When you want to redraw, call InvalidateRect(Prova, true);

Loading...