core: got a first version of gl context reloading working. now need to clean and test more.

This commit is contained in:
Mathieu Virbel 2012-03-03 18:45:23 +01:00
parent 4ee7014da9
commit d05777e954
9 changed files with 135 additions and 97 deletions

View File

@ -431,7 +431,6 @@ class LabelBase(object):
# If the text is 1px width, usually, the data is black.
# Don't blit that kind of data, otherwise, you have a little black bar.
if data is not None and data.width > 1:
print 'XXXXXXXXX BLIT DATA ON ', texture
texture.blit_data(data)
def _texture_refresh(self, *l):

View File

@ -27,6 +27,7 @@ cdef class Instruction:
cdef void flag_update(self, int do_parent=?)
cdef void flag_update_done(self)
cdef void set_parent(self, Instruction parent)
cdef void reload(self)
cdef void radd(self, InstructionGroup ig)
cdef void rinsert(self, InstructionGroup ig, int index)
@ -37,6 +38,7 @@ cdef class InstructionGroup(Instruction):
cdef InstructionGroup compiled_children
cdef GraphicsCompiler compiler
cdef void build(self)
cdef void reload(self)
cpdef add(self, Instruction c)
cpdef insert(self, int index, Instruction c)
cpdef remove(self, Instruction c)
@ -80,8 +82,10 @@ cdef class CanvasBase(InstructionGroup):
pass
cdef class Canvas(CanvasBase):
cdef object __weakref__
cdef CanvasBase _before
cdef CanvasBase _after
cdef void reload(self)
cpdef clear(self)
cpdef add(self, Instruction c)
cpdef remove(self, Instruction c)
@ -89,8 +93,6 @@ cdef class Canvas(CanvasBase):
cdef class RenderContext(Canvas):
cdef object __weakref__
cdef Shader _shader
cdef dict state_stacks
#cdef TextureManager texture_manager

View File

