Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 8 of 8

Thread: iPad texture loading differences (32-bit vs. 64-bit)

  1. #1
    Junior Member Newbie
    Join Date
    Feb 2014
    Posts
    6

    iPad texture loading differences (32-bit vs. 64-bit)

    I am working on a drawing application and I am noticing significant differences in textures loaded on a 32-bit iPad vs. a 64-bit iPad.

    I create a default brush texture with this code:

    Code :
    UIGraphicsBeginImageContext(CGSizeMake(64, 64));
    CGContextRef defBrushTextureContext = UIGraphicsGetCurrentContext();
    UIGraphicsPushContext(defBrushTextureContext);
     
    size_t num_locations = 3;
    CGFloat locations[3] = { 0.0, 0.8, 1.0 };
    CGFloat components[12] = { 1.0,1.0,1.0, 1.0,
        1.0,1.0,1.0, 1.0,
        1.0,1.0,1.0, 0.0 };
    CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef myGradient = CGGradientCreateWithColorComponents (myColorspace, components, locations, num_locations);
     
    CGPoint myCentrePoint = CGPointMake(32, 32);
    float myRadius = 20;
     
    CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
    CGContextDrawRadialGradient (UIGraphicsGetCurrentContext(), myGradient, myCentrePoint,
                                 0, myCentrePoint, myRadius,
                                 options);
     
    CFRelease(myGradient);
    CFRelease(myColorspace);
    UIGraphicsPopContext();
     
    [self setBrushTexture:UIGraphicsGetImageFromCurrentImageContext()];
     
    UIGraphicsEndImageContext();

    And then actually set the brush texture like this:

    Code :
    -(void) setBrushTexture:(UIImage*)brushImage{
    // save our current texture.
    currentTexture = brushImage;
     
    // first, delete the old texture if needed
    if (brushTexture){
        glDeleteTextures(1, &brushTexture);
        brushTexture = 0;
    }
     
    // fetch the cgimage for us to draw into a texture
    CGImageRef brushCGImage = brushImage.CGImage;
     
    // Make sure the image exists
    if(brushCGImage) {
        // Get the width and height of the image
        GLint width = CGImageGetWidth(brushCGImage);
        GLint height = CGImageGetHeight(brushCGImage);
     
        // Texture dimensions must be a power of 2. If you write an application that allows users to supply an image,
        // you'll want to add code that checks the dimensions and takes appropriate action if they are not a power of 2.
     
        // Allocate  memory needed for the bitmap context
        GLubyte* brushData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
        // Use  the bitmatp creation function provided by the Core Graphics framework.
        CGContextRef brushContext = CGBitmapContextCreate(brushData, width, height, 8, width * 4, CGImageGetColorSpace(brushCGImage), kCGImageAlphaPremultipliedLast);
        // After you create the context, you can draw the  image to the context.
        CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), brushCGImage);
        // You don't need the context at this point, so you need to release it to avoid memory leaks.
        CGContextRelease(brushContext);
     
        // Use OpenGL ES to generate a name for the texture.
        glGenTextures(1, &brushTexture);
        // Bind the texture name.
        glBindTexture(GL_TEXTURE_2D, brushTexture);
        // Set the texture parameters to use a minifying filter and a linear filer (weighted average)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        // Specify a 2D texture image, providing the a pointer to the image data in memory
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData);
        // Release  the image data; it's no longer needed
        free(brushData);
    }
    }

    I've updated CGFloats to be GLfloats throughout my project no success, since I thought maybe that was the issue. Maybe there is an issue with this rendering code?

    Code :
    if(frameBuffer){
        // draw the stroke element
        [self prepOpenGLStateForFBO:frameBuffer];
        [self prepOpenGLBlendModeForColor:element.color];
        CheckGLError();
    }
     
    // find our screen scale so that we can convert from
    // points to pixels
    GLfloat scale = self.contentScaleFactor;
     
    // fetch the vertex data from the element
    struct Vertex* vertexBuffer = [element generatedVertexArrayWithPreviousElement:previousElement forScale:scale];
     
    glLineWidth(2);
     
    // if the element has any data, then draw it
    if(vertexBuffer){
        glVertexPointer(2, GL_FLOAT, sizeof(struct Vertex), &vertexBuffer[0].Position[0]);
        glColorPointer(4, GL_FLOAT, sizeof(struct Vertex), &vertexBuffer[0].Color[0]);
        glTexCoordPointer(2, GL_FLOAT, sizeof(struct Vertex), &vertexBuffer[0].Texture[0]);
        glDrawArrays(GL_TRIANGLES, 0, (GLint)[element numberOfSteps] * (GLint)[element numberOfVerticesPerStep]);
        CheckGLError();
    }
     
    if(frameBuffer){
        [self unprepOpenGLState];
    }

    The vertex struct is the following:

    Code :
    struct Vertex{
        GLfloat Position[2];    // x,y position
        GLfloat Color [4];      // rgba color
        GLfloat Texture[2];    // x,y texture coord
    };

  2. #2
    Advanced Member Frequent Contributor arekkusu's Avatar
    Join Date
    Nov 2003
    Posts
    781
    Quote Originally Posted by jordancarney View Post
    I am working on a drawing application and I am noticing significant differences in textures loaded on a 32-bit iPad vs. a 64-bit iPad.
    Are you comparing a 32-bit build and a 64-bit build on the same machine, or native builds on two different iPads?

    Code :
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData);
    I'd start right here. View the contents of brushData as raw hex bytes on both 32- and 64-bit builds. If the contents are different, then your problem is with CG and has nothing to do with OpenGL.

    I've updated CGFloats to be GLfloats throughout my project no success, since I thought maybe that was the issue.
    A CGFloat is a double in a 64-bit build. A GLfloat is always a 32 bit float. Anywhere you use CGFloat or CGPoint (or NSSize, NSInteger, etc etc) you need to ensure you are handling type conversion.

  3. #3
    Junior Member Newbie
    Join Date
    Feb 2014
    Posts
    6
    Quote Originally Posted by arekkusu View Post
    Are you comparing a 32-bit build and a 64-bit build on the same machine, or native builds on two different iPads?
    Two different iPads.

    Quote Originally Posted by arekkusu View Post
    Code :
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData);
    I'd start right here. View the contents of brushData as raw hex bytes on both 32- and 64-bit builds. If the contents are different, then your problem is with CG and has nothing to do with OpenGL.
    A guy on the Apple developer forums pointed to the same line as you and recommended:

    "You've likely created a texture with a variant of RGBA/UNSIGNED_BYTE pixel format, which has fairly limited precision. You can instead create a texture where each color component is a 16 bit half-float value, which will have much greater precision."

    I changed my code to this:

    Code :
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_HALF_FLOAT_OES, brushData);

    But it seemed to make things really crazy like I was drawing with five brushes.

    Quote Originally Posted by arekkusu View Post
    A CGFloat is a double in a 64-bit build. A GLfloat is always a 32 bit float. Anywhere you use CGFloat or CGPoint (or NSSize, NSInteger, etc etc) you need to ensure you are handling type conversion.
    I made sure I was handling type conversion in my entire project. Not the issue, unfortunately.

  4. #4
    Advanced Member Frequent Contributor arekkusu's Avatar
    Join Date
    Nov 2003
    Posts
    781
    Quote Originally Posted by jordancarney View Post
    Two different iPads.
    So, stop doing that. First narrow down the problem to figure out of it is due to the 64-bit CPU, or the completely different GPU and GL drivers in the two iPads.

    Build your project 32-bit on the 64-bit iPad and see if the problem still happens. Debug the data you have on the CPU (your brushData from CG) to see if the problem occurs before or after you hand the data to OpenGL.


    But it seemed to make things really crazy like I was drawing with five brushes.
    Obviously, taking a pointer to CG's unsigned bytes and asking GL to bit-cast it as half floats will produce crazy results. If you're lucky it will crash because your malloc was too small.

    If you are doing something complicated (like blending 10,000 layers each with 0.001 alpha) then there certainly could be GPU-specific differences, and changing the format of the framebuffer texture could make older iPads work more like the new iPad. But that's a leap of logic. You need to determine if the problem is due to CG or GL before going any further.

  5. #5
    Junior Member Newbie
    Join Date
    Feb 2014
    Posts
    6
    Quote Originally Posted by arekkusu View Post
    So, stop doing that. First narrow down the problem to figure out of it is due to the 64-bit CPU, or the completely different GPU and GL drivers in the two iPads.

    Build your project 32-bit on the 64-bit iPad and see if the problem still happens. Debug the data you have on the CPU (your brushData from CG) to see if the problem occurs before or after you hand the data to OpenGL.
    I guess the issue is not 32-bit/64-bit based. I ran the 32-bit and the 64-bit builds on the 64-bit iPad and the textures all look the same.

  6. #6
    Advanced Member Frequent Contributor arekkusu's Avatar
    Join Date
    Nov 2003
    Posts
    781
    So that indicates there is something GPU-specific. Keep debugging: at this point you could delete all of the CG code from your project to eliminate it as a problem. Replace the texture with something simple like a 1x1 grey pixel or 2x2 gradient.

    Then examine each part of your GL state and keep reducing it until you isolate where unexpected results are introduced. (i.e. in the code you posted, there are at least three more variables -- blending, texturing, and the fbo.)

    If you were using ES2, I would suggest looking at all lowp variables in your shaders, since the precision qualifiers are only hints, and are treated differently on the A7 GPU (lowp is mediump). But your code is using ES1, so all of the color, texture, and blending values are supposed to be clamped to [0,1] all of the time.

    How much are you drawing with blending? If you are rendering something complicated (lots of overlapping blended triangles) the higher internal precision can produce noticeably different results from older GPUs. To eliminate this, you could try rendering only one triangle at a time, with a glFlush() (or a glReadPixels or other commands that will force rendering to occur) in-between each triangle. That should force the higher-precision tile to be written to the FBO, quantizing it to the real texture format. (Obviously this will kill performance-- just a debug tool.)

  7. #7
    Junior Member Newbie
    Join Date
    Feb 2014
    Posts
    6
    Quote Originally Posted by arekkusu View Post
    So that indicates there is something GPU-specific. Keep debugging: at this point you could delete all of the CG code from your project to eliminate it as a problem. Replace the texture with something simple like a 1x1 grey pixel or 2x2 gradient.
    I tried this with no success. I've also tried loading in a texture from an image, it doesn't seem to be the root of the issue.

    If you were using ES2, I would suggest looking at all lowp variables in your shaders, since the precision qualifiers are only hints, and are treated differently on the A7 GPU (lowp is mediump). But your code is using ES1, so all of the color, texture, and blending values are supposed to be clamped to [0,1] all of the time.
    I actually updated to ES2 in hope that maybe ES1 in conjunction with the A7 was causing some issues; that wasn't the case.

    How much are you drawing with blending? If you are rendering something complicated (lots of overlapping blended triangles) the higher internal precision can produce noticeably different results from older GPUs. To eliminate this, you could try rendering only one triangle at a time, with a glFlush() (or a glReadPixels or other commands that will force rendering to occur) in-between each triangle. That should force the higher-precision tile to be written to the FBO, quantizing it to the real texture format. (Obviously this will kill performance-- just a debug tool.)
    I am rendering lots of overlapping triangle. I'm not exactly sure how to go about doing the glFlush thing, but I'll give it a try.

  8. #8
    Junior Member Newbie
    Join Date
    Feb 2014
    Posts
    6
    In addition to the strange texture issue, if this will help may help you figure out what is going on, I am also noticing strange color changes on the 32-bit iPad when I create brushes with really low alpha values. Could be this be the same issue?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •