Почему pyglet.image и текстура такие тяжелые? - PullRequest
2 голосов
/ 04 апреля 2019

Envrionment:

  • питон: 3.6.6
  • Пиглет: 1.3.2

Вот мой код и результаты:

import pyglet

images = []
textures = []

with_textures = True
count = 10
for x in range(count):
    image = pyglet.image.load("big.png")  # 2.1 Mb source 2400*2400px
    images.append(image)
    if with_textures:
        texture_grid = pyglet.image.ImageGrid(image, 10, 10).get_texture_sequence()
        textures.append(texture_grid)

# RES in htop result without textures
# count = 10 - 300Mb
# count = 20 - 553Mb
# count = 30 - 753Mb
# count = 40 - 973Mb
# ~23Mb just for each Image

# RES in htop result with textures
# count = 10 - 996Mb
# count = 20 - 1878Mb
# count = 30 - 2716Mb
# count = 40 - 3597Mb
# ~86Mb for Image and prepared grid


input("Press enter to exit")

Вопросы:

  1. Почему каждый файл объемом 2,1 МБ приводит к 23 МБ использования памяти с pyglet.image.AbstractImage ?
    • Если ImageGrid используется для создания листа спрайта -> это приводит к дополнительным ~ 60 МБ
  2. Как с этим бороться? Потому что, если игра содержит более 50 спрайтов, было бы нереально выделять такое количество памяти только для текстур.
  3. Может быть, есть какой-то другой подход к созданию игр, в котором используются спрайты? Или я должен изменить свою технологию стека (pyglet в качестве основной библиотеки, также пытался с pygame) для клиентской стороны?

PS: Впервые я переписал свое приложение с pygame до pyglet, потому что я не рассматривал некоторые аспекты цикла событий в pygame, и теперь я не тестировал использование ресурсов pyglet библиотека для моих сценариев использования.

Обновление / уточнение:

Я использую ImageGrid как для 3-й части в вершинах, так и для 2-й части в pyglet.sprite.Sprite

Пример использования в 3D детали:

# texture_group is created once for each sprite sheet, same as texture_grid
texture_group = pyglet.graphics.TextureGroup(texture_grid, order_group)
...

tex_map = texture_grid[self.texture_grid_index].texture.tex_coords
tex_coords = ('t3f', tex_map)
self.entity = self.batch.add(
    4, pyglet.gl.GL_QUADS,
    texture_group,
    ('v3f', (x, y, z,
             x_, y, z,
             x_, y_, z,
             x, y_, z)
     ),
    tex_coords)

Пример использования в 2D детали:

pyglet.sprite.Sprite(img=texture_grid[self.texture_grid_index], x=0, y=0,
                     batch=self.batch, group=some_order_group)

Обновление № 2:

Как я выяснил, допустимые размеры для использования pyglet.image.CompressedImageData:

1 True
2 True
4 True
8 True
16 True
32 True
64 True
128 True
256 True
512 True
1024 True
2048 True
4096 True

Но не могу получить текстуру от CompressedImageData:

big = pyglet.image.load("big.png")  # 2048*2048
compressed_format = pyglet.graphics.GL_COMPRESSED_ALPHA
compressed_image = pyglet.image.CompressedImageData(
    big.width, big.height, compressed_format, big.data)
compressed_image.texture  # exception GLException: b'invalid enumerant'

Пробовал со всеми возможными GL_COMPRESS в pyglet:

allowed_formats = [x for x in dir(pyglet.graphics) if "GL_COMPRESSED_" in x]
big = pyglet.image.load("big.png")  # 2048*2048
for form in allowed_formats:
    compressed_image = pyglet.image.CompressedImageData(
        big.width, big.height, form, big.data)
    try:
        compressed_image.texture
        print("positive:", form)  # 0 positive prints
    except Exception:
        pass

Обновление № 3:

Исключения составляют:

Traceback (most recent call last):
  File "<ipython-input-72-1b802ff07742>", line 7, in <module>
    compressed_image.texture
  File "/<venv>/lib/python3.6/site-packages/pyglet/image/__init__.py", line 410, in texture
    return self.get_texture()
  File "/<venv>/lib/python3.6/site-packages/pyglet/image/__init__.py", line 1351, in get_texture
    len(self.data), self.data)
ctypes.ArgumentError: argument 3: <class 'TypeError'>: wrong type

Что происходит в pyglet:

if self._have_extension():
    glCompressedTexImage2DARB(texture.target, texture.level,
                              self.gl_format,
                              self.width, self.height, 0,
                              len(self.data), self.data)

IPDB:

> /<venv>/lib/python3.6/site-packages/pyglet/image/__init__.py(1349)get_texture()
   1348             import ipdb;ipdb.set_trace()
-> 1349             glCompressedTexImage2DARB(texture.target, texture.level,
   1350                                       self.gl_format,

ipdb> glCompressedTexImage2DARB
<_FuncPtr object at 0x7fca957ee1d8>
ipdb> texture.target
3553
ipdb> texture.level
0
ipdb> self.gl_format
'GL_COMPRESSED_TEXTURE_FORMATS_ARB'    
ipdb> self.width
2048
ipdb> self.height
2048
ipdb> len(self.data)
16777216
ipdb> type(self.data)
<class 'bytes'>

1 Ответ

4 голосов
/ 08 апреля 2019

Ответ на # 1 прост и понятен: PNG - сжатый формат.Изображение 2400 × 2400 просто должно занимать 2400 × 2400 × 32 бита = 23040000 байт в оперативной памяти после распаковки, даже если его размер диска составляет 2 МБ.

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

Например, в Pyglet есть класс с именем CompressedImageData , который позволяет использовать встроенный графический процессор.в сжатии текстур, если вы используете OpenGL для рендеринга.Если нет, вы, вероятно, застряли в реализации одного из этих методов в программном обеспечении, поскольку сжатие PNG в основном не подходит для быстрого доступа к пикселям. Здесь вы можете найти больше информации о сжатии текстур графического процессора.

В качестве быстрого и грязного обходного пути, вы можете попытаться уменьшить количество цветов в вашем изображении до ≤256 и использовать палитру.Это сразу даст преимущество в 4 раза.

...