2
votes

I'm trying to implement the glReadPixels function from OpenGL in Unity3D (C#).

public IntPtr buffer;
private const int GL_RGBA                   = 0x1908;
private const int GL_UNSIGNED_BYTE          = 0x1401;

[DllImport("/System/Library/Frameworks/OpenGL.framework/OpenGL")]
public static extern void glReadPixels (int x, int y, int width, int height, int format, int type, IntPtr buffer);

This is the memory allocation:

byte[] information=new byte[20*20*3];
IntPtr unmanagedPointer = Marshal.AllocHGlobal(information.Length);
Marshal.Copy(information, 0, unmanagedPointer, information.Length);

So, in this script attached to a camera, I manually call

GetComponent<Camera>().Render();

And, on OnPostRender:

void OnPostRender() {
    glReadPixels (0, 0, 20, 20, GL_RGBA, GL_UNSIGNED_BYTE, unmanagedPointer);
}

However, Unity3D editor crashes and no other clue is available to know exactly what happened. I don't know If I need to do anything else to use that OpenGL function.

EDIT: This is the crash report from Unity:

1   libsystem_c.dylib               0x00007fff91bd9b73 abort + 129
2   com.unity3d.UnityEditor5.x      0x0000000100718ae7 HandleSignal(int, __siginfo*, void*) + 1031
3   libmono.0.dylib                 0x00000001056738e2 mono_chain_signal + 71
4   libmono.0.dylib                 0x00000001055c0f37 mono_sigsegv_signal_handler + 213
5   libsystem_platform.dylib        0x00007fff91ca1f1a _sigtramp + 26
6   libGLImage.dylib                0x00007fff8e6a381f storeVecColor + 2975
7   libGLImage.dylib                0x00007fff8e6b17a2 glgProcessColor + 14610
8   libGLImage.dylib                0x00007fff8e685104 __glgProcessPixelsWithProcessor_block_invoke + 108
9   libdispatch.dylib               0x00007fff884f5344 _dispatch_client_callout2 + 8
10  libdispatch.dylib               0x00007fff884f5873 _dispatch_apply_serial + 42
11  libdispatch.dylib               0x00007fff884e9c13 _dispatch_client_callout + 8
12  libdispatch.dylib               0x00007fff884f49a1 _dispatch_sync_f_invoke + 39
13  libdispatch.dylib               0x00007fff884f4f60 dispatch_apply_f + 290
14  libGLImage.dylib                0x00007fff8e684ece glgProcessPixelsWithProcessor + 6869
15  com.apple.GeForceTeslaGLDriver  0x0000123440311ea0 0x123440000000 + 3219104
16  GLEngine                        0x00007fff923acbf8 glReadPixels_Exec + 1390
17  libGL.dylib                     0x00007fff91770bd5 glReadPixels + 57

UPDATE: Finally, I managed to fix that crash. unmanagedPointer was not correctly being assigned. However, there's still some problem with memory allocation. If I render the camera on Update() (which calls glReadPixels on OnPostRender), Unity crashes about 3-4 seconds after.

As you can see on this crash report:

6   libGPUSupport.dylib             0x00000001188a627d 
gpulCheckForFramebufferIOSurfaceChanges + 54
7   libGPUSupport.dylib             0x00000001188a6202 gldUpdateReadFramebuffer + 42
8   GLEngine                        0x00007fff9247e01c gleUpdateReadFramebufferState + 425
9   GLEngine                        0x00007fff923ac751 glReadPixels_Exec + 

It seems to have something to do with gpulCheckForFramebuffer. Any idea?

1
I assume you actually allocate memory in the buffer for glReadPixels to copy to? IIRC glReadPixels reads from the current framebuffer. Do you actually know if there is one bound? This seems like a very weird thing to want to do. Why not use one of the existing Unity methods (Application.CaptureScreenshot, RenderTexture or Texture2D.ReadPixels). - user673679
I'll second the remark about allocating a buffer for glReadPixels: looks like you didn't, and you definitely should. Also, are you sure you have Unity running in OpenGL mode (i.e. by using the command line parameters), and not the default D3D rendering pipeline? And last but not least: please tell us more about what you are trying to achieve with glReadPixels. There might be a much better way to achieve your goal. - Paul-Jan
I am using an orthographic camera to slice the scene changing far and near attributes. I started using Texture2D.ReadPixels but it's very slow. I read this method was way faster but I'm still trying to implement it. I definitely think the crash has to do with memory allocation. I've updated the post with the code I'm using right now. Thanks! - pabgn
@user673679 I think no framebuffer is bound. How do I bind one? Thanks. - pabgn
Ordinarily you'd have a framebuffer object and call glBindFramebuffer() on it... I suspect you'd have to create a framebuffer object yourself, and using Texture2D.GetNativeTextureID, bind the framebuffer and attach the texture to it (glBindFramebuffer and glFramebufferTexture(2D)), then call glReadPixels. I imagine Texture2D.ReadPixels does exactly this underneath though, so I doubt it would be faster... - user673679

1 Answers

3
votes

You are not allocating enough memory for the result. Your allocate 3 bytes per pixel for your 20x20 image:

byte[] information=new byte[20*20*3];

But for the glReadPixels() call, you are specifying the GL_RGBA format:

glReadPixels (0, 0, 20, 20, GL_RGBA, GL_UNSIGNED_BYTE, information);

When reading with the GL_RGBA format, 4 bytes per pixel will be written to the provided destination (one byte each of R, G, B, and A).

You will either need to allocate 4 bytes per pixel:

byte[] information = new byte[20 * 20 * 4];

or change the format to GL_RGB, which requires only 3 bytes per pixel in the result:

glReadPixels(0, 0, 20, 20, GL_RGB, GL_UNSIGNED_BYTE, information);