glDeleteTextures does not seem to free up texture memory on Windows, is there no solution?
Asked Answered
A

3

8

I have been having some problems with my openGL application running out of memory and I am trying to track down my problem. To this end, I created a small test program that basically just loads a giant texture from a file calls glDeleteTextures and then loads it again, if I run this test program on OSX this process can run for hundreds of iterations with no problems (the most I ran it for was 1024 and this had no problems) but after about 14 iterations on windows I get a GLU_OUT_OF_MEMORY error. The texture I am loading over and over is a huge sky dome (4096 x 1024 and 4.9MB).

Here is the code I am running with comments for the non lisp inclined:

Test Program:

 (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)))))

Create-texture-from-file (most of this method is made up of openGL calls so I think most openGL people should have a reasonable understanding of what's happening here but I can clarify anything thats confusing):

(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))))

I have read elsewhere the glDeleteTextures does not completely dealocate all resources but none of these posts offered any alternative or solutions. Is there no such solution?

Annulment answered 1/12, 2011 at 0:26 Comment(4)
What happens when you don't create mipmaps?Sheets
@genpfault: When I don't create the mipmaps it runs out of memory after 17 iterations (instead of 14).Annulment
But if you don't generate mipmaps, does it generate GL_OUT_OF_MEMORY instead of GLU_OUT_OF_MEMORY?Stifle
Try calling glIsTexture and glAreTexturesResident after deleting texture. Use the same texID as when deleting texture (it should return false in both cases, but if not we know where's problem)Dayton
C
10

Windows OpenGL drivers are designed on the assumption that your code will actually draw stuff at some point. Because of that, they tend to avoid doing things exactly when you ask them to be done, deferring them to a more convenient time. And that's what you're running into here.

Just because you tell OpenGL that you're done with a texture object doesn't mean that OpenGL must immediately relinquish that memory. Doing that generally requires some pretty heavy-weight operations. So instead, drivers will defer this until later.

The problem is that "later" is usually defined as some form of glDraw* command, glEnd, glFlush/Finish, or a swap buffer command. Or something of that nature. If all you do is sit in a loop and create/delete textures, the driver may not get the chance to actually delete anything.

Try making your test less artificial. Add a glFinish after each glDeleteTextures. If that doesn't work, draw stuff between deletions.

Ultimately, you cannot force drivers to do what you want. All you can do is make your test application behave more like a normal application.

Carnation answered 1/12, 2011 at 1:27 Comment(6)
Thanks you so much for your answer Nicol but I don't think this is the problem (at least not the only one). I added the glFinish and I still get the same crash. I was initially inspired to create this test program because I was getting the same sort of troublesome behavior inside my actual application. In my app I have a world (a big openGLView) and a reload button which will reload the openGL (from specifications described in an XML file), I first ran into this problem when pressing this reload button around 14 times, when pressing the button the openGLView would be displayed every time.Annulment
In the world described in my last comment I had this same large sky dome.Annulment
What graphics card are you using?Carnation
I have an ATI Radeon HD 4670 and my co worker experiences the same sort of behavior on a NVidia GeForce 8600M GT.Annulment
It also happens on a low end HP test machine with Intel HD graphics and an IMac running bootcamp/Windows 7 with a GeForce GT 130.Annulment
Thank you for great explanation. After using glFinish it actualy begining to be usefull to use glDeleteTextures.Boykin
N
0

Also, since your error has the GLU prefix, could you try running the same program without building mipmaps?[already done] I don't think GLU_OUT_OF_MEMORY necessarily means that you are out of GPU memory (in fact the mipmaps are probably built on the CPU), so maybe you are suffering from Heap fragmentation. The fact that you get the error on different Windows machines with very different GPUs seems to indicate that it's not a specific OpenGL driver problem; they are too different across vendors.

Since you are using a large texture, and if not using gluBuild2DMipmaps doesn't solve the problem, you could pre-allocate the GPU memory for your large texture(s) once when your app starts and reuse the texture objects with glTexSubImage instead of creating new ones on reload.

edit: the following does not provide a solution (see question comments), but is still useful in my opinion

Since you get GLU_OUT_OF_MEMORY and not GL_OUT_OF_MEMORYI assume the problem occurs during mipmap creation with gluBuild2DMipmaps. Since maybe the glu function is at fault, perhaps you could try building your mipmaps in an offline process and then simply load them in Create-texture-from-file. Besides potentially solving your out of memory error, this would let you have much better control over the actual mipmap generation. You could for example use a gaussian filter instead of a box filter. It would also ensure identical mipmap content across platforms. My understanding is that this is how mipmaps are usually handled.

Nephelinite answered 6/12, 2011 at 16:47 Comment(0)
S
0

I think you could try to push an empty image to the texture you want to delete by using glTexImage2d/3d. It worked for me with VBO's - I get rid of the data from the graphic cards by removing a buffer data using glBufferData and passing an empty array handle, because the glDeleteBuffers didn't removed the data by itself.

Salas answered 13/2, 2014 at 9:42 Comment(1)
I tried this and had no luck :( Neither did drawing a simple triangle, glFlush(), glFinish() nor a swap buffers appear to make the driver release the memory. AMD Omega 14.12 drivers.Drain

© 2022 - 2024 — McMap. All rights reserved.