1

I'm using the Syphon framework to try and push frames of video from a server to a client application.

Syphon requires you to use OpenGL textures instead of normal images.

Because of this, I'm trying to render a CGImageRef as a texture and send it on to be published.

I'm creating my CGL context as so:

CGLPixelFormatAttribute attribs[13] = {
    kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core, // This sets the context to 3.2
    kCGLPFAColorSize,     (CGLPixelFormatAttribute)24,
    kCGLPFAAlphaSize,     (CGLPixelFormatAttribute)8,
    kCGLPFAAccelerated,
    kCGLPFADoubleBuffer,
    kCGLPFASampleBuffers, (CGLPixelFormatAttribute)1,
    kCGLPFASamples,       (CGLPixelFormatAttribute)4,
    (CGLPixelFormatAttribute)0
};

CGLPixelFormatObj pix;
GLint npix;
CGLChoosePixelFormat(attribs, &pix, &npix);

CGLCreateContext(pix, 0, &_ctx);

I already have a CGImageRef that I know can be rendered properly as an NSImage.

I'm rendering the texture as so:

CGLLockContext(cgl_ctx);

if (_texture) {
    glDeleteTextures(1, &_texture);
}

int width = 1920;
int height = 1080;

GLubyte* imageData = malloc(width * height * 4);
CGContextRef imageContext = CGBitmapContextCreate(imageData, width, height, 8, width * 4, CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);
CGContextDrawImage(imageContext, CGRectMake(0.0, 0.0, width, height), image);
CGContextRelease(imageContext);

GLuint frameBuffer;
GLenum status;

glGenFramebuffersEXT(1, &frameBuffer);

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frameBuffer);
glGenTextures(1, &_texture);
glBindTexture(GL_TEXTURE_2D, _texture);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_TEXTURE_2D, imageData);

status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
    NSLog(@"OpenGL Error");
}

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

CGLUnlockContext(cgl_ctx);

The rendering code is in a different class, but the context should be passed through and is the same.

I've tried the advice in pretty much every other instance of this problem to no avail.

JamEngulfer
  • 747
  • 4
  • 11
  • 33

2 Answers2

1

The second-to-last parameter in glTexImage2D is:

type
Specifies the data type of the pixel data. The following symbolic values are accepted: GL_UNSIGNED_BYTE, GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, GL_FLOAT, GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, GL_UNSIGNED_INT_8_8_8_8, GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, and GL_UNSIGNED_INT_2_10_10_10_REV.

GL_TEXTURE_2D doesn't make sense there, it should be whatever the data type of elements of imageData is.

You should also be checking your OpenGL errors with glGetError or ARB_debug_output. You would have immediately be shown what's wrong:

Source:OpenGL   Type:Error  ID:5    Severity:High   Message:GL_INVALID_ENUM in glTexImage2D(incompatible format = GL_RGBA, type = GL_TEXTURE_2D)
orost
  • 361
  • 2
  • 6
  • Given my imageContext, what would you suggest for the type? Would I use GL_UNSIGNED_BYTE? – JamEngulfer Jun 09 '15 at 23:11
  • @JamEngulfer221 I'm not familiar with the framework you're using. But image data is usually float or unsigned int. – orost Jun 09 '15 at 23:15
  • Looking at the code, surely the elements of imageData would be `GLubyte`, or unsigned bytes? – JamEngulfer Jun 09 '15 at 23:23
  • @JamEngulfer221 If they are unsigned bytes, then `GL_UNSIGNED_BYTE` is the type you want, yes. – orost Jun 09 '15 at 23:25
  • Also, how can I get error reporting like you posted? I can't seem to find good documentation on using the things you mentioned. – JamEngulfer Jun 09 '15 at 23:31
  • @JamEngulfer221 `glGetError` is described [here](https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetError.xml), and `ARB_debug_output` [here](https://www.opengl.org/registry/specs/ARB/debug_output.txt). The latter is an extension, so it may or may not be available on your system. – orost Jun 09 '15 at 23:42
  • I changed the code as you suggested, but it still returns a black screen. – JamEngulfer Jun 10 '15 at 00:22
  • The actual cause of the problem was me passing data in the wrong order and using a null context. However, this answer would have fixed the problem if I had set everything up correctly in the first place. – JamEngulfer Jun 10 '15 at 20:03
1

There are a few problems in this code. The following are critical to get things working:

  • As also pointed out in an earlier answer by @orost, the type parameter for the glTexImage2D() call is invalid. It should be:

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    
  • The texture is never attached as an FBO target. While you set up the FBO, you need:

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                           GL_TEXTURE_2D, _texture, 0);
    

There are a couple more items that probably won't keep you from getting it running, but I would recommend to change them anyway:

  • You don't need to specify data for the texture if you're going to create the content by rendering to it. The data you pass to glTexImage2D() is uninitialized anyway, so that can't do much good. It's much cleaner to pass NULL as the data argument, as I already did in the call shown above.
  • Since you're using OpenGL 3.2, there's really no need to use the EXT form of the FBO entry points. This is standard functionality in OpenGL 3.x. The EXT form will probably work as long as you use it consistently, but you risk ugly surprises if you mix it with standard entry points.
Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
  • Looking at my code, I thought that imageData would be initialised, because I thought I drew the image to a context that used imageData somehow. – JamEngulfer Jun 10 '15 at 09:10
  • On the topic of specifying the data, that data IS the thing I want to render. If I don't include it, there's literally nothing to render, because the image data is mentioned nowhere else in the rendering code. – JamEngulfer Jun 10 '15 at 10:08
  • I think I understand your intentions, but that's not how it works. When you pass a data pointer to `glTexImage2D()`, it's only used during the call to fill the texture with data. The pointer is not permanently associated with the texture. If you render to the texture, and want a CPU copy of the resulting image, you'll need to read it back with `glReadPixels()`. – Reto Koradi Jun 10 '15 at 13:53
  • I just read the documentation for `glTexImage2D()` and it appears to disagree with you. It says: `If target is GL_TEXTURE_2D, ... data is read from data as a sequence of signed or unsigned bytes...` – JamEngulfer Jun 10 '15 at 15:04
  • @JamEngulfer221 Yes, how is that different from what I said? The `glTexImage2D()` call reads the data you pass in *during the call*. It doesn't use it in any way after the call returns. – Reto Koradi Jun 10 '15 at 15:11
  • Oh right, I see what you meant. Sorry, I must have misread it. – JamEngulfer Jun 10 '15 at 15:35