xpra icon
Bug tracker and wiki

source: xpra/trunk/src/xpra/gl_client_window.py @ 925

Last change on this file since 925 was 925, checked in by antoine, 3 years ago

don't bother trying GL with gtk3, also fix pydev warnings and whitespace

File size: 13.7 KB
Line 
1# This file is part of Parti.
2# Copyright (C) 2012 Serviware (Arthur Huillet, <ahuillet@serviware.com>)
3# Copyright (C) 2012 Antoine Martin <antoine@devloop.org.uk>
4# Parti is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
6
7from wimpiggy.log import Logger
8log = Logger()
9
10from xpra.client_window import ClientWindow, queue_draw
11from xpra.scripts.main import ENCODINGS
12
13
14import gtk.gdkgl, gtk.gtkgl         #@UnresolvedImport
15assert gtk.gdkgl is not None and gtk.gtkgl is not None
16
17from OpenGL.GL import GL_VERSION, GL_PROJECTION, GL_MODELVIEW, GL_VERTEX_ARRAY, \
18    GL_TEXTURE_COORD_ARRAY, GL_FRAGMENT_PROGRAM_ARB, \
19    GL_PROGRAM_ERROR_STRING_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, \
20    GL_TEXTURE_RECTANGLE_ARB, GL_UNPACK_ROW_LENGTH, \
21    GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER, GL_NEAREST, \
22    GL_UNSIGNED_BYTE, GL_RGB, GL_LUMINANCE, GL_LINEAR, \
23    GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_QUADS, \
24    glActiveTexture, \
25    glGetString, glViewport, glMatrixMode, glLoadIdentity, glOrtho, \
26    glEnableClientState, glDisable, glGenTextures, \
27    glBindTexture, glPixelStorei, glEnable, glBegin, \
28    glTexParameteri, glVertexPointeri, glTexCoordPointeri, \
29    glTexImage2D, glTexSubImage2D, \
30    glDrawArrays, glMultiTexCoord2i, \
31    glVertex2i, glEnd
32from OpenGL.GL.ARB.vertex_program import glGenProgramsARB, glBindProgramARB, glProgramStringARB
33from xpra.gl_colorspace_conversions import GL_COLORSPACE_CONVERSIONS
34
35gl_major = glGetString(GL_VERSION)[0]
36gl_minor = glGetString(GL_VERSION)[2]
37if gl_major<=1 and gl_minor<1:
38    raise ImportError("** OpenGL output requires OpenGL version 1.1 or greater, not %s.%s" % (gl_major, gl_minor))
39
40
41class GLClientWindow(ClientWindow):
42    def __init__(self, client, wid, x, y, w, h, metadata, override_redirect):
43        ClientWindow.__init__(self, client, wid, x, y, w, h, metadata, override_redirect)
44        display_mode = (gtk.gdkgl.MODE_RGB | gtk.gdkgl.MODE_SINGLE)
45        #FIXME: We use single buffer because double doesn't work, figure out why
46        self.glconfig = gtk.gdkgl.Config(mode=display_mode)
47        self.glarea = gtk.gtkgl.DrawingArea(self.glconfig)
48        self.glarea.set_size_request(w, h)
49        self.glarea.show()
50        self.add(self.glarea)
51        self._on_close = []
52        self.textures = [ 0 ]
53
54    def do_configure_event(self, event):
55        ClientWindow.do_configure_event(self, event)
56        drawable = self.glarea.get_gl_drawable()
57        context = self.glarea.get_gl_context()
58
59        self.use_openGL_CSC = True
60        self.yuv420_shader = None
61        self.current_mode = 0 # 0 = uninitialized 1 = RGB 2 = YUV
62
63        if not drawable.gl_begin(context):
64            raise Exception("** Cannot create OpenGL rendering context!")
65
66        w, h = self.get_size()
67        log("Configure widget size size is %d, %d" % (w, h))
68        glViewport(0, 0, w, h)
69        glMatrixMode(GL_PROJECTION)
70        glLoadIdentity()
71        glOrtho(0.0, w, h, 0.0, -1.0, 1.0)
72        glMatrixMode(GL_MODELVIEW)
73        glEnableClientState(GL_VERTEX_ARRAY)
74        glEnableClientState(GL_TEXTURE_COORD_ARRAY)
75        glDisable(GL_FRAGMENT_PROGRAM_ARB)
76
77        if self.textures[0] == 0:
78            self.textures = glGenTextures(3)
79
80        drawable.gl_end()
81
82    def do_expose_event(self, event):
83        log("do_expose_event(%s) area=%s", event, event.area)
84        if not (self.flags() & gtk.MAPPED):
85            return
86        self.render_image()
87        self.glarea.window.invalidate_rect(self.glarea.allocation, False)
88        # Update window synchronously (fast).
89        self.glarea.window.process_updates(False)
90
91    def draw_region(self, x, y, width, height, coding, img_data, rowstride):
92        #log("draw_region(%s, %s, %s, %s, %s, %s bytes, %s)", x, y, width, height, coding, len(img_data), rowstride)
93        if coding == "mmap":
94            # No GL output for mmap
95            assert coding != "mmap"
96        elif coding == "rgb24":
97            if rowstride>0:
98                assert len(img_data) == rowstride * height
99            else:
100                assert len(img_data) == width * 3 * height
101            self.update_texture_rgb24(img_data, x, y, width, height, rowstride)
102        elif coding == "x264":
103            assert "x264" in ENCODINGS
104            from xpra.x264.codec import DECODERS, Decoder     #@UnresolvedImport
105            self.paint_with_video_decoder(DECODERS, Decoder, "x264", img_data, x, y, width, height, rowstride)
106        elif coding == "vpx":
107            assert "vpx" in ENCODINGS
108            from xpra.vpx.codec import DECODERS, Decoder     #@UnresolvedImport
109            self.paint_with_video_decoder(DECODERS, Decoder, "vpx", img_data, x, y, width, height, rowstride)
110        else:
111            raise Exception("** No JPEG/PNG support for OpenGL")
112        queue_draw(self, x, y, width, height)
113
114    #FIXME: This is a copypaste from window_backing.py...
115    def paint_with_video_decoder(self, decoders, factory, coding, img_data, x, y, width, height, rowstride):
116        assert x==0 and y==0
117        decoder = decoders.get(self._id)
118        if decoder and (decoder.get_width()!=width or decoder.get_height()!=height):
119            log("paint_with_video_decoder: window dimensions have changed from %s to %s", (decoder.get_width(), decoder.get_height()), (width, height))
120            decoder.clean()
121            decoder.init(width, height)
122        if decoder is None:
123            decoder = factory()
124            decoder.init(width, height)
125            decoders[self._id] = decoder
126            def close_decoder():
127                log("closing %s decoder for window %s", coding, self._id)
128                decoder.clean()
129                del decoders[self._id]
130            self._on_close.append(close_decoder)
131        try:
132            if self.use_openGL_CSC:
133                decompress = decoder.decompress_image_to_yuv
134                update_texture = self.update_texture_yuv420
135            else:
136                decompress = decoder.decompress_image_to_rgb
137                update_texture = self.update_texture_rgb24
138
139            err, outstride, data = decompress(img_data)
140            if err!=0:
141                log.error("paint_with_video_decoder: ouch, decompression error %s", err)
142                return
143            if not data:
144                log.error("paint_with_video_decoder: ouch, no data from %s decoder", coding)
145                return
146            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)
147            update_texture(data, x, y, width, height, outstride)
148        finally:
149            if not self.use_openGL_CSC:
150                decoder.free_image()
151
152    def update_texture_rgb24(self, img_data, x, y, width, height, rowstride):
153        drawable = self.glarea.get_gl_drawable()
154        context = self.glarea.get_gl_context()
155        if not drawable.gl_begin(context):
156            raise Exception("** Cannot create OpenGL rendering context!")
157
158        glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[0])
159        glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstride/3)
160
161        if self.current_mode == 2:
162            raise Exception("** YUV -> RGB mode change unimplemented!")
163        elif self.current_mode == 0:
164            log("Creating new RGB texture")
165            w, h = self.get_size()
166            # First time we draw must be full image
167            assert w == width and h == height
168            glEnable(GL_TEXTURE_RECTANGLE_ARB)
169            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
170            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
171            glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0)
172            self.current_mode = 1
173        log("Updating RGB texture")
174        glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, img_data)
175        drawable.gl_end()
176        self.render_image()
177
178    def update_texture_yuv420(self, img_data, x, y, width, height, rowstrides):
179        drawable = self.glarea.get_gl_drawable()
180        context = self.glarea.get_gl_context()
181        window_width, window_height = self.get_size()
182        if not drawable.gl_begin(context):
183            raise Exception("** Cannot create OpenGL rendering context!")
184
185        if self.current_mode == 1:
186            raise Exception("** RGB -> YUV mode change unimplemented!")
187        elif self.current_mode == 0:
188            log("Creating new YUV textures")
189
190            # Create textures of the same size as the window's
191            glEnable(GL_TEXTURE_RECTANGLE_ARB)
192            glActiveTexture(GL_TEXTURE0);
193            glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[0])
194            glEnable(GL_TEXTURE_RECTANGLE_ARB)
195            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
196            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
197            glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width, window_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);
198
199            glActiveTexture(GL_TEXTURE1);
200            glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[1])
201            glEnable(GL_TEXTURE_RECTANGLE_ARB)
202            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
203            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
204            glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width/2, window_height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);
205
206            glActiveTexture(GL_TEXTURE2);
207            glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[2])
208            glEnable(GL_TEXTURE_RECTANGLE_ARB)
209            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
210            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
211            glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, window_width/2, window_height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);
212
213            log("Assigning fragment program")
214            glEnable(GL_FRAGMENT_PROGRAM_ARB)
215            if not self.yuv420_shader:
216                self.yuv420_shader = [ 1 ]
217                glGenProgramsARB(1, self.yuv420_shader)
218                glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, self.yuv420_shader[0])
219                prog = GL_COLORSPACE_CONVERSIONS
220                glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, len(prog), prog)
221                log.error(glGetString(GL_PROGRAM_ERROR_STRING_ARB))
222                glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, self.yuv420_shader[0])
223
224            self.current_mode = 2
225
226        # Clamp width and height to the actual texture size
227        if x + width > window_width:
228            width = window_width - x
229        if y + height > window_height:
230            height = window_height - y
231
232        glActiveTexture(GL_TEXTURE0);
233        glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[0])
234        glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[0])
235        glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[0])
236
237        glActiveTexture(GL_TEXTURE1);
238        glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[1])
239        glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[1])
240        glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[1])
241
242        glActiveTexture(GL_TEXTURE2);
243        glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.textures[2])
244        glPixelStorei(GL_UNPACK_ROW_LENGTH, rowstrides[2])
245        glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, x, y, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, img_data[2])
246
247        drawable.gl_end()
248        self.render_image()
249
250    def render_image(self):
251        drawable = self.glarea.get_gl_drawable()
252        context = self.glarea.get_gl_context()
253        w, h = self.get_size()
254        if not drawable.gl_begin(context):
255            raise Exception("** Cannot create OpenGL rendering context!")
256        texcoords = [ [ 0, 0 ],
257                      [ 0, h ],
258                      [ w, h ],
259                      [ w, 0 ] ]
260        vtxcoords = texcoords
261
262        if self.current_mode == 1: #RGB
263            glVertexPointeri(vtxcoords)
264            glTexCoordPointeri(texcoords)
265            glDrawArrays(GL_QUADS, 0, 4);
266        elif self.current_mode == 2: #YUV
267            glEnable(GL_FRAGMENT_PROGRAM_ARB)
268            glBegin(GL_QUADS);
269            glMultiTexCoord2i(GL_TEXTURE0, 0, 0);
270            glMultiTexCoord2i(GL_TEXTURE1, 0, 0);
271            glMultiTexCoord2i(GL_TEXTURE2, 0, 0);
272            glVertex2i(0, 0);
273
274            glMultiTexCoord2i(GL_TEXTURE0, 0, h);
275            glMultiTexCoord2i(GL_TEXTURE1, 0, h/2);
276            glMultiTexCoord2i(GL_TEXTURE2, 0, h/2);
277            glVertex2i(0, h);
278
279            glMultiTexCoord2i(GL_TEXTURE0, w, h);
280            glMultiTexCoord2i(GL_TEXTURE1, w/2, h/2);
281            glMultiTexCoord2i(GL_TEXTURE2, w/2, h/2);
282            glVertex2i(w, h);
283
284            glMultiTexCoord2i(GL_TEXTURE0, w, 0);
285            glMultiTexCoord2i(GL_TEXTURE1, w/2, 0);
286            glMultiTexCoord2i(GL_TEXTURE2, w/2, 0);
287            glVertex2i(w, 0);
288            glEnd()
289        drawable.swap_buffers()
290        drawable.gl_end()
291
292    def move_resize(self, x, y, w, h):
293        assert self._override_redirect
294        self.window.move_resize(x, y, w, h)
295
296    def destroy(self):
297        self._unfocus()
298        self.glarea.destroy()
299        gtk.Window.destroy(self)
300        for cb in self._on_close:
301            try:
302                log("calling %s", cb)
303                cb()
304            except:
305                log.error("error on close callback %s", cb, exc_info=True)
306        self._on_close = []
Note: See TracBrowser for help on using the repository browser.