Mouscavich's picture

GL.ReadPixels behaving differently on two PCs?

Hello,

I have seen a couple of other threads on these forums in which people use GL.ReadPixels() to create a bitmap object, but then that bitmap turns out to be blank. I was having the same problem on my main PC at home which caused my program to misbehave. But when I tried the same code on my secondary PC, the returned bitmap was not empty, and the program behaved as expected.

I have debugged through the code on both machines, and the debuggers show identical information right up until the point at which I iterate over the bitmap looking for non-black pixels. My main PC shows only black pixels (despite me explicitly drawing a polygon immediately beforehand), and my other PC correctly shows the polygon pixels exactly where expected.

I'm hoping that someone can offer up avenues I can explore to try to get to the bottom of this because I'm quite new to OpenGL / OpenTK, and I've exhausted the obvious areas of investigation that my newbie understanding limits me to. I even tried loading the bitmap as a texture (as suggested in yet another thread), but I couldn't get that to work, so that's another story... ;)

Here is the technical information that (hopefully) explains the situation I'm in:

OpenTK version: OpenTK-2010-10-06 (dll version is given as 1.0.278.44921)
Application: .Net 2.0

The code that produces the bitmap (taken from elsewhere in these forums):

        public Bitmap GrabScreenshot()
        {
            Bitmap bmp = new Bitmap(glControl.ClientSize.Width, glControl.ClientSize.Height);
            System.Drawing.Imaging.BitmapData data =
                bmp.LockBits(glControl.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,
                             System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            GL.ReadPixels(0, 0, glControl.ClientSize.Width, glControl.ClientSize.Height, PixelFormat.Bgr,
                          PixelType.UnsignedByte, data.Scan0);
            bmp.UnlockBits(data);
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
 
            return bmp;
        }

Note: To test the returned bitmap, I'm just iterating over it with GetPixels() at the moment.

Primary PC: Windows 7 64-bit
Visual Studio 2012 Ultimate
Nvidia GeForce GTX 560Ti
GLSL Version 4.2.0
DirectX Version 9.0c
DirectX shader model vs: 5.0 ps: 5.0

Secondary PC: Windows XP 32-bit
Visual C# 2010 Express
Nvidia Quadro NVS 300
GLSL Version 3.3.0
DirectX Version 9.0c
DirectX shader model vs: 3.0 ps: 3.0

As you can see, my primary PC is the more up-to-date one, so I'd expect it to be the one that works, but it is in fact the older PC that works.
I can supply the whole GL extensions dump (from GL Extensions Viewer 4.0.8) if needed, but they are a bit lengthy.
Who'd be a software developer? :)

Many thanks in advance for any suggestions or advice you can offer up.

Kind regards,
Andy


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

Thanks for giving detailed information, this makes things much more straightforward.

The first thing you should try is to add a call to GL.GetError() just before and just after the call to GL.ReadPixels(). If there is an error, you are in undefined territory and GL.ReadPixels may return anything. Check the ReadPixels documentation for a list of relevant error codes.

The second thing is to make sure you are reading the correct thing: the contents of the back buffer are undefined after a call to SwapBuffers(). If you call SwapBuffers() and then GL.ReadPixels(), you will get back an undefined result. The correct sequence is the following (pseudo-code):

...
GrabScreenshot()
{
    GL.Flush();
    GL.ReadBuffer(ReadBufferMode.Back);
    GL.ReadPixels();
}
SwapBuffers();

The third thing to note is that many systems have trouble reading from the front buffer. There are many many different circumstances that can make a read from the front buffer fail - it's best if you simply avoid that (i.e. use the pseudocode above.)

If none of these help, please compile a debug version from https://github.com/opentk/opentk and post the debug log from the output window in Visual Studio (ctrl+alt+o).

Mouscavich's picture

Hello again, and thanks very much for the information.

I added the calls to GL.GetError() as you suggested, and they were returning the NoError value.
I double-checked the sequence of events to ensure that I'm drawing to the glControl prior to any calls to SwapBuffers(), and I am.
I added the calls to Flush() and ReadBuffer as you suggested, but they did not make any difference, but it's good advice about not reading from the front buffer - I did not know that, and I probably would have continued doing it had you not mentioned it, so thanks again!

So I compiled the debug version as you requested, and got the following debug log:

http://pastebin.com/rpEXbcCX
(Sorry - I had to use pastebin because the forums spam filter wouldn't let me post the log in-line)

This seemed to suggest that I was missing a dll called SDL2.dll.
So, out of curiosity, I included the x86 version of that dll (my project is built as an x86 configuration), and re-ran. This gave a different error:

http://pastebin.com/DzPnaGjC

This is now suggesting that the graphics context cannot be created.
Could this be a corrupted installation on my PC?

Once again, any advice you can give would be most welcome.

Regards,
Andy

the Fiddler's picture

The first message about the missing SDL2.dll is harmless. The SDL backend is completely optional and mainly meant for non-Windows platforms (such as the upcoming SteamOS.)

SDL cannot currently initialize OpenGL contexts on windows it doesn't own, so the failure with GLControl+SDL is expected. This must be fixed in SDL - I'm working on a patch that will hopefully be included in SDL 2.0.2.

I just reviewed the GLControl implementation on Windows, and it appears that it was not setting the CS_OWNDC flag in the class-style bitmask. This might cause the pixels owned by the GLControl to fail the ownership test, leading to the GL.ReadPixels issue you are seeing. Commit d9afed2 fixes this - can you please update from github master and try again?

If this still doesn't help, please log an issue report with a small test case.

Mouscavich's picture

Hello again.

Thanks for the further advice and the updated version.
Sadly, the issue was not resolved, so I did as you asked and raised an issue report containing instructions on re-creating the problem from scratch.
Please see https://github.com/opentk/opentk/issues/3

In case it's any help, here is the output log from the test case (using the commit you created):
The thread 'vshost.LoadReference' (0x16d0) has exited with code 0 (0x0).
'ReadPixelsTest.vshost.exe' (Managed (v4.0.30319)): Loaded 'K:\Win7Downloads\Downloads\Programming_Stuff\ReadPixelsTest\ReadPixelsTest\bin\Release\ReadPixelsTest.exe', Symbols loaded.
'ReadPixelsTest.vshost.exe' (Managed (v4.0.30319)): Loaded 'K:\Win7Downloads\Downloads\Programming_Stuff\ReadPixelsTest\ReadPixelsTest\bin\Release\OpenTK.GLControl.dll', Symbols loaded.
'ReadPixelsTest.vshost.exe' (Managed (v4.0.30319)): Loaded 'K:\Win7Downloads\Downloads\Programming_Stuff\ReadPixelsTest\ReadPixelsTest\bin\Release\OpenTK.dll', Symbols loaded.
'ReadPixelsTest.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Creating default GraphicsMode (32, 24, 8).
Detected configuration: Windows / .Net
SetProcessDPIAware() returned True
A first chance exception of type 'System.DllNotFoundException' occurred in OpenTK.dll
Creating GraphicsContext.
GraphicsMode: Index: , Color: 32 (8888), Depth: 24, Stencil: 8, Samples: 0, Accum: 0 (0000), Buffers: 2, Stereo: False
IWindowInfo: Windows.WindowInfo: Handle 133056, Parent (null)
GraphicsContextFlags: Default
Requested version: 1.0
Loaded opengl32.dll: 416284672
Creating temporary context for wgl extensions.
DisplayDevice 1 (primary) supports 141 resolutions.
DisplayDevice 2 (secondary) supports 141 resolutions.
Retrieving ARB pixel formats.... failed.
Device context: 2063668582
Retrieving PFD pixel formats...
Setting pixel format... 64
Load extensions for OpenTK.Platform.Windows.Wgl... 50 extensions loaded in 18 ms.
Destroying window: Windows.WindowInfo: Handle 133058, Parent (null)
OpenGL will be bound to window:133056 on thread:10
Retrieving ARB pixel formats.... Setting pixel format... 10
Using WGL_ARB_create_context... success! (id: 131072)
Load extensions for OpenTK.Platform.Windows.Wgl... 26 extensions loaded in 0 ms.
Load extensions for OpenTK.Platform.Windows.Wgl... 50 extensions loaded in 12 ms.
Loading extensions for OpenTK.Graphics.OpenGL.GL... 2648 extensions loaded in 501.7161 ms.
Loading extensions for OpenTK.Graphics.OpenGL4.GL... 672 extensions loaded in 148.5208 ms.
Loading extensions for OpenTK.Graphics.ES10.GL... 106 extensions loaded in 17.9311 ms.
Loading extensions for OpenTK.Graphics.ES11.GL... 384 extensions loaded in 59.2635 ms.
Loading extensions for OpenTK.Graphics.ES20.GL... 342 extensions loaded in 72.7862 ms.
Loading extensions for OpenTK.Graphics.ES30.GL... 446 extensions loaded in 94.5429 ms.
'ReadPixelsTest.vshost.exe' (Managed (v4.0.30319)): Loaded 'K:\Win7Downloads\Downloads\Programming_Stuff\ReadPixelsTest\ReadPixelsTest\bin\Release\QuickFont.dll'
Disposing context 131072.
The thread 'vshost.RunParkingWindow' (0xe70) has exited with code 0 (0x0).
The thread '' (0x438) has exited with code 0 (0x0).
OpenTK.Toolkit leaked, did you forget to call Dispose()?
OpenTK.Platform.Factory+UnsupportedPlatform leaked, did you forget to call Dispose()?
OpenTK.Platform.Windows.WinFactory leaked, did you forget to call Dispose()?
OpenTK.Platform.Factory leaked, did you forget to call Dispose()?
The program '[4368] ReadPixelsTest.vshost.exe: Program Trace' has exited with code 0 (0x0).
The program '[4368] ReadPixelsTest.vshost.exe: Managed (v4.0.30319)' has exited with code 0 (0x0).

I hope that's of some use.
If you need any further information from me to get to the bottom of the issue, then please let me know - I'm more than happy to help.

Many thanks for all your efforts, and good luck in tracking down the issue :)

Regards,
Andy

the Fiddler's picture

Follow up here: https://github.com/opentk/opentk/issues/3

In short, you cannot read pixels from an invisible window. You must either move the readback from the Load event to the Paint event, or you must render to a framebuffer object and read back from that.

Mouscavich's picture

Hi,

Many thanks for the information.
Once you pointed out that the controls aren't necessarily visible during OnLoad(), it all seemed obvious why the results were inconsistent. Rookie mistake on my part :/

I did try (briefly) moving the read into the Paint() event, but did not have much success.
However, I ported my application into a GameWindow and did the readback from a FBO, and this works perfectly! Both machines successfully detect the pre-drawn pixels as expected, and render the rest of the graphics based on the values returned.

Once again, thank you for your time and effort - it's greatly appreciated.
I've closed off the GitHub issue.

Regards,
Andy