Home | History | Annotate | Download | only in dri
      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 (at) vmware.com>
     29  * Author: Jakob Bornecrantz <wallbraker (at) 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 uint32_t drifb_ID = 0;
     42 
     43 static void
     44 swap_fences_unref(struct dri_drawable *draw);
     45 
     46 static boolean
     47 dri_st_framebuffer_validate(struct st_context_iface *stctx,
     48                             struct st_framebuffer_iface *stfbi,
     49                             const enum st_attachment_type *statts,
     50                             unsigned count,
     51                             struct pipe_resource **out)
     52 {
     53    struct dri_context *ctx = (struct dri_context *)stctx->st_manager_private;
     54    struct dri_drawable *drawable =
     55       (struct dri_drawable *) stfbi->st_manager_private;
     56    struct dri_screen *screen = dri_screen(drawable->sPriv);
     57    unsigned statt_mask, new_mask;
     58    boolean new_stamp;
     59    int i;
     60    unsigned int lastStamp;
     61    struct pipe_resource **textures =
     62       drawable->stvis.samples > 1 ? drawable->msaa_textures
     63                                   : drawable->textures;
     64 
     65    statt_mask = 0x0;
     66    for (i = 0; i < count; i++)
     67       statt_mask |= (1 << statts[i]);
     68 
     69    /* record newly allocated textures */
     70    new_mask = (statt_mask & ~drawable->texture_mask);
     71 
     72    /*
     73     * dPriv->dri2.stamp is the server stamp.  dPriv->lastStamp is the
     74     * client stamp.  It has the value of the server stamp when last
     75     * checked.
     76     */
     77    do {
     78       lastStamp = drawable->dPriv->lastStamp;
     79       new_stamp = (drawable->texture_stamp != lastStamp);
     80 
     81       if (new_stamp || new_mask || screen->broken_invalidate) {
     82          if (new_stamp && drawable->update_drawable_info)
     83             drawable->update_drawable_info(drawable);
     84 
     85          drawable->allocate_textures(ctx, drawable, statts, count);
     86 
     87          /* add existing textures */
     88          for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
     89             if (textures[i])
     90                statt_mask |= (1 << i);
     91          }
     92 
     93          drawable->texture_stamp = lastStamp;
     94          drawable->texture_mask = statt_mask;
     95       }
     96    } while (lastStamp != drawable->dPriv->lastStamp);
     97 
     98    if (!out)
     99       return TRUE;
    100 
    101    /* Set the window-system buffers for the state tracker. */
    102    for (i = 0; i < count; i++)
    103       pipe_resource_reference(&out[i], textures[statts[i]]);
    104 
    105    return TRUE;
    106 }
    107 
    108 static boolean
    109 dri_st_framebuffer_flush_front(struct st_context_iface *stctx,
    110                                struct st_framebuffer_iface *stfbi,
    111                                enum st_attachment_type statt)
    112 {
    113    struct dri_context *ctx = (struct dri_context *)stctx->st_manager_private;
    114    struct dri_drawable *drawable =
    115       (struct dri_drawable *) stfbi->st_manager_private;
    116 
    117    /* XXX remove this and just set the correct one on the framebuffer */
    118    drawable->flush_frontbuffer(ctx, drawable, statt);
    119 
    120    return TRUE;
    121 }
    122 
    123 /**
    124  * The state tracker framebuffer interface flush_swapbuffers callback
    125  */
    126 static boolean
    127 dri_st_framebuffer_flush_swapbuffers(struct st_context_iface *stctx,
    128                                      struct st_framebuffer_iface *stfbi)
    129 {
    130    struct dri_context *ctx = (struct dri_context *)stctx->st_manager_private;
    131    struct dri_drawable *drawable =
    132       (struct dri_drawable *) stfbi->st_manager_private;
    133 
    134    if (drawable->flush_swapbuffers)
    135       drawable->flush_swapbuffers(ctx, drawable);
    136 
    137    return TRUE;
    138 }
    139 
    140 /**
    141  * This is called when we need to set up GL rendering to a new X window.
    142  */
    143 boolean
    144 dri_create_buffer(__DRIscreen * sPriv,
    145 		  __DRIdrawable * dPriv,
    146 		  const struct gl_config * visual, boolean isPixmap)
    147 {
    148    struct dri_screen *screen = sPriv->driverPrivate;
    149    struct dri_drawable *drawable = NULL;
    150 
    151    if (isPixmap)
    152       goto fail;		       /* not implemented */
    153 
    154    drawable = CALLOC_STRUCT(dri_drawable);
    155    if (drawable == NULL)
    156       goto fail;
    157 
    158    dri_fill_st_visual(&drawable->stvis, screen, visual);
    159 
    160    /* setup the st_framebuffer_iface */
    161    drawable->base.visual = &drawable->stvis;
    162    drawable->base.flush_front = dri_st_framebuffer_flush_front;
    163    drawable->base.validate = dri_st_framebuffer_validate;
    164    drawable->base.flush_swapbuffers = dri_st_framebuffer_flush_swapbuffers;
    165    drawable->base.st_manager_private = (void *) drawable;
    166 
    167    drawable->screen = screen;
    168    drawable->sPriv = sPriv;
    169    drawable->dPriv = dPriv;
    170    drawable->desired_fences = screen->default_throttle_frames;
    171    if (drawable->desired_fences > DRI_SWAP_FENCES_MAX)
    172       drawable->desired_fences = DRI_SWAP_FENCES_MAX;
    173 
    174    dPriv->driverPrivate = (void *)drawable;
    175    p_atomic_set(&drawable->base.stamp, 1);
    176    drawable->base.ID = p_atomic_inc_return(&drifb_ID);
    177    drawable->base.state_manager = &screen->base;
    178 
    179    return GL_TRUE;
    180 fail:
    181    FREE(drawable);
    182    return GL_FALSE;
    183 }
    184 
    185 void
    186 dri_destroy_buffer(__DRIdrawable * dPriv)
    187 {
    188    struct dri_drawable *drawable = dri_drawable(dPriv);
    189    struct dri_screen *screen = drawable->screen;
    190    struct st_api *stapi = screen->st_api;
    191    int i;
    192 
    193    pipe_surface_reference(&drawable->drisw_surface, NULL);
    194 
    195    for (i = 0; i < ST_ATTACHMENT_COUNT; i++)
    196       pipe_resource_reference(&drawable->textures[i], NULL);
    197    for (i = 0; i < ST_ATTACHMENT_COUNT; i++)
    198       pipe_resource_reference(&drawable->msaa_textures[i], NULL);
    199 
    200    swap_fences_unref(drawable);
    201 
    202    /* Notify the st manager that this drawable is no longer valid */
    203    stapi->destroy_drawable(stapi, &drawable->base);
    204 
    205    FREE(drawable);
    206 }
    207 
    208 /**
    209  * Validate the texture at an attachment.  Allocate the texture if it does not
    210  * exist.  Used by the TFP extension.
    211  */
    212 static void
    213 dri_drawable_validate_att(struct dri_context *ctx,
    214                           struct dri_drawable *drawable,
    215                           enum st_attachment_type statt)
    216 {
    217    enum st_attachment_type statts[ST_ATTACHMENT_COUNT];
    218    unsigned i, count = 0;
    219 
    220    /* check if buffer already exists */
    221    if (drawable->texture_mask & (1 << statt))
    222       return;
    223 
    224    /* make sure DRI2 does not destroy existing buffers */
    225    for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
    226       if (drawable->texture_mask & (1 << i)) {
    227          statts[count++] = i;
    228       }
    229    }
    230    statts[count++] = statt;
    231 
    232    drawable->texture_stamp = drawable->dPriv->lastStamp - 1;
    233 
    234    drawable->base.validate(ctx->st, &drawable->base, statts, count, NULL);
    235 }
    236 
    237 /**
    238  * These are used for GLX_EXT_texture_from_pixmap
    239  */
    240 static void
    241 dri_set_tex_buffer2(__DRIcontext *pDRICtx, GLint target,
    242                     GLint format, __DRIdrawable *dPriv)
    243 {
    244    struct dri_context *ctx = dri_context(pDRICtx);
    245    struct st_context_iface *st = ctx->st;
    246    struct dri_drawable *drawable = dri_drawable(dPriv);
    247    struct pipe_resource *pt;
    248 
    249    if (st->thread_finish)
    250       st->thread_finish(st);
    251 
    252    dri_drawable_validate_att(ctx, drawable, ST_ATTACHMENT_FRONT_LEFT);
    253 
    254    /* Use the pipe resource associated with the X drawable */
    255    pt = drawable->textures[ST_ATTACHMENT_FRONT_LEFT];
    256 
    257    if (pt) {
    258       enum pipe_format internal_format = pt->format;
    259 
    260       if (format == __DRI_TEXTURE_FORMAT_RGB)  {
    261          /* only need to cover the formats recognized by dri_fill_st_visual */
    262          switch (internal_format) {
    263          case PIPE_FORMAT_B10G10R10A2_UNORM:
    264             internal_format = PIPE_FORMAT_B10G10R10X2_UNORM;
    265             break;
    266          case PIPE_FORMAT_BGRA8888_UNORM:
    267             internal_format = PIPE_FORMAT_BGRX8888_UNORM;
    268             break;
    269          case PIPE_FORMAT_ARGB8888_UNORM:
    270             internal_format = PIPE_FORMAT_XRGB8888_UNORM;
    271             break;
    272          default:
    273             break;
    274          }
    275       }
    276 
    277       drawable->update_tex_buffer(drawable, ctx, pt);
    278 
    279       ctx->st->teximage(ctx->st,
    280             (target == GL_TEXTURE_2D) ? ST_TEXTURE_2D : ST_TEXTURE_RECT,
    281             0, internal_format, pt, FALSE);
    282    }
    283 }
    284 
    285 static void
    286 dri_set_tex_buffer(__DRIcontext *pDRICtx, GLint target,
    287                    __DRIdrawable *dPriv)
    288 {
    289    dri_set_tex_buffer2(pDRICtx, target, __DRI_TEXTURE_FORMAT_RGBA, dPriv);
    290 }
    291 
    292 const __DRItexBufferExtension driTexBufferExtension = {
    293    .base = { __DRI_TEX_BUFFER, 2 },
    294 
    295    .setTexBuffer       = dri_set_tex_buffer,
    296    .setTexBuffer2      = dri_set_tex_buffer2,
    297    .releaseTexBuffer   = NULL,
    298 };
    299 
    300 /**
    301  * Get the format and binding of an attachment.
    302  */
    303 void
    304 dri_drawable_get_format(struct dri_drawable *drawable,
    305                         enum st_attachment_type statt,
    306                         enum pipe_format *format,
    307                         unsigned *bind)
    308 {
    309    switch (statt) {
    310    case ST_ATTACHMENT_FRONT_LEFT:
    311    case ST_ATTACHMENT_BACK_LEFT:
    312    case ST_ATTACHMENT_FRONT_RIGHT:
    313    case ST_ATTACHMENT_BACK_RIGHT:
    314       /* Other pieces of the driver stack get confused and behave incorrectly
    315        * when they get an sRGB drawable. st/mesa receives "drawable->stvis"
    316        * though other means and handles it correctly, so we don't really need
    317        * to use an sRGB format here.
    318        */
    319       *format = util_format_linear(drawable->stvis.color_format);
    320       *bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
    321       break;
    322    case ST_ATTACHMENT_DEPTH_STENCIL:
    323       *format = drawable->stvis.depth_stencil_format;
    324       *bind = PIPE_BIND_DEPTH_STENCIL; /* XXX sampler? */
    325       break;
    326    default:
    327       *format = PIPE_FORMAT_NONE;
    328       *bind = 0;
    329       break;
    330    }
    331 }
    332 
    333 
    334 /**
    335  * swap_fences_pop_front - pull a fence from the throttle queue
    336  *
    337  * If the throttle queue is filled to the desired number of fences,
    338  * pull fences off the queue until the number is less than the desired
    339  * number of fences, and return the last fence pulled.
    340  */
    341 static struct pipe_fence_handle *
    342 swap_fences_pop_front(struct dri_drawable *draw)
    343 {
    344    struct pipe_screen *screen = draw->screen->base.screen;
    345    struct pipe_fence_handle *fence = NULL;
    346 
    347    if (draw->desired_fences == 0)
    348       return NULL;
    349 
    350    if (draw->cur_fences >= draw->desired_fences) {
    351       screen->fence_reference(screen, &fence, draw->swap_fences[draw->tail]);
    352       screen->fence_reference(screen, &draw->swap_fences[draw->tail++], NULL);
    353       draw->tail &= DRI_SWAP_FENCES_MASK;
    354       --draw->cur_fences;
    355    }
    356    return fence;
    357 }
    358 
    359 
    360 /**
    361  * swap_fences_push_back - push a fence onto the throttle queue
    362  *
    363  * push a fence onto the throttle queue and pull fences of the queue
    364  * so that the desired number of fences are on the queue.
    365  */
    366 static void
    367 swap_fences_push_back(struct dri_drawable *draw,
    368 		      struct pipe_fence_handle *fence)
    369 {
    370    struct pipe_screen *screen = draw->screen->base.screen;
    371 
    372    if (!fence || draw->desired_fences == 0)
    373       return;
    374 
    375    while(draw->cur_fences == draw->desired_fences)
    376       swap_fences_pop_front(draw);
    377 
    378    draw->cur_fences++;
    379    screen->fence_reference(screen, &draw->swap_fences[draw->head++],
    380 			   fence);
    381    draw->head &= DRI_SWAP_FENCES_MASK;
    382 }
    383 
    384 
    385 /**
    386  * swap_fences_unref - empty the throttle queue
    387  *
    388  * pulls fences of the throttle queue until it is empty.
    389  */
    390 static void
    391 swap_fences_unref(struct dri_drawable *draw)
    392 {
    393    struct pipe_screen *screen = draw->screen->base.screen;
    394 
    395    while(draw->cur_fences) {
    396       screen->fence_reference(screen, &draw->swap_fences[draw->tail++], NULL);
    397       draw->tail &= DRI_SWAP_FENCES_MASK;
    398       --draw->cur_fences;
    399    }
    400 }
    401 
    402 void
    403 dri_pipe_blit(struct pipe_context *pipe,
    404               struct pipe_resource *dst,
    405               struct pipe_resource *src)
    406 {
    407    struct pipe_blit_info blit;
    408 
    409    if (!dst || !src)
    410       return;
    411 
    412    /* From the GL spec, version 4.2, section 4.1.11 (Additional Multisample
    413     *  Fragment Operations):
    414     *
    415     *      If a framebuffer object is not bound, after all operations have
    416     *      been completed on the multisample buffer, the sample values for
    417     *      each color in the multisample buffer are combined to produce a
    418     *      single color value, and that value is written into the
    419     *      corresponding color buffers selected by DrawBuffer or
    420     *      DrawBuffers. An implementation may defer the writing of the color
    421     *      buffers until a later time, but the state of the framebuffer must
    422     *      behave as if the color buffers were updated as each fragment was
    423     *      processed. The method of combination is not specified. If the
    424     *      framebuffer contains sRGB values, then it is recommended that the
    425     *      an average of sample values is computed in a linearized space, as
    426     *      for blending (see section 4.1.7).
    427     *
    428     * In other words, to do a resolve operation in a linear space, we have
    429     * to set sRGB formats if the original resources were sRGB, so don't use
    430     * util_format_linear.
    431     */
    432 
    433    memset(&blit, 0, sizeof(blit));
    434    blit.dst.resource = dst;
    435    blit.dst.box.width = dst->width0;
    436    blit.dst.box.height = dst->height0;
    437    blit.dst.box.depth = 1;
    438    blit.dst.format = dst->format;
    439    blit.src.resource = src;
    440    blit.src.box.width = src->width0;
    441    blit.src.box.height = src->height0;
    442    blit.src.box.depth = 1;
    443    blit.src.format = src->format;
    444    blit.mask = PIPE_MASK_RGBA;
    445    blit.filter = PIPE_TEX_FILTER_NEAREST;
    446 
    447    pipe->blit(pipe, &blit);
    448 }
    449 
    450 static void
    451 dri_postprocessing(struct dri_context *ctx,
    452                    struct dri_drawable *drawable,
    453                    enum st_attachment_type att)
    454 {
    455    struct pipe_resource *src = drawable->textures[att];
    456    struct pipe_resource *zsbuf = drawable->textures[ST_ATTACHMENT_DEPTH_STENCIL];
    457 
    458    if (ctx->pp && src)
    459       pp_run(ctx->pp, src, src, zsbuf);
    460 }
    461 
    462 /**
    463  * DRI2 flush extension, the flush_with_flags function.
    464  *
    465  * \param context           the context
    466  * \param drawable          the drawable to flush
    467  * \param flags             a combination of _DRI2_FLUSH_xxx flags
    468  * \param throttle_reason   the reason for throttling, 0 = no throttling
    469  */
    470 void
    471 dri_flush(__DRIcontext *cPriv,
    472           __DRIdrawable *dPriv,
    473           unsigned flags,
    474           enum __DRI2throttleReason reason)
    475 {
    476    struct dri_context *ctx = dri_context(cPriv);
    477    struct dri_drawable *drawable = dri_drawable(dPriv);
    478    struct st_context_iface *st;
    479    unsigned flush_flags;
    480    boolean swap_msaa_buffers = FALSE;
    481 
    482    if (!ctx) {
    483       assert(0);
    484       return;
    485    }
    486 
    487    st = ctx->st;
    488    if (st->thread_finish)
    489       st->thread_finish(st);
    490 
    491    if (drawable) {
    492       /* prevent recursion */
    493       if (drawable->flushing)
    494          return;
    495 
    496       drawable->flushing = TRUE;
    497    }
    498    else {
    499       flags &= ~__DRI2_FLUSH_DRAWABLE;
    500    }
    501 
    502    /* Flush the drawable. */
    503    if ((flags & __DRI2_FLUSH_DRAWABLE) &&
    504        drawable->textures[ST_ATTACHMENT_BACK_LEFT]) {
    505       struct pipe_context *pipe = st->pipe;
    506 
    507       if (drawable->stvis.samples > 1 &&
    508           reason == __DRI2_THROTTLE_SWAPBUFFER) {
    509          /* Resolve the MSAA back buffer. */
    510          dri_pipe_blit(st->pipe,
    511                        drawable->textures[ST_ATTACHMENT_BACK_LEFT],
    512                        drawable->msaa_textures[ST_ATTACHMENT_BACK_LEFT]);
    513 
    514          if (drawable->msaa_textures[ST_ATTACHMENT_FRONT_LEFT] &&
    515              drawable->msaa_textures[ST_ATTACHMENT_BACK_LEFT]) {
    516             swap_msaa_buffers = TRUE;
    517          }
    518 
    519          /* FRONT_LEFT is resolved in drawable->flush_frontbuffer. */
    520       }
    521 
    522       dri_postprocessing(ctx, drawable, ST_ATTACHMENT_BACK_LEFT);
    523 
    524       if (ctx->hud) {
    525          hud_run(ctx->hud, ctx->st->cso_context,
    526                  drawable->textures[ST_ATTACHMENT_BACK_LEFT]);
    527       }
    528 
    529       pipe->flush_resource(pipe, drawable->textures[ST_ATTACHMENT_BACK_LEFT]);
    530 
    531       if (pipe->invalidate_resource &&
    532           (flags & __DRI2_FLUSH_INVALIDATE_ANCILLARY)) {
    533          if (drawable->textures[ST_ATTACHMENT_DEPTH_STENCIL])
    534             pipe->invalidate_resource(pipe, drawable->textures[ST_ATTACHMENT_DEPTH_STENCIL]);
    535          if (drawable->msaa_textures[ST_ATTACHMENT_DEPTH_STENCIL])
    536             pipe->invalidate_resource(pipe, drawable->msaa_textures[ST_ATTACHMENT_DEPTH_STENCIL]);
    537       }
    538    }
    539 
    540    flush_flags = 0;
    541    if (flags & __DRI2_FLUSH_CONTEXT)
    542       flush_flags |= ST_FLUSH_FRONT;
    543    if (reason == __DRI2_THROTTLE_SWAPBUFFER)
    544       flush_flags |= ST_FLUSH_END_OF_FRAME;
    545 
    546    /* Flush the context and throttle if needed. */
    547    if (dri_screen(ctx->sPriv)->throttling_enabled &&
    548        drawable &&
    549        (reason == __DRI2_THROTTLE_SWAPBUFFER ||
    550         reason == __DRI2_THROTTLE_FLUSHFRONT)) {
    551       /* Throttle.
    552        *
    553        * This pulls a fence off the throttling queue and waits for it if the
    554        * number of fences on the throttling queue has reached the desired
    555        * number.
    556        *
    557        * Then flushes to insert a fence at the current rendering position, and
    558        * pushes that fence on the queue. This requires that the st_context_iface
    559        * flush method returns a fence even if there are no commands to flush.
    560        */
    561       struct pipe_screen *screen = drawable->screen->base.screen;
    562       struct pipe_fence_handle *fence;
    563 
    564       fence = swap_fences_pop_front(drawable);
    565       if (fence) {
    566          (void) screen->fence_finish(screen, NULL, fence, PIPE_TIMEOUT_INFINITE);
    567          screen->fence_reference(screen, &fence, NULL);
    568       }
    569 
    570       st->flush(st, flush_flags, &fence);
    571 
    572       if (fence) {
    573          swap_fences_push_back(drawable, fence);
    574          screen->fence_reference(screen, &fence, NULL);
    575       }
    576    }
    577    else if (flags & (__DRI2_FLUSH_DRAWABLE | __DRI2_FLUSH_CONTEXT)) {
    578       st->flush(st, flush_flags, NULL);
    579    }
    580 
    581    if (drawable) {
    582       drawable->flushing = FALSE;
    583    }
    584 
    585    /* Swap the MSAA front and back buffers, so that reading
    586     * from the front buffer after SwapBuffers returns what was
    587     * in the back buffer.
    588     */
    589    if (swap_msaa_buffers) {
    590       struct pipe_resource *tmp =
    591          drawable->msaa_textures[ST_ATTACHMENT_FRONT_LEFT];
    592 
    593       drawable->msaa_textures[ST_ATTACHMENT_FRONT_LEFT] =
    594          drawable->msaa_textures[ST_ATTACHMENT_BACK_LEFT];
    595       drawable->msaa_textures[ST_ATTACHMENT_BACK_LEFT] = tmp;
    596 
    597       /* Now that we have swapped the buffers, this tells the state
    598        * tracker to revalidate the framebuffer.
    599        */
    600       p_atomic_inc(&drawable->base.stamp);
    601    }
    602 }
    603 
    604 /**
    605  * dri_throttle - A DRI2ThrottleExtension throttling function.
    606  */
    607 static void
    608 dri_throttle(__DRIcontext *cPriv, __DRIdrawable *dPriv,
    609              enum __DRI2throttleReason reason)
    610 {
    611    dri_flush(cPriv, dPriv, 0, reason);
    612 }
    613 
    614 
    615 const __DRI2throttleExtension dri2ThrottleExtension = {
    616     .base = { __DRI2_THROTTLE, 1 },
    617 
    618     .throttle          = dri_throttle,
    619 };
    620 
    621 
    622 /* vim: set sw=3 ts=8 sts=3 expandtab: */
    623