2012-02-27 2 views
3

Я разрабатываю небольшую игру с пикетом. Разумеется, одним центральным элементом являются цветные прямоугольники. Сначала я сделал это, создав образы в памяти и blit() их, которые отлично работали. После того, как вы заметили, насколько уродливые, обходные и неэффективные (да, я профилировал - ColorRect.draw() занял значительное время и стал в 10 раз более эффективным благодаря этому изменению), я начал создавать списки вершин вместо pyglet.graphics.Batch (я скопировал большую часть кода дословно из один из примеров). С тех пор я испытываю странное исключение в каком-то низкоуровневом коде OpenGL, в котором я не смог найти причину или воспроизвести надежно.pyglet.graphics: IndexError при создании массива ctypes

Нет никакого явного отношения к событиям геймплея - как и в случае, ничего исключительного не происходит непосредственно перед этим, или я постоянно его пропускаю. Поскольку ошибка происходит где-то глубоко в цикле событий, я не могу легко отследить, какое из них обновляет положение. Честно говоря, я в тупике. Таким образом, я буду следить за тем, что я узнал, и надеяться на какой-то экстрасенс.

Я пробовал это на Windows 7 32 бит (я могу обойти его в Ubuntu 11.10 в ближайшее время) с Python 3.2.2, с версией пиглета 043180b64260 (вытащенный из кода Goggle и построенный из источника, Версия 1.1.4 сложнее установить, так как она не запускается автоматически 2to3, хотя, похоже, она также готова к работе py3k). Вероятно, я обновлю последнюю версию меркурийской версии, но это всего лишь несколько коммитов, и изменения кажутся совершенно несвязанными.

Полные отслеживающая (цензура некоторых путей из принципа, но обратите внимание, что это в своем собственном virtualenv):

Traceback (most recent call last): 
    File "<my main file>", line 152, in <module> 
    main() 
    File "<my main file>", line 148, in main 
    run() 
    File "<my main file>", line 125, in run 
    pyglet.app.run() 
    File "<virtualenv>\Lib\site-packages\pyglet\app\__init__.py", line 123, in run 
    event_loop.run() 
    File "<virtualenv>\Lib\site-packages\pyglet\app\base.py", line 135, in run 
    self._run_estimated() 
    File "<virtualenv>\Lib\site-packages\pyglet\app\base.py", line 164, in _run_estimated 
    timeout = self.idle() 
    File "<virtualenv>\Lib\site-packages\pyglet\app\base.py", line 278, in idle 
    window.switch_to() 
    File "<virtualenv>\Lib\site-packages\pyglet\window\win32\__init__.py", line 305, in switch_to 
    self.context.set_current() 
    File "<virtualenv>\Lib\site-packages\pyglet\gl\win32.py", line 213, in set_current 
    super(Win32Context, self).set_current() 
    File "<virtualenv>\Lib\site-packages\pyglet\gl\base.py", line 320, in set_current 
    buffers = (gl.GLuint * len(buffers))(*buffers) 
IndexError: invalid index 

Бега с посмертным (активно пошаговым кодом, пока не произойдет раньше неосуществимые как FPS пошел от 60 до 7) pdb показывает:

  • buffers представляет собой список целых чисел; Я понятия не имею, что они представляют или откуда они берутся, но их вытаскивают из списка с именем self.object_space._doomed_textures (где self - это объект окна). Соответствующий комментарий говорит, что этот блок кода выпускает текстуру, запланированную для удаления. Я не думаю, что я явно использую текстуры где угодно, но кто знает, что делает пиглет под капотом. Я предполагаю, что эти целые числа являются идентификаторами или чем-то вроде текстур, подлежащих уничтожению.
  • gl.GLuint - это псевдоним для ctypes.c_ulong; Таким образом, (gl.GLuint * len(buffers))(*buffers) создает массив ulong той же длины и содержимого
  • Я могу оценить одно и то же выражение в подсказке pdb без ошибок или повреждения данных.

Независимые эксперименты (вне virtualenv и без импорта pyglet) с ctypes показывает, что IndexError возникает, если слишком много аргументов приведены в конструкторе массива. Это не имеет смысла, и эксперименты, и логика предполагают, что длина и аргумент count всегда должны совпадать.

  1. Есть ли другие случаи, когда это исключение может возникнуть? Может быть, это ошибка пеглета, или я неправильно использую библиотеку и пропустил соответствующее предупреждение?
  2. Будет ли код, который создает и поддерживает списки вершин, может быть использован для отладки? Вероятно, что-то не так. Я уже смотрел на него, но, поскольку у меня мало опыта работы с pyglet.graphics, это было ограниченным использованием. Просто оставьте комментарий, если вы хотите увидеть код ColorRect.
  3. Любые другие идеи, что может вызвать это?
+1

Можете ли вы сделать shure, не используется многопоточность? Это может вызвать проблемы в 'base.py', если задействованы несколько потоков. «Буферы» могут быть изменены другим потоком, пока массив выделяется до того, как отдельные конструкторы GLuint будут запущены для его заполнения. Таким образом, 'len (buffer)' не будет соответствовать '(* buffers)' length. – dronus

+0

@dronus Неожиданная идея! Я нигде не использую нити, и я сомневаюсь, что пиглет пытается работать параллельно. Но я проверю, '' _thread 'в sys.modules'. – delnan

+0

@dronus Забудьте о '_thread', он, видимо, всегда импортируется (я проверил в сеансе свежего интерпретатора). 'threading' с другой стороны импортируется в моем коде, но не в сеансах интерпретатора. 'threading.enumerate' дает просто' [<_MainThread (MainThread, start 4256)>] 'хотя. Я добавлю утверждение, что это всегда верно, и исследуйте, кто импортировал 'threading'. – delnan

ответ

0

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

buffers = (gl.GLuint * len(buffers))(*buffers) 

Так что, если я правильно понимаю, вы умножив размер в GLuint (4 байта) с вашим фактически буферами длину (если инициализация). Может быть, поэтому ваш индекс недействителен, потому что он слишком высок?

Обычно это нормально, поскольку буфер находится в байтах, но вы сказали, что это список ints?

Надеюсь, это поможет

+0

Мое понимание ctypes заключается в том, что 'some_c_type * some_int' создает тип' some_int' элементов типа 'some_c_type' (обратите внимание, что' GLuint is c_ulong'). Например, 'c_long * 10' дает' ', и этот класс может быть вызван аргументами' 10'. Пытаться привести его в действие с 11 или более параметрами приводит к «IndexError», передавая значения вне диапазона, просто обрезается молча (try '(c_byte * 10) (* range (4000, 4010))'). – delnan

+0

Я посмотрел немного больше на ctypes, они говорят это. [Цитата из] (http://docs.python.org/library/ctypes.html) "Примечание. Некоторые примеры кода ссылаются на тип ctypes c_int.Этот тип является псевдонимом для типа c_long в 32-битных системах. Таким образом, вы не должны путаться, если c_long напечатан, если вы ожидаете c_int - они на самом деле одного типа ». Я не знаю, насколько релевантна эта страница для вас, но я уверен, что она одинакова для uint и ulong. – ForceMagic

+0

Ваша точка бытия? – delnan