Loading PNG to GLuint

Member
Posts: 215
Joined: 2008.06
Post: #1
Me again. Right. I'm amazed at how difficult this has become, but I suppose I'm going at it the wrong way. I am trying to load in a .png image into a GLuint so that I can use it for texturing.

I found some Apple sample code:

Code:
- (void)textureFromImage:(NSImage*)theImg textureName:(GLuint*)texName
{
    NSBitmapImageRep* bitmap = [NSBitmapImageRep alloc];
    int samplesPerPixel = 0;
    NSSize imgSize = [theImg size];
    
    [theImg lockFocus];
    [bitmap initWithFocusedViewRect:
     NSMakeRect(0.0, 0.0, imgSize.width, imgSize.height)];
    [theImg unlockFocus];
    
    // Set proper unpacking row length for bitmap.
    glPixelStorei(GL_UNPACK_ROW_LENGTH, [bitmap pixelsWide]);
    
    // Set byte aligned unpacking (needed for 3 byte per pixel bitmaps).
    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
    
    // Generate a new texture name if one was not provided.
    if (*texName == 0)
        glGenTextures (1, texName);
    glBindTexture (GL_TEXTURE_RECTANGLE_EXT, *texName);
    
    // Non-mipmap filtering (redundant for texture_rectangle).
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER,  GL_LINEAR);
    samplesPerPixel = [bitmap samplesPerPixel];
    
    // Nonplanar, RGB 24 bit bitmap, or RGBA 32 bit bitmap.
    if(![bitmap isPlanar] &&
       (samplesPerPixel == 3 || samplesPerPixel == 4))
    {
        glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0,
                     samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8,
                     [bitmap pixelsWide],
                     [bitmap pixelsHigh],
                     0,
                     samplesPerPixel == 4 ? GL_RGBA : GL_RGB,
                     GL_UNSIGNED_BYTE,
                     [bitmap bitmapData]);
    }
    else
    {
        // Handle other bitmap formats.
    }
    
    // Clean up.
    [bitmap release];
}

However, this method does not return a useful texture. When I draw my object I simply get a white quad.

Code:
//glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    
    glEnable(GL_TEXTURE_2D);
        //texture is an instance field set by it's creator.
    glBindTexture(GL_TEXTURE_2D, texture);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
    glBegin(GL_QUADS);
    {
        glTexCoord2f(0.0f, 0.0f);
        glVertex2f(-[halfwidths magnitudeX], -[halfwidths magnitudeY]);
        glTexCoord2f(1.0f, 0.0f);
        glVertex2f([halfwidths magnitudeX], -[halfwidths magnitudeY]);
        glTexCoord2f(1.0f, 1.0f);
        glVertex2f([halfwidths magnitudeX], [halfwidths magnitudeY]);
        glTexCoord2f(0.0f, 1.0f);
        glVertex2f(-[halfwidths magnitudeX], [halfwidths magnitudeY]);
    }
    glEnd();

Can anyone tell me what's going on, a better way to do this, point me to an Obj-C class to do this, or tell me to quit while I'm way behind?! This has been 3 weeks of nothing but torture trying to get PNG loading working. Thanks ahead of time.

Mac users swear by their computers, PC users swear at their computers. ~Unknown

iSayz
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #2
There's an obvious problem with your code, but... if you've spent three weeks on this, then your biggest problem is that you need to learn how to use the debugger.

Step through your function one line at a time in the debugger and watch it execute. At each line, check all the variables and make sure they're what you expect. When you hit glTexImage, look at the pointer you're passing in (learn how to use gdb) and make sure the pixels you're passing to GL are correct.

Now, for your obvious bug. Take a look at the texture target you're binding when you load the texture. Now compare with the texture target you're binding when you draw the texture. OpenGL Profiler can help you find this sort of bug-- just break on your draw call and look at the current state and the current textures.
Quote this message in a reply
Member
Posts: 215
Joined: 2008.06
Post: #3
arekkusu Wrote:There's an obvious problem with your code, but... if you've spent three weeks on this, then your biggest problem is that you need to learn how to use the debugger.

Step through your function one line at a time in the debugger and watch it execute. At each line, check all the variables and make sure they're what you expect. When you hit glTexImage, look at the pointer you're passing in (learn how to use gdb) and make sure the pixels you're passing to GL are correct.

Now, for your obvious bug. Take a look at the texture target you're binding when you load the texture. Now compare with the texture target you're binding when you draw the texture. OpenGL Profiler can help you find this sort of bug-- just break on your draw call and look at the current state and the current textures.

I have already stepped through using the GDB debugger, and my biggest issue is that I'm not sure what I should be looking for. Also, when I run OpenGL Profiler, it refuses to attach to my project. Any idea why? To clear up the naming issue I believe you are trying to highlight: shortly after the call textureFromImage: is called, [o setTexture:tex]; is sent.

EDIT: Using the debugger, GLuint tex is initialized to 107808. After the load it is still 107808. When my object is drawn it is STILL 107808. What's going on?

Mac users swear by their computers, PC users swear at their computers. ~Unknown

iSayz
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #4
In gdb, you're looking for anything unexpected, like the NSBitmapImageRep being NULL, or pixelsWide not being the size of your image. You should get in the habit of using assertions-- in your debug build, assert that the image was loaded, and that no GL errors occurred.

If Profiler won't attach to your app, launch it from Profiler instead-- add your app with the + button in Profiler's main window. Set a breakpoint on something you know you'll hit, like CGLFlushDrawable. Then launch your application, you should break the first time you're about to swap. At that point, you can inspect all of the GL state (the changed "from default state" checkbox is your friend here) as well as the current content of the back/depth/stencil buffers and all resources such as textures.

To be very specific-- in your image loader, you're binding and creating a RECTANGLE texture. But then when you draw, you're binding a 2D texture. Of course that's not going to work. Profiler will show you this-- in the resource viewer you should see a RECTANGLE texture with the name that you Gen'd. And in the state inspector you'll see that TEXTURE_2D is enabled, not TEXTURE_RECTANGLE. Profiler's "break on errors" checkbox should also catch this, because it's illegal to bind a texture name to a target different than the one it was initially used as.
Quote this message in a reply
Member
Posts: 215
Joined: 2008.06
Post: #5
arekkusu Wrote:In gdb, you're looking for anything unexpected, like the NSBitmapImageRep being NULL, or pixelsWide not being the size of your image. You should get in the habit of using assertions-- in your debug build, assert that the image was loaded, and that no GL errors occurred.

If Profiler won't attach to your app, launch it from Profiler instead-- add your app with the + button in Profiler's main window. Set a breakpoint on something you know you'll hit, like CGLFlushDrawable. Then launch your application, you should break the first time you're about to swap. At that point, you can inspect all of the GL state (the changed "from default state" checkbox is your friend here) as well as the current content of the back/depth/stencil buffers and all resources such as textures.

To be very specific-- in your image loader, you're binding and creating a RECTANGLE texture. But then when you draw, you're binding a 2D texture. Of course that's not going to work. Profiler will show you this-- in the resource viewer you should see a RECTANGLE texture with the name that you Gen'd. And in the state inspector you'll see that TEXTURE_2D is enabled, not TEXTURE_RECTANGLE. Profiler's "break on errors" checkbox should also catch this, because it's illegal to bind a texture name to a target different than the one it was initially used as.

OH! I had no idea that OGL would be unable to convert between a PoT image size and a variable image size. Alright, we're getting somewhere. So, with those changes in place, specifically:

Code:
glEnable(GL_TEXTURE_RECTANGLE_EXT);
    glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texture);
    
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
    glBegin(GL_QUADS);
    {
        glTexCoord2f(0.0f, 0.0f);
        glVertex2f(-[halfwidths magnitudeX], -[halfwidths magnitudeY]);
        glTexCoord2f(1.0f, 0.0f);
        glVertex2f([halfwidths magnitudeX], -[halfwidths magnitudeY]);
        glTexCoord2f(1.0f, 1.0f);
        glVertex2f([halfwidths magnitudeX], [halfwidths magnitudeY]);
        glTexCoord2f(0.0f, 1.0f);
        glVertex2f(-[halfwidths magnitudeX], [halfwidths magnitudeY]);
    }
    glEnd();

