mirror of https://github.com/kivy/kivy.git
core/graphics: include fbo in the gl reloading process. standardize add/remove_reload_observers for texture, fbo and context
This commit is contained in:
parent
b85fe03599
commit
15087eba65
|
@ -404,14 +404,14 @@ class LabelBase(object):
|
|||
else:
|
||||
texture = Texture.create_from_data(data, mipmap=mipmap)
|
||||
texture.flip_vertical()
|
||||
texture.add_observer(self._texture_refresh)
|
||||
texture.add_reload_observer(self._texture_refresh)
|
||||
elif self.width != texture.width or self.height != texture.height:
|
||||
if data is None:
|
||||
texture = Texture.create(size=self.size, mipmap=mipmap)
|
||||
else:
|
||||
texture = Texture.create_from_data(data, mipmap=mipmap)
|
||||
texture.flip_vertical()
|
||||
texture.add_observer(self._texture_refresh)
|
||||
texture.add_reload_observer(self._texture_refresh)
|
||||
'''
|
||||
# Avoid that for the moment.
|
||||
# The thing is, as soon as we got a region, the blitting is not going in
|
||||
|
|
|
@ -10,6 +10,7 @@ cdef extern from "math.h":
|
|||
double cos(double) nogil
|
||||
double sin(double) nogil
|
||||
double sqrt(double) nogil
|
||||
double pow(double x, double y) nogil
|
||||
|
||||
cdef extern from "stdlib.h":
|
||||
ctypedef unsigned long size_t
|
||||
|
@ -20,6 +21,7 @@ cdef extern from "stdlib.h":
|
|||
|
||||
cdef extern from "string.h":
|
||||
void *memcpy(void *dest, void *src, size_t n)
|
||||
void *memset(void *dest, int c, size_t len)
|
||||
|
||||
cdef extern from "Python.h":
|
||||
object PyString_FromStringAndSize(char *s, Py_ssize_t len)
|
||||
|
|
|
@ -2,28 +2,36 @@ from kivy.graphics.instructions cimport Instruction, Canvas
|
|||
from kivy.graphics.texture cimport Texture
|
||||
from kivy.graphics.vbo cimport VBO, VertexBatch
|
||||
from kivy.graphics.shader cimport Shader
|
||||
from kivy.graphics.fbo cimport Fbo
|
||||
|
||||
cdef class Context:
|
||||
cdef list observers
|
||||
cdef list l_texture
|
||||
cdef list l_canvas
|
||||
cdef list l_vbo
|
||||
cdef list l_vertexbatch
|
||||
cdef list l_shader
|
||||
cdef list l_fbo
|
||||
|
||||
cdef list lr_texture
|
||||
cdef list lr_canvas
|
||||
cdef list lr_vbo
|
||||
cdef list lr_fbo
|
||||
|
||||
cdef object trigger_gl_dealloc
|
||||
cdef void register_texture(self, Texture texture)
|
||||
cdef void register_canvas(self, Canvas canvas)
|
||||
cdef void register_vbo(self, VBO vbo)
|
||||
cdef void register_vertexbatch(self, VertexBatch vb)
|
||||
cdef void register_shader(self, Shader shader)
|
||||
cdef void register_fbo(self, Fbo fbo)
|
||||
|
||||
cdef void dealloc_texture(self, Texture texture)
|
||||
cdef void dealloc_vbo(self, VBO vbo)
|
||||
cdef void dealloc_vertexbatch(self, VertexBatch vbo)
|
||||
cdef void dealloc_shader(self, Shader shader)
|
||||
cdef void dealloc_fbo(self, Fbo fbo)
|
||||
|
||||
cdef object trigger_gl_dealloc
|
||||
cdef void flush(self)
|
||||
|
||||
cpdef Context get_context()
|
||||
|
|
|
@ -18,6 +18,7 @@ from kivy.graphics.vbo cimport VBO, VertexBatch
|
|||
from kivy.logger import Logger
|
||||
from kivy.clock import Clock
|
||||
from kivy.graphics.c_opengl cimport *
|
||||
from kivy.weakmethod import WeakMethod
|
||||
from time import time
|
||||
IF USE_OPENGL_DEBUG == 1:
|
||||
from kivy.graphics.c_opengl_debug cimport *
|
||||
|
@ -28,15 +29,21 @@ cdef Context context = None
|
|||
cdef class Context:
|
||||
|
||||
def __init__(self):
|
||||
self.observers = []
|
||||
self.l_texture = []
|
||||
self.l_canvas = []
|
||||
self.l_vbo = []
|
||||
self.l_vertexbatch = []
|
||||
self.l_shader = []
|
||||
self.l_fbo = []
|
||||
self.flush()
|
||||
self.trigger_gl_dealloc = Clock.create_trigger(self.gl_dealloc, 0)
|
||||
|
||||
cdef void flush(self):
|
||||
self.lr_texture = []
|
||||
self.lr_canvas = []
|
||||
self.lr_vbo = []
|
||||
self.trigger_gl_dealloc = Clock.create_trigger(self.gl_dealloc, 0)
|
||||
self.lr_fbo = []
|
||||
|
||||
cdef void register_texture(self, Texture texture):
|
||||
self.l_texture.append(ref(texture))
|
||||
|
@ -53,6 +60,9 @@ cdef class Context:
|
|||
cdef void register_shader(self, Shader shader):
|
||||
self.l_shader.append(ref(shader))
|
||||
|
||||
cdef void register_fbo(self, Fbo fbo):
|
||||
self.l_fbo.append(ref(fbo))
|
||||
|
||||
cdef void dealloc_texture(self, Texture texture):
|
||||
if texture._nofree or texture.__class__ is TextureRegion:
|
||||
return
|
||||
|
@ -78,10 +88,29 @@ cdef class Context:
|
|||
glDetachShader(shader.program, shader.fragment_shader.shader)
|
||||
glDeleteProgram(shader.program)
|
||||
|
||||
cdef void flush(self):
|
||||
self.lr_texture = []
|
||||
self.lr_canvas = []
|
||||
self.lr_vbo = []
|
||||
cdef void dealloc_fbo(self, Fbo fbo):
|
||||
if fbo.buffer_id != -1:
|
||||
self.lr_fbo.append((fbo.buffer_id, fbo.depthbuffer_id))
|
||||
self.trigger_gl_dealloc()
|
||||
|
||||
def add_reload_observer(self, callback):
|
||||
'''Add a callback to be called after the whole graphics context have
|
||||
been reloaded. This is where you can reupload your custom data in GPU.
|
||||
|
||||
:Parameters:
|
||||
`callback`: func(context) -> return None
|
||||
The first parameter will be the context itself
|
||||
'''
|
||||
self.observers.append(WeakMethod(callback))
|
||||
|
||||
def remove_reload_observer(self, callback):
|
||||
'''Remove a callback from the observer list, previously added by
|
||||
:func:`add_reload_observer`.
|
||||
'''
|
||||
for cb in self.observers[:]:
|
||||
if cb.is_dead() or cb() is callback:
|
||||
self.observers.remove(cb)
|
||||
continue
|
||||
|
||||
def reload(self):
|
||||
cdef VBO vbo
|
||||
|
@ -90,21 +119,20 @@ cdef class Context:
|
|||
cdef Shader shader
|
||||
cdef Canvas canvas
|
||||
|
||||
start = time()
|
||||
Logger.info('Context: Reloading graphics data...')
|
||||
Logger.info('Context: Collect and flush all garbage')
|
||||
self.gc()
|
||||
self.flush()
|
||||
|
||||
Cache.remove('kv.image')
|
||||
Cache.remove('kv.texture')
|
||||
Cache.remove('kv.shader')
|
||||
|
||||
start = time()
|
||||
Logger.info('Context: Reloading graphics data...')
|
||||
Logger.debug('Context: Collect and flush all garbage')
|
||||
self.gc()
|
||||
self.flush()
|
||||
|
||||
# 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
|
||||
Logger.info('Context: Reload textures')
|
||||
Logger.debug('Context: Reload textures')
|
||||
cdef list l = self.l_texture[:]
|
||||
for item in l:
|
||||
texture = item()
|
||||
|
@ -126,31 +154,39 @@ cdef class Context:
|
|||
continue
|
||||
texture.reload()
|
||||
|
||||
Logger.info('Context: Reload vbos')
|
||||
Logger.debug('Context: Reload vbos')
|
||||
for item in self.l_vbo[:]:
|
||||
vbo = item()
|
||||
if vbo is not None:
|
||||
Logger.info('Context: reloaded %r' % item())
|
||||
Logger.debug('Context: reloaded %r' % item())
|
||||
vbo.reload()
|
||||
Logger.info('Context: Reload vertex batchs')
|
||||
Logger.debug('Context: Reload vertex batchs')
|
||||
for item in self.l_vertexbatch[:]:
|
||||
batch = item()
|
||||
if batch is not None:
|
||||
Logger.info('Context: reloaded %r' % item())
|
||||
Logger.debug('Context: reloaded %r' % item())
|
||||
batch.reload()
|
||||
Logger.info('Context: Reload shaders')
|
||||
Logger.debug('Context: Reload shaders')
|
||||
for item in self.l_shader[:]:
|
||||
shader = item()
|
||||
if shader is not None:
|
||||
Logger.info('Context: reloaded %r' % item())
|
||||
Logger.debug('Context: reloaded %r' % item())
|
||||
shader.reload()
|
||||
Logger.info('Context: Reload canvas')
|
||||
Logger.debug('Context: Reload canvas')
|
||||
for item in self.l_canvas[:]:
|
||||
canvas = item()
|
||||
if canvas is not None:
|
||||
Logger.info('Context: reloaded %r' % item())
|
||||
Logger.debug('Context: reloaded %r' % item())
|
||||
canvas.reload()
|
||||
|
||||
# call reload observers that want to do something after a whole gpu
|
||||
# reloading.
|
||||
for callback in self.observers[:]:
|
||||
if callback.is_dead():
|
||||
self.observers.remove(callback)
|
||||
continue
|
||||
callback()(self)
|
||||
|
||||
glFinish()
|
||||
dt = time() - start
|
||||
Logger.info('Context: Reloading done in %2.4fs' % dt)
|
||||
|
@ -164,17 +200,26 @@ cdef class Context:
|
|||
|
||||
def gl_dealloc(self, *largs):
|
||||
# dealloc all gl resources asynchronously
|
||||
cdef GLuint i
|
||||
cdef GLuint i, j
|
||||
if len(self.lr_vbo):
|
||||
Logger.info('Context: releasing %d vbos' % len(self.lr_vbo))
|
||||
Logger.debug('Context: releasing %d vbos' % len(self.lr_vbo))
|
||||
while len(self.lr_vbo):
|
||||
i = self.lr_vbo.pop()
|
||||
glDeleteBuffers(1, &i)
|
||||
if len(self.lr_texture):
|
||||
Logger.info('Context: releasing %d textures' % len(self.lr_texture))
|
||||
Logger.debug('Context: releasing %d textures: %r' % (
|
||||
len(self.lr_texture), self.lr_texture))
|
||||
while len(self.lr_texture):
|
||||
i = self.lr_texture.pop()
|
||||
glDeleteTextures(1, &i)
|
||||
if len(self.lr_fbo):
|
||||
Logger.debug('Context: releasing %d fbos' % len(self.lr_fbo))
|
||||
while len(self.lr_fbo):
|
||||
i, j = self.lr_fbo.pop()
|
||||
if i != -1:
|
||||
glDeleteFramebuffers(1, &i)
|
||||
if j != -1:
|
||||
glDeleteRenderbuffers(1, &j)
|
||||
|
||||
|
||||
cpdef Context get_context():
|
||||
|
|
|
@ -13,6 +13,7 @@ cdef class Fbo(RenderContext):
|
|||
cdef GLint _viewport[4]
|
||||
cdef Texture _texture
|
||||
cdef int _is_bound
|
||||
cdef list observers
|
||||
|
||||
cpdef clear_buffer(self)
|
||||
cpdef bind(self)
|
||||
|
@ -23,3 +24,4 @@ cdef class Fbo(RenderContext):
|
|||
cdef void apply(self)
|
||||
cdef void raise_exception(self, str message, int status=?)
|
||||
cdef str resolve_status(self, int status)
|
||||
cdef void reload(self)
|
||||
|
|
|
@ -45,17 +45,18 @@ include "config.pxi"
|
|||
include "opcodes.pxi"
|
||||
|
||||
from os import environ
|
||||
from kivy import Logger
|
||||
from kivy.logger import Logger
|
||||
from kivy.weakmethod import WeakMethod
|
||||
from kivy.graphics.texture cimport Texture
|
||||
from kivy.graphics.transformation cimport Matrix
|
||||
from kivy.graphics.context cimport get_context
|
||||
|
||||
from c_opengl cimport *
|
||||
from kivy.graphics.c_opengl cimport *
|
||||
IF USE_OPENGL_DEBUG == 1:
|
||||
from c_opengl_debug cimport *
|
||||
from instructions cimport RenderContext, Canvas
|
||||
from kivy.graphics.c_opengl_debug cimport *
|
||||
from kivy.graphics.instructions cimport RenderContext, Canvas
|
||||
|
||||
cdef list fbo_stack = [0]
|
||||
cdef object fbo_release_trigger = None
|
||||
cdef list fbo_release_list = []
|
||||
|
||||
|
||||
|
@ -106,6 +107,8 @@ cdef class Fbo(RenderContext):
|
|||
raise Exception(message)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
get_context().register_fbo(self)
|
||||
|
||||
RenderContext.__init__(self, *args, **kwargs)
|
||||
|
||||
if 'clear_color' not in kwargs:
|
||||
|
@ -119,32 +122,25 @@ cdef class Fbo(RenderContext):
|
|||
if 'texture' not in kwargs:
|
||||
kwargs['texture'] = None
|
||||
|
||||
self._buffer_id = -1
|
||||
self._depthbuffer_id = -1
|
||||
self._width, self._height = kwargs['size']
|
||||
self.clear_color = kwargs['clear_color']
|
||||
self._depthbuffer_attached = int(kwargs['with_depthbuffer'])
|
||||
self._push_viewport = int(kwargs['push_viewport'])
|
||||
self._is_bound = 0
|
||||
self._texture = kwargs['texture']
|
||||
self.buffer_id = -1
|
||||
self.depthbuffer_id = -1
|
||||
self._width, self._height = kwargs['size']
|
||||
self.clear_color = kwargs['clear_color']
|
||||
self._depthbuffer_attached = int(kwargs['with_depthbuffer'])
|
||||
self._push_viewport = int(kwargs['push_viewport'])
|
||||
self._is_bound = 0
|
||||
self._texture = kwargs['texture']
|
||||
self.observers = []
|
||||
|
||||
self.create_fbo()
|
||||
|
||||
def __dealloc__(self):
|
||||
# add fbo deletion outside gc call.
|
||||
if fbo_release_list is not None:
|
||||
fbo_release_list.append((self._buffer_id, self._depthbuffer_id))
|
||||
if fbo_release_trigger is not None:
|
||||
fbo_release_trigger()
|
||||
get_context().dealloc_fbo(self)
|
||||
|
||||
cdef void delete_fbo(self):
|
||||
print 'XXXD Delete fbo', self
|
||||
self._texture = None
|
||||
self._depthbuffer_attached = 0
|
||||
# delete in asynchronous way the framebuffers
|
||||
if fbo_release_list is not None:
|
||||
fbo_release_list.append((self._buffer_id, self._depthbuffer_id))
|
||||
if fbo_release_trigger is not None:
|
||||
fbo_release_trigger()
|
||||
get_context().dealloc_fbo(self)
|
||||
self._buffer_id = -1
|
||||
self._depthbuffer_id = -1
|
||||
|
||||
|
@ -268,6 +264,43 @@ cdef class Fbo(RenderContext):
|
|||
self.release()
|
||||
self.flag_update_done()
|
||||
|
||||
cdef void reload(self):
|
||||
# recreate the framebuffer, without deleting it. the deletion is not
|
||||
# handled by us.
|
||||
self.create_fbo()
|
||||
self.flag_update()
|
||||
# notify observers
|
||||
for callback in self.observers:
|
||||
if callback.is_dead():
|
||||
self.observers.remove(callback)
|
||||
continue
|
||||
callback()(self)
|
||||
|
||||
def add_reload_observer(self, callback):
|
||||
'''Add a callback to be called after the whole graphics context have
|
||||
been reloaded. This is where you can reupload your custom data in GPU.
|
||||
|
||||
.. versionadded:: 1.1.2
|
||||
|
||||
:Parameters:
|
||||
`callback`: func(context) -> return None
|
||||
The first parameter will be the context itself
|
||||
'''
|
||||
self.observers.append(WeakMethod(callback))
|
||||
|
||||
def remove_reload_observer(self, callback):
|
||||
'''Remove a callback from the observer list, previously added by
|
||||
:func:`add_reload_observer`.
|
||||
|
||||
.. versionadded:: 1.1.2
|
||||
|
||||
'''
|
||||
for cb in self.observers[:]:
|
||||
if cb.is_dead() or cb() is callback:
|
||||
self.observers.remove(cb)
|
||||
continue
|
||||
|
||||
|
||||
property size:
|
||||
'''Size of the framebuffer, in (width, height) format.
|
||||
|
||||
|
@ -308,20 +341,3 @@ cdef class Fbo(RenderContext):
|
|||
def __get__(self):
|
||||
return self._texture
|
||||
|
||||
# Releasing fbo through GC is problematic. Same as any GL deletion.
|
||||
def fbo_release(*largs):
|
||||
cdef GLuint fbo_id, render_id
|
||||
if not fbo_release_list:
|
||||
return
|
||||
Logger.trace('FBO: releasing %d fbos' % len(fbo_release_list))
|
||||
for l in fbo_release_list:
|
||||
fbo_id, render_id = l
|
||||
if fbo_id != -1:
|
||||
glDeleteFramebuffers(1, &fbo_id)
|
||||
if render_id != -1:
|
||||
glDeleteRenderbuffers(1, &render_id)
|
||||
del fbo_release_list[:]
|
||||
|
||||
if 'KIVY_DOC_INCLUDE' not in environ:
|
||||
from kivy.clock import Clock
|
||||
fbo_release_trigger = Clock.create_trigger(fbo_release)
|
||||
|
|
|
@ -154,14 +154,15 @@ include "opengl_utils_def.pxi"
|
|||
|
||||
from os import environ
|
||||
from array import array
|
||||
from kivy.weakmethod import WeakMethod
|
||||
from kivy.logger import Logger
|
||||
from kivy.cache import Cache
|
||||
from kivy.graphics.context cimport get_context
|
||||
|
||||
from c_opengl cimport *
|
||||
from kivy.graphics.c_opengl cimport *
|
||||
IF USE_OPENGL_DEBUG == 1:
|
||||
from c_opengl_debug cimport *
|
||||
from opengl_utils cimport *
|
||||
from kivy.graphics.c_opengl_debug cimport *
|
||||
from kivy.graphics.opengl_utils cimport *
|
||||
|
||||
# compatibility layer
|
||||
cdef GLuint GL_BGR = 0x80E0
|
||||
|
@ -549,9 +550,6 @@ cdef class Texture:
|
|||
self.update_tex_coords()
|
||||
|
||||
def __dealloc__(self):
|
||||
# Add texture deletion outside GC call.
|
||||
# This case happen if some texture have been not deleted
|
||||
# before application exit...
|
||||
get_context().dealloc_texture(self)
|
||||
|
||||
cdef void update_tex_coords(self):
|
||||
|
@ -564,15 +562,29 @@ cdef class Texture:
|
|||
self._tex_coords[6] = self._uvx
|
||||
self._tex_coords[7] = self._uvy + self._uvh
|
||||
|
||||
def add_observer(self, callback):
|
||||
'''Add a callback to be called when the texture need to be recreated.
|
||||
'''
|
||||
self.observers.append(callback)
|
||||
def add_reload_observer(self, callback):
|
||||
'''Add a callback to be called after the whole graphics context have
|
||||
been reloaded. This is where you can reupload your custom data in GPU.
|
||||
|
||||
def remove_observer(self, callback):
|
||||
'''Remove a callback from the observer list.
|
||||
.. versionadded:: 1.1.2
|
||||
|
||||
:Parameters:
|
||||
`callback`: func(context) -> return None
|
||||
The first parameter will be the context itself
|
||||
'''
|
||||
self.observers.remove(callback)
|
||||
self.observers.append(WeakMethod(callback))
|
||||
|
||||
def remove_reload_observer(self, callback):
|
||||
'''Remove a callback from the observer list, previously added by
|
||||
:func:`add_reload_observer`.
|
||||
|
||||
.. versionadded:: 1.1.2
|
||||
|
||||
'''
|
||||
for cb in self.observers[:]:
|
||||
if cb.is_dead() or cb() is callback:
|
||||
self.observers.remove(cb)
|
||||
continue
|
||||
|
||||
cpdef flip_vertical(self):
|
||||
'''Flip tex_coords for vertical displaying'''
|
||||
|
@ -701,10 +713,13 @@ cdef class Texture:
|
|||
self._id = texture.id
|
||||
texture._nofree = 1
|
||||
# then update content again
|
||||
for cb in self.observers:
|
||||
cb(self)
|
||||
|
||||
for callback in self.observers[:]:
|
||||
if callback.is_dead():
|
||||
self.observers.remove(callback)
|
||||
continue
|
||||
callback()(self)
|
||||
return
|
||||
|
||||
from kivy.core.image import Image
|
||||
image = Image(self._source)
|
||||
self._id = image.texture.id
|
||||
|
|
|
@ -12,24 +12,15 @@ __all__ = ('Triangle', 'Quad', 'Rectangle', 'BorderImage', 'Ellipse', 'Line',
|
|||
include "config.pxi"
|
||||
include "common.pxi"
|
||||
|
||||
from vbo cimport *
|
||||
from vertex cimport *
|
||||
from instructions cimport *
|
||||
from c_opengl cimport *
|
||||
from kivy.graphics.vbo cimport *
|
||||
from kivy.graphics.vertex cimport *
|
||||
from kivy.graphics.instructions cimport *
|
||||
from kivy.graphics.c_opengl cimport *
|
||||
IF USE_OPENGL_DEBUG == 1:
|
||||
from c_opengl_debug cimport *
|
||||
from kivy.graphics.c_opengl_debug cimport *
|
||||
from kivy.logger import Logger
|
||||
from kivy.graphics.texture import Texture
|
||||
|
||||
cdef extern from "string.h":
|
||||
void *memset(void *s, int c, int n)
|
||||
|
||||
cdef extern from "Python.h":
|
||||
object PyString_FromStringAndSize(char *s, Py_ssize_t len)
|
||||
|
||||
cdef extern from "math.h":
|
||||
double sqrt(double x) nogil
|
||||
double pow(double x, double y) nogil
|
||||
|
||||
class GraphicException(Exception):
|
||||
'''Exception fired when a graphic error is fired.
|
||||
|
|
Loading…
Reference in New Issue