指定屏幕中的所有像素
Specifying all pixels in screen
我正在尝试 Pygame 逐个像素地更改我的屏幕。我知道我不应该这样做,因为它效率不高,但我只是想构建我自己的 3D 引擎版本以用于(有趣的)学习目的。
在下面的代码中我注意到 Pygame 可以非常快速地改变整个屏幕 (METHOD=1),像素改变方法明显更慢。我尝试直接使用 Surface.fill
(METHOD=2) 但这很慢。然后我尝试了 (METHOD=3) pygame.surfarray.blit_array
并且 blit 非常快但是我在 python 中的双循环非常慢。这对我来说感觉很奇怪,因为它是一个简单的双循环,用整数填充一个 numpy 数组,我认为它不会成为瓶颈。
编辑:根据@BlackJack 的建议,我使用 METHOD=4 基本上将循环发送到 Cython 模块中。我按照以下安装方式进行操作:
我遵循了 http://docs.cython.org/src/userguide/numpy_tutorial.html
中显示的优化技术
循环时间得到了根本改善!快 100 倍!如果我查看我可以管理多少 FPS,我会看到:
- 方法 2:01.89 fps
- 方法 3:04.83 fps
- 方法 4:444.4 fps
下面是我的工作示例(假设 Cython 已正确安装并正常工作,这不是一件小事)。不需要预编译模块,因为我使用的是 pyximport
PyGameTest.py
from __future__ import division
import pygame, random, time
import numpy as np
import pyximport
pyximport.install(setup_args={'include_dirs': np.get_include()})
import FastLoops
#n = r*256^2 + g*256 + b
IRED=256*256*150
IGRN=256*150
IBLU=150
arr=[IRED,IGRN,IBLU]
BLK=0
RESX=600
RESY=600
TARGFPS=60
# initialize game engine
pygame.init()
# set screen width/height and caption
Surface = pygame.display.set_mode([RESX, RESY])
pygame.display.set_caption('My Game')
# initialize clock. used later in the loop.
clock = pygame.time.Clock()
METHOD=4
i0=0
t0=time.time()
nl=0
# Loop until the user clicks close button
done = False
while done == False:
# write event handlers here
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if METHOD==1:
col=arr[random.randint(0,2)]
Surface.fill(col)
Surface.fill(BLK,pygame.Rect(i0,i0,10,10))
elif METHOD==2:
for i in range(RESX):
if i<i0:
col=IBLU
else:
col=IGRN
for j in range(RESY):
Surface.fill(col,pygame.Rect(i,j,1,1))
elif METHOD==3:
myarray = np.zeros([RESX, RESY], dtype=np.int32)
for i in range(RESX):
if i<i0:
col=IBLU
else:
col=IGRN
for j in range(RESY):
myarray[i,j]=col
pygame.surfarray.blit_array(Surface,myarray)
elif METHOD==4:
myarray = FastLoops.Loop(RESX,RESY,IBLU,IRED,i0)
pygame.surfarray.blit_array(Surface,myarray)
nl+=1
i0+=RESX/TARGFPS
if i0>=RESX:
t1=time.time()
fps=round(nl/(t1-t0),2)
print 'METHOD '+str(METHOD)+':'
print ' Time = ' + str(round(t1-t0,2)) + 's for ' + str(nl) + ' frames'
print ' FPS = '+str(fps)
i0=0
t0=time.time()
nl=0
# display what’s drawn. this might change.
pygame.display.update()
# run at 20 fps
clock.tick(TARGFPS)
# close the window and quit
pygame.quit()
FastLoops.pyx
from __future__ import division
import numpy as np
cimport numpy as np
cimport cython
DTYPE = np.int32
ctypedef np.int32_t DTYPE_t
@cython.boundscheck(False) # turn of bounds-checking for entire function
def Loop(int RESX, int RESY, long IBLU, long IGRN, int i0):
cdef int i, j
cdef np.ndarray[DTYPE_t, ndim=2] myarray = np.zeros([RESX, RESY], dtype=DTYPE)
for i in range(RESX):
if i<i0:
col=IBLU
else:
col=IGRN
for j in range(RESY):
myarray[i,j]=col
return myarray
预计纯 python 循环比调用用 C 实现的函数或方法慢得多,甚至可能通过图形硬件加速。
尽可能尝试使用 Numpy 方法,因为这些方法是用 C 实现的,或者使用用 C 或 Fortran 实现的库。
如果您确实需要单像素级访问,您可以尝试在 Cython 中编写慢速部分并将该代码编译为 Python 的扩展模块。
免责声明:我是 Pythran 项目的开发人员之一。
如果Cython性能不够,可以试试Pythran。
在您的特定测试用例中,以下代码:
import numpy as np
#pythran export Loop(int, int, int, int, int)
def Loop(RESX, RESY, IBLU, IGRN, i0):
myarray = np.empty((RESX, RESY), dtype=np.int32)
for i in range(RESX):
if i < i0:
col = IBLU
else:
col = IGRN
myarray[i, :] = IBLU if i < i0 else IGRN
return myarray
使我在 Cython 上的速度提高了 2 倍。
使用numpy
风格(还有待改进)
import numpy as np
#pythran export Loop(int, int, int, int, int)
def Loop(RESX, RESY, IBLU, IGRN, i0):
myarray = np.empty((RESX, RESY), dtype=np.int32)
for i in range(RESX):
myarray[:, :] = IBLU if i < i0 else IGRN
return myarray
给我 x3 加速。
我正在尝试 Pygame 逐个像素地更改我的屏幕。我知道我不应该这样做,因为它效率不高,但我只是想构建我自己的 3D 引擎版本以用于(有趣的)学习目的。
在下面的代码中我注意到 Pygame 可以非常快速地改变整个屏幕 (METHOD=1),像素改变方法明显更慢。我尝试直接使用 Surface.fill
(METHOD=2) 但这很慢。然后我尝试了 (METHOD=3) pygame.surfarray.blit_array
并且 blit 非常快但是我在 python 中的双循环非常慢。这对我来说感觉很奇怪,因为它是一个简单的双循环,用整数填充一个 numpy 数组,我认为它不会成为瓶颈。
编辑:根据@BlackJack 的建议,我使用 METHOD=4 基本上将循环发送到 Cython 模块中。我按照以下安装方式进行操作:
循环时间得到了根本改善!快 100 倍!如果我查看我可以管理多少 FPS,我会看到:
- 方法 2:01.89 fps
- 方法 3:04.83 fps
- 方法 4:444.4 fps
下面是我的工作示例(假设 Cython 已正确安装并正常工作,这不是一件小事)。不需要预编译模块,因为我使用的是 pyximport
PyGameTest.py
from __future__ import division
import pygame, random, time
import numpy as np
import pyximport
pyximport.install(setup_args={'include_dirs': np.get_include()})
import FastLoops
#n = r*256^2 + g*256 + b
IRED=256*256*150
IGRN=256*150
IBLU=150
arr=[IRED,IGRN,IBLU]
BLK=0
RESX=600
RESY=600
TARGFPS=60
# initialize game engine
pygame.init()
# set screen width/height and caption
Surface = pygame.display.set_mode([RESX, RESY])
pygame.display.set_caption('My Game')
# initialize clock. used later in the loop.
clock = pygame.time.Clock()
METHOD=4
i0=0
t0=time.time()
nl=0
# Loop until the user clicks close button
done = False
while done == False:
# write event handlers here
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if METHOD==1:
col=arr[random.randint(0,2)]
Surface.fill(col)
Surface.fill(BLK,pygame.Rect(i0,i0,10,10))
elif METHOD==2:
for i in range(RESX):
if i<i0:
col=IBLU
else:
col=IGRN
for j in range(RESY):
Surface.fill(col,pygame.Rect(i,j,1,1))
elif METHOD==3:
myarray = np.zeros([RESX, RESY], dtype=np.int32)
for i in range(RESX):
if i<i0:
col=IBLU
else:
col=IGRN
for j in range(RESY):
myarray[i,j]=col
pygame.surfarray.blit_array(Surface,myarray)
elif METHOD==4:
myarray = FastLoops.Loop(RESX,RESY,IBLU,IRED,i0)
pygame.surfarray.blit_array(Surface,myarray)
nl+=1
i0+=RESX/TARGFPS
if i0>=RESX:
t1=time.time()
fps=round(nl/(t1-t0),2)
print 'METHOD '+str(METHOD)+':'
print ' Time = ' + str(round(t1-t0,2)) + 's for ' + str(nl) + ' frames'
print ' FPS = '+str(fps)
i0=0
t0=time.time()
nl=0
# display what’s drawn. this might change.
pygame.display.update()
# run at 20 fps
clock.tick(TARGFPS)
# close the window and quit
pygame.quit()
FastLoops.pyx
from __future__ import division
import numpy as np
cimport numpy as np
cimport cython
DTYPE = np.int32
ctypedef np.int32_t DTYPE_t
@cython.boundscheck(False) # turn of bounds-checking for entire function
def Loop(int RESX, int RESY, long IBLU, long IGRN, int i0):
cdef int i, j
cdef np.ndarray[DTYPE_t, ndim=2] myarray = np.zeros([RESX, RESY], dtype=DTYPE)
for i in range(RESX):
if i<i0:
col=IBLU
else:
col=IGRN
for j in range(RESY):
myarray[i,j]=col
return myarray
预计纯 python 循环比调用用 C 实现的函数或方法慢得多,甚至可能通过图形硬件加速。
尽可能尝试使用 Numpy 方法,因为这些方法是用 C 实现的,或者使用用 C 或 Fortran 实现的库。
如果您确实需要单像素级访问,您可以尝试在 Cython 中编写慢速部分并将该代码编译为 Python 的扩展模块。
免责声明:我是 Pythran 项目的开发人员之一。
如果Cython性能不够,可以试试Pythran。 在您的特定测试用例中,以下代码:
import numpy as np
#pythran export Loop(int, int, int, int, int)
def Loop(RESX, RESY, IBLU, IGRN, i0):
myarray = np.empty((RESX, RESY), dtype=np.int32)
for i in range(RESX):
if i < i0:
col = IBLU
else:
col = IGRN
myarray[i, :] = IBLU if i < i0 else IGRN
return myarray
使我在 Cython 上的速度提高了 2 倍。
使用numpy
风格(还有待改进)
import numpy as np
#pythran export Loop(int, int, int, int, int)
def Loop(RESX, RESY, IBLU, IGRN, i0):
myarray = np.empty((RESX, RESY), dtype=np.int32)
for i in range(RESX):
myarray[:, :] = IBLU if i < i0 else IGRN
return myarray
给我 x3 加速。