1 /**************************************************************************
2  *
3  * Copyright 2009, VMware, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 /*
28  * Author: Keith Whitwell <keithw@vmware.com>
29  * Author: Jakob Bornecrantz <wallbraker@gmail.com>
30  */
31 
32 #include "dri_screen.h"
33 #include "dri_context.h"
34 #include "dri_drawable.h"
35 
36 #include "pipe/p_screen.h"
37 #include "util/u_format.h"
38 #include "util/u_memory.h"
39 #include "util/u_inlines.h"
40 
41 static void
42 swap_fences_unref(struct dri_drawable *draw);
43 
44 static boolean
dri_st_framebuffer_validate(struct st_framebuffer_iface * stfbi,const enum st_attachment_type * statts,unsigned count,struct pipe_resource ** out)45 dri_st_framebuffer_validate(struct st_framebuffer_iface *stfbi,
46                             const enum st_attachment_type *statts,
47                             unsigned count,
48                             struct pipe_resource **out)
49 {
50    struct dri_drawable *drawable =
51       (struct dri_drawable *) stfbi->st_manager_private;
52    struct dri_screen *screen = dri_screen(drawable->sPriv);
53    unsigned statt_mask, new_mask;
54    boolean new_stamp;
55    int i;
56    unsigned int lastStamp;
57 
58    statt_mask = 0x0;
59    for (i = 0; i < count; i++)
60       statt_mask |= (1 << statts[i]);
61 
62    /* record newly allocated textures */
63    new_mask = (statt_mask & ~drawable->texture_mask);
64 
65    /*
66     * dPriv->dri2.stamp is the server stamp.  dPriv->lastStamp is the
67     * client stamp.  It has the value of the server stamp when last
68     * checked.
69     */
70    do {
71       lastStamp = drawable->dPriv->lastStamp;
72       new_stamp = (drawable->texture_stamp != lastStamp);
73 
74       if (new_stamp || new_mask || screen->broken_invalidate) {
75          if (new_stamp && drawable->update_drawable_info)
76             drawable->update_drawable_info(drawable);
77 
78          drawable->allocate_textures(drawable, statts, count);
79 
80          /* add existing textures */
81          for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
82             if (drawable->textures[i])
83                statt_mask |= (1 << i);
84          }
85 
86          drawable->texture_stamp = lastStamp;
87          drawable->texture_mask = statt_mask;
88       }
89    } while (lastStamp != drawable->dPriv->lastStamp);
90 
91    if (!out)
92       return TRUE;
93 
94    for (i = 0; i < count; i++) {
95       out[i] = NULL;
96       pipe_resource_reference(&out[i], drawable->textures[statts[i]]);
97    }
98 
99    return TRUE;
100 }
101 
102 static boolean
dri_st_framebuffer_flush_front(struct st_framebuffer_iface * stfbi,enum st_attachment_type statt)103 dri_st_framebuffer_flush_front(struct st_framebuffer_iface *stfbi,
104                                enum st_attachment_type statt)
105 {
106    struct dri_drawable *drawable =
107       (struct dri_drawable *) stfbi->st_manager_private;
108 
109    /* XXX remove this and just set the correct one on the framebuffer */
110    drawable->flush_frontbuffer(drawable, statt);
111 
112    return TRUE;
113 }
114 
115 /**
116  * This is called when we need to set up GL rendering to a new X window.
117  */
118 boolean
dri_create_buffer(__DRIscreen * sPriv,__DRIdrawable * dPriv,const struct gl_config * visual,boolean isPixmap)119 dri_create_buffer(__DRIscreen * sPriv,
120 		  __DRIdrawable * dPriv,
121 		  const struct gl_config * visual, boolean isPixmap)
122 {
123    struct dri_screen *screen = sPriv->driverPrivate;
124    struct dri_drawable *drawable = NULL;
125 
126    if (isPixmap)
127       goto fail;		       /* not implemented */
128 
129    drawable = CALLOC_STRUCT(dri_drawable);
130    if (drawable == NULL)
131       goto fail;
132 
133    dri_fill_st_visual(&drawable->stvis, screen, visual);
134 
135    /* setup the st_framebuffer_iface */
136    drawable->base.visual = &drawable->stvis;
137    drawable->base.flush_front = dri_st_framebuffer_flush_front;
138    drawable->base.validate = dri_st_framebuffer_validate;
139    drawable->base.st_manager_private = (void *) drawable;
140 
141    drawable->screen = screen;
142    drawable->sPriv = sPriv;
143    drawable->dPriv = dPriv;
144    drawable->desired_fences = screen->default_throttle_frames;
145    if (drawable->desired_fences > DRI_SWAP_FENCES_MAX)
146       drawable->desired_fences = DRI_SWAP_FENCES_MAX;
147 
148    dPriv->driverPrivate = (void *)drawable;
149    p_atomic_set(&drawable->base.stamp, 1);
150 
151    return GL_TRUE;
152 fail:
153    FREE(drawable);
154    return GL_FALSE;
155 }
156 
157 void
dri_destroy_buffer(__DRIdrawable * dPriv)158 dri_destroy_buffer(__DRIdrawable * dPriv)
159 {
160    struct dri_drawable *drawable = dri_drawable(dPriv);
161    int i;
162 
163    pipe_surface_reference(&drawable->drisw_surface, NULL);
164 
165    for (i = 0; i < ST_ATTACHMENT_COUNT; i++)
166       pipe_resource_reference(&drawable->textures[i], NULL);
167 
168    swap_fences_unref(drawable);
169 
170    FREE(drawable);
171 }
172 
173 /**
174  * Validate the texture at an attachment.  Allocate the texture if it does not
175  * exist.  Used by the TFP extension.
176  */
177 static void
dri_drawable_validate_att(struct dri_drawable * drawable,enum st_attachment_type statt)178 dri_drawable_validate_att(struct dri_drawable *drawable,
179                           enum st_attachment_type statt)
180 {
181    enum st_attachment_type statts[ST_ATTACHMENT_COUNT];
182    unsigned i, count = 0;
183 
184    /* check if buffer already exists */
185    if (drawable->texture_mask & (1 << statt))
186       return;
187 
188    /* make sure DRI2 does not destroy existing buffers */
189    for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
190       if (drawable->texture_mask & (1 << i)) {
191          statts[count++] = i;
192       }
193    }
194    statts[count++] = statt;
195 
196    drawable->texture_stamp = drawable->dPriv->lastStamp - 1;
197 
198    drawable->base.validate(&drawable->base, statts, count, NULL);
199 }
200 
201 /**
202  * These are used for GLX_EXT_texture_from_pixmap
203  */
204 static void
dri_set_tex_buffer2(__DRIcontext * pDRICtx,GLint target,GLint format,__DRIdrawable * dPriv)205 dri_set_tex_buffer2(__DRIcontext *pDRICtx, GLint target,
206                     GLint format, __DRIdrawable *dPriv)
207 {
208    struct dri_context *ctx = dri_context(pDRICtx);
209    struct dri_drawable *drawable = dri_drawable(dPriv);
210    struct pipe_resource *pt;
211 
212    dri_drawable_validate_att(drawable, ST_ATTACHMENT_FRONT_LEFT);
213 
214    /* Use the pipe resource associated with the X drawable */
215    pt = drawable->textures[ST_ATTACHMENT_FRONT_LEFT];
216 
217    if (pt) {
218       enum pipe_format internal_format = pt->format;
219 
220       if (format == __DRI_TEXTURE_FORMAT_RGB)  {
221          /* only need to cover the formats recognized by dri_fill_st_visual */
222          switch (internal_format) {
223          case PIPE_FORMAT_B8G8R8A8_UNORM:
224             internal_format = PIPE_FORMAT_B8G8R8X8_UNORM;
225             break;
226          case PIPE_FORMAT_A8R8G8B8_UNORM:
227             internal_format = PIPE_FORMAT_X8R8G8B8_UNORM;
228             break;
229          default:
230             break;
231          }
232       }
233 
234       drawable->update_tex_buffer(drawable, ctx, pt);
235 
236       ctx->st->teximage(ctx->st,
237             (target == GL_TEXTURE_2D) ? ST_TEXTURE_2D : ST_TEXTURE_RECT,
238             0, internal_format, pt, FALSE);
239    }
240 }
241 
242 static void
dri_set_tex_buffer(__DRIcontext * pDRICtx,GLint target,__DRIdrawable * dPriv)243 dri_set_tex_buffer(__DRIcontext *pDRICtx, GLint target,
244                    __DRIdrawable *dPriv)
245 {
246    dri_set_tex_buffer2(pDRICtx, target, __DRI_TEXTURE_FORMAT_RGBA, dPriv);
247 }
248 
249 const __DRItexBufferExtension driTexBufferExtension = {
250     { __DRI_TEX_BUFFER, __DRI_TEX_BUFFER_VERSION },
251    dri_set_tex_buffer,
252    dri_set_tex_buffer2,
253    NULL,
254 };
255 
256 /**
257  * Get the format and binding of an attachment.
258  */
259 void
dri_drawable_get_format(struct dri_drawable * drawable,enum st_attachment_type statt,enum pipe_format * format,unsigned * bind)260 dri_drawable_get_format(struct dri_drawable *drawable,
261                         enum st_attachment_type statt,
262                         enum pipe_format *format,
263                         unsigned *bind)
264 {
265    switch (statt) {
266    case ST_ATTACHMENT_FRONT_LEFT:
267    case ST_ATTACHMENT_BACK_LEFT:
268    case ST_ATTACHMENT_FRONT_RIGHT:
269    case ST_ATTACHMENT_BACK_RIGHT:
270       *format = drawable->stvis.color_format;
271       *bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
272       break;
273    case ST_ATTACHMENT_DEPTH_STENCIL:
274       *format = drawable->stvis.depth_stencil_format;
275       *bind = PIPE_BIND_DEPTH_STENCIL; /* XXX sampler? */
276       break;
277    default:
278       *format = PIPE_FORMAT_NONE;
279       *bind = 0;
280       break;
281    }
282 }
283 
284 
285 /**
286  * swap_fences_pop_front - pull a fence from the throttle queue
287  *
288  * If the throttle queue is filled to the desired number of fences,
289  * pull fences off the queue until the number is less than the desired
290  * number of fences, and return the last fence pulled.
291  */
292 static struct pipe_fence_handle *
swap_fences_pop_front(struct dri_drawable * draw)293 swap_fences_pop_front(struct dri_drawable *draw)
294 {
295    struct pipe_screen *screen = draw->screen->base.screen;
296    struct pipe_fence_handle *fence = NULL;
297 
298    if (draw->desired_fences == 0)
299       return NULL;
300 
301    if (draw->cur_fences >= draw->desired_fences) {
302       screen->fence_reference(screen, &fence, draw->swap_fences[draw->tail]);
303       screen->fence_reference(screen, &draw->swap_fences[draw->tail++], NULL);
304       draw->tail &= DRI_SWAP_FENCES_MASK;
305       --draw->cur_fences;
306    }
307    return fence;
308 }
309 
310 
311 /**
312  * swap_fences_push_back - push a fence onto the throttle queue
313  *
314  * push a fence onto the throttle queue and pull fences of the queue
315  * so that the desired number of fences are on the queue.
316  */
317 static void
swap_fences_push_back(struct dri_drawable * draw,struct pipe_fence_handle * fence)318 swap_fences_push_back(struct dri_drawable *draw,
319 		      struct pipe_fence_handle *fence)
320 {
321    struct pipe_screen *screen = draw->screen->base.screen;
322 
323    if (!fence || draw->desired_fences == 0)
324       return;
325 
326    while(draw->cur_fences == draw->desired_fences)
327       swap_fences_pop_front(draw);
328 
329    draw->cur_fences++;
330    screen->fence_reference(screen, &draw->swap_fences[draw->head++],
331 			   fence);
332    draw->head &= DRI_SWAP_FENCES_MASK;
333 }
334 
335 
336 /**
337  * swap_fences_unref - empty the throttle queue
338  *
339  * pulls fences of the throttle queue until it is empty.
340  */
341 static void
swap_fences_unref(struct dri_drawable * draw)342 swap_fences_unref(struct dri_drawable *draw)
343 {
344    struct pipe_screen *screen = draw->screen->base.screen;
345 
346    while(draw->cur_fences) {
347       screen->fence_reference(screen, &draw->swap_fences[draw->tail++], NULL);
348       draw->tail &= DRI_SWAP_FENCES_MASK;
349       --draw->cur_fences;
350    }
351 }
352 
353 
354 /**
355  * dri_throttle - A DRI2ThrottleExtension throttling function.
356  *
357  * pulls a fence off the throttling queue and waits for it if the
358  * number of fences on the throttling queue has reached the desired
359  * number.
360  *
361  * Then flushes to insert a fence at the current rendering position, and
362  * pushes that fence on the queue. This requires that the st_context_iface
363  * flush method returns a fence even if there are no commands to flush.
364  */
365 static void
dri_throttle(__DRIcontext * driCtx,__DRIdrawable * dPriv,enum __DRI2throttleReason reason)366 dri_throttle(__DRIcontext *driCtx, __DRIdrawable *dPriv,
367 	     enum __DRI2throttleReason reason)
368 {
369     struct dri_drawable *draw = dri_drawable(dPriv);
370     struct st_context_iface *ctxi;
371     struct pipe_screen *screen = draw->screen->base.screen;
372     struct pipe_fence_handle *fence;
373 
374     if (reason != __DRI2_THROTTLE_SWAPBUFFER &&
375 	reason != __DRI2_THROTTLE_FLUSHFRONT)
376 	return;
377 
378     fence = swap_fences_pop_front(draw);
379     if (fence) {
380 	(void) screen->fence_finish(screen, fence, PIPE_TIMEOUT_INFINITE);
381 	screen->fence_reference(screen, &fence, NULL);
382     }
383 
384     if (driCtx == NULL)
385 	return;
386 
387     ctxi = dri_context(driCtx)->st;
388     ctxi->flush(ctxi, 0, &fence);
389     if (fence) {
390 	swap_fences_push_back(draw, fence);
391 	screen->fence_reference(screen, &fence, NULL);
392     }
393 }
394 
395 
396 const __DRI2throttleExtension dri2ThrottleExtension = {
397     .base = { __DRI2_THROTTLE, __DRI2_THROTTLE_VERSION },
398     .throttle = dri_throttle,
399 };
400 
401 
402 /* vim: set sw=3 ts=8 sts=3 expandtab: */
403