PDA

View Full Version : Problem with GL.ReadPixels() CPU usage



Stupid_2D
08-25-2011, 10:49 AM
Hello everyone,

I am currently having troubles implementing a mouseselection.

The problem is, that ReadPixels lets my programm jump to 100% CPU usage. Anyone know why?

I am using OpenTK for C#
Windows 7 64 bit professional
Nvidia 8700 M GT (latest driver)
Intel Dual Core T9300 2.5 Ghz




GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();

GL.Color3(1,0.5,1);
GL.Begin(BeginMode.Quads);
GL.Vertex2(0,0);
GL.Vertex2(5,0);
GL.Vertex2(5,5);
GL.Vertex2(0,5);
GL.End();

GL.ReadPixels(Mouse_X, Mouse_Y,1, 1, PixelFormat.Rgba, PixelType.UnsignedByte, ref Pixel);

GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);


GL.Color3(1,0.5,1);

GL.Begin(BeginMode.Quads);
GL.Vertex2(0,0);
GL.Vertex2(5,0);
GL.Vertex2(5,5);
GL.Vertex2(0,5);
GL.End();
this.SwapBuffers();

The Little Body
08-25-2011, 04:17 PM
glReadPixels calls are relatively slow ... :(

Why don't make the GL.ReadPixels call at a lesser frequency that the display ?
(cf. not to make the GL.ReadPixel call at each refresh, but when two or three refresh are done for example)

And why you display a second time the same triangle ???

Alfonse Reinheart
08-25-2011, 04:46 PM
You might get better performance if you used BGRA instead of RGBA in your pixel transfer.

The Little Body
08-25-2011, 06:00 PM
From http://www.mesa3d.org/brianp/sig97/gotchas.htm

"glDrawPixels isn't as fast as expected
Some older graphics systems handle ABGR-order pixels faster than RGBA-order. Try the GL_EXT_abgr extension. Also, be sure to disable rasterization options such as depth testing, fog, stenciling, scissoring, pixel scaling, dithering and biasing, if you don't need them. GL_UNSIGNED_BYTE is typically the fastest data type."

Stupid_2D
08-26-2011, 05:04 AM
Thank you for your replies. I see i should have given more detail:

For some strange reason i cannot edit my startingtopic.

Considering my code i posted here, I made a very simple example of what I am trying to do.

Actually the first quad which i draw before readingPixels got a unique color for picking purposes. The second quad actually is the quad i want to render with a texture and so on.
Confusingly i made them identical in code which made it look stupid.


Changing the pixel format didnt help at all.
Also i have no extension enabled(Texture2D, culling, whatever). Or do i need to disable extensions which are enabled on standard?


Picking every second frame halved the cpu load, making it every third frame starts letting me expirience inputlags and cpu is still at ~20%



So is there anything else i can do or should make sure?


Is it because i am not using any shader?

ZbuffeR
08-26-2011, 10:52 AM
Did you try :
GL.ReadPixels(Mouse_X, Mouse_Y,1, 1, PixelFormat.Bgra, PixelType.UnsignedByte, ref Pixel);

Ilian Dinev
08-26-2011, 11:05 AM
Try something a bit different:

copy that pixel into a 1x1 texture, and then read that tiny texture:


int result;
glBindTexture(GL_TEXTURE_2D,tiny1x1Texture);
glTexSubImage2D(GL_TEXTURE_2D,0, 0,0,Mouse_X, Mouse_Y,1, 1);
glGetTexImage(GL_TEXTURE_2D,0,GL_RGBA,GL_UNSIGNED_ BYTE, &result);

Stupid_2D
08-27-2011, 02:34 AM
Thanks again guys.

@ZBuffer, i tried every Pixelformat. Nothing changed sadly.

@LLia Diney, I dont really know how to copy a pixel into a texture?


OpenTK comes with a few examples, and i noticed that the picking example from OpenTK consumes the same ammount of CPU. I am now testing different videocard drivers. Hope that will work :p

Ilian Dinev
08-27-2011, 08:08 AM
Whoops, a typo. Had to be glCopyTexSubImage2D instead of glTexSubImage2D

Stupid_2D
08-28-2011, 07:24 AM
Thx, Ill give it a try.



int TextureID;
GL.GenTextures(1, out TextureID);
GL.BindTexture(TextureTarget.Texture2D,TextureID);
GL.CopyTexSubImage2D(TextureTarget.Texture2D,0,0,0 ,Mouse_X,Mouse_Y,1,1);
GL.GetTexImage(TextureTarget.Texture2D,0,PixelForm at.Rgba,PixelType.UnsignedByte,ref Pixel);


Doesnt work but eats same CPU.

Stupid_2D
09-03-2011, 04:04 AM
Anyone else got any ideas?
Or did i implement Ilian Dinev's code wrong?

V-man
09-03-2011, 06:05 AM
I'm not exactly sure I understand your problem so please clarify it.
Are you rendering and using glReadPixels to read a single pixel as the user moves the mouse?

You might want to read the entire screen with glReadPixels if the scene is not changing. As the user moves the mouse, you would no longer need to use glReadPixels to read single pixels.

Stupid_2D
09-04-2011, 02:05 AM
Lets try this approach, whats the best way to determine where the mouse is in my scene?

What i do is:

I render my objects on screen with a unique color RGBA. Since there are not so many its all red from 1 to 120 so far. So the colordata looks like (i,0,0,0) where i is basically my object ID.
After this "selection render" I draw the scene with normal colors, textures, blending and stuff.

Normal Scene (http://img233.imageshack.us/img233/1402/normalscene.png)
Selection Scene (http://img94.imageshack.us/img94/6740/selectionscene.png)


GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();

if(selectionFrameSkip)
{
selectionFrameSkip = false;
GL.Disable(EnableCap.Blend);
GL.Disable(EnableCap.Texture2D);
VBOHelper.RenderSelection();


GL.ReadPixels(Mouse_X,Mouse_Y, 1, 1, PixelFormat.Rgba, PixelType.UnsignedByte, ref Pixel);

VBOHelper.Select(Pixel,clickstate);

clickstate = false;
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );



}else selectionFrameSkip = true;


GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Blend);

VBOHelper.RenderScene();

Context.SwapBuffers();



public static void RenderScene()
{
foreach(VBO vbo in Vbos)
{
if(vbo.Visible)
{
GL.PushMatrix();
GL.Translate(vbo.Translation);
GL.Rotate(vbo.Rotation,0,0,1);

VBOHelper.bind_Vertex(vbo);
VBOHelper.bind_Texture(vbo);
VBOHelper.bind_Color(vbo);
VBOHelper.bind_Indicies(vbo);

GL.PopMatrix();
}
}
VBOHelper.bind_Null();
}



public static void RenderSelection()
{
foreach(VBO vbo in Vbos)
{
if(vbo.Visible)
{
GL.PushMatrix();
GL.Translate(vbo.Translation);
GL.Rotate(vbo.Rotation,0,0,1);

VBOHelper.bind_Vertex(vbo);
VBOHelper.bind_Selection(vbo); // BInd unique color
VBOHelper.bind_Indicies(vbo);
GL.PopMatrix();
}
}
VBOHelper.bind_Null();
}



private static void bind_Vertex(VBO _vbo)
{
if (_vbo.VertexBufferID != 0)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo.VertexBufferID);
GL.VertexPointer(2, VertexPointerType.Float, Vector2.SizeInBytes, IntPtr.Zero);
GL.EnableClientState(ArrayCap.VertexArray);
}
}
private static void bind_Indicies(VBO _vbo)
{
if (_vbo.IndiciesBufferID != 0)
{
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _vbo.IndiciesBufferID);
GL.DrawElements(BeginMode.Triangles, _vbo.IndiciesData.Length,
DrawElementsType.UnsignedInt, IntPtr.Zero);
}
}
private static void bind_Color(VBO _vbo)
{
if (_vbo.ColorBufferID != 0)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo.ColorBufferID);
GL.ColorPointer(4, ColorPointerType.Float, Vector4.SizeInBytes, IntPtr.Zero);
GL.EnableClientState(ArrayCap.ColorArray);
}
}
private static void bind_Texture(VBO _vbo)
{
if(_vbo.TextureBufferID!=0)
{
GL.BindTexture(TextureTarget.Texture2D , _vbo.TexID);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo.TextureBufferID);
GL.TexCoordPointer(2,TexCoordPointerType.Float,Vec tor2.SizeInBytes, IntPtr.Zero);
GL.EnableClientState(ArrayCap.TextureCoordArray);
}
}
private static void bind_Selection(VBO _vbo)
{
if(_vbo.SelectionColorBufferID!=0)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo.SelectionColorBufferID);
GL.ColorPointer(4, ColorPointerType.Float, Vector4.SizeInBytes, IntPtr.Zero);
GL.EnableClientState(ArrayCap.ColorArray);
}
}
private static void bind_Null()
{
GL.BindBuffer (BufferTarget.ElementArrayBuffer, 0);
}

