Skip to content

Commit

Permalink
#147: update gl code to match client/window backing updates
Browse files Browse the repository at this point in the history
* gl window now only deals with initialization
* move the actual drawing code to the gl backing
* split paint_with_video_decoder so gl backing can override the new do_video_paint method

git-svn-id: https://xpra.org/svn/Xpra/trunk@2008 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Oct 31, 2012
1 parent 4ff9f21 commit 21b0efc
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 572 deletions.
2 changes: 1 addition & 1 deletion src/xpra/client_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ def queue_draw(gtkwindow, x, y, width, height):
from wimpiggy.log import Logger
log = Logger()

from xpra.window_backing import new_backing
try:
from wimpiggy.prop import prop_get
has_wimpiggy_prop = True
Expand Down Expand Up @@ -206,6 +205,7 @@ def get_workspace(self):


def new_backing(self, w, h):
from xpra.window_backing import new_backing
self._backing = new_backing(self._id, w, h, self._backing, self._client.supports_mmap, self._client.mmap)

def update_metadata(self, metadata):
Expand Down
304 changes: 30 additions & 274 deletions src/xpra/gl_client_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,14 @@
from wimpiggy.log import Logger
log = Logger()

from xpra.client_window import ClientWindow, queue_draw
from xpra.scripts.main import ENCODINGS
from xpra.client_window import ClientWindow


import gtk.gdkgl, gtk.gtkgl #@UnresolvedImport
assert gtk.gdkgl is not None and gtk.gtkgl is not None

from OpenGL.GL import GL_VERSION, GL_PROJECTION, GL_MODELVIEW, GL_VERTEX_ARRAY, \
GL_TEXTURE_COORD_ARRAY, GL_FRAGMENT_PROGRAM_ARB, \
GL_PROGRAM_ERROR_STRING_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, \
GL_TEXTURE_RECTANGLE_ARB, GL_UNPACK_ROW_LENGTH, \
GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER, GL_NEAREST, \
GL_UNSIGNED_BYTE, GL_RGB, GL_LUMINANCE, GL_LINEAR, \
GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_QUADS, \
glActiveTexture, \
glGetString, glViewport, glMatrixMode, glLoadIdentity, glOrtho, \
glEnableClientState, glDisable, glGenTextures, \
glBindTexture, glPixelStorei, glEnable, glBegin, \
glTexParameteri, glVertexPointeri, glTexCoordPointeri, \
glTexImage2D, glTexSubImage2D, \
glDrawArrays, glMultiTexCoord2i, \
glVertex2i, glEnd
from OpenGL.GL.ARB.vertex_program import glGenProgramsARB, glBindProgramARB, glProgramStringARB
from OpenGL.GL import GL_VERSION, glGetString
from OpenGL.GL.ARB.fragment_program import glInitFragmentProgramARB
from xpra.gl_colorspace_conversions import GL_COLORSPACE_CONVERSIONS

#sanity checks: OpenGL version
try:
Expand All @@ -52,6 +35,8 @@
#this allows us to do CSC via OpenGL:
#see http://www.opengl.org/registry/specs/ARB/fragment_program.txt
use_openGL_CSC = glInitFragmentProgramARB()
if not use_openGL_CSC:
raise ImportError("** OpenGL output requires glInitFragmentProgramARB")
finally:
gldrawable.gl_end()
del glcontext, gldrawable, glext, glconfig
Expand All @@ -60,271 +45,42 @@


class GLClientWindow(ClientWindow):
MODE_UNINITIALIZED = 0
MODE_RGB = 1
MODE_YUV = 2

def __init__(self, client, wid, x, y, w, h, metadata, override_redirect, client_properties, auto_refresh_delay):
log.info("GLClientWindow(..)")
self._configured = False
ClientWindow.__init__(self, client, wid, x, y, w, h, metadata, override_redirect, client_properties, auto_refresh_delay)
display_mode = (gtk.gdkgl.MODE_RGB | gtk.gdkgl.MODE_SINGLE)
self.glconfig = gtk.gdkgl.Config(mode=display_mode)
self.glarea = gtk.gtkgl.DrawingArea(self.glconfig)
self.glarea.set_size_request(w, h)
self.glarea.show()
self.add(self.glarea)
self._video_decoder = None
self._video_decoder_codec = None
self.textures = None # OpenGL texture IDs
self.current_mode = GLClientWindow.MODE_UNINITIALIZED
self.add(self._backing.glarea)

def do_configure_event(self, event):
log.info("do_configure_event(%s)", event)
self._configured = True
ClientWindow.do_configure_event(self, event)
drawable = self.glarea.get_gl_drawable()
context = self.glarea.get_gl_context()

self.yuv420_shader = None

# Re-create textures
self.current_mode = GLClientWindow.MODE_UNINITIALIZED

if not drawable.gl_begin(context):
raise Exception("** Cannot create OpenGL rendering context!")

w, h = self.get_size()
log("Configure widget size: %d x %d" % (w, h))
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0.0, w, h, 0.0, -1.0, 1.0)
glMatrixMode(GL_MODELVIEW)
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glDisable(GL_FRAGMENT_PROGRAM_ARB)

if self.textures is None:
self.textures = glGenTextures(3)

drawable.gl_end()
self._backing.init(w, h)

def do_expose_event(self, event):
log("do_expose_event(%s) area=%s", event, event.area)
log.info("do_expose_event(%s) area=%s, mapped=%s", event, event.area, self.flags() & gtk.MAPPED)
if not (self.flags() & gtk.MAPPED):
return
self.render_image()
self.glarea.window.invalidate_rect(self.glarea.allocation, False)
# Update window synchronously (fast).
self.glarea.window.process_updates(False)

def draw_region(self, x, y, width, height, coding, img_data, rowstride, options):
#log("draw_region(%s, %s, %s, %s, %s, %s bytes, %s, %s)", x, y, width, height, coding, len(img_data), rowstride, options)
if coding == "mmap":
# No GL output for mmap
assert coding != "mmap"
elif coding == "rgb24":
if rowstride>0:
assert len(img_data) == rowstride * height
else:
assert len(img_data) == width * 3 * height
self.update_texture_rgb24(img_data, x, y, width, height, rowstride)
elif coding == "x264":
assert "x264" in ENCODINGS
from xpra.x264.codec import Decoder #@UnresolvedImport
self.paint_with_video_decoder(Decoder, "x264", img_data, x, y, width, height, rowstride, options)
elif coding == "vpx":
assert "vpx" in ENCODINGS
from xpra.vpx.codec import Decoder #@UnresolvedImport @Reimport
self.paint_with_video_decoder(Decoder, "vpx", img_data, x, y, width, height, rowstride, options)
else:
raise Exception("** No JPEG/PNG support for OpenGL")
queue_draw(self, x, y, width, height)
return True

#FIXME: This is a copypaste from window_backing.py...
def paint_with_video_decoder(self, factory, coding, img_data, x, y, width, height, rowstride, options):
assert x==0 and y==0
if self._video_decoder:
if self._video_decoder_codec!=coding:
self._video_decoder.clean()
self._video_decoder = None
elif self._video_decoder.get_width()!=width or self._video_decoder.get_height()!=height:
log("paint_with_video_decoder: window dimensions have changed from %s to %s", (self._video_decoder.get_width(), self._video_decoder.get_height()), (width, height))
self._video_decoder.clean()
self._video_decoder.init_context(width, height, options)
if self._video_decoder is None:
self._video_decoder = factory()
self._video_decoder_codec = coding
self._video_decoder.init_context(width, height, options)
self._backing.render()

def new_backing(self, w, h):
log.info("new_backing(%s, %s)", w, h)
#self._backing = new_backing(self._id, w, h, self._backing, self._client.supports_mmap, self._client.mmap)
w = max(1, w)
h = max(1, h)
lock = None
if self._backing:
lock = self._backing._video_decoder_lock
try:
if use_openGL_CSC:
decompress = self._video_decoder.decompress_image_to_yuv
update_texture = self.update_texture_yuv420
else:
decompress = self._video_decoder.decompress_image_to_rgb
update_texture = self.update_texture_rgb24

err, outstride, data = decompress(img_data, options)
if err!=0:
log.error("paint_with_video_decoder: ouch, decompression error %s", err)
return
if not data:
log.error("paint_with_video_decoder: ouch, no data from %s decoder", coding)
return
log("paint_with_video_decoder: decompressed %s to %s bytes (%s%%) of rgb24 (%s*%s*3=%s) (outstride: %s)", len(img_data), len(data), int(100*len(img_data)/len(data)),width, height, width*height*3, outstride)
update_texture(data, x, y, width, height, outstride)
if lock:
lock.acquire()
if self._backing is None:
from xpra.gl_window_backing import GLPixmapBacking
self._backing = GLPixmapBacking(self._id, w, h, self._client.supports_mmap, self._client.mmap)
if self._configured:
self._backing.init(w, h)
finally:
if not use_openGL_CSC:
self._video_decoder.free_image()

def update_texture_rgb24(self, img_data, x, y, width, height, rowstride):
drawable = self.glarea.get_gl_drawable()
context = self.glarea.get_gl_context()
if not drawable.gl_begin(context):
raise Exception("** Cannot create OpenGL rendering context!")
assert self.textures is not None

glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[0])
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstride/3)

if self.current_mode == GLClientWindow.MODE_YUV:
raise Exception("** YUV -> RGB mode change unimplemented!")
elif self.current_mode == GLClientWindow.MODE_UNINITIALIZED:
log("Creating new RGB texture")
w, h = self.get_size()
# First time we draw must be full image
assert w == width and h == height
glEnable(GL_TEXTURE_RECTANGLE_ARB)
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0)
self.current_mode = GLClientWindow.MODE_RGB
log("Updating RGB texture")
glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, img_data)
drawable.gl_end()
self.render_image()

def update_texture_yuv420(self, img_data, x, y, width, height, rowstrides):
drawable = self.glarea.get_gl_drawable()
context = self.glarea.get_gl_context()
window_width, window_height = self.get_size()
if not drawable.gl_begin(context):
raise Exception("** Cannot create OpenGL rendering context!")
assert self.textures is not None

if self.current_mode == GLClientWindow.MODE_RGB:
raise Exception("** RGB -> YUV mode change unimplemented!")
elif self.current_mode == GLClientWindow.MODE_UNINITIALIZED:
log("Creating new YUV textures")

# Create textures of the same size as the window's
glEnable(GL_TEXTURE_RECTANGLE_ARB)
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[0])
glEnable(GL_TEXTURE_RECTANGLE_ARB)
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width, window_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[1])
glEnable(GL_TEXTURE_RECTANGLE_ARB)
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width/2, window_height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[2])
glEnable(GL_TEXTURE_RECTANGLE_ARB)
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width/2, window_height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);

log("Assigning fragment program")
glEnable(GL_FRAGMENT_PROGRAM_ARB)
if not self.yuv420_shader:
self.yuv420_shader = [ 1 ]
glGenProgramsARB(1, self.yuv420_shader)
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, self.yuv420_shader[0])
prog = GL_COLORSPACE_CONVERSIONS
glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, len(prog), prog)
log.error(glGetString(GL_PROGRAM_ERROR_STRING_ARB))
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, self.yuv420_shader[0])

self.current_mode = GLClientWindow.MODE_YUV

# Clamp width and height to the actual texture size
if x + width > window_width:
width = window_width - x
if y + height > window_height:
height = window_height - y

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[0])
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[0])
glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[0])

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[1])
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[1])
glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[1])

glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[2])
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[2])
glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[2])

drawable.gl_end()
self.render_image()

def render_image(self):
drawable = self.glarea.get_gl_drawable()
context = self.glarea.get_gl_context()
w, h = self.get_size()
if not drawable.gl_begin(context):
raise Exception("** Cannot create OpenGL rendering context!")
texcoords = [ [ 0, 0 ],
[ 0, h ],
[ w, h ],
[ w, 0 ] ]
vtxcoords = texcoords

if self.current_mode == GLClientWindow.MODE_RGB:
glVertexPointeri(vtxcoords)
glTexCoordPointeri(texcoords)
glDrawArrays(GL_QUADS, 0, 4);
elif self.current_mode == GLClientWindow.MODE_YUV:
glEnable(GL_FRAGMENT_PROGRAM_ARB)
glBegin(GL_QUADS);
glMultiTexCoord2i(GL_TEXTURE0, 0, 0);
glMultiTexCoord2i(GL_TEXTURE1, 0, 0);
glMultiTexCoord2i(GL_TEXTURE2, 0, 0);
glVertex2i(0, 0);

glMultiTexCoord2i(GL_TEXTURE0, 0, h);
glMultiTexCoord2i(GL_TEXTURE1, 0, h/2);
glMultiTexCoord2i(GL_TEXTURE2, 0, h/2);
glVertex2i(0, h);

glMultiTexCoord2i(GL_TEXTURE0, w, h);
glMultiTexCoord2i(GL_TEXTURE1, w/2, h/2);
glMultiTexCoord2i(GL_TEXTURE2, w/2, h/2);
glVertex2i(w, h);

glMultiTexCoord2i(GL_TEXTURE0, w, 0);
glMultiTexCoord2i(GL_TEXTURE1, w/2, 0);
glMultiTexCoord2i(GL_TEXTURE2, w/2, 0);
glVertex2i(w, 0);
glEnd()
drawable.swap_buffers()
drawable.gl_end()

def move_resize(self, x, y, w, h):
assert self._override_redirect
self.window.move_resize(x, y, w, h)

def destroy(self):
self._unfocus()
self.glarea.destroy()
gtk.Window.destroy(self)
if self._video_decoder:
self._video_decoder.clean()
self._video_decoder = None
if lock:
lock.release()
Loading

0 comments on commit 21b0efc

Please sign in to comment.