Issue with internal jit.gl.texture not having valid texture ID.
Hello.
I'm squishing bugs in a soon to be released Jitter external, and have an issue I'm trying to understand and fix.
When our object is initialized we create a new internal jit.gl.texture object. This jit.gl.texture is used as an FBO GL_COLOR_ATTACHMENT0 so we can render into it (similar to maybe a slab). Upon the first load of a patch that has our external (lets, uh, call it jit.gl.syphonclient for fun), all is well, our internal jit.gl.texture has the dims I specified, has a valid texture ID, etc, and my FBO attachment code works. Yay and stuff. Close the patch, re-open it, and the jit.gl.texture object exists, has the wrong dims, and the texture ID is 0. Why would instantiating the same patch again cause different behaviour? What is interesting is if I cause the rendering context to be rebuilt via say a fullscreen method, it works. But the initial load does not.
We create our jit.gl.syphonclient like so:
t_jit_gl_syphon_client *jit_gl_syphon_client_new(t_symbol * dest_name)
{
t_jit_gl_syphon_client *jit_gl_syphon_client_instance;
// make jit object
if (jit_gl_syphon_client_instance = (t_jit_gl_syphon_client *)jit_object_alloc(_jit_gl_syphon_client_class))
{
// TODO : is this right ?
// set up attributes
jit_attr_setsym(jit_gl_syphon_client_instance->servername, _jit_sym_name, gensym("servername"));
jit_attr_setsym(jit_gl_syphon_client_instance->appname, _jit_sym_name, gensym("appname"));
jit_gl_syphon_client_instance->needsRedraw = YES;
// instantiate a single internal jit.gl.texture should we need it.
jit_gl_syphon_client_instance->output = jit_object_new(ps_jit_gl_texture,dest_name);
jit_gl_syphon_client_instance->latestBounds = NSMakeRect(0, 0, 640, 480);
if (jit_gl_syphon_client_instance->output)
{
jit_gl_syphon_client_instance->texturename = jit_symbol_unique();
// set texture attributes.
jit_attr_setsym(jit_gl_syphon_client_instance->output,_jit_sym_name, jit_gl_syphon_client_instance->texturename);
jit_attr_setsym(jit_gl_syphon_client_instance->output,gensym("defaultimage"),gensym("black"));
jit_attr_setlong(jit_gl_syphon_client_instance->output,gensym("rectangle"), 1);
jit_attr_setlong(jit_gl_syphon_client_instance->output, gensym("flip"), 0);
jit_gl_syphon_client_instance->dim[0] = 640;
jit_gl_syphon_client_instance->dim[1] = 480;
jit_attr_setlong_array(jit_gl_syphon_client_instance->output, _jit_sym_dim, 2, jit_gl_syphon_client_instance->dim);
}
else
{
post("error creating internal texture object");
jit_object_error((t_object *)jit_gl_syphon_client_instance,"jit.gl.syphonserver: could not create texture");
jit_gl_syphon_client_instance->texturename = _jit_sym_nothing;
}
// create and attach ob3d
jit_ob3d_new(jit_gl_syphon_client_instance, dest_name);
jit_gl_syphon_client_instance->syClient = [[SyphonNameboundClient alloc] init];
}
else
{
jit_gl_syphon_client_instance = NULL;
}
return jit_gl_syphon_client_instance;
}
This is how we handle context destination changed / drawto :
t_jit_err jit_gl_syphon_client_dest_changed(t_jit_gl_syphon_client *jit_gl_syphon_client_instance)
{
// try and find a context.
t_jit_gl_context jit_ctx = 0;
// jitter context
jit_ctx = jit_gl_get_context();
if (jit_gl_syphon_client_instance->output)
jit_attr_setsym(jit_gl_syphon_client_instance->output,ps_drawto,jit_attr_getsym(jit_gl_syphon_client_instance,ps_drawto));
jit_gl_syphon_client_instance->needsRedraw = YES;
return JIT_ERR_NONE;
}
t_jit_err jit_gl_syphon_client_drawto(t_jit_gl_syphon_client *jit_gl_syphon_client_instance, t_symbol *s, int argc, t_atom *argv)
{
object_attr_setvalueof(jit_gl_syphon_client_instance->output,s,argc,argv);
jit_ob3d_dest_name_set((t_jit_object *)jit_gl_syphon_client_instance, NULL, argc, argv);
return JIT_ERR_NONE;
}
And in our render method, we do something like (this is snipped, for brevity):
// clearly we need our texture for this...
if(jit_gl_syphon_client_instance->output)
{
jit_gl_syphon_client_instance->needsRedraw = NO;
// where the hell are we?
jit_ob3d_set_context(jit_gl_syphon_client_instance);
CGLContextObj cgl_ctx = CGLGetCurrentContext();
//NSLog(@"Jitter render context: %p, CGLContext: %p", jit_gl_get_context(), cgl_ctx);
// add texture to OB3D list.
jit_attr_setsym(jit_gl_syphon_client_instance,ps_texture, jit_attr_getsym(jit_gl_syphon_client_instance->output, gensym("name")));
// Bind the Syphon Texture early, so we can base the viewport on the framesize and update our internal texture
// ahead of rendering.
SyphonImage *frame = [client newFrameImageForContext:cgl_ctx];
jit_gl_syphon_client_instance->latestBounds.size = [frame textureSize];
// we need to update our internal texture to the latest known size of our syphonservers image.
long newdim[2]; // output dim
newdim[0] = jit_gl_syphon_client_instance->latestBounds.size.width;
newdim[1] = jit_gl_syphon_client_instance->latestBounds.size.height;
// update our internal attribute so attr messages work
jit_attr_setlong_array(jit_gl_syphon_client_instance, _jit_sym_dim, 2, newdim);
// save some state
GLint previousFBO; // make sure we pop out to the right FBO
GLint previousReadFBO;
GLint previousDrawFBO;
GLint previousMatrixMode;
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &previousFBO);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &previousReadFBO);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &previousDrawFBO);
glGetIntegerv(GL_MATRIX_MODE, &previousMatrixMode);
// save texture state, client state, etc.
glPushAttrib(GL_ALL_ATTRIB_BITS);
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
// We are going to bind our FBO to our internal jit.gl.texture as COLOR_0 attachment
// We need the ID, width/height.
GLuint texname = jit_attr_getlong(jit_gl_syphon_client_instance->output,ps_glid);
GLuint width = jit_attr_getlong(jit_gl_syphon_client_instance->output,ps_width);
GLuint height = jit_attr_getlong(jit_gl_syphon_client_instance->output,ps_height);
post("texture id is %u width %u height %u", texname, width, height);
// FBO generation/attachment to texture
GLuint tempFBO;
glGenFramebuffers(1, &tempFBO);
glBindFramebuffer(GL_FRAMEBUFFER, tempFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, texname, 0);
// it work?
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status == GL_FRAMEBUFFER_COMPLETE)
{ // snipped
}
else
{
post("jit.gl.syphonclient could not attach to FBO, error: %xjit.gl.syphonclient.mxo", status);
}
Hopefully this is legible in the forum, and makes some sense. What does one have to do when using an internal texture to ensure it is available for use, has a valid internal texture ID and size? Why would loading the same patch at different times cause different behaviours? Is there some caching of objects that maybe is precluding some initialization I may have in the 'wrong' place from running the second time? Are our OB3D api calls correct?
Thanks very much. once this is squashed, Syphon Public Beta 1 will be available :)
Oh, I should add some more detail perhaps. The first run opening/running of the patch we get valid FBO attachment, and valid dims. The second run through, Texture ID, width and height are all 0, and we error with framebuffer status invalid attachment:
First opening:
Jitter 1.7.0 installed
dumpout: clear
dumpout: dim 640 480
jit.gl.render: building GL on window "clientcontext"...
dumpout: clear
dumpout: servername
dumpout: appname Simple Server
texture id is 1 width 640 height 480
second:
dumpout: clear
dumpout: servername
dumpout: appname Simple Server
dumpout: dim 640 480
jit.gl.render: building GL on window "clientcontext"...
texture id is 0 width 0 height 0
jit.gl.syphonclient could not attach to FBO, error: 8cd7 jit.gl.syphonclient.mxo
8cd7 is GL ERROR : GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
Thanks again for any insight.
Hi Anton,
The first thing which comes to mind is that the texture has not been bound yet. The texture must be bound at least once while its parent context is valid in order to generate the gpu texture resource (and hence id). The next thing that your message demonstrates is that, unlike the first time, something like a clear message and servername message are happening *before* context creation (and thus texture creation). Any setup or binding code related to that could be the culprit. Hard to know, without more info.
Let us know how it goes.
-Joshua
Thanks Joshua, binding then immediately unbinding the texture when the context changes fixed it.