V-man
09-05-2011, 11:25 AM
That's fine but if you are calling glReadPixels every time there is a mouse move event, it will of course increase CPU usage.

I noticed that you are using it to read just 1 pixel
GL.ReadPixels(Mouse_X,Mouse_Y, 1, 1, PixelFormat.Rgba, PixelType.UnsignedByte, ref Pixel);

Stupid_2D
09-06-2011, 11:39 AM
Yeah from that pixel i read the color value and automatically know what VBO the mouse is over. Actually readpixel is called every second rendered frame. Thats a problem then?

Is there any better way?

V-man
09-09-2011, 08:13 AM
Comment out your call to glReadPixels and you'll know.

If that is the problem (calling glReadPixels every second), then follow my suggestion.

Stupid_2D
09-10-2011, 12:58 AM
It is the problem.

Ok i understand, read the entire screen, put the data into some array and then read it when a MouseMove occurs.

But is that better? I mean there are moving objects on my screen all the time. So how do i know when the scene has changed / or when is the best moment to call glReadPixels?

V-man
09-10-2011, 05:30 AM
If there are things moving all the time, the you have no choice but to call glReadPixels every time.
The alternative is to use PBO to do a async read and then get the result on the next frame update. This requires GL 2.1

Stupid_2D
09-11-2011, 06:22 AM
If found a soloution, strangely adding a second SwapBuffers call let the cpu drop to ~10%



// ============= FIX =====================
Context.SwapBuffers();
// ============= FIX =====================


GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();

if(selectionFrameSkip)
{
selectionFrameSkip = false;
GL.Disable(EnableCap.Blend);
GL.Disable(EnableCap.Texture2D);
VBOHelper.RenderSelection();


GL.ReadPixels(Mouse_X,Mouse_Y, 1, 1, PixelFormat.Rgba, PixelType.UnsignedByte, ref Pixel);

VBOHelper.Select(Pixel,clickstate);

clickstate = false;
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );



}else selectionFrameSkip = true;


GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Blend);

VBOHelper.RenderScene();

Context.SwapBuffers();