Sorry, forgot to copy to the mail list. ---------- Forwarded message ---------- From: zhigang gong <zhigang.g...@gmail.com> Date: Fri, Nov 25, 2011 at 5:18 PM Subject: Re: [Mesa-dev] Question about the texture's reference count processing in _mesa_update_texture To: Brian Paul <bri...@vmware.com>
Hi Brian, The test case is attached. It is based on the eglkms.c in mesa-demos package. Here is a explaintation of the case: 1. use gbm to create two bos. one is shadow bo and the other is screen bo. 2. create a shadow image by using the shadow bo's handle. Then creat shadow texture from the shadow image. 3. create a screen image by using the screen bo directly. Then create screen texture from the screen image. 4. create two frame buffer objects, one is screen fbo and the other is screen fbo. 5. bind shadow texture to shadow fbo, and bind screen texture to screen fbo. 6. choose shadow fbo as render target, and use shader to fill a rectangle in it. 7. choose screen fbo as render target, set the shadow texture as a source texture. use shader to copy shadow texture to screen fbo. 8. Use KMS to show the screen fbo to the hardware frame buffer. 9. delete all fbos. 10. delete all textures. 11. destroy all images. 12. use the shadow bo's handle to create another shadow image. 13. destroy the shadow image. 14. destroy all gbm bos. 15. other cleanups. Now the key step is in 7 and 12. By default, the function will fail at step 12. It will fail to create another shadow image from the shadow bo's handle, EGL will complain: "Region for name 1 already exists but is not compatible " But this confused me, as the previous shadow image is already destroied at step 11. Why EGL can still find a image region with the same handle? I traced into mesa and got some finding: glDrawArray will implicitly increase the shadow texture's reference count. And then latter when we delete the texture, the count will remain 1 rather then zero, and then it will not decrease the corresponding image region's reference counter and then when we destroy the shadow image, the image's reference counter will also be 1 and can't be freed. Then next time we use the same handle to create a new image, it will find the previou zombie image region and find they are not compatible. Just simply comment out the glDrawArray in step 7 can avoid this case to hit the problem, and it can create the second smaller image successfully. Any hint here. BTW, Sorry I can't make a simpler test case here, as it seems that using the shadow texture as a source texture is a must condition to hit this problem. Any hint to fix this problem? Thanks. On Wed, Nov 23, 2011 at 11:49 PM, Brian Paul <bri...@vmware.com> wrote: > > On 11/23/2011 12:28 AM, zhigang gong wrote: >> >> Hi guys, >> I have a question about the internal implementation of glDrawArrays or >> any rendering functions. >> In the code path, it calls to _mesa_update_texture(), and if there is >> really a complete texture, >> it will call _mesa_reference_texobj to set it to _Current, and >> increase the texture object's reference >> counter. >> My question is when it will decrease the reference counter? This >> implicit increasing seems >> causes the reference counter unbalanced, and then will cause >> unexpected behaviour. Here is an >> example: >> 1 glGenTextures(1, &tex); >> 2 glActiveTexture(GL_TEXTURE0); >> 3 glBindTexture(GL_TEXTURE_2D, tex); >> 4 glTexImage2D(GL_TEXTURE_2D, 0, iformat, >> w, h, 0, format, type, bits); >> 5 glEnable(GL_TEXTURE_2D); >> 6 glUseProgram(shader_prog); >> 7 ... /*setup attribute array here.*/ >> 8 glDrawArray(GL_TRIANGLE_FAN, 0, 4); >> 9 glUseProgram(0); >> 10 glDeleteTexture(1, &tex); >> At Line 1, tex object is created with reference count initialized to 1. >> At Line 3, tex object's reference count increased to 2. >> At Line 8, it implicit increases tex object's reference count to 3. >> At Line 10, it implict unbinds the tex object which decreases the >> count to 2. >> then it try to unreference the tex object which >> decreases the count to1. >> You can see that finally, the tex object's reference's count is 1 not >> zero >> and thus it will not be freed. This is not what I expected. >> Is there any mistakes in my use case? Or anyone can tell me how to >> make sure the texture object get freed after the usage of it. > > As long as the texture object is still bound to unit 0's GL_TEXTURE_2D, the > texture will not get deleted. > > When another texture gets bound to the binding point, the "deleted" texture's > ref count should go to zero and really be deleted. For example, binding the > default texture object should do that: > > glBindTexture(GL_TEXTURE_2D, 0); > > If this isn't working, can you provide a test program? > > -Brian
/* * Copyright © 2011 Kristian Høgsberg * Copyright © 2011 Benjamin Franzke * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include <stdio.h> #include <stdlib.h> #define EGL_EGLEXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES #include <gbm.h> #include "gl_wrap.h" #include <GL/glext.h> #include <EGL/egl.h> #include <EGL/eglext.h> #include <drm.h> #include <xf86drmMode.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #ifdef GL_OES_EGL_image static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES_func; static PFNEGLCREATEIMAGEKHRPROC glEGLCreateImageKHR_func; #endif struct kms { drmModeConnector *connector; drmModeEncoder *encoder; drmModeModeInfo mode; uint32_t fb_id; }; static EGLBoolean setup_kms(int fd, struct kms *kms) { drmModeRes *resources; drmModeConnector *connector; drmModeEncoder *encoder; int i; resources = drmModeGetResources(fd); if (!resources) { fprintf(stderr, "drmModeGetResources failed\n"); return EGL_FALSE; } for (i = 0; i < resources->count_connectors; i++) { connector = drmModeGetConnector(fd, resources->connectors[i]); if (connector == NULL) continue; if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes > 0) break; drmModeFreeConnector(connector); } if (i == resources->count_connectors) { fprintf(stderr, "No currently active connector found.\n"); return EGL_FALSE; } for (i = 0; i < resources->count_encoders; i++) { encoder = drmModeGetEncoder(fd, resources->encoders[i]); if (encoder == NULL) continue; if (encoder->encoder_id == connector->encoder_id) break; drmModeFreeEncoder(encoder); } kms->connector = connector; kms->encoder = encoder; kms->mode = connector->modes[0]; return EGL_TRUE; } static const char device_name[] = "/dev/dri/card0"; GLint solid_prog, solid_color_uniform_location, copy_prog; static GLint compile_glsl_prog(GLenum type, const char *source) { GLint ok; GLint prog; prog = glCreateShader(type); glShaderSource(prog, 1, (const GLchar **) &source, NULL); glCompileShader(prog); glGetShaderiv(prog, GL_COMPILE_STATUS, &ok); if (!ok) { GLchar *info; GLint size; glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &size); info = malloc(size); glGetShaderInfoLog(prog, size, NULL, info); fprintf(stderr, "Failed to compile %s: %s\n", type == GL_FRAGMENT_SHADER ? "FS" : "VS", info); fprintf(stderr, "Program source:\n%s", source); } return prog; } static void link_glsl_prog(GLint prog) { GLint ok; glLinkProgram(prog); glGetProgramiv(prog, GL_LINK_STATUS, &ok); if (!ok) { GLchar *info; GLint size; glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size); info = malloc(size); glGetProgramInfoLog(prog, size, NULL, info); fprintf(stderr, "Failed to link: %s\n", info); fprintf(stderr, "GLSL link failure\n"); } } #define VERTEX_POS 0 #define VERTEX_SOURCE 1 static void init_shader(void) { const char *solid_vs = "attribute vec4 v_position;" "void main()\n" "{\n" " gl_Position = v_position;\n" "}\n"; const char *solid_fs = "uniform vec4 color;\n" "void main()\n" "{\n" " gl_FragColor = color;\n" "}\n"; const char *copy_vs = "attribute vec4 v_position;\n" "attribute vec4 v_texcoord0;\n" "varying vec2 source_texture;\n" "void main()\n" "{\n" " gl_Position = v_position;\n" " source_texture = v_texcoord0.xy;\n" "}\n"; const char *copy_fs = "varying vec2 source_texture;\n" "uniform sampler2D sampler;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(sampler, source_texture).rgba;\n" " } \n" ; GLint sampler_uniform_location; GLint fs_prog, vs_prog; solid_prog = glCreateProgram(); vs_prog = compile_glsl_prog(GL_VERTEX_SHADER, solid_vs); fs_prog = compile_glsl_prog(GL_FRAGMENT_SHADER, solid_fs); glAttachShader(solid_prog, vs_prog); glAttachShader(solid_prog, fs_prog); glBindAttribLocation(solid_prog, VERTEX_POS, "v_position"); link_glsl_prog(solid_prog); solid_color_uniform_location = glGetUniformLocation(solid_prog, "color"); copy_prog = glCreateProgram(); vs_prog = compile_glsl_prog(GL_VERTEX_SHADER, copy_vs); fs_prog = compile_glsl_prog(GL_FRAGMENT_SHADER, copy_fs); glAttachShader(copy_prog, vs_prog); glAttachShader(copy_prog, fs_prog); glBindAttribLocation(copy_prog, VERTEX_POS, "v_position"); glBindAttribLocation(copy_prog, VERTEX_SOURCE, "v_texcoord0"); link_glsl_prog(copy_prog); glUseProgram(copy_prog); sampler_uniform_location = glGetUniformLocation(copy_prog, "sampler"); glUniform1i(sampler_uniform_location, 0); glUseProgram(0); } /* use shader to copy tex to current fbo. */ static void copy_tex(GLint tex) { static float vertices[8] = { -1, -1, 1, -1, 1, 1, -1, 1 }; static float texcoords[8] = { 0, 1, 1, 1, 1, 0, 0, 0 }; glVertexAttribPointer(VERTEX_POS, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vertices); glEnableVertexAttribArray(VERTEX_POS); glVertexAttribPointer(VERTEX_SOURCE, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), texcoords); glEnableVertexAttribArray(VERTEX_SOURCE); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glUseProgram(copy_prog); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glUseProgram(0); glBindTexture(GL_TEXTURE_2D, 0); glDisableVertexAttribArray(VERTEX_SOURCE); glDisableVertexAttribArray(VERTEX_POS); } /* use shader to fill a green triangle to current fbo.*/ static void render_stuff(int width, int height) { static const GLfloat verts[3][2] = { { -1, -1 }, { 1, -1 }, { 0, 1 } }; float color[4] = {0, 1, 0, 0}; glViewport(0, 0, (GLint) width, (GLint) height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glUseProgram(solid_prog); glUniform4fv(solid_color_uniform_location, 1, color); glVertexAttribPointer(VERTEX_POS, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), verts); glEnableVertexAttribArray(VERTEX_POS); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableVertexAttribArray(VERTEX_POS); glUseProgram(0); } static GLuint create_tex_from_img(EGLImageKHR image) { GLuint tex; glGenTextures(1, &tex); fprintf(stderr, "before bind texture\n"); glBindTexture(GL_TEXTURE_2D, tex); fprintf(stderr, "after bind texture\n"); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glEGLImageTargetTexture2DOES_func (GL_TEXTURE_2D, image); glBindTexture(GL_TEXTURE_2D, 0); return tex; } static GLuint create_fbo_render2tex(GLint tex) { GLuint fb; glGenFramebuffers(1, &fb); glBindFramebuffer(GL_FRAMEBUFFER, fb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE) { fprintf(stderr, "framebuffer not complete\n"); exit(1); } return fb; } static EGLImageKHR create_image_from_handle(EGLDisplay dpy, EGLContext ctx, int fd, int handle, int width, int height, int stride) { struct drm_gem_flink flink; EGLImageKHR image; EGLint attribs[] = { EGL_WIDTH, 0, EGL_HEIGHT, 0, EGL_DRM_BUFFER_STRIDE_MESA, 0, EGL_DRM_BUFFER_FORMAT_MESA, EGL_DRM_BUFFER_FORMAT_ARGB32_MESA, EGL_DRM_BUFFER_USE_MESA, EGL_DRM_BUFFER_USE_SHARE_MESA, EGL_NONE }; attribs[1] = width; attribs[3] = height; attribs[5] = stride/4; flink.handle = handle; if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) < 0) return EGL_NO_IMAGE_KHR; image = glEGLCreateImageKHR_func(dpy , ctx, EGL_DRM_BUFFER_MESA, (void *) (uintptr_t)flink.name, attribs); if (image == EGL_NO_IMAGE_KHR) return EGL_NO_IMAGE_KHR; return image; } int main(int argc, char *argv[]) { EGLDisplay dpy; EGLContext ctx; EGLImageKHR shadow_image, screen_image; EGLint major, minor; const char *ver, *extensions; uint32_t handle, stride, shadow_handle, shadow_stride; struct kms kms; int ret, fd; struct gbm_device *gbm; struct gbm_bo *shadow_bo, *screen_bo; drmModeCrtcPtr saved_crtc; GLuint shadow_tex, screen_tex, shadow_fb, screen_fb; fd = open(device_name, O_RDWR); if (fd < 0) { /* Probably permissions error */ fprintf(stderr, "couldn't open %s, skipping\n", device_name); return -1; } gbm = gbm_create_device(fd); if (gbm == NULL) { fprintf(stderr, "couldn't create gbm device\n"); ret = -1; goto close_fd; } dpy = eglGetDisplay(gbm); if (dpy == EGL_NO_DISPLAY) { fprintf(stderr, "eglGetDisplay() failed\n"); ret = -1; goto destroy_gbm_device; } if (!eglInitialize(dpy, &major, &minor)) { printf("eglInitialize() failed\n"); ret = -1; goto egl_terminate; } ver = eglQueryString(dpy, EGL_VERSION); printf("EGL_VERSION = %s\n", ver); extensions = eglQueryString(dpy, EGL_EXTENSIONS); printf("EGL_EXTENSIONS: %s\n", extensions); if (!strstr(extensions, "EGL_KHR_surfaceless_opengl")) { printf("No support for EGL_KHR_surfaceless_opengl\n"); ret = -1; goto egl_terminate; } if (!setup_kms(fd, &kms)) { ret = -1; goto egl_terminate; } eglBindAPI(EGL_OPENGL_API); ctx = eglCreateContext(dpy, NULL, EGL_NO_CONTEXT, NULL); if (ctx == NULL) { fprintf(stderr, "failed to create context\n"); ret = -1; goto egl_terminate; } if (!eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx)) { fprintf(stderr, "failed to make context current\n"); ret = -1; goto destroy_context; } init_shader(); glEGLImageTargetTexture2DOES_func = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress("glEGLImageTargetTexture2DOES"); glEGLCreateImageKHR_func = (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress("eglCreateImageKHR"); shadow_bo = gbm_bo_create(gbm, kms.mode.hdisplay, kms.mode.vdisplay, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_RENDERING); screen_bo = gbm_bo_create(gbm, kms.mode.hdisplay, kms.mode.vdisplay, GBM_BO_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (screen_bo == NULL) { fprintf(stderr, "failed to create gbm screen bo\n"); ret = -1; goto unmake_current; } handle = gbm_bo_get_handle(screen_bo).u32; stride = gbm_bo_get_pitch(screen_bo); shadow_handle = gbm_bo_get_handle(shadow_bo).u32; shadow_stride = gbm_bo_get_pitch(shadow_bo); shadow_image = create_image_from_handle(dpy, ctx, fd, shadow_handle, kms.mode.hdisplay, kms.mode.vdisplay, shadow_stride); screen_image = eglCreateImageKHR(dpy, NULL, EGL_NATIVE_PIXMAP_KHR, screen_bo, NULL); if (screen_image == EGL_NO_IMAGE_KHR || shadow_image == EGL_NO_IMAGE_KHR) { fprintf(stderr, "failed to create egl image\n"); ret = -1; goto destroy_gbm_bo; } shadow_tex = create_tex_from_img(shadow_image); screen_tex = create_tex_from_img(screen_image); shadow_fb = create_fbo_render2tex(shadow_tex); screen_fb = create_fbo_render2tex(screen_tex); glBindFramebuffer(GL_FRAMEBUFFER, shadow_fb); render_stuff(kms.mode.hdisplay, kms.mode.vdisplay); /* Copy shadow texture to screen texture by using shader. */ glBindFramebuffer(GL_FRAMEBUFFER, screen_fb); copy_tex(shadow_tex); glFlush(); ret = drmModeAddFB(fd, kms.mode.hdisplay, kms.mode.vdisplay, 24, 32, stride, handle, &kms.fb_id); if (ret) { fprintf(stderr, "failed to create fb\n"); goto rm_rb; } saved_crtc = drmModeGetCrtc(fd, kms.encoder->crtc_id); if (saved_crtc == NULL) goto rm_fb; ret = drmModeSetCrtc(fd, kms.encoder->crtc_id, kms.fb_id, 0, 0, &kms.connector->connector_id, 1, &kms.mode); if (ret) { fprintf(stderr, "failed to set mode: %m\n"); goto free_saved_crtc; } getchar(); ret = drmModeSetCrtc(fd, saved_crtc->crtc_id, saved_crtc->buffer_id, saved_crtc->x, saved_crtc->y, &kms.connector->connector_id, 1, &saved_crtc->mode); if (ret) { fprintf(stderr, "failed to restore crtc: %m\n"); } free_saved_crtc: drmModeFreeCrtc(saved_crtc); rm_rb: glDeleteFramebuffers(1, &shadow_fb); glDeleteFramebuffers(1, &screen_fb); glDeleteTextures(1, &shadow_tex); glDeleteTextures(1, &screen_tex); rm_fb: drmModeRmFB(fd, kms.fb_id); eglDestroyImageKHR(dpy, shadow_image); eglDestroyImageKHR(dpy, screen_image); shadow_image = create_image_from_handle(dpy, ctx, fd, shadow_handle, kms.mode.hdisplay / 2 , kms.mode.vdisplay / 2 , shadow_stride/2); if (shadow_image == EGL_NO_IMAGE_KHR) { /* As we already destroied the first shadow image which created from the same handle, we should be able to create a smaller image from the same handle again. This is true, if we don't call into copy_tex(shadow_tex); But if we call into copy_tex, then we fail here. You will get error message from the mesa side as below: "Region for name 1 already exists but is not compatible " copy_tex(shadow_tex) is a function to use shadow texture as a texture source, and access it in a shader program to render the screen fbo. I traced into the code, and found that glDrawArray will implicitly increase the shadow texture's reference count. And then latter when we delete the texture, the count will remain 1 rather then zero, and then it will not decrease the corresponding image region's reference counter and then when we destroy the shadow image, the image's reference counter will also be 1 and can't be freed. Then next time we use the same handle to create a new image, it will find the previou zombie image region and find they are not compatible. simply comment out the glDrawArray can avoid the function goes here too. Don't know how to fix this problem. */ fprintf(stderr, "hit a bug?\n"); } else eglDestroyImageKHR(dpy, shadow_image); destroy_gbm_bo: gbm_bo_destroy(shadow_bo); gbm_bo_destroy(screen_bo); unmake_current: eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); destroy_context: eglDestroyContext(dpy, ctx); egl_terminate: eglTerminate(dpy); destroy_gbm_device: gbm_device_destroy(gbm); close_fd: close(fd); return ret; }
_______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev