glDeleteTextures似乎没有释放Windows上的纹理内存,有没有解决方案?

时间:2011-12-01 00:26:30

标签: opengl textures

我的openGL应用程序内存不足时遇到了一些问题,我正在尝试追踪我的问题。为此,我创建了一个小的测试程序,基本上只是从文件调用glDeleteTextures加载一个巨大的纹理,然后再次加载它,如果我在OSX上运行这个测试程序,这个过程可以运行数百次迭代而没有问题(最多我运行它是1024,这没有问题)但在Windows上大约14次迭代后,我得到一个GLU_OUT_OF_MEMORY错误。我一遍又一遍地装载的纹理是一个巨大的天空圆顶(4096 x 1024和4.9MB)。

以下是我运行的代码,其中包含对非lisp倾向的评论:

测试程序:

 (with-glcontext (shared-opengl-view)
   ;;loop 1024 times
   (dotimes (i 1024)
    ;; create a texture and return its texture name
    (let ((texture (create-texture-from-file (truename "lui:resources;sky domes;DarkClouds.png"))))
      ;;Allocate two bytes to pass to glDeleteTextures
      (ccl::rlet ((&texName :long))
        ;; put the texture string into our allocated bytes
        (setf (ccl::%get-long &texName) texture)
          ;; Delete the textures?
          (glDeleteTextures 1 &texName)))))

从文件创建纹理(这个方法的大部分由openGL调用组成,所以我认为大多数openGL人员应该对这里发生的事情有一个合理的理解,但我可以澄清任何令人困惑的事情):

(defun CREATE-TEXTURE-FROM-FILE (Filename &key Verbose (Build-Mipmaps t) Repeat (Mag-Filter *Default-OpenGL-Texture-Magnification-Filter*) Forced-Depth) "
in:  Filename {string}, 
&key Verbose {boolean}, Repeat
out: OpenGL-Texture-Name {int}, width, height, depth
Load the <Filename> texture inside the texture directory.
- Ideal file should be 32 bit ARGB compatible, e.g., .png with mask or 24 bit RGB
- 8 bit and 16 bit image will work too 
- Image size must be 2^n x 2^m, at least 64 x 64
- This function must be called with active AGL context, e.g., inside OpenGL-Window INIT     method."
 (declare (ftype function create-image-from-file))
 (rlet ((&texName :long))
   (multiple-value-bind (&Image Width Height Depth) (create-image-from-file Filename :verbose Verbose :flip-vertical t :forced-depth Forced-Depth)
     (unless &Image (return-from create-texture-from-file nil))
     (glPixelStorei GL_UNPACK_ROW_LENGTH Width)  ; Set proper unpacking row length for image
     (glPixelStorei GL_UNPACK_ALIGNMENT 1)       ; Set byte aligned unpacking (needed for 3-byte-per-pixel image)
     (glGenTextures 1 &texName)
     ; Specify the texture's properties.
     (glBindTexture GL_TEXTURE_2D (%get-long &texName))
     (glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_S (if Repeat GL_REPEAT GL_CLAMP_TO_EDGE))
     (glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_T (if Repeat GL_REPEAT GL_CLAMP_TO_EDGE))
     (glTexParameteri gl_texture_2d gl_texture_mag_filter Mag-Filter)   ;; make textures look smooth (can be costly)
     ;; Mipmaps: make texture look good at different sizes
     (if Build-Mipmaps
      (glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_LINEAR_MIPMAP_NEAREST)
      (glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_LINEAR))
    ;; Experimental: anisotropic filtering to make textures less blurry at angle
    #-:cocotron  (glTexParameterf GL_TEXTURE_2D GL_TEXTURE_MAX_ANISOTROPY_EXT 16.0)
     (let ((PixelFormat (ecase Depth (32 GL_RGBA) (24 GL_RGB)))
         (InternalFormat (ecase Depth (32 GL_RGBA8) (24 GL_RGB8))))
      (glTexImage2D GL_TEXTURE_2D 0 InternalFormat width height 0 PixelFormat GL_UNSIGNED_BYTE &Image)
      (when Build-Mipmaps
        (when Verbose (format t "~%Building Mipmaps~%"))
      (unless 
          ;; The calls to gluBuild2DMipmaps will return GLU_OUT_OF_MEMORY when we run out.
          (zerop (print (gluBuild2DMipmaps GL_TEXTURE_2D InternalFormat width height PixelFormat GL_UNSIGNED_BYTE &Image)))
        (print (get-opengl-error))
        (error "could not create mipmaps"))
      (when Verbose (format t "Completed Mipmaps~%"))))
  ;; OpenGL should have copied now the image data into texture memory: release
  (dispose-vector &Image)
  ;; return the texture handle and image dimensions
  (values
   (%get-long &texName)
   Width 
   Height
   Depth))))

我在其他地方读过glDeleteTextures没有完全处理所有资源,但这些帖子都没有提供任何替代或解决方案。没有这样的解决方案吗?

3 个答案:

答案 0 :(得分:10)

Windows OpenGL驱动程序的设计假设您的代码实际上会在某些时候绘制内容。因此,当你要求他们完成时,他们倾向于避免做事,将他们推迟到更方便的时间。这就是你在这里遇到的。

仅仅因为你告诉OpenGL你已经完成了一个纹理对象并不意味着OpenGL必须立即放弃那个内存。这样做通常需要一些非常重量级的操作。因此,司机会将此推迟到以后。

问题是“以后”通常被定义为某种形式的glDraw*命令,glEndglFlush/Finish或交换缓冲区命令。或者那种性质的东西。如果您只是坐在循环中并创建/删除纹理,则驱动程序可能无法实际删除任何内容。

尝试减少人工测试。在每个glFinish之后添加glDeleteTextures。如果这不起作用,请在删除之间绘制内容。

最终,您无法强制驱动程序执行您想要的操作。您所能做的就是让您的测试应用程序更像普通应用程序。

答案 1 :(得分:0)

另外,由于您的错误有GLU前缀,您是否可以尝试运行相同的程序而不构建mipmap? [已完成]我不认为GLU_OUT_OF_MEMORY一定意味着你是超出GPU内存(实际上mipmap可能是在CPU上构建的),所以你可能会遇到Heap fragmentation。您在具有非常不同的GPU的不同Windows机器上获得错误的事实似乎表明它不是特定的OpenGL驱动程序问题;它们在各个供应商之间过于不同。

由于您使用的是大纹理,如果不使用gluBuild2DMipmaps无法解决问题,您可以在应用启动时为您的大纹理预分配GPU内存并重复使用使用glTexSubImage构造纹理对象,而不是在重新加载时创建新对象。

编辑:以下内容未提供解决方案(请参阅问题评论),但在我看来仍然有用

由于您获得GLU_OUT_OF_MEMORY而不是GL_OUT_OF_MEMORY我认为在使用gluBuild2DMipmaps创建mipmap期间会出现问题。由于可能是glu函数出错了,也许您可​​以尝试在离线过程中构建mipmap,然后只需将它们加载到Create-texture-from-file中。除了可能解决内存不足错误之外,这还可以让您更好地控制实际的mipmap生成。例如,您可以使用高斯滤波器而不是盒式滤波器。它还可以确保跨平台的相同mipmap内容。我的理解是,这通常是处理mipmap的方式。

答案 2 :(得分:0)

我认为您可以尝试使用glTexImage2d / 3d将空图像推送到要删除的纹理。它对VBO有用 - 我通过使用glBufferData删除缓冲区数据并传递一个空数组句柄来摆脱图形卡中的数据,因为glDeleteBuffers没有自行删除数据。