Cocoa/OpenGL Losing Textures

Apprentice
Posts: 11
Joined: 2007.04
Post: #1
So in my continuing struggle to build a 2D tile based map editor with cocoa and open GL I have encountered another road block -- I am losing textures like crazy!

Before I get in to the code I wonder if there isn't some fundamental concept of loading textures that I am overlooking, so let me ask these questions first:

1) Is loading a texture in a function that returns a GLuint and then passing that GLuint to different classes a bad way to load/pass around textures?

2) When I say "losing textures" I mean that using a GLboolean and testing the texture with glIsTexture returns GL_FALSE -- could this mean something else?

If answers to those questions don't get me anywhere I'll post some code. The annoying thing is that it seems fairly random when the textures don't work. Oh and I load them using a URL to get a CGImageRef and convert that to a GLuint -- it works, sometimes....
Quote this message in a reply
tigakub
Unregistered
 
Post: #2
I'm not sure if this is relevant, but I've found that I loose textures too. I use NSImage to load images, then extract them into my own C++ buffers (I do this because I reformat them to ARGB. I originally thought that after I use glTexImage2D to upload the texture, that I could delete the C++ buffers. I was wrong. I guess that OpenGL needs these buffers to stick around so that it can recover the image if GPU texture memory gets wiped.

So the upshot is this question: are you releasing the CGImageRef after creating the textures?
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2007.04
Post: #3
Yes I am -- I wasn't aware that you needed it either. I'll try keeping that around and see what happens.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #4
tigakub Wrote:I originally thought that after I use glTexImage2D to upload the texture, that I could delete the C++ buffers. I was wrong. I guess that OpenGL needs these buffers to stick around so that it can recover the image if GPU texture memory gets wiped.

Freeing the memory passed to TexImage2D is perfectly legal (unless you're using the APPLE_client_storage extension). I do it all the time.

There's something else at work here. Hard to say what with no information, though Rasp
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2007.04
Post: #5
Ok here's the situation. The use for this texture is to hold a tileset, so as of right now it is loaded from a subclass of the NSOpenGLView (which is set as the document of a scroll view ... that took me forever to figure out how to do).

Here is the load image/convert to GLuint method:

Code:
- (BOOL) loadTexture: (NSString *) path
{
    // set our member variable
    currentTexturePath = path;
    
    // load the image
    currentTexture = [self loadImage: path ];
    
    if( currentTexture)
    {
        hasTexture = true;
        [ mapView setTexture: path withTexture: currentTexture
                                   withWidth: tileSetWidth
                                   withHeight: tileSetHeight ];
        
        return YES;
    }
    else
        return NO;
}

- (GLuint)loadImage: (NSString *) path
{
    CFURLRef    url = CFURLCreateWithFileSystemPath(NULL,(CFStringRef)path,kCFURLPOSIXPathStyle,false)​;
        
    CGImageSourceRef    myImageSourceRef = CGImageSourceCreateWithURL(url, nil);
    CGImageRef            myImageRef = CGImageSourceCreateImageAtIndex(myImageSourceRef,0,nil);
    
    GLuint myTextureName;
    
    size_t width = CGImageGetWidth(myImageRef);
    size_t height = CGImageGetHeight(myImageRef);
    
    tileSetWidth = width;
    tileSetHeight = height;
    
    printf( "Width: %d\n", tileSetWidth);
    printf( "Height: %d\n", tileSetHeight);
    
    CGRect rect = {{0,0},{width,height}};
    void * myData = calloc(width * 4, height );
    
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    CGContextRef myBitmapContext = CGBitmapContextCreate (myData,
                        width, height, 8,
                        width*4, space,
                        kCGImageAlphaPremultipliedLast);
                        
    CGContextDrawImage(myBitmapContext, rect, myImageRef);
    CGContextRelease(myBitmapContext);
    
    glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glGenTextures(1, &myTextureName);
    
    glBindTexture(GL_TEXTURE_2D, myTextureName);
    glTexParameteri(GL_TEXTURE_2D,
                        GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height,
                        0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, myData);
    
    free(myData);    
        
    return myTextureName;
}

Every now and then this works, but more often then not I get a lost texture call when I implement the views "DrawRect" like so:

Code:
- (void)drawRect:(NSRect )aRect
{
    [self reshape];
    
    glClear(GL_COLOR_BUFFER_BIT);
    
    if(hasTexture)
    {
        // Test our texture
        GLboolean test = glIsTexture(currentTexture);
    
        if( test!= GL_TRUE)
        {
            NSLog( @"Attempting reload of texture...\n" );
            [ self loadTexture: currentTexturePath ];
            [ self setNeedsDisplay: YES ];
        }
        else
        {
            // get ready for texture rendering
            glEnable(GL_TEXTURE_2D);                            // Enable Texture Mapping
            glShadeModel(GL_SMOOTH);                            // Enable Smooth Shading

            glTranslatef(0.0f, 0.0f, 0.0f);
    
            // bind our texture
            glBindTexture( GL_TEXTURE_2D, currentTexture);

            glBegin(GL_QUADS);
            {
                glTexCoord2f(0.0f,0.0f); glVertex2f( 0,  0  );   // bottom left
                glTexCoord2f(1.0f,0.0f); glVertex2f( tileSetWidth,  0 );   // bottom right
                glTexCoord2f(1.0f,1.0f); glVertex2f( tileSetWidth, tileSetHeight );   // top right
                glTexCoord2f(0.0f,1.0f); glVertex2f( 0,  tileSetHeight );   // top left
            }
            glEnd();
            
            glDisable(GL_TEXTURE_2D);

        }
    }
    
    glFlush();

}

This happens with every texture i try to load.

I'm hoping this is one of those glaringly obvious easy fixes...
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #6
Code looks OK at a glance... don't think I've seen the buggy bit yet Rasp

Are you loading the images when the same OpenGL context is current?

Why are you checking glIsTexture at all?

Are you calling glDeleteTextures somewhere accidentally?

Does the OpenGL profiler tell you anything?
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2007.04
Post: #7
Quote:Are you loading the images when the same OpenGL context is current?

Ahh I think that might've been it. I have two separate open gl views embedded in different nsscrollview objects -- one for the map drawing and one for the tileset drawing. In the reshape code for both of the open gl views I have:

[ [self openGLContext ] makeCurrentContext ];

So I suppose when I adjust the map view window it screws up the texture loading which is supposed to take place in the to the tile set view context.

I put [[self openGLContext] makeCurrentContext]; at the beginning of my texture loading function in the tile set view and I haven't been able to get it to screw up as of yet. Does this seem right?
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #8
NSOpenGLView makes sure that its context is current before calling -reshape and -drawRect:. Generally, you should only make OpenGL calls from those functions (and the functions they call). That avoids any possibility of getting the current context mixed up.

If you *really* need to make GL calls from elsewhere (and I don't see why you'd need to) then yes, you'll need to scatter -makeCurrentContext calls around the place. Be careful!
Quote this message in a reply
Apprentice
Posts: 11
Joined: 2007.04
Post: #9
So what is the best way to load a texture in my situation? Set up some sort of boolean variable that figures out whether a texture load has been requested inside the drawRect function? I don't understand why manually setting the current context is dangerous.


And thanks for all your help by the way, I really appreciate it.
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #10
gradyeightythree Wrote:So what is the best way to load a texture in my situation? Set up some sort of boolean variable that figures out whether a texture load has been requested inside the drawRect function?
I've implemented something similar to this before. In one of my games, I was loading textures from a secondary thread, and since I was doing OpenGL stuff on the main thread, I couldn't upload them right away. So, each texture object carried a "deferred?" bit with it, and once the secondary thread completed, the main thread went through the loaded textures and uploaded each deferred texture's pixel data to OpenGL.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Opengl/Cocoa text rendering tesil 15 16,750 Mar 20, 2012 11:16 AM
Last Post: OneSadCookie
  [SOLVED]OpenGL edges of textures mk12 2 4,305 Sep 2, 2010 08:07 PM
Last Post: mk12
  OpenGL Image Textures mikey 52 23,870 Jun 30, 2009 10:42 AM
Last Post: AnotherJake
  Dealing with inverted textures in OpenGL johncmurphy 7 6,886 Jun 15, 2009 08:11 AM
Last Post: Skorche
  OpenGL Text Rendering (in Cocoa) daveh84 5 7,648 Feb 19, 2009 12:44 PM
Last Post: TomorrowPlusX