I end up with a quad colored with a solid single color (dark red in this case). Any idea what may be causing it to draw only this color? I have not yet tried to run my project from the Profiler, I'll let you know how that goes. Thanks!

USED OGLProfiler: How do I set breakpoints? And I've been tracking my texture, it's value is STILL 107808. I have no idea how that's happening.

Mac users swear by their computers, PC users swear at their computers. ~Unknown

iSayz
Quote this message in a reply
Moderator
Posts: 3,571
Joined: 2003.06
Post: #6
Not to detract from what you're learning right now, but here's some more code donation that might help for comparison. To use it:

- create a new iPhone project using the "OpenGL ES Application" template
- add CoreGraphics.framework
- drag a POT image into the Resources group, named "Sprite.png"
- copy and paste this code to replace all the code in EAGLView.m [the only major addition to the template code is LoadTextureWithAlpha]:

Code:
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>

#import "EAGLView.h"

#define USE_DEPTH_BUFFER 0

// A class extension to declare private methods
@interface EAGLView ()

@property (nonatomic, retain) EAGLContext *context;
@property (nonatomic, assign) NSTimer *animationTimer;

- (BOOL) createFramebuffer;
- (void) destroyFramebuffer;

@end

GLuint    sprite;

GLuint LoadTextureWithAlpha(NSString *imageFileName)
{
    GLuint            texID;
    CGImageRef        image;
    void            *pixels;
    unsigned        width, height;
    CGContextRef    context;
    CGColorSpaceRef    colorSpace;
    
    image = [[UIImage imageNamed:imageFileName] CGImage];
    if (image == nil)
    {
        NSLog(@"could not find %@", imageFileName);
        return 0;
    }
    width = CGImageGetWidth(image);
    height = CGImageGetHeight(image);
    
    pixels = calloc(width * height * 4, 1);
    colorSpace = CGImageGetColorSpace(image);
    context = CGBitmapContextCreate(pixels, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
    
    glGenTextures(1, &texID);
    glBindTexture(GL_TEXTURE_2D, texID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    
    CGImageRelease(image);
    CGContextRelease(context);
    free(pixels);
    
    return texID;
}

@implementation EAGLView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;


// You must implement this method
+ (Class)layerClass {
    return [CAEAGLLayer class];
}


//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {
    
    if ((self = [super initWithCoder:coder])) {
        // Get the layer
        CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
        
        eaglLayer.opaque = YES;
        eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                        [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
        
        context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
        
        if (!context || ![EAGLContext setCurrentContext:context]) {
            [self release];
            return nil;
        }
        
        animationInterval = 1.0 / 60.0;
        
        sprite = LoadTextureWithAlpha(@"Sprite.png");
        
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        
    }
    return self;
}


- (void)drawView {
    
    // Replace the implementation of this method to do your own custom drawing
    
    const GLfloat squareVertices[] = {
        -0.5f, -0.5f,
        0.5f,  -0.5f,
        -0.5f,  0.5f,
        0.5f,   0.5f,
    };
  
    GLshort    genericTexCoords[] = { 0, 1, 1, 1, 0, 0, 1, 0 };
    
    [EAGLContext setCurrentContext:context];
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glRotatef(3.0f, 0.0f, 0.0f, 1.0f);
    
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glVertexPointer(2, GL_FLOAT, 0, squareVertices);
    glTexCoordPointer(2, GL_SHORT, 0, genericTexCoords);
    
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, sprite);
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}


- (void)layoutSubviews {
    [EAGLContext setCurrentContext:context];
    [self destroyFramebuffer];
    [self createFramebuffer];
    [self drawView];
}


- (BOOL)createFramebuffer {
    
    glGenFramebuffersOES(1, &viewFramebuffer);
    glGenRenderbuffersOES(1, &viewRenderbuffer);
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
    
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
    
    if (USE_DEPTH_BUFFER) {
        glGenRenderbuffersOES(1, &depthRenderbuffer);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
        glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
    }
    
    if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
        return NO;
    }
    
    return YES;
}


