Problem with GL.ReadPixels() CPU usage

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();



glReadPixels calls are relatively slow … :frowning:

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 ???

You might get better performance if you used BGRA instead of RGBA in your pixel transfer.

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.”

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?

Did you try :
GL.ReadPixels(Mouse_X, Mouse_Y,1, 1, PixelFormat.Bgra, PixelType.UnsignedByte, ref Pixel);

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);

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 :stuck_out_tongue:

Whoops, a typo. Had to be glCopyTexSubImage2D instead of glTexSubImage2D

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,PixelFormat.Rgba,PixelType.UnsignedByte,ref Pixel);

Doesnt work but eats same CPU.

Anyone else got any ideas?
Or did i implement Ilian Dinev’s code wrong?

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.

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
Selection Scene


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,Vector2.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);			
		}

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);

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?

Comment out your call to glReadPixels and you’ll know.

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

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?

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

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();