@ -24,15 +24,15 @@ from weakref import ref
cdef int _need_reset_gl = 1
cdef int _active_texture = -1
cdef list rc_list = []
cdef list canvas_list = []
cdef void gl_rcs_gc():
rc_list[:] = [x for x in rc_list if x() is not None]
canvas_list[:] = [x for x in canvas_list if x() is not None]
cdef void gl_rcs_reload():
cdef RenderContext rc
cdef Canvas rc
gl_rcs_gc()
for item in rc_list:
for item in canvas_list:
rc = item()
if not rc:
continue
@ -91,6 +91,11 @@ cdef class Instruction:
cdef void set_parent(self, Instruction parent):
self.parent = parent
cdef void reload(self):
self.flags |= GI_NEEDS_UPDATE
self.flags &= ~GI_NO_APPLY_ONCE
self.flags &= ~GI_IGNORE
property needs_redraw:
def __get__(self):
if (self.flags & GI_NEEDS_UPDATE) > 0:
@ -187,6 +192,12 @@ cdef class InstructionGroup(Instruction):
cdef Instruction c
return [c for c in self.children if c.group == groupname]
cdef void reload(self):
Instruction.reload(self)
cdef Instruction c
for c in self.children:
c.reload()
cdef class ContextInstruction(Instruction):
'''The ContextInstruction class is the base for the creation of instructions
@ -498,10 +509,22 @@ cdef class Canvas(CanvasBase):
'''
def __init__(self, **kwargs):
canvas_list.append(ref(self))
CanvasBase.__init__(self, **kwargs)
self._before = None
self._after = None
cdef void reload(self):
return
cdef Canvas c
if self._before is not None:
c = self._before
c.reload()
CanvasBase.reload(self)
if self._after is not None:
c = self._after
c.reload()
cpdef clear(self):
cdef Instruction c
for c in self.children[:]:
@ -599,7 +622,6 @@ cdef class RenderContext(Canvas):
- The state stack (color, texture, matrix...)
'''
def __init__(self, *args, **kwargs):
rc_list.append(ref(self))
cdef str key
self.bind_texture = dict()
Canvas.__init__(self, **kwargs)
@ -705,6 +727,7 @@ cdef class RenderContext(Canvas):
cdef void reload(self):
pushActiveContext(self)
reset_gl_context()
Canvas.reload(self)
popActiveContext()
def __setitem__(self, key, val):

View File

@ -189,7 +189,7 @@ cpdef int gl_has_texture_format(str fmt):
return gl_has_texture_conversion(fmt)
cdef int gl_context = 1
from kivy.graphics.vbo cimport gl_vbos_reload
from kivy.graphics.vbo cimport gl_vbos_reload, gl_batchs_reload
from kivy.graphics.shader cimport gl_shaders_reload
from kivy.graphics.texture cimport gl_textures_reload
from kivy.graphics.instructions cimport gl_rcs_reload
@ -202,12 +202,15 @@ def gl_reload():
gl_context += 1
print '--> reload initiated'
print ' > vbos'
gl_vbos_reload()
print ' > shaders'
gl_shaders_reload()
print ' > textures'
gl_textures_reload()
print ' > vbos'
gl_vbos_reload()
print ' > vertex batches'
gl_batchs_reload()
print ' > shaders'
gl_shaders_reload()
print ' > render contexts'
gl_rcs_reload()
print ' > force recompilation.'
print '<-- reload done'

View File

@ -174,7 +174,7 @@ cdef class Shader:
glUseProgram(0)
self.vertex_shader = None
self.fragment_shader = None
self.uniform_values = dict()
#self.uniform_values = dict()
self.uniform_locations = dict()
self._success = 0
self.program = glCreateProgram()
@ -188,15 +188,16 @@ cdef class Shader:
glUseProgram(self.program)
for k,v in self.uniform_values.iteritems():
self.upload_uniform(k, v)
# XXX Very very weird bug. On virtualbox / win7 / glew, if we don't call
# glFlush or glFinish or glGetIntegerv(GL_CURRENT_PROGRAM, ...), it seem
# that the pipeline is broken, and we have glitch issue. In order to
# prevent that on possible other hardware, i've (mathieu) prefered to
# include a glFlush here. However, it could be nice to know exactly what
# is going on. Even the glGetIntegerv() is not working here. Broken
# driver on virtualbox / win7 ????
# FIXME maybe include that instruction for glew usage only.
glFlush()
IF USE_GLEW == 1:
# XXX Very very weird bug. On virtualbox / win7 / glew, if we don't call
# glFlush or glFinish or glGetIntegerv(GL_CURRENT_PROGRAM, ...), it seem
# that the pipeline is broken, and we have glitch issue. In order to
# prevent that on possible other hardware, i've (mathieu) prefered to
# include a glFlush here. However, it could be nice to know exactly what
# is going on. Even the glGetIntegerv() is not working here. Broken
# driver on virtualbox / win7 ????
# FIXME maybe include that instruction for glew usage only.
glFlush()
cdef void stop(self):
'''Stop using the shader

View File

@ -5,7 +5,7 @@ cdef void gl_textures_reload()
cdef class Texture:
cdef object __weakref__
cdef str _source
cdef object _source
cdef float _tex_coords[8]
cdef int _width
cdef int _height
@ -26,13 +26,18 @@ cdef class Texture:
cdef int _nofree
cdef list observers
cdef update_tex_coords(self)
cdef set_min_filter(self, str x)
cdef set_mag_filter(self, str x)
cdef set_wrap(self, str x)
cdef reload(self)
cdef void update_tex_coords(self)
cdef void set_min_filter(self, str x)
cdef void set_mag_filter(self, str x)
cdef void set_wrap(self, str x)
cdef void reload(self)
cpdef flip_vertical(self)
cpdef get_region(self, x, y, width, height)
cpdef bind(self)
cdef class TextureRegion(Texture):
cdef int x
cdef int y
cdef Texture owner
cdef void reload(self)

