| /************************************************************************** |
| * |
| * Copyright 2009, VMware, Inc. |
| * All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sub license, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the |
| * next paragraph) shall be included in all copies or substantial portions |
| * of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. |
| * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR |
| * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| * |
| **************************************************************************/ |
| /* |
| * Author: Keith Whitwell <keithw@vmware.com> |
| * Author: Jakob Bornecrantz <wallbraker@gmail.com> |
| */ |
| |
| #include "dri_screen.h" |
| #include "dri_context.h" |
| #include "dri_drawable.h" |
| |
| #include "pipe/p_screen.h" |
| #include "util/u_format.h" |
| #include "util/u_memory.h" |
| #include "util/u_inlines.h" |
| |
| static void |
| swap_fences_unref(struct dri_drawable *draw); |
| |
| static boolean |
| dri_st_framebuffer_validate(struct st_context_iface *stctx, |
| struct st_framebuffer_iface *stfbi, |
| const enum st_attachment_type *statts, |
| unsigned count, |
| struct pipe_resource **out) |
| { |
| struct dri_context *ctx = (struct dri_context *)stctx->st_manager_private; |
| struct dri_drawable *drawable = |
| (struct dri_drawable *) stfbi->st_manager_private; |
| struct dri_screen *screen = dri_screen(drawable->sPriv); |
| unsigned statt_mask, new_mask; |
| boolean new_stamp; |
| int i; |
| unsigned int lastStamp; |
| struct pipe_resource **textures = |
| drawable->stvis.samples > 1 ? drawable->msaa_textures |
| : drawable->textures; |
| |
| statt_mask = 0x0; |
| for (i = 0; i < count; i++) |
| statt_mask |= (1 << statts[i]); |
| |
| /* record newly allocated textures */ |
| new_mask = (statt_mask & ~drawable->texture_mask); |
| |
| /* |
| * dPriv->dri2.stamp is the server stamp. dPriv->lastStamp is the |
| * client stamp. It has the value of the server stamp when last |
| * checked. |
| */ |
| do { |
| lastStamp = drawable->dPriv->lastStamp; |
| new_stamp = (drawable->texture_stamp != lastStamp); |
| |
| if (new_stamp || new_mask || screen->broken_invalidate) { |
| if (new_stamp && drawable->update_drawable_info) |
| drawable->update_drawable_info(drawable); |
| |
| drawable->allocate_textures(ctx, drawable, statts, count); |
| |
| /* add existing textures */ |
| for (i = 0; i < ST_ATTACHMENT_COUNT; i++) { |
| if (textures[i]) |
| statt_mask |= (1 << i); |
| } |
| |
| drawable->texture_stamp = lastStamp; |
| drawable->texture_mask = statt_mask; |
| } |
| } while (lastStamp != drawable->dPriv->lastStamp); |
| |
| if (!out) |
| return TRUE; |
| |
| /* Set the window-system buffers for the state tracker. */ |
| for (i = 0; i < count; i++) { |
| out[i] = NULL; |
| pipe_resource_reference(&out[i], textures[statts[i]]); |
| } |
| |
| return TRUE; |
| } |
| |
| static boolean |
| dri_st_framebuffer_flush_front(struct st_context_iface *stctx, |
| struct st_framebuffer_iface *stfbi, |
| enum st_attachment_type statt) |
| { |
| struct dri_context *ctx = (struct dri_context *)stctx->st_manager_private; |
| struct dri_drawable *drawable = |
| (struct dri_drawable *) stfbi->st_manager_private; |
| |
| /* XXX remove this and just set the correct one on the framebuffer */ |
| drawable->flush_frontbuffer(ctx, drawable, statt); |
| |
| return TRUE; |
| } |
| |
| /** |
| * This is called when we need to set up GL rendering to a new X window. |
| */ |
| boolean |
| dri_create_buffer(__DRIscreen * sPriv, |
| __DRIdrawable * dPriv, |
| const struct gl_config * visual, boolean isPixmap) |
| { |
| struct dri_screen *screen = sPriv->driverPrivate; |
| struct dri_drawable *drawable = NULL; |
| |
| if (isPixmap) |
| goto fail; /* not implemented */ |
| |
| drawable = CALLOC_STRUCT(dri_drawable); |
| if (drawable == NULL) |
| goto fail; |
| |
| dri_fill_st_visual(&drawable->stvis, screen, visual); |
| |
| /* setup the st_framebuffer_iface */ |
| drawable->base.visual = &drawable->stvis; |
| drawable->base.flush_front = dri_st_framebuffer_flush_front; |
| drawable->base.validate = dri_st_framebuffer_validate; |
| drawable->base.st_manager_private = (void *) drawable; |
| |
| drawable->screen = screen; |
| drawable->sPriv = sPriv; |
| drawable->dPriv = dPriv; |
| drawable->desired_fences = screen->default_throttle_frames; |
| if (drawable->desired_fences > DRI_SWAP_FENCES_MAX) |
| drawable->desired_fences = DRI_SWAP_FENCES_MAX; |
| |
| dPriv->driverPrivate = (void *)drawable; |
| p_atomic_set(&drawable->base.stamp, 1); |
| |
| return GL_TRUE; |
| fail: |
| FREE(drawable); |
| return GL_FALSE; |
| } |
| |
| void |
| dri_destroy_buffer(__DRIdrawable * dPriv) |
| { |
| struct dri_drawable *drawable = dri_drawable(dPriv); |
| int i; |
| |
| pipe_surface_reference(&drawable->drisw_surface, NULL); |
| |
| for (i = 0; i < ST_ATTACHMENT_COUNT; i++) |
| pipe_resource_reference(&drawable->textures[i], NULL); |
| for (i = 0; i < ST_ATTACHMENT_COUNT; i++) |
| pipe_resource_reference(&drawable->msaa_textures[i], NULL); |
| |
| swap_fences_unref(drawable); |
| |
| FREE(drawable); |
| } |
| |
| /** |
| * Validate the texture at an attachment. Allocate the texture if it does not |
| * exist. Used by the TFP extension. |
| */ |
| static void |
| dri_drawable_validate_att(struct dri_context *ctx, |
| struct dri_drawable *drawable, |
| enum st_attachment_type statt) |
| { |
| enum st_attachment_type statts[ST_ATTACHMENT_COUNT]; |
| unsigned i, count = 0; |
| |
| /* check if buffer already exists */ |
| if (drawable->texture_mask & (1 << statt)) |
| return; |
| |
| /* make sure DRI2 does not destroy existing buffers */ |
| for (i = 0; i < ST_ATTACHMENT_COUNT; i++) { |
| if (drawable->texture_mask & (1 << i)) { |
| statts[count++] = i; |
| } |
| } |
| statts[count++] = statt; |
| |
| drawable->texture_stamp = drawable->dPriv->lastStamp - 1; |
| |
| drawable->base.validate(ctx->st, &drawable->base, statts, count, NULL); |
| } |
| |
| /** |
| * These are used for GLX_EXT_texture_from_pixmap |
| */ |
| static void |
| dri_set_tex_buffer2(__DRIcontext *pDRICtx, GLint target, |
| GLint format, __DRIdrawable *dPriv) |
| { |
| struct dri_context *ctx = dri_context(pDRICtx); |
| struct dri_drawable *drawable = dri_drawable(dPriv); |
| struct pipe_resource *pt; |
| |
| dri_drawable_validate_att(ctx, drawable, ST_ATTACHMENT_FRONT_LEFT); |
| |
| /* Use the pipe resource associated with the X drawable */ |
| pt = drawable->textures[ST_ATTACHMENT_FRONT_LEFT]; |
| |
| if (pt) { |
| enum pipe_format internal_format = pt->format; |
| |
| if (format == __DRI_TEXTURE_FORMAT_RGB) { |
| /* only need to cover the formats recognized by dri_fill_st_visual */ |
| switch (internal_format) { |
| case PIPE_FORMAT_BGRA8888_UNORM: |
| internal_format = PIPE_FORMAT_BGRX8888_UNORM; |
| break; |
| case PIPE_FORMAT_ARGB8888_UNORM: |
| internal_format = PIPE_FORMAT_XRGB8888_UNORM; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| drawable->update_tex_buffer(drawable, ctx, pt); |
| |
| ctx->st->teximage(ctx->st, |
| (target == GL_TEXTURE_2D) ? ST_TEXTURE_2D : ST_TEXTURE_RECT, |
| 0, internal_format, pt, FALSE); |
| } |
| } |
| |
| static void |
| dri_set_tex_buffer(__DRIcontext *pDRICtx, GLint target, |
| __DRIdrawable *dPriv) |
| { |
| dri_set_tex_buffer2(pDRICtx, target, __DRI_TEXTURE_FORMAT_RGBA, dPriv); |
| } |
| |
| const __DRItexBufferExtension driTexBufferExtension = { |
| .base = { __DRI_TEX_BUFFER, 2 }, |
| |
| .setTexBuffer = dri_set_tex_buffer, |
| .setTexBuffer2 = dri_set_tex_buffer2, |
| .releaseTexBuffer = NULL, |
| }; |
| |
| /** |
| * Get the format and binding of an attachment. |
| */ |
| void |
| dri_drawable_get_format(struct dri_drawable *drawable, |
| enum st_attachment_type statt, |
| enum pipe_format *format, |
| unsigned *bind) |
| { |
| switch (statt) { |
| case ST_ATTACHMENT_FRONT_LEFT: |
| case ST_ATTACHMENT_BACK_LEFT: |
| case ST_ATTACHMENT_FRONT_RIGHT: |
| case ST_ATTACHMENT_BACK_RIGHT: |
| /* Other pieces of the driver stack get confused and behave incorrectly |
| * when they get an sRGB drawable. st/mesa receives "drawable->stvis" |
| * though other means and handles it correctly, so we don't really need |
| * to use an sRGB format here. |
| */ |
| *format = util_format_linear(drawable->stvis.color_format); |
| *bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW; |
| break; |
| case ST_ATTACHMENT_DEPTH_STENCIL: |
| *format = drawable->stvis.depth_stencil_format; |
| *bind = PIPE_BIND_DEPTH_STENCIL; /* XXX sampler? */ |
| break; |
| default: |
| *format = PIPE_FORMAT_NONE; |
| *bind = 0; |
| break; |
| } |
| } |
| |
| |
| /** |
| * swap_fences_pop_front - pull a fence from the throttle queue |
| * |
| * If the throttle queue is filled to the desired number of fences, |
| * pull fences off the queue until the number is less than the desired |
| * number of fences, and return the last fence pulled. |
| */ |
| static struct pipe_fence_handle * |
| swap_fences_pop_front(struct dri_drawable *draw) |
| { |
| struct pipe_screen *screen = draw->screen->base.screen; |
| struct pipe_fence_handle *fence = NULL; |
| |
| if (draw->desired_fences == 0) |
| return NULL; |
| |
| if (draw->cur_fences >= draw->desired_fences) { |
| screen->fence_reference(screen, &fence, draw->swap_fences[draw->tail]); |
| screen->fence_reference(screen, &draw->swap_fences[draw->tail++], NULL); |
| draw->tail &= DRI_SWAP_FENCES_MASK; |
| --draw->cur_fences; |
| } |
| return fence; |
| } |
| |
| |
| /** |
| * swap_fences_push_back - push a fence onto the throttle queue |
| * |
| * push a fence onto the throttle queue and pull fences of the queue |
| * so that the desired number of fences are on the queue. |
| */ |
| static void |
| swap_fences_push_back(struct dri_drawable *draw, |
| struct pipe_fence_handle *fence) |
| { |
| struct pipe_screen *screen = draw->screen->base.screen; |
| |
| if (!fence || draw->desired_fences == 0) |
| return; |
| |
| while(draw->cur_fences == draw->desired_fences) |
| swap_fences_pop_front(draw); |
| |
| draw->cur_fences++; |
| screen->fence_reference(screen, &draw->swap_fences[draw->head++], |
| fence); |
| draw->head &= DRI_SWAP_FENCES_MASK; |
| } |
| |
| |
| /** |
| * swap_fences_unref - empty the throttle queue |
| * |
| * pulls fences of the throttle queue until it is empty. |
| */ |
| static void |
| swap_fences_unref(struct dri_drawable *draw) |
| { |
| struct pipe_screen *screen = draw->screen->base.screen; |
| |
| while(draw->cur_fences) { |
| screen->fence_reference(screen, &draw->swap_fences[draw->tail++], NULL); |
| draw->tail &= DRI_SWAP_FENCES_MASK; |
| --draw->cur_fences; |
| } |
| } |
| |
| void |
| dri_pipe_blit(struct pipe_context *pipe, |
| struct pipe_resource *dst, |
| struct pipe_resource *src) |
| { |
| struct pipe_blit_info blit; |
| |
| if (!dst || !src) |
| return; |
| |
| /* From the GL spec, version 4.2, section 4.1.11 (Additional Multisample |
| * Fragment Operations): |
| * |
| * If a framebuffer object is not bound, after all operations have |
| * been completed on the multisample buffer, the sample values for |
| * each color in the multisample buffer are combined to produce a |
| * single color value, and that value is written into the |
| * corresponding color buffers selected by DrawBuffer or |
| * DrawBuffers. An implementation may defer the writing of the color |
| * buffers until a later time, but the state of the framebuffer must |
| * behave as if the color buffers were updated as each fragment was |
| * processed. The method of combination is not specified. If the |
| * framebuffer contains sRGB values, then it is recommended that the |
| * an average of sample values is computed in a linearized space, as |
| * for blending (see section 4.1.7). |
| * |
| * In other words, to do a resolve operation in a linear space, we have |
| * to set sRGB formats if the original resources were sRGB, so don't use |
| * util_format_linear. |
| */ |
| |
| memset(&blit, 0, sizeof(blit)); |
| blit.dst.resource = dst; |
| blit.dst.box.width = dst->width0; |
| blit.dst.box.height = dst->height0; |
| blit.dst.box.depth = 1; |
| blit.dst.format = dst->format; |
| blit.src.resource = src; |
| blit.src.box.width = src->width0; |
| blit.src.box.height = src->height0; |
| blit.src.box.depth = 1; |
| blit.src.format = src->format; |
| blit.mask = PIPE_MASK_RGBA; |
| blit.filter = PIPE_TEX_FILTER_NEAREST; |
| |
| pipe->blit(pipe, &blit); |
| } |
| |
| static void |
| dri_postprocessing(struct dri_context *ctx, |
| struct dri_drawable *drawable, |
| enum st_attachment_type att) |
| { |
| struct pipe_resource *src = drawable->textures[att]; |
| struct pipe_resource *zsbuf = drawable->textures[ST_ATTACHMENT_DEPTH_STENCIL]; |
| |
| if (ctx->pp && src) |
| pp_run(ctx->pp, src, src, zsbuf); |
| } |
| |
| /** |
| * DRI2 flush extension, the flush_with_flags function. |
| * |
| * \param context the context |
| * \param drawable the drawable to flush |
| * \param flags a combination of _DRI2_FLUSH_xxx flags |
| * \param throttle_reason the reason for throttling, 0 = no throttling |
| */ |
| void |
| dri_flush(__DRIcontext *cPriv, |
| __DRIdrawable *dPriv, |
| unsigned flags, |
| enum __DRI2throttleReason reason) |
| { |
| struct dri_context *ctx = dri_context(cPriv); |
| struct dri_drawable *drawable = dri_drawable(dPriv); |
| unsigned flush_flags; |
| boolean swap_msaa_buffers = FALSE; |
| |
| if (!ctx) { |
| assert(0); |
| return; |
| } |
| |
| if (drawable) { |
| /* prevent recursion */ |
| if (drawable->flushing) |
| return; |
| |
| drawable->flushing = TRUE; |
| } |
| else { |
| flags &= ~__DRI2_FLUSH_DRAWABLE; |
| } |
| |
| /* Flush the drawable. */ |
| if ((flags & __DRI2_FLUSH_DRAWABLE) && |
| drawable->textures[ST_ATTACHMENT_BACK_LEFT]) { |
| struct pipe_context *pipe = ctx->st->pipe; |
| |
| if (drawable->stvis.samples > 1 && |
| reason == __DRI2_THROTTLE_SWAPBUFFER) { |
| /* Resolve the MSAA back buffer. */ |
| dri_pipe_blit(ctx->st->pipe, |
| drawable->textures[ST_ATTACHMENT_BACK_LEFT], |
| drawable->msaa_textures[ST_ATTACHMENT_BACK_LEFT]); |
| |
| if (drawable->msaa_textures[ST_ATTACHMENT_FRONT_LEFT] && |
| drawable->msaa_textures[ST_ATTACHMENT_BACK_LEFT]) { |
| swap_msaa_buffers = TRUE; |
| } |
| |
| /* FRONT_LEFT is resolved in drawable->flush_frontbuffer. */ |
| } |
| |
| dri_postprocessing(ctx, drawable, ST_ATTACHMENT_BACK_LEFT); |
| |
| if (ctx->hud) { |
| hud_draw(ctx->hud, drawable->textures[ST_ATTACHMENT_BACK_LEFT]); |
| } |
| |
| pipe->flush_resource(pipe, drawable->textures[ST_ATTACHMENT_BACK_LEFT]); |
| |
| if (pipe->invalidate_resource && |
| (flags & __DRI2_FLUSH_INVALIDATE_ANCILLARY)) { |
| if (drawable->textures[ST_ATTACHMENT_DEPTH_STENCIL]) |
| pipe->invalidate_resource(pipe, drawable->textures[ST_ATTACHMENT_DEPTH_STENCIL]); |
| if (drawable->msaa_textures[ST_ATTACHMENT_DEPTH_STENCIL]) |
| pipe->invalidate_resource(pipe, drawable->msaa_textures[ST_ATTACHMENT_DEPTH_STENCIL]); |
| } |
| } |
| |
| flush_flags = 0; |
| if (flags & __DRI2_FLUSH_CONTEXT) |
| flush_flags |= ST_FLUSH_FRONT; |
| if (reason == __DRI2_THROTTLE_SWAPBUFFER) |
| flush_flags |= ST_FLUSH_END_OF_FRAME; |
| |
| /* Flush the context and throttle if needed. */ |
| if (dri_screen(ctx->sPriv)->throttling_enabled && |
| drawable && |
| (reason == __DRI2_THROTTLE_SWAPBUFFER || |
| reason == __DRI2_THROTTLE_FLUSHFRONT)) { |
| /* Throttle. |
| * |
| * This pulls a fence off the throttling queue and waits for it if the |
| * number of fences on the throttling queue has reached the desired |
| * number. |
| * |
| * Then flushes to insert a fence at the current rendering position, and |
| * pushes that fence on the queue. This requires that the st_context_iface |
| * flush method returns a fence even if there are no commands to flush. |
| */ |
| struct pipe_screen *screen = drawable->screen->base.screen; |
| struct pipe_fence_handle *fence; |
| |
| fence = swap_fences_pop_front(drawable); |
| if (fence) { |
| (void) screen->fence_finish(screen, NULL, fence, PIPE_TIMEOUT_INFINITE); |
| screen->fence_reference(screen, &fence, NULL); |
| } |
| |
| ctx->st->flush(ctx->st, flush_flags, &fence); |
| |
| if (fence) { |
| swap_fences_push_back(drawable, fence); |
| screen->fence_reference(screen, &fence, NULL); |
| } |
| } |
| else if (flags & (__DRI2_FLUSH_DRAWABLE | __DRI2_FLUSH_CONTEXT)) { |
| ctx->st->flush(ctx->st, flush_flags, NULL); |
| } |
| |
| if (drawable) { |
| drawable->flushing = FALSE; |
| } |
| |
| /* Swap the MSAA front and back buffers, so that reading |
| * from the front buffer after SwapBuffers returns what was |
| * in the back buffer. |
| */ |
| if (swap_msaa_buffers) { |
| struct pipe_resource *tmp = |
| drawable->msaa_textures[ST_ATTACHMENT_FRONT_LEFT]; |
| |
| drawable->msaa_textures[ST_ATTACHMENT_FRONT_LEFT] = |
| drawable->msaa_textures[ST_ATTACHMENT_BACK_LEFT]; |
| drawable->msaa_textures[ST_ATTACHMENT_BACK_LEFT] = tmp; |
| |
| /* Now that we have swapped the buffers, this tells the state |
| * tracker to revalidate the framebuffer. |
| */ |
| p_atomic_inc(&drawable->base.stamp); |
| } |
| } |
| |
| /** |
| * dri_throttle - A DRI2ThrottleExtension throttling function. |
| */ |
| static void |
| dri_throttle(__DRIcontext *cPriv, __DRIdrawable *dPriv, |
| enum __DRI2throttleReason reason) |
| { |
| dri_flush(cPriv, dPriv, 0, reason); |
| } |
| |
| |
| const __DRI2throttleExtension dri2ThrottleExtension = { |
| .base = { __DRI2_THROTTLE, 1 }, |
| |
| .throttle = dri_throttle, |
| }; |
| |
| |
| /* vim: set sw=3 ts=8 sts=3 expandtab: */ |