- (void)destroyFramebuffer {
    
    glDeleteFramebuffersOES(1, &viewFramebuffer);
    viewFramebuffer = 0;
    glDeleteRenderbuffersOES(1, &viewRenderbuffer);
    viewRenderbuffer = 0;
    
    if(depthRenderbuffer) {
        glDeleteRenderbuffersOES(1, &depthRenderbuffer);
        depthRenderbuffer = 0;
    }
}


- (void)startAnimation {
    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
}


- (void)stopAnimation {
    self.animationTimer = nil;
}


- (void)setAnimationTimer:(NSTimer *)newTimer {
    [animationTimer invalidate];
    animationTimer = newTimer;
}


- (void)setAnimationInterval:(NSTimeInterval)interval {
    
    animationInterval = interval;
    if (animationTimer) {
        [self stopAnimation];
        [self startAnimation];
    }
}


- (void)dealloc {
    
    [self stopAnimation];
    
    if ([EAGLContext currentContext] == context) {
        [EAGLContext setCurrentContext:nil];
    }
    
    [context release];  
    [super dealloc];
}

@end
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #7
So, now there are two problems, both of which stem from you not having read the documentation.

First, this isn't an issue of converting NPOT images or anything like that. When you bind an object, like with glBindTexture, you're binding a specific name in a specific target. Another word for target is "namespace" if that helps you understand it.

If you create a 2D texture object, and try to use it as a vertex buffer object, do you expect OpenGL to magically convert it for you? Of course not. What if you try to use it as a 3D texture object? A 2D texture object is sort of like a 3D texture object with only one slice of data, but they're technically completely different. They're in different namespaces. So this is an error. You should call glGetError at least once (at the end of your main rendering loop, protected by #if DEBUG) to catch this kind of bug. It's your bug-- you're misusing the API.

Second, RECTANGLE texture objects have some specific differences from 2D texture objects. That's why they exist-- they're different. One of the differences is how the texture coordinates are used. You need to pass in unnormalized coordinates. If you pass in [0,1] you're going to sample exactly one pixel, so you see the same color everywhere.

Read the documentation, it explains all of this.
Quote this message in a reply
Member
Posts: 215
Joined: 2008.06
Post: #8
arekkusu Wrote:So, now there are two problems, both of which stem from you not having read the documentation.

First, this isn't an issue of converting NPOT images or anything like that. When you bind an object, like with glBindTexture, you're binding a specific name in a specific target. Another word for target is "namespace" if that helps you understand it.

If you create a 2D texture object, and try to use it as a vertex buffer object, do you expect OpenGL to magically convert it for you? Of course not. What if you try to use it as a 3D texture object? A 2D texture object is sort of like a 3D texture object with only one slice of data, but they're technically completely different. They're in different namespaces. So this is an error. You should call glGetError at least once (at the end of your main rendering loop, protected by #if DEBUG) to catch this kind of bug. It's your bug-- you're misusing the API.

Second, RECTANGLE texture objects have some specific differences from 2D texture objects. That's why they exist-- they're different. One of the differences is how the texture coordinates are used. You need to pass in unnormalized coordinates. If you pass in [0,1] you're going to sample exactly one pixel, so you see the same color everywhere.

Read the documentation, it explains all of this.

Awesome! That explains a lot. Changing the draw code to glTexCoord2f(256.0f, 256.0f) fixed it...except it's upside down. Is there a way to invert the image before storage as the texture, or is that explained in the documentation? I'm sensing some annoyance on your part, arekksu, and I would just like to say thank you. Myself and others thank you for your help here. Thanks again, and you're right: I really should just RTFM, but I'm in a bit of rush... However, once this is ready to go, I will be sure to read the appropriate documentation before bugging you guys.

Mac users swear by their computers, PC users swear at their computers. ~Unknown

iSayz
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #9
I'm happy to explain these things, but I've started a FAQ thread to attempt to document some of these recurring issues in one place. Texturing is very easy... after you've figured it out once. But these questions keep coming up.

As for flipping the image, the documentation explains how both GL's coordinate system and a texture's image is treated with the origin in the lower left. Yes, this is upside down compared to every file format out there. Yes, everybody runs into this. There are several ways to deal with it, the simplest way is to flip your geometry upside down.
Quote this message in a reply
Member
Posts: 215
Joined: 2008.06
Post: #10
arekkusu Wrote:I'm happy to explain these things, but I've started a FAQ thread to attempt to document some of these recurring issues in one place. Texturing is very easy... after you've figured it out once. But these questions keep coming up.

As for flipping the image, the documentation explains how both GL's coordinate system and a texture's image is treated with the origin in the lower left. Yes, this is upside down compared to every file format out there. Yes, everybody runs into this. There are several ways to deal with it, the simplest way is to flip your geometry upside down.

So you recommend doing a glRotatef(180.0f, 0.0f, 0.0f, 1.0f) before drawing to solve this issue? I'm somewhat surprised that NSImage does not have a method to invert the image. Again, thanks for the help!

Mac users swear by their computers, PC users swear at their computers. ~Unknown

iSayz
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #11
I wouldn't use glRotate per draw. You can generally assume that all of your images are going to be loaded in the same way, so you can flip your entire coordinate system once so that the origin is at top left. The place to do that is the projection matrix.

If you do this, you still need to keep in mind that the GL API expects the origin at lower left, and you can't control non-drawing API with a matrix. For example, if you use glReadPixels, those coordinates will be upside down compared to your drawing.

If it's conceptually easier for you to leave the origin alone, then of course you can flip images when you load them. That's just some extra cost to shuffle the data around.
Quote this message in a reply
Member
Posts: 215
Joined: 2008.06
Post: #12
arekkusu Wrote:I wouldn't use glRotate per draw. You can generally assume that all of your images are going to be loaded in the same way, so you can flip your entire coordinate system once so that the origin is at top left. The place to do that is the projection matrix.

If you do this, you still need to keep in mind that the GL API expects the origin at lower left, and you can't control non-drawing API with a matrix. For example, if you use glReadPixels, those coordinates will be upside down compared to your drawing.

If it's conceptually easier for you to leave the origin alone, then of course you can flip images when you load them. That's just some extra cost to shuffle the data around.

Well, after some thought, my system works really well with the coordinate system that I have now. It certainly seems like the minor overhead of flipping the image data would be preferred over having to completely re-think my coordinate system. Of course, I say all this before seeing HOW to flip the data... How would you recommend flipping the data and what are some of the caveats of the technique you recommend?

Mac users swear by their computers, PC users swear at their computers. ~Unknown

iSayz
Quote this message in a reply
Moderator
Posts: 3,571
Joined: 2003.06
Post: #13
With CG I scale it to flip Y, and change the destination rect:

Code:
CGContextScaleCTM(context, 1.0f, -1.0f);
CGContextDrawImage(context, CGRectMake(0, -(float)imageSize.height, imageSize.width, imageSize.height), image);

But if all you're drawing is sprites on quads, then simply flip either your sprite verts or your tex coords.
Quote this message in a reply
Member
Posts: 215
Joined: 2008.06
Post: #14
AnotherJake Wrote:With CG I scale it to flip Y, and change the destination rect:

Code:
CGContextScaleCTM(context, 1.0f, -1.0f);
CGContextDrawImage(context, CGRectMake(0, -(float)imageSize.height, imageSize.width, imageSize.height), image);

But if all you're drawing is sprites on quads, then simply flip either your sprite verts or your tex coords.

Right, duh! Thanks! That works like a charm. I can't tell you guys how relieved I am to have this under my belt. Thanks a lot.

Mac users swear by their computers, PC users swear at their computers. ~Unknown

iSayz
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #15
Talyn Wrote:USED OGLProfiler: How do I set breakpoints?

1) Launch OpenGL Profiler
2) Go to the Help menu, and choose "OpenGL Profiler User Guide"
3) read "Using Breakpoints" > "Setting Breakpoints on OpenGL Functions".

See? The documentation is sitting there, waiting for you. The time you invest learning will save you 100x the time you'll waste otherwise.
Quote this message in a reply
Post Reply