macosopengltexturesscenekitiosurface

Convert IOSurface-backed texture to GL_TEXTURE_2D


I want to render an IOSurface texture with mipmapping.

I am generating an OpenGL texture from an IOSurface like so:

let error = CGLTexImageIOSurface2D(context.cglContextObj!, 
GLenum(GL_TEXTURE_RECTANGLE_ARB),GLenum(GL_RGBA), GLsizei(width), GLsizei(height),
GLenum(GL_BGRA), GLenum(GL_UNSIGNED_INT_8_8_8_8_REV), surface!, 0)

And rendering it with a simple fragment shader:

#version 120
uniform sampler2DRect texture;
varying vec2 texCoord;
uniform vec2 size;

void main(){
    vec4 tex = texture2DRect(texture, texCoord*size);
    gl_FragColor = tex;
}

This works fine. But unfortunately, the texture is not mipmapped and is very grainy/pixellated when viewed at a smaller size. Upon further research, it appears that not only are IOSurface backed textures limited to a GL_TEXTURE_RECTANGLE_ARB target, they also do not support mipmapping.

Allright. So my best alternative here seems to be to:

  1. Copy the GL_TEXTURE_RECTANGLE_ARB texture to a GL_TEXTURE_2D texture
  2. Generate mipmaps for this new texture

Do note that I am developing on Mac, which does not support OpenGL 4.3, so I cannot use the convenient glCopyImageSubData here. Instead I am basing my approach for Step 1 here on this answer: How to copy texture1 to texture2 efficiently?

My full code now looks like this:

if !hasCreatedTexture {
    glGenTextures(1, &texture);
    glGenTextures(1, &arbTexture);
    glGenFramebuffers(1, &fbo)
    hasCreatedTexture = true
}
glEnable(GLenum(GL_TEXTURE_RECTANGLE_ARB))
glBindTexture(GLenum(GL_TEXTURE_RECTANGLE_ARB), arbTexture)

let error = CGLTexImageIOSurface2D(context.cglContextObj!, GLenum(GL_TEXTURE_RECTANGLE_ARB), GLenum(GL_RGBA), GLsizei(width), GLsizei(height), GLenum(GL_BGRA), GLenum(GL_UNSIGNED_INT_8_8_8_8_REV), surface!, 0)


glBindFramebuffer(GLenum(GL_FRAMEBUFFER), fbo);
glFramebufferTexture2D(GLenum(GL_READ_FRAMEBUFFER), GLenum(GL_COLOR_ATTACHMENT0),
                           GLenum(GL_TEXTURE_RECTANGLE_ARB), arbTexture, 0);
glFramebufferTexture2D(GLenum(GL_DRAW_FRAMEBUFFER), GLenum(GL_COLOR_ATTACHMENT1),
                           GLenum(GL_TEXTURE_2D), texture, 0);
glDrawBuffer(GLenum(GL_COLOR_ATTACHMENT1));
glBlitFramebuffer(0, 0, GLint(width), GLint(height), 0, 0, GLint(width), GLint(height), GLbitfield(GL_COLOR_BUFFER_BIT), GLenum(GL_NEAREST));
glBindTexture(GLenum(GL_TEXTURE_2D), texture)

And I changed my shader to support GL_TEXTURE_2D:

#version 120
uniform sampler2D texture;
varying vec2 texCoord;

void main(){
    vec4 tex = texture2D(texture, texCoord);
    gl_FragColor = tex;
}

And here is where I'm stuck. The image is all black and I have no clue why it's not working.

I tried looking at the pixel data like so:

var pixelData = UnsafeMutablePointer<GLubyte>(allocatingCapacity: Int(width * height * 4))

glGetTexImage(GLenum(GL_TEXTURE_2D), 0, GLenum(GL_BGRA),
GLenum(GL_UNSIGNED_INT_8_8_8_8_REV), pixelData)
print(NSData(bytes: pixelData, length: 50000).description)

And it's all zeroes. Is it something wrong with my approach or could the issue be specifically related to dealing with IOSurface backed textures?


Solution

  • Finally got this working using the following:

    func createMipTexture() {
        guard let surface = self.surface else { return }
        glBindTexture(GLenum(GL_TEXTURE_2D), texture);
        glEnable(GLenum(GL_TEXTURE_2D))
    
        guard let address = IOSurfaceGetBaseAddress(surface) as UnsafeMutableRawPointer? else { return }
        IOSurfaceLock(surface, .readOnly, nil)
        glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, GLsizei(width), GLsizei(height), 0,  GLenum(GL_BGRA), GLenum(GL_UNSIGNED_INT_8_8_8_8_REV), address)
        IOSurfaceUnlock(surface, .readOnly, nil);
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAX_LEVEL), 100)
        glHint(GLenum(GL_GENERATE_MIPMAP_HINT), GLenum(GL_NICEST));
        glGenerateMipmap(GLenum(GL_TEXTURE_2D))
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_REPEAT);
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_REPEAT);
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR);
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR_MIPMAP_LINEAR);
    }