加载同一图像的多个实例的最佳方式

时间:2013-10-07 20:38:59

标签: python image kivy grid-layout

我正在搞乱Kivy,试图通过某种2D网格来获得一些类似应用的游戏。在我的设计中,每个单元格网格都应该有自己的图形表示,具体取决于内部的内容。 下面是我目前的简单代码,它只创建网格并在每个单元格中插入一些图像。

class MyWidget(Widget):
    def __init__(self,images,*args, **kwargs):
        super(MyWidget, self).__init__(*args, **kwargs)
        self.images = []
        for img in images:
            self.add_image(img)
        self.bind(pos=self.callback,size=self.callback)
    def add_image(self,image):
        self.images.append(Image(source=image,allow_stretch = True,keep_ratio = False))
        self.add_widget(self.images[-1])
    def callback(self,instance,value):
        for image in instance.images:
            image.pos = instance.pos
            image.size = instance.size

class StartScreen(Screen):
    def __init__(self,**kwargs):
        super(StartScreen, self).__init__(**kwargs)
        i = 10
        self.layout = GridLayout(cols=i)
        self.add_widget(self.layout)
        for i in range(i*i):
            self.layout.add_widget(MyWidget(['./images/grass.png','./images/bug1.png']))

class TestApp(App):
    def build(self):
        return StartScreen()

问题在于,虽然所有图像都是相同的,但是根据我所看到的,每个单元格都会再次加载到内存中。效率不高,特别是当有10000个左右的细胞时。 我试图将相同的图像添加到每个单元格,但事实证明每个窗口小部件只能有一个父窗口。 我也尝试初始化一个已经初始化的纹理的新图像,但是没有改进。 我试图获得加载图像的纹理,然后用它作为纹理创建矩形。像这样:

def add_image(self,texture):
    with self.canvas:
        rect = Rectangle(texture=texture,pos=self.pos, size=self.size)
        self.rects.append(rect)

纹理是:

Image(source='./images/grass.png',allow_stretch = True,keep_ratio = False).texture

它提高了内存使用率(对于具有200kB图像的10000个单元格,从430MB到160MB。但是,对于两个图像来说,这仍然很多。)

我的问题:在Kivy中,是否有更有效的方法来创建具有大量重复图像的2D网格? 也许我对这个问题的处理方式存在缺陷 - 我在制作游戏方面确实没有过期......

1 个答案:

答案 0 :(得分:1)

你看到的内存问题来自你的小部件而不是纹理管理不当,kivy中的图像纹理使用kivy的内部cache机制进行缓存,所以如果你尝试在一分钟内加载图像100次,那么kivy就会重新 - 使用缓存中的现有纹理。

此缓存在设置的时间后会超时,并且会在此超时后从磁盘重新加载。出于这个原因,Image小部件有一个reload方法和一个可以设置的nocache属性。您可以手动设置缓存::

from kivy.cache import Cache
Cache._categories['kv.image']['limit'] = 0
Cache._categories['kv.texture']['limit'] = 0 

对于游戏,虽然应该总是尝试使用Atlas,但它的缓存不会超时,整个机制的目的是让您获得更好的性能和纹理管理。其他优点包括上传到gpu的单个纹理=上传时间的巨大改进,从磁盘时间读取的数量大大减少。

每个小部件,无论多么微不足道的开销,以及处理相同Widget的10,000多次迭代。您一定会遇到内存使用问题。小部件不适合这种用法。你应该看看使用重复纹理, 操纵纹理坐标,直接绘制到画布上而不使用小部件。像(未经测试的)::

texture = Image('grid.jpg').texture
texture.wrap = 'repeat'
texture.uvsize = (20, 20)
with self.canvas:
    Color(1, 1, 1)
    Rectangle(pos=(0, 0), size=(2000, 2000), texture=texture)

如果您对编写游戏感兴趣,可能需要将KivEnt和cymunk视为物理引擎。