Похоже, что glDeleteTextures не освобождает текстурную память в Windows, нет ли решения? - PullRequest
8 голосов
/ 01 декабря 2011

У меня возникли проблемы с нехваткой памяти в приложении openGL, и я пытаюсь отследить свою проблему.С этой целью я создал небольшую тестовую программу, которая в основном просто загружает гигантскую текстуру из файла с именем glDeleteTextures, а затем загружает ее снова, если я запускаю эту тестовую программу в OSX, этот процесс может выполняться без сотен итераций без проблем (большинствоЯ запускал его для 1024, и с этим проблем не было), но после 14 итераций в Windows я получаю ошибку GLU_OUT_OF_MEMORY.Текстура, которую я загружаю снова и снова, представляет собой огромный небесный купол (4096 x 1024 и 4,9 МБ).

Вот код, который я запускаю с комментариями для не склонных к 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)))))

Create-texture-from-file (большая часть этогометод состоит из вызовов 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 ]

10 голосов
/ 01 декабря 2011

Драйверы Windows OpenGL разработаны на основе предположения, что ваш код на самом деле будет рисовать вещи в какой-то момент.Из-за этого они, как правило, стараются избегать того, чтобы делать что-то, когда вы просите это сделать, откладывая их на более удобное время.И это то, с чем вы здесь сталкиваетесь.

То, что вы говорите OpenGL, что вы сделали с объектом текстуры, не означает, что OpenGL должен немедленно освободить эту память.Для этого обычно требуются довольно тяжелые операции.Поэтому вместо этого драйверы откладывают это до более позднего периода.

Проблема в том, что «позднее» обычно определяется как некоторая форма команды glDraw*, glEnd, glFlush/Finish или команды буфера подкачки.Или что-то в этом роде.Если все, что вы делаете, это сидите в цикле и создаете / удаляете текстуры, у драйвера может не быть возможности фактически удалить что-либо.

Попробуйте сделать свой тест менее искусственным.Добавьте glFinish после каждого glDeleteTextures.Если это не сработает, рисуйте вещи между удалениями.

В конечном счете, вы не можете заставить драйверов делать то, что вы хотите.Все, что вы можете сделать, это заставить ваше тестовое приложение вести себя как обычное приложение.

0 голосов
/ 13 февраля 2014

Я думаю, вы можете попытаться вставить пустое изображение в текстуру, которую хотите удалить, используя glTexImage2d / 3d.Это работало для меня с VBO - я избавлялся от данных с графических карт, удаляя данные буфера, используя glBufferData и передавая пустой дескриптор массива, потому что glDeleteBuffers не удалил данные сам по себе.

0 голосов
/ 06 декабря 2011

Кроме того, поскольку ваша ошибка имеет префикс GLU, не могли бы вы попробовать запустить ту же программу без построения mipmaps? [уже сделано] Я не думаю, что GLU_OUT_OF_MEMORY обязательно означает, что у вас нет GPU памяти (на самом деле mipmaps, вероятно, построены на процессоре), поэтому, возможно, вы страдаете от фрагментации кучи . Тот факт, что вы получаете сообщение об ошибке на разных компьютерах Windows с очень разными графическими процессорами, по-видимому, указывает на то, что это не конкретная проблема с драйвером OpenGL; они слишком разные у разных поставщиков.

Поскольку вы используете большую текстуру, а если не используете gluBuild2DMipmaps, это не решает проблему, вы можете предварительно выделить память GPU для вашей большой текстуры (текстур) один раз при запуске приложения и повторно использовать объекты текстуры с glTexSubImage вместо создания новых при перезагрузке.

edit: следующее не дает решения (см. Комментарии к вопросу), но все еще полезно, по моему мнению

Поскольку вы получаете GLU_OUT_OF_MEMORY, а не GL_OUT_OF_MEMORY Я предполагаю, что проблема возникает при создании mipmap с gluBuild2DMipmaps. Так как, возможно, виновата функция glu, возможно, вы могли бы попытаться построить свои mip-карты в автономном процессе, а затем просто загрузить их в Create-texture-from-file. Помимо возможного устранения ошибки нехватки памяти, это позволит вам гораздо лучше контролировать фактическое создание mip-карт. Например, вы можете использовать гауссов фильтр вместо блочного фильтра. Это также обеспечит идентичный контент mipmap на разных платформах. Насколько я понимаю, именно так обычно обрабатываются мипмапы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...