View File

@ -170,13 +170,11 @@ cdef GLuint GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1
cdef GLuint GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2
cdef GLuint GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3
cdef object _texture_release_trigger = None
cdef list _texture_release_list = []
cdef object texture_release_trigger = None
cdef list texture_release_list = []
cdef list texture_list = []
cdef void gl_textures_gc():
# Remove all the vbos not weakref
texture_list[:] = [x for x in texture_list if x() is not None]
cdef void gl_textures_reload():
# Force reloading of textures
@ -184,8 +182,8 @@ cdef void gl_textures_reload():
cdef list l
Cache.remove('kv.image')
Cache.remove('kv.texture')
gl_textures_gc()
del _texture_release_list[:]
texture_list[:] = [x for x in texture_list if x() is not None]
del texture_release_list[:]
# duplicate the current list, new texture might be created
l = texture_list[:]
@ -193,16 +191,13 @@ cdef void gl_textures_reload():
# First step, prevent double loading by setting everything to -1
# We do this because texture might be loaded in seperate texture at first,
# then merged from the cache cause of the same source
print '------- texture reload phase 0'
for item in l:
texture = item()
if texture is None:
continue
print ' -', texture
texture._id = -1
# First time, only reload base texture
print '------- texture reload phase 1'
for item in l:
texture = item()
if texture is None or isinstance(texture, TextureRegion):
@ -210,16 +205,12 @@ cdef void gl_textures_reload():
texture.reload()
# Second time, update texture region id
print '------- texture reload phase 2'
for item in l:
texture = item()
if texture is None or not isinstance(texture, TextureRegion):
continue
texture.reload()
print '------- texture reload ended'
cdef dict _gl_color_fmt = {
'rgba': GL_RGBA, 'bgra': GL_BGRA, 'rgb': GL_RGB, 'bgr': GL_BGR,
@ -228,17 +219,20 @@ cdef dict _gl_color_fmt = {
's3tc_dxt3': GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
's3tc_dxt5': GL_COMPRESSED_RGBA_S3TC_DXT5_EXT }
cdef dict _gl_buffer_fmt = {
'ubyte': GL_UNSIGNED_BYTE, 'ushort': GL_UNSIGNED_SHORT,
'uint': GL_UNSIGNED_INT, 'byte': GL_BYTE,
'short': GL_SHORT, 'int': GL_INT, 'float': GL_FLOAT }
cdef dict _gl_buffer_size = {
'ubyte': sizeof(GLubyte), 'ushort': sizeof(GLushort),
'uint': sizeof(GLuint), 'byte': sizeof(GLbyte),
'short': sizeof(GLshort), 'int': sizeof(GLint),
'float': sizeof(GLfloat) }
cdef dict _gl_texture_min_filter = {
'nearest': GL_NEAREST, 'linear': GL_LINEAR,
'nearest_mipmap_nearest': GL_NEAREST_MIPMAP_NEAREST,
@ -247,7 +241,6 @@ cdef dict _gl_texture_min_filter = {
'linear_mipmap_linear': GL_LINEAR_MIPMAP_LINEAR }
cdef inline int _nearest_pow2(int v):
# From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
# Credit: Sean Anderson
@ -440,7 +433,7 @@ cdef _texture_create(int width, int height, str colorfmt, str bufferfmt,
make_npot = 0
# depending if npot is available, use the real size or pot size
if make_npot and gl_has_capability(GLCAP_NPOT):
if make_npot and gl_has_capability(c_GLCAP_NPOT):
texture_width = width
texture_height = height
else:
@ -543,8 +536,6 @@ def texture_create(size=None, colorfmt=None, bufferfmt=None, mipmap=False):
def texture_create_from_data(im, mipmap=False):
'''Create a texture from an ImageData class
'''
print ' create texture from data', im
cdef int width = im.width
cdef int height = im.height
cdef int allocate = 1
@ -598,18 +589,19 @@ cdef class Texture:
self._source = source
self._nofree = 0
self.update_tex_coords()
print 'create texture', self, texture_list
def __dealloc__(self):
# Add texture deletion outside GC call.
# This case happen if some texture have been not deleted
# before application exit...
if _texture_release_list is not None and self._id != -1 and self._nofree == 0:
_texture_release_list.append(self._id)
if _texture_release_trigger is not None:
_texture_release_trigger()
if self.__class__ is not Texture:
return
if texture_release_list is not None and self._id != -1 and self._nofree == 0:
texture_release_list.append(self._id)
if texture_release_trigger is not None:
texture_release_trigger()
cdef update_tex_coords(self):
cdef void update_tex_coords(self):
self._tex_coords[0] = self._uvx
self._tex_coords[1] = self._uvy
self._tex_coords[2] = self._uvx + self._uvw
@ -644,17 +636,17 @@ cdef class Texture:
'''Bind the texture to current opengl state'''
glBindTexture(self._target, self._id)
cdef set_min_filter(self, str x):
cdef void set_min_filter(self, str x):
cdef GLuint _value = _str_to_gl_texture_min_filter(x)
glTexParameteri(self._target, GL_TEXTURE_MIN_FILTER, _value)
self._min_filter = x
cdef set_mag_filter(self, str x):
cdef void set_mag_filter(self, str x):
cdef GLuint _value = _str_to_gl_texture_mag_filter(x)
glTexParameteri(self._target, GL_TEXTURE_MAG_FILTER, _value)
self._mag_filter = x
cdef set_wrap(self, str x):
cdef void set_wrap(self, str x):
cdef GLuint _value = _str_to_gl_texture_wrap(x)
glTexParameteri(self._target, GL_TEXTURE_WRAP_S, _value)
glTexParameteri(self._target, GL_TEXTURE_WRAP_T, _value)
@ -745,38 +737,34 @@ cdef class Texture:
if _mipmap_generation:
glGenerateMipmap(target)
cdef reload(self):
cdef void reload(self):
cdef Texture texture
print ' - want to reload texture', self.id, self._source
print ' ', isinstance(self, Texture), isinstance(self, TextureRegion)
if self._id != -1:
print ' -< abort, already reloaded.', self._id
return
if self._source is None:
print ' -< Unable to reload this texture automatically, call observers'
# manual texture recreation
texture = texture_create(self.size, self.colorfmt, self.bufferfmt,
self.mipmap)
self._id = texture.id
self._nofree = 1
texture._nofree = 1
# then update content again
for cb in self.observers:
cb(self)
return
print ' reloading...'
from kivy.core.image import Image
image = Image(self._source)
print ' reloading give image', image
print ' reloading give texture', image.texture
self._id = image.texture.id
texture = image.texture
texture._nofree = 1
print ' reloading give new id', self._id
# then update content again
for cb in self.observers:
cb(self)
def __str__(self):
return '<Texture id=%d size=(%d, %d) source=%r observers=%r>' % (
self._id, self.width, self.height, self._source, self.observers)
return '<Texture hash=%r id=%d size=%r colorfmt=%r bufferfmt=%r source=%r observers=%d>' % (
id(self), self._id, self.size, self.colorfmt, self.bufferfmt,
self._source, len(self.observers))
property size:
'''Return the (width, height) of the texture (readonly)
@ -927,28 +915,7 @@ cdef class TextureRegion(Texture):
'''Handle a region of a Texture class. Useful for non power-of-2
texture handling.'''
cdef int x
cdef int y
cdef Texture owner
def __init__(self, int x, int y, int width, int height, Texture origin):
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN'
print' TEXTURE REGIONNNNNNNNNNNNNNNNNNNNNNNNNNNN', origin
Texture.__init__(self, width, height, origin.target, origin.id)
self._is_allocated = 1
self._mipmap = origin._mipmap
@ -967,20 +934,33 @@ cdef class TextureRegion(Texture):
self.update_tex_coords()
def __str__(self):
return '<TextureRegion id=%d size=(%d, %d)>' % (
self._id, self.width, self.height)
return '<TextureRegion of %r hash=%r id=%d size=%r colorfmt=%r bufferfmt=%r source=%r observers=%d>' % (
self.owner, id(self), self._id, self.size, self.colorfmt,
self.bufferfmt, self._source, len(self.observers))
cdef void reload(self):
# texture region are reloaded _after_ normal texture
# so that could work, except if it's a region of region
# it's safe to retrigger a reload, since the owner texture will be not
# really reloaded if its id is not -1.
self.owner.reload()
self._id = self.owner.id
# then update content again
for cb in self.observers:
cb(self)
# Releasing texture through GC is problematic
def _texture_release(*largs):
cdef GLuint texture_id
if not _texture_release_list:
if not texture_release_list:
return
Logger.trace('Texture: releasing %d textures' % len(_texture_release_list))
for texture_id in _texture_release_list:
Logger.trace('Texture: releasing %d textures' % len(texture_release_list))
for texture_id in texture_release_list:
glDeleteTextures(1, &texture_id)
del _texture_release_list[:]
del texture_release_list[:]
if 'KIVY_DOC_INCLUDE' not in environ:
from kivy.clock import Clock
_texture_release_trigger = Clock.create_trigger(_texture_release)
texture_release_trigger = Clock.create_trigger(_texture_release)

View File

@ -5,6 +5,7 @@ from vertex cimport vertex_t, vertex_attr_t
cdef int vbo_vertex_attr_count()
cdef vertex_attr_t *vbo_vertex_attr_list()
cdef void gl_vbos_reload()
cdef void gl_batchs_reload()
cdef class VBO:
cdef object __weakref__
@ -29,6 +30,8 @@ cdef class VBO:
cdef class VertexBatch:
cdef object __weakref__
cdef VBO vbo
cdef Buffer elements
cdef Buffer vbo_index
@ -48,3 +51,4 @@ cdef class VertexBatch:
cdef void set_mode(self, str mode)
cdef str get_mode(self)
cdef int count(self)
cdef void reload(self)

View File

@ -31,11 +31,16 @@ vattr[1] = ['vTexCoords0', 1, 2, GL_FLOAT, sizeof(GLfloat) * 2, 1]
cdef object _vbo_release_trigger = None
cdef list _vbo_release_list = []
cdef list vbo_list = []
cdef list batch_list = []
cdef gl_vbos_gc():
# Remove all the vbos not weakref.
vbo_list[:] = [x for x in vbo_list if x() is not None]
cdef gl_batchs_gc():
# Remove all the vbos not weakref.
batch_list[:] = [x for x in batch_list if x() is not None]
cdef void gl_vbos_reload():
# Force reloading of vbos
@ -47,6 +52,16 @@ cdef void gl_vbos_reload():
continue
vbo.reload()
cdef void gl_batchs_reload():
# Force reloading of Batchs
cdef VertexBatch batch
gl_batchs_gc()
for item in batch_list:
batch = item()
if not batch:
continue
batch.reload()
cdef int vbo_vertex_attr_count():
'''Return the number of vertex attributes used in VBO
@ -129,6 +144,7 @@ cdef class VBO:
cdef class VertexBatch:
def __init__(self, **kwargs):
batch_list.append(ref(self))
self.usage = GL_DYNAMIC_DRAW
cdef object lushort = sizeof(unsigned short)
self.vbo = kwargs.get('vbo')
@ -150,6 +166,11 @@ cdef class VertexBatch:
if _vbo_release_trigger is not None:
_vbo_release_trigger()
cdef void reload(self):
self.need_upload = 1
self.elements_size = 0
glGenBuffers(1, &self.id)
cdef void clear_data(self):
# clear old vertices from vbo and then reset index buffer
self.vbo.remove_vertex_data(<unsigned short*>self.vbo_index.pointer(),