• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Incomplete rendering returned from glReadPixels
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Incomplete rendering returned from glReadPixels


  • Subject: Incomplete rendering returned from glReadPixels
  • From: Ben Haller <email@hidden>
  • Date: Tue, 22 Jun 2010 09:25:44 -0400

Hi all. I'm trying to render some graphics using NSOpenGLView and then save the rendered bits as a TIFF image. I'm having trouble getting this working. I already posted this question to the mac- opengl list, and didn't get a solution. I'm posting here now because I'm using NSOpenGLView, and perhaps my difficulties are related to it, so maybe it's really a Cocoa issue in that sense. Anyhow, I'm hoping somebody else in the Cocoa world has figured out this problem.
I'm seeing incomplete rendering in the saved TIFF; it looks like the glReadPixels returns the pixels of the framebuffer before all of the drawing that I've requested has completed. Perhaps a picture is worth a thousand words. The way these images ought to look:


http://www.sticksoftware.com/task3_landscape.tiff

 The way they often do look:

http://www.sticksoftware.com/task1_landscape.tiff

So as you can see, the rendering is perfectly fine except that not all the drawing has completed yet. Note these two images were both generated by the same exact code, in the same run of the app; but sometimes the rendering gets completed, and sometimes it doesn't.

Here's my top-level method to write the image; this is a method on a subclass of NSOpenGLView:


- (void)writeToFile:(NSString *)filePath
{
if (![self window])
{
// OpenGL doesn't seem to display unless we're in an onscreen window :-<
NSRect popViewFrame = [self frame];
NSWindow *popViewWindow = [[NSWindow alloc] initWithContentRect:popViewFrame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];

[popViewWindow setContentView:self];
[popViewWindow orderFront:nil];
[self setNeedsDisplay:YES];
[self display];
}

NSImage *snapImage = [self imageFromView];
NSData *tiffData = [snapImage TIFFRepresentation];

[tiffData writeToFile:filePath atomically:YES];
}



It's a little weird because this method gets called sometimes on a view that is not yet installed in a window, so I just make a window then and there. (This is run in a headless command-line app, I ought to mention.) And yes, I don't bother cleaning up the window and such; right after this image writes out, the task exits, so cleanup doesn't matter. Note that I am sure the rendering is in fact complete; I see the window flash onscreen, and it has everything in it. (I can also run my app in a GUI mode, and all the rendering looks right then.)


The -imageFromView method is pretty much straight from the web (in fact, I think this code has gone over the list before):


// From http://www.ripplon.com/code/Cocoa/NSOpenGLView-imageFromView.m

@implementation NSOpenGLView (AKImageGeneration)

static void memxor(unsigned char *dst, unsigned char *src, unsigned int bytes)
{
while (bytes--) *dst++ ^= *src++;
}


static void memswap(unsigned char *a, unsigned char *b, unsigned int bytes)
{
memxor(a, b, bytes);
memxor(b, a, bytes);
memxor(a, b, bytes);
}


- (NSImage *)imageFromView
{
	NSRect bounds;
	int height, width, row, bytesPerRow;
	NSBitmapImageRep *imageRep;
	unsigned char *bitmapData;
	NSImage *image;

	bounds = [self bounds];

	height = bounds.size.height;
	width = bounds.size.width;

	imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
													   pixelsWide: width
													   pixelsHigh: height
													bitsPerSample: 8
												  samplesPerPixel: 4
														 hasAlpha: YES
														 isPlanar: NO
												   colorSpaceName: NSCalibratedRGBColorSpace
													  bytesPerRow: 0				// indicates no empty bytes at row end
													 bitsPerPixel: 0];

	[[self openGLContext] makeCurrentContext];

	glFlush();

bitmapData = [imageRep bitmapData];

bytesPerRow = [imageRep bytesPerRow];

glPixelStorei(GL_PACK_ROW_LENGTH, 8*bytesPerRow/[imageRep bitsPerPixel]);

glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bitmapData);

glFlush();


// Flip the bitmap vertically to account for OpenGL coordinate system difference
// from NSImage coordinate system.

for (row = 0; row < height/2; row++)
{
unsigned char *a, *b;

a = bitmapData + row * bytesPerRow;
b = bitmapData + (height - 1 - row) * bytesPerRow;

memswap(a, b, bytesPerRow);
}

// Create the NSImage from the bitmap

image = [[[NSImage alloc] initWithSize: NSMakeSize(width, height)] autorelease];
[image addRepresentation: imageRep];

// Release memory

[imageRep release];

// Previously we did not flip the bitmap, and instead did [image setFlipped:YES];
// This does not work properly (i.e., the image remained inverted) when pasting
// the image to AppleWorks or GraphicConvertor.

return image;
}


@end


I added the two glFlush() calls in an attempt to fix this bug, but they make no difference; the image still sometimes gets written out with partial data. Changing them to glFinish() calls makes no difference. Indeed, adding sleep(1) calls to try to guarantee that drawing has completed before I get the pixels also makes no difference.
Finally, I suppose some code from my NSOpenGLView subclass might be relevant:



- (id)initWithFrame:(NSRect)frame
{
GLuint pixelFormatAttributes[] =
{
//NSOpenGLPFANoRecovery,
NSOpenGLPFAWindow,
NSOpenGLPFAAccelerated,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
//NSOpenGLPFADepthSize, 24,
//NSOpenGLPFAStencilSize, 8,
NSOpenGLPFADepthSize, 0,
NSOpenGLPFAStencilSize, 0,
NSOpenGLPFAAccumSize, 0,
0
};

NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*)pixelFormatAttributes];

if (!pixelFormat)
[NSException raise:NSInternalInconsistencyException format:@"No OpenGL pixel format in %@", NSStringFromSelector(_cmd)];


if (self = [super initWithFrame:frame pixelFormat:[pixelFormat autorelease]])
{
}

return self;
}


- (void)drawRect:(NSRect)rect
{
NSRect bounds = [self bounds];

// Update the viewport
glViewport(0, 0, bounds.size.width, bounds.size.height);

// Update the projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, bounds.size.width, bounds.size.height, 0.0); // swap bottom and top to create a flipped coordinate system
glMatrixMode(GL_MODELVIEW);

// Clear to white; we used to use glClear(), but that does not work correctly when saving to a TIFF, bizarrely...
glColor3f(1.0, 1.0, 1.0);
glRecti(0, 0, bounds.size.width, bounds.size.height);

// Frame our view
glColor3f(0.25, 0.25, 0.25);
glRecti(0, 0, 1, bounds.size.height);
glRecti(1, 0, bounds.size.width - 1, 1);
glRecti(bounds.size.width - 1, 0, bounds.size.width, bounds.size.height);
glRecti(1, bounds.size.height - 1, bounds.size.width - 1, bounds.size.height);

// Further drawing
...

[[self openGLContext] flushBuffer];
}



How can I get this to work properly? My OpenGL Reference Manual (page 2) says: "...each primitive is drawn completely before any subsequent command takes effect... state-querying commands return data that's consistent with complete execution of all previously issued OpenGL commands." So doesn't that mean that this result should be impossible, according to the spec? I'm very inexperienced with OpenGL, though, so I may have made some rookie mistake in the code above. (Feel free to critique my code in other respects too, I'd like to learn.)


   Thanks!

Ben Haller
McGill University

_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Follow-Ups:
    • Re: Incomplete rendering returned from glReadPixels
      • From: Michael Ash <email@hidden>
  • Prev by Date: Re: an oldie but a goodie: observing properties of collected objects
  • Next by Date: Flip and Enlarge CALayer at the same time
  • Previous by thread: NSToolbar setup in conjunction with IB
  • Next by thread: Re: Incomplete rendering returned from glReadPixels
  • Index(es):
    • Date
    • Thread