xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #465: newbuffer-v2.patch​

File newbuffer-v2.patch​, 13.8 KB (added by Antoine Martin, 7 years ago)

updated patch using the new buffer API, works but leaks memory..

Line 
1Index: xpra/client/gl/gl_window_backing.py
2===================================================================
3--- xpra/client/gl/gl_window_backing.py (revision 5915)
4+++ xpra/client/gl/gl_window_backing.py (working copy)
5@@ -5,6 +5,7 @@
6 # later version. See the file COPYING for details.
7 
8 #only works with gtk2:
9+import sys
10 import os
11 from gtk import gdk
12 assert gdk
13@@ -16,12 +17,13 @@
14 log = Logger("opengl", "paint")
15 OPENGL_DEBUG = os.environ.get("XPRA_OPENGL_DEBUG", "0")=="1"
16 
17-
18 from xpra.codecs.codec_constants import get_subsampling_divs
19 from xpra.client.gl.gl_check import get_DISPLAY_MODE
20 from xpra.client.gl.gl_colorspace_conversions import YUV2RGB_shader, RGBP2RGB_shader
21 from xpra.client.gtk2.window_backing import GTK2WindowBacking, fire_paint_callbacks
22-from OpenGL.GL import GL_PROJECTION, GL_MODELVIEW, \
23+from OpenGL import version as OpenGL_version
24+from OpenGL.GL import \
25+    GL_PROJECTION, GL_MODELVIEW, \
26     GL_UNPACK_ROW_LENGTH, GL_UNPACK_ALIGNMENT, \
27     GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER, GL_NEAREST, \
28     GL_UNSIGNED_BYTE, GL_LUMINANCE, GL_LINEAR, \
29@@ -97,6 +99,13 @@
30 from ctypes import c_char_p
31 
32 
33+
34+#support for memory views requires Python 2.7 and PyOpenGL 3.1
35+memoryview_type = None
36+if sys.version_info[:2]>=(2,7) and OpenGL_version.__version__.split('.')[:2]>=['3','1']:
37+    memoryview_type = memoryview
38+
39+
40 # Texture number assignment
41 #  1 = Y plane
42 #  2 = U plane
43@@ -501,11 +510,19 @@
44         #we get from the avcodec and vpx decoders cannot be used.
45         #PyOpenGL version 3.1 has some support for the memory view interface
46         #but this requires memoryview support which requires python>=2.7
47-        img.clone_pixel_data()
48+        pixels = img.get_pixels()
49+        assert len(pixels)==3
50+        plane_types = set([type(plane) for plane in img.get_pixels()])
51+        log("memoryview_type=%s, plane_types=%s", memoryview_type, plane_types)
52+        if memoryview_type is not None and len(plane_types)==1 and list(plane_types)[0]==memoryview_type:
53+            log.info("using memoryview directly!")
54+        else:
55+            img.clone_pixel_data()
56         gobject.idle_add(self.gl_paint_planar, img, x, y, enc_width, enc_height, width, height, callbacks)
57 
58     def gl_paint_planar(self, img, x, y, enc_width, enc_height, width, height, callbacks):
59         #this function runs in the UI thread, no video_decoder lock held
60+        log("gl_paint_planar%s", (img, x, y, enc_width, enc_height, width, height, callbacks))
61         try:
62             pixel_format = img.get_pixel_format()
63             assert pixel_format in ("YUV420P", "YUV422P", "YUV444P", "GBRP"), "sorry the GL backing does not handle pixel format '%s' yet!" % (pixel_format)
64@@ -569,6 +586,7 @@
65             pixel_data = img_data[index]
66             log("texture %s: div=%s, rowstride=%s, %sx%s, data=%s bytes", index, divs[index], rowstrides[index], width/div_w, height/div_h, len(pixel_data))
67             glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, width/div_w, height/div_h, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixel_data)
68+        img.free()
69 
70     def render_planar_update(self, rx, ry, rw, rh, x_scale=1, y_scale=1):
71         log("%s.render_planar_update%s pixel_format=%s", self, (rx, ry, rw, rh, x_scale, y_scale), self.pixel_format)
72Index: xpra/codecs/csc_swscale/colorspace_converter.pyx
73===================================================================
74--- xpra/codecs/csc_swscale/colorspace_converter.pyx    (revision 5910)
75+++ xpra/codecs/csc_swscale/colorspace_converter.pyx    (working copy)
76@@ -27,6 +27,9 @@
77     object PyBuffer_FromMemory(void *ptr, Py_ssize_t size)
78     int PyObject_AsReadBuffer(object obj, void ** buffer, Py_ssize_t * buffer_len) except -1
79 
80+    int PyMemoryView_Check(object obj)
81+    Py_buffer *PyMemoryView_GET_BUFFER(object obj)
82+
83 cdef extern from "../inline.h":
84     pass
85 
86@@ -400,6 +403,7 @@
87         cdef int height
88         cdef int stride
89         cdef int result
90+        cdef Py_buffer *py_buffer
91         start = time.time()
92         iplanes = image.get_planes()
93         assert iplanes in ImageWrapper.PLANE_OPTIONS, "invalid number of planes: %s" % iplanes
94@@ -416,7 +420,13 @@
95         for i in xrange(4):
96             if i<iplanes:
97                 input_stride[i] = strides[i]
98-                PyObject_AsReadBuffer(input[i], <const void**> &input_image[i], &pic_buf_len)
99+                if PyMemoryView_Check(input[i]):
100+                    log.info("found memory view!")
101+                    py_buffer = PyMemoryView_GET_BUFFER(input[i])
102+                    input_image[i] = <const uint8_t *> py_buffer.buf
103+                    log.info("using py_buffer @ %#x", <unsigned long> py_buffer.buf)
104+                else:
105+                    PyObject_AsReadBuffer(input[i], <const void**> &input_image[i], &pic_buf_len)
106             else:
107                 #some versions of swscale check all 4 planes
108                 #even when we only pass 1! see "check_image_pointers"
109Index: xpra/codecs/dec_avcodec2/decoder.pyx
110===================================================================
111--- xpra/codecs/dec_avcodec2/decoder.pyx        (revision 5910)
112+++ xpra/codecs/dec_avcodec2/decoder.pyx        (working copy)
113@@ -25,7 +25,35 @@
114     object PyBuffer_FromReadWriteMemory(void *ptr, Py_ssize_t size)
115     int PyObject_AsReadBuffer(object obj, void ** buffer, Py_ssize_t * buffer_len) except -1
116 
117+    ctypedef struct PyMemoryViewObject:
118+        pass
119+    ctypedef struct Py_buffer:
120+        void *buf
121+        Py_ssize_t len
122+        int readonly
123+        char *format
124+        int ndim
125+        Py_ssize_t *shape
126+        Py_ssize_t *strides
127+        Py_ssize_t *suboffsets
128+        Py_ssize_t itemsize
129+        void *internal
130+    cdef enum:
131+        PyBUF_SIMPLE
132+        PyBUF_WRITABLE
133+        PyBUF_FORMAT
134+        PyBUF_ANY_CONTIGUOUS
135 
136+    void PyBuffer_Release(Py_buffer *view)
137+    object PyMemoryView_FromBuffer(Py_buffer *info)
138+    int PyMemoryView_Check(object obj)
139+    Py_buffer *PyMemoryView_GET_BUFFER(object obj)
140+
141+    int PyBuffer_FillInfo(Py_buffer *view, object obj, void *buf,
142+                Py_ssize_t len, int readonly, int infoflags) except -1
143+    int  PyObject_GetBuffer(object, Py_buffer *, int) except -1
144+
145+
146 cdef extern from "string.h":
147     void * memcpy(void * destination, void * source, size_t num) nogil
148     void * memset(void * ptr, int value, size_t num) nogil
149@@ -248,16 +276,38 @@
150 
151     def free(self):                             #@DuplicatedSignature
152         log("AVImageWrapper.free()")
153+        self.free_pybuffers()
154         ImageWrapper.free(self)
155         self.xpra_free_frame()
156 
157     def clone_pixel_data(self):
158         log("AVImageWrapper.clone_pixel_data()")
159+        self.free_pybuffers()
160         ImageWrapper.clone_pixel_data(self)
161         self.xpra_free_frame()
162 
163+    def free_pybuffers(self):
164+        cdef Py_buffer* py_buffer
165+        #we can now clear the buffer objects:
166+        if self.pixels:
167+            if self.planes == 0:
168+                #no planes, simple buffer:
169+                assert self.pixels, "no pixels!"
170+                if PyMemoryView_Check(self.pixels):
171+                    py_buffer = PyMemoryView_GET_BUFFER(self.pixels)
172+                    log.info("freeing pybuffer %s: %#x", type(self.pixels), <unsigned long> py_buffer)
173+                    PyBuffer_Release(py_buffer)
174+            else:
175+                assert self.planes>0, "invalid number of planes: %s" % self.planes
176+                for plane in self.pixels:
177+                    if PyMemoryView_Check(plane):
178+                        py_buffer = PyMemoryView_GET_BUFFER(plane)
179+                        log.info("freeing pybuffer %s: %#x", type(plane), <unsigned long> py_buffer)
180+                        PyBuffer_Release(py_buffer)
181+
182     def xpra_free_frame(self):
183         log("AVImageWrapper.xpra_free_frame() av_frames=%s", self.av_frames)
184+        #and tell av_frame that *we* are done with it too:
185         if self.av_frames:
186             for av_frame in self.av_frames:
187                 av_frame.xpra_free()
188@@ -481,6 +531,9 @@
189         cdef object plane_offsets, plane_sizes
190         assert self.codec_ctx!=NULL
191         assert self.codec!=NULL
192+        cdef Py_ssize_t *shape = [0]
193+        cdef Py_buffer pybuf
194+        cdef str astr=""
195 
196         #copy the whole input buffer into a padded C buffer:
197         PyObject_AsReadBuffer(input, <const void**> &buf, &buf_len)
198@@ -557,10 +610,13 @@
199                     stride = av_frame.linesize[i]
200                     size = height * stride
201                     outsize += size
202-                    if READ_ONLY:
203-                        plane = PyBuffer_FromMemory(<void *>av_frame.data[i], size)
204-                    else:
205-                        plane = PyBuffer_FromReadWriteMemory(<void *>av_frame.data[i], size)
206+
207+                    shape[0] = size
208+                    PyBuffer_FillInfo(&pybuf, astr, <void *>av_frame.data[i], size, False, PyBUF_SIMPLE)
209+                    pybuf.readonly = READ_ONLY
210+                    pybuf.format = "B"
211+                    pybuf.shape = shape
212+                    plane = PyMemoryView_FromBuffer(&pybuf)
213                     out.append(plane)
214                     strides.append(stride)
215             else:
216@@ -567,10 +623,14 @@
217                 #RGB mode: "out" is a single buffer
218                 strides = av_frame.linesize[0]+av_frame.linesize[1]+av_frame.linesize[2]
219                 outsize = self.codec_ctx.height * strides
220-                if READ_ONLY:
221-                    out = PyBuffer_FromMemory(<void *>av_frame.data[0], outsize)
222-                else:
223-                    out = PyBuffer_FromReadWriteMemory(<void *>av_frame.data[0], outsize)
224+
225+                shape[0] = outsize
226+                PyBuffer_FillInfo(&pybuf, astr, <void *>av_frame.data[0], outsize, False, PyBUF_SIMPLE)
227+                pybuf.readonly = READ_ONLY
228+                pybuf.format = "B"
229+                pybuf.shape = shape
230+                out = PyMemoryView_FromBuffer(&pybuf)
231+
232                 nplanes = 0
233 
234             #FIXME: we could lose track of framewrappers if an error occurs before the end:
235@@ -589,8 +649,7 @@
236         img.av_frames = framewrappers
237         self.frames += 1
238         #add to weakref list after cleaning it up:
239-        self.weakref_images = [x for x in self.weakref_images if x() is not None]
240-        self.weakref_images.append(weakref.ref(img))
241+        self.weakref_images = [x for x in self.weakref_images if x() is not None]+[weakref.ref(img)]
242         log("%s.decompress_image(%s:%s, %s)=%s", self, type(input), buf_len, options, img)
243         return img
244 
245Index: xpra/codecs/vpx/decoder.pyx
246===================================================================
247--- xpra/codecs/vpx/decoder.pyx (revision 5910)
248+++ xpra/codecs/vpx/decoder.pyx (working copy)
249@@ -30,9 +30,37 @@
250 cdef extern from "Python.h":
251     ctypedef int Py_ssize_t
252     ctypedef object PyObject
253+
254     int PyObject_AsReadBuffer(object obj, void ** buffer, Py_ssize_t * buffer_len) except -1
255-    object PyBuffer_FromMemory(void *ptr, Py_ssize_t size)
256 
257+    ctypedef struct PyMemoryViewObject:
258+        pass
259+    ctypedef struct Py_buffer:
260+        void *buf
261+        Py_ssize_t len
262+        int readonly
263+        char *format
264+        int ndim
265+        Py_ssize_t *shape
266+        Py_ssize_t *strides
267+        Py_ssize_t *suboffsets
268+        Py_ssize_t itemsize
269+        void *internal
270+    cdef enum:
271+        PyBUF_SIMPLE
272+        PyBUF_WRITABLE
273+        PyBUF_FORMAT
274+        PyBUF_ANY_CONTIGUOUS
275+
276+    void PyBuffer_Release(Py_buffer *view)
277+    int PyMemoryView_Check(object obj)
278+    object PyMemoryView_FromBuffer(Py_buffer *info)
279+    Py_buffer *PyMemoryView_GET_BUFFER(object obj)
280+
281+    int PyBuffer_FillInfo(Py_buffer *view, object obj, void *buf,
282+                Py_ssize_t len, int readonly, int infoflags) except -1
283+    int  PyObject_GetBuffer(object, Py_buffer *, int) except -1
284+
285 ctypedef unsigned char uint8_t
286 ctypedef long vpx_img_fmt_t
287 ctypedef void vpx_codec_iface_t
288@@ -172,15 +200,27 @@
289         self.buffers.append(ptr)
290 
291     def clone_pixel_data(self):
292+        oldpixels = self.pixels
293         ImageWrapper.clone_pixel_data(self)
294-        self.free_buffers()
295+        self.free_buffers(oldpixels)
296 
297     def free(self):
298+        oldpixels = self.pixels
299         ImageWrapper.free(self)
300-        self.free_buffers()
301+        self.free_buffers(oldpixels)
302 
303+    def free_pybuffers(self):
304+        #release the new style buffers:
305+        cdef Py_buffer* py_buffer
306+        if self.pixels:
307+            for plane in self.pixels:
308+                if PyMemoryView_Check(plane):
309+                    py_buffer = PyMemoryView_GET_BUFFER(plane)
310+                    PyBuffer_Release(py_buffer)
311+
312     def free_buffers(self):
313         cdef void *ptr
314+        #release the underlying memory:
315         if self.buffers:
316             for x in self.buffers:
317                 #cython magic:
318@@ -283,7 +323,11 @@
319         cdef object image
320         cdef object plane
321         cdef void *padded_buf
322+        cdef str astr=""
323         cdef Py_ssize_t plane_len = 0
324+        cdef Py_buffer pybuf
325+        cdef Py_ssize_t *shape = [0]
326+
327         assert self.context!=NULL
328         assert PyObject_AsReadBuffer(input, <const void**> &buf, &buf_len)==0
329 
330@@ -318,8 +362,13 @@
331             memcpy(padded_buf, <void *>img.planes[i], plane_len)
332             memset(<void *>((<char *>padded_buf)+plane_len), 0, stride)
333 
334-            plane = PyBuffer_FromMemory(padded_buf, plane_len)
335-            pixels.append(plane)
336+            shape[0] = plane_len
337+            PyBuffer_FillInfo(&pybuf, astr, padded_buf, plane_len, False, PyBUF_SIMPLE)
338+            pybuf.format = "B"
339+            pybuf.shape = shape
340+            mv = PyMemoryView_FromBuffer(&pybuf)
341+            log.info("returning memory view: %s", mv)
342+            pixels.append(mv)
343 
344             image.add_buffer(<unsigned long> padded_buf)
345         log("vpx returning decoded %s image %s with colorspace=%s", self.encoding, image, image.get_pixel_format())