/* * Copyright 2011 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 TUNGSTEN GRAPHICS 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: Thomas Hellstrom */ #include #include #include #include #include #include "vmwgfx_saa.h" #include "vmwgfx_drmi.h" #include "vmwgfx_saa_priv.h" /* * Damage to be added as soon as we attach storage to the pixmap. */ static Bool vmwgfx_pixmap_add_damage(PixmapPtr pixmap) { struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); DrawablePtr draw = &pixmap->drawable; BoxRec box; if (spix->damage) return TRUE; if (!saa_add_damage(pixmap)) return FALSE; box.x1 = 0; box.x2 = draw->width; box.y1 = 0; box.y2 = draw->height; if (vpix->hw) { REGION_RESET(draw->pScreen, &spix->dirty_hw, &box); REGION_EMPTY(draw->pScreen, &spix->dirty_shadow); } else { REGION_RESET(draw->pScreen, &spix->dirty_shadow, &box); REGION_EMPTY(draw->pScreen, &spix->dirty_hw); } return TRUE; } static void vmwgfx_pixmap_remove_damage(PixmapPtr pixmap) { struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); if (!spix->damage || vpix->hw || vpix->gmr || vpix->malloc) return; DamageUnregister(&pixmap->drawable, spix->damage); DamageDestroy(spix->damage); spix->damage = NULL; } static void vmwgfx_pixmap_remove_present(struct vmwgfx_saa_pixmap *vpix) { if (vpix->dirty_present) REGION_DESTROY(pixmap->drawable.pScreen, vpix->dirty_present); if (vpix->present_damage) REGION_DESTROY(pixmap->drawable.pScreen, vpix->present_damage); if (vpix->pending_update) REGION_DESTROY(pixmap->drawable.pScreen, vpix->pending_update); if (vpix->pending_present) REGION_DESTROY(pixmap->drawable.pScreen, vpix->pending_present); vpix->dirty_present = NULL; vpix->present_damage = NULL; vpix->pending_update = NULL; vpix->pending_present = NULL; } static Bool vmwgfx_pixmap_add_present(PixmapPtr pixmap, Bool present_opt) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); ScreenPtr pScreen = pixmap->drawable.pScreen; (void) pScreen; if (present_opt) { vpix->dirty_present = REGION_CREATE(pScreen, NULL, 0); if (!vpix->dirty_present) return FALSE; vpix->present_damage = REGION_CREATE(pScreen, NULL, 0); if (!vpix->present_damage) goto out_no_present_damage; } vpix->pending_update = REGION_CREATE(pScreen, NULL, 0); if (!vpix->pending_update) goto out_no_pending_update; vpix->pending_present = REGION_CREATE(pScreen, NULL, 0); if (!vpix->pending_present) goto out_no_pending_present; return TRUE; out_no_pending_present: REGION_DESTROY(pScreen, vpix->pending_update); out_no_pending_update: if (vpix->present_damage) REGION_DESTROY(pScreen, vpix->present_damage); out_no_present_damage: if (vpix->dirty_present) REGION_DESTROY(pScreen, vpix->dirty_present); return FALSE; } static void vmwgfx_pixmap_free_storage(struct vmwgfx_saa_pixmap *vpix) { if (!(vpix->backing & VMWGFX_PIX_MALLOC) && vpix->malloc) { free(vpix->malloc); vpix->malloc = NULL; } if (!(vpix->backing & VMWGFX_PIX_SURFACE) && vpix->hw) { xa_surface_destroy(vpix->hw); vpix->hw = NULL; } if (!(vpix->backing & VMWGFX_PIX_GMR) && vpix->gmr) { vmwgfx_dmabuf_destroy(vpix->gmr); vpix->gmr = NULL; } } static Bool vmwgfx_pixmap_create_gmr(struct vmwgfx_saa *vsaa, PixmapPtr pixmap) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); size_t size; struct vmwgfx_dmabuf *gmr; void *addr; if (vpix->gmr) return TRUE; size = pixmap->devKind * pixmap->drawable.height; gmr = vmwgfx_dmabuf_alloc(vsaa->drm_fd, size); if (!gmr) return FALSE; if (vpix->malloc) { addr = vmwgfx_dmabuf_map(gmr); if (!addr) goto out_no_transfer; memcpy(addr, vpix->malloc, size); vmwgfx_dmabuf_unmap(gmr); } else if (!vmwgfx_pixmap_add_damage(pixmap)) goto out_no_transfer; vpix->backing |= VMWGFX_PIX_GMR; vpix->backing &= ~VMWGFX_PIX_MALLOC; vpix->gmr = gmr; vmwgfx_pixmap_free_storage(vpix); return TRUE; out_no_transfer: vmwgfx_dmabuf_destroy(gmr); return FALSE; } static Bool vmwgfx_pixmap_create_sw(struct vmwgfx_saa *vsaa, PixmapPtr pixmap) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); if (!(vpix->backing & (VMWGFX_PIX_MALLOC | VMWGFX_PIX_GMR))) return FALSE; if (!vpix->malloc && (vpix->backing & VMWGFX_PIX_MALLOC)) { vpix->malloc = malloc(pixmap->devKind * pixmap->drawable.height); if (!vpix->malloc) goto out_no_malloc; if (!vmwgfx_pixmap_add_damage(pixmap)) goto out_no_damage; } else if (vpix->backing & VMWGFX_PIX_GMR) return vmwgfx_pixmap_create_gmr(vsaa, pixmap); return TRUE; out_no_damage: free(vpix->malloc); vpix->malloc = NULL; out_no_malloc: return FALSE; } /** * * Makes sure all presented contents covered by @region are read * back and are present in a valid GMR. */ static Bool vmwgfx_pixmap_present_readback(struct vmwgfx_saa *vsaa, PixmapPtr pixmap, RegionPtr region) { struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); RegionRec intersection; if (!spix->damage || !REGION_NOTEMPTY(vsaa->pScreen, &spix->dirty_hw) || !vpix->dirty_present) return TRUE; /* * Intersect dirty region with region to be read back, if any. */ REGION_NULL(vsaa->pScreen, &intersection); REGION_COPY(vsaa->pScreen, &intersection, &spix->dirty_hw); REGION_INTERSECT(vsaa->pScreen, &intersection, &intersection, vpix->dirty_present); if (region) REGION_INTERSECT(vsaa->pScreen, &intersection, &intersection, region); if (!REGION_NOTEMPTY(vsaa->pScreen, &intersection)) goto out; /* * Make really sure there is a GMR to read back to. */ if (!vmwgfx_pixmap_create_gmr(vsaa, pixmap)) goto out_err; if (vmwgfx_present_readback(vsaa->drm_fd, vpix->fb_id, &intersection) != 0) goto out_err; REGION_SUBTRACT(vsaa->pScreen, &spix->dirty_hw, &spix->dirty_hw, &intersection); out: REGION_UNINIT(vsaa->pScreen, &intersection); return TRUE; out_err: REGION_UNINIT(vsaa->pScreen, &intersection); return FALSE; } static Bool vmwgfx_saa_dma(struct vmwgfx_saa *vsaa, PixmapPtr pixmap, RegionPtr reg, Bool to_hw) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); if (!vpix->hw || (!vpix->gmr && !vpix->malloc)) return TRUE; if (vpix->gmr && vsaa->can_optimize_dma) { uint32_t handle, dummy; if (xa_surface_handle(vpix->hw, &handle, &dummy) != 0) goto out_err; if (vmwgfx_dma(0, 0, reg, vpix->gmr, pixmap->devKind, handle, to_hw) != 0) goto out_err; } else { void *data = vpix->malloc; int ret; if (vpix->gmr) { data = vmwgfx_dmabuf_map(vpix->gmr); if (!data) goto out_err; } ret = xa_surface_dma(vsaa->xa_ctx, vpix->hw, data, pixmap->devKind, (int) to_hw, (struct xa_box *) REGION_RECTS(reg), REGION_NUM_RECTS(reg)); if (vpix->gmr) vmwgfx_dmabuf_unmap(vpix->gmr); if (ret) goto out_err; } return TRUE; out_err: LogMessage(X_ERROR, "DMA %s surface failed.\n", to_hw ? "to" : "from"); return FALSE; } static Bool vmwgfx_download_from_hw(struct saa_driver *driver, PixmapPtr pixmap, RegionPtr readback) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); RegionRec intersection; if (!vmwgfx_pixmap_present_readback(vsaa, pixmap, readback)) return FALSE; if (!REGION_NOTEMPTY(vsaa->pScreen, &spix->dirty_hw)) return TRUE; if (!vpix->hw) return TRUE; REGION_NULL(vsaa->pScreen, &intersection); REGION_INTERSECT(vsaa->pScreen, &intersection, readback, &spix->dirty_hw); readback = &intersection; if (!vmwgfx_pixmap_create_sw(vsaa, pixmap)) goto out_err; if (!vmwgfx_saa_dma(vsaa, pixmap, readback, FALSE)) goto out_err; REGION_SUBTRACT(vsaa->pScreen, &spix->dirty_hw, &spix->dirty_hw, readback); REGION_UNINIT(vsaa->pScreen, &intersection); return TRUE; out_err: REGION_UNINIT(vsaa->pScreen, &intersection); return FALSE; } static Bool vmwgfx_upload_to_hw(struct saa_driver *driver, PixmapPtr pixmap, RegionPtr upload) { return vmwgfx_saa_dma(to_vmwgfx_saa(driver), pixmap, upload, TRUE); } static void vmwgfx_release_from_cpu(struct saa_driver *driver, PixmapPtr pixmap, saa_access_t access) { // LogMessage(X_INFO, "Release 0x%08lx access 0x%08x\n", // (unsigned long) pixmap, (unsigned) access); } static void * vmwgfx_sync_for_cpu(struct saa_driver *driver, PixmapPtr pixmap, saa_access_t access) { /* * Errors in this functions will turn up in subsequent map * calls. */ (void) vmwgfx_pixmap_create_sw(to_vmwgfx_saa(driver), pixmap); return NULL; } static void * vmwgfx_map(struct saa_driver *driver, PixmapPtr pixmap, saa_access_t access) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); if (vpix->malloc) return vpix->malloc; else if (vpix->gmr) return vmwgfx_dmabuf_map(vpix->gmr); else return NULL; } static void vmwgfx_unmap(struct saa_driver *driver, PixmapPtr pixmap, saa_access_t access) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); if (vpix->gmr) return vmwgfx_dmabuf_unmap(vpix->gmr); // LogMessage(X_INFO, "Unmap 0x%08lx access 0x%08x\n", // (unsigned long) pixmap, (unsigned) access); ; } static Bool vmwgfx_create_pixmap(struct saa_driver *driver, struct saa_pixmap *spix, int w, int h, int depth, unsigned int usage_hint, int bpp, int *new_pitch) { struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); *new_pitch = ((w * bpp + FB_MASK) >> FB_SHIFT) * sizeof(FbBits); WSBMINITLISTHEAD(&vpix->sync_x_head); WSBMINITLISTHEAD(&vpix->scanout_list); return TRUE; } Bool vmwgfx_hw_kill(struct vmwgfx_saa *vsaa, struct saa_pixmap *spix) { struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); if (!vpix->hw) return TRUE; /* * Read back any dirty regions from hardware. */ if (!vmwgfx_download_from_hw(&vsaa->driver, spix->pixmap, &spix->dirty_hw)) return FALSE; xa_surface_destroy(vpix->hw); vpix->hw = NULL; /* * Remove damage tracking if this is not a scanout pixmap. */ if (WSBMLISTEMPTY(&vpix->scanout_list)) vmwgfx_pixmap_remove_damage(spix->pixmap); return TRUE; } void vmwgfx_flush_dri2(ScreenPtr pScreen) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(pScreen)); struct _WsbmListHead *list, *next; ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); if (!pScrn->vtSema) return; WSBMLISTFOREACHSAFE(list, next, &vsaa->sync_x_list) { struct vmwgfx_saa_pixmap *vpix = WSBMLISTENTRY(list, struct vmwgfx_saa_pixmap, sync_x_head); struct saa_pixmap *spix = &vpix->base; PixmapPtr pixmap = spix->pixmap; if (vmwgfx_upload_to_hw(&vsaa->driver, pixmap, &spix->dirty_shadow)) { REGION_EMPTY(vsaa->pScreen, &spix->dirty_shadow); WSBMLISTDELINIT(list); } } } static void vmwgfx_destroy_pixmap(struct saa_driver *driver, PixmapPtr pixmap) { ScreenPtr pScreen = to_vmwgfx_saa(driver)->pScreen; struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); (void) pScreen; vpix->backing = 0; vmwgfx_pixmap_free_storage(vpix); /* * Any damage we've registered has already been removed by the server * at this point. Any attempt to unregister / destroy it will result * in a double free. */ vmwgfx_pixmap_remove_present(vpix); WSBMLISTDELINIT(&vpix->sync_x_head); if (vpix->hw_is_dri2_fronts) LogMessage(X_ERROR, "Incorrect dri2 front count.\n"); } /** * * Makes sure we have a surface with valid contents. */ static void vmwgfx_copy_stride(uint8_t *dst, uint8_t *src, unsigned int dst_pitch, unsigned int src_pitch, unsigned int dst_height, unsigned int src_height) { unsigned int i; unsigned int height = (dst_height < src_height) ? dst_height : src_height; unsigned int pitch = (dst_pitch < src_pitch) ? dst_pitch : src_pitch; for(i=0; idrawable.pScreen; struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(pScreen)); struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); DrawablePtr draw = &pixmap->drawable; unsigned int size = pixmap->devKind * draw->height; BoxRec b_box; RegionRec b_reg; /* * Ignore copying errors. At worst they will show up as rendering * artefacts. */ if (vpix->malloc) { void *new_malloc = malloc(size); if (!new_malloc) return FALSE; vmwgfx_copy_stride(new_malloc, vpix->malloc, pixmap->devKind, old_pitch, draw->height, old_height); free(vpix->malloc); vpix->malloc = new_malloc; } if (vpix->gmr) { struct vmwgfx_dmabuf *gmr; void *new_addr; void *old_addr; gmr = vmwgfx_dmabuf_alloc(vsaa->drm_fd, size); if (!gmr) return FALSE; new_addr = vmwgfx_dmabuf_map(gmr); old_addr = vmwgfx_dmabuf_map(vpix->gmr); if (new_addr && old_addr) vmwgfx_copy_stride(new_addr, old_addr, pixmap->devKind, old_pitch, draw->height, old_height); else LogMessage(X_ERROR, "Failed pixmap resize copy.\n"); if (old_addr) vmwgfx_dmabuf_unmap(vpix->gmr); if (new_addr) vmwgfx_dmabuf_unmap(gmr); vmwgfx_dmabuf_destroy(vpix->gmr); vpix->gmr = gmr; } if (vpix->hw) { if (xa_surface_redefine(vpix->hw, draw->width, draw->height, draw->depth, xa_type_argb, xa_format_unknown, vpix->xa_flags, 1) != 0) return FALSE; } b_box.x1 = 0; b_box.x2 = draw->width; b_box.y1 = 0; b_box.y2 = draw->height; REGION_INIT(pScreen, &b_reg, &b_box, 1); REGION_INTERSECT(pScreen, &spix->dirty_shadow, &spix->dirty_shadow, &b_reg); REGION_INTERSECT(pScreen, &spix->dirty_hw, &spix->dirty_hw, &b_reg); if (vpix->dirty_present) REGION_INTERSECT(pScreen, vpix->dirty_present, vpix->dirty_present, &b_reg); if (vpix->pending_update) REGION_INTERSECT(pScreen, vpix->pending_update, vpix->pending_update, &b_reg); if (vpix->pending_present) REGION_INTERSECT(pScreen, vpix->pending_present, vpix->pending_present, &b_reg); if (vpix->present_damage) REGION_INTERSECT(pScreen, vpix->present_damage, vpix->present_damage, &b_reg); REGION_UNINIT(pScreen, &b_reg); return TRUE; } static Bool vmwgfx_modify_pixmap_header (PixmapPtr pixmap, int w, int h, int depth, int bpp, int devkind, void *pixdata) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); unsigned int old_height; unsigned int old_width; unsigned int old_pitch; if (!vpix) { LogMessage(X_ERROR, "Not an SAA pixmap.\n"); return FALSE; } if (pixdata) { vpix->backing = 0; vmwgfx_pixmap_free_storage(vpix); return FALSE; } if (depth <= 0) depth = pixmap->drawable.depth; if (bpp <= 0) bpp = pixmap->drawable.bitsPerPixel; if (w <= 0) w = pixmap->drawable.width; if (h <= 0) h = pixmap->drawable.height; if (w <= 0 || h <= 0 || depth <= 0) return FALSE; old_height = pixmap->drawable.height; old_width = pixmap->drawable.width; old_pitch = pixmap->devKind; if (!miModifyPixmapHeader(pixmap, w, h, depth, bpp, devkind, NULL)) goto out_no_modify; if (!vpix->backing) vpix->backing = VMWGFX_PIX_MALLOC; vmwgfx_pix_resize(pixmap, old_pitch, old_height, old_width); vmwgfx_pixmap_free_storage(vpix); return TRUE; out_no_modify: return FALSE; } static Bool vmwgfx_present_prepare(struct vmwgfx_saa *vsaa, struct vmwgfx_saa_pixmap *src_vpix, struct vmwgfx_saa_pixmap *dst_vpix) { ScreenPtr pScreen = vsaa->pScreen; unsigned int dummy; (void) pScreen; if (src_vpix == dst_vpix || !src_vpix->hw || xa_surface_handle(src_vpix->hw, &vsaa->src_handle, &dummy) != 0) return FALSE; REGION_NULL(pScreen, &vsaa->present_region); vsaa->diff_valid = FALSE; vsaa->dst_vpix = dst_vpix; vsaa->present_flush(pScreen); return TRUE; } /** * Determine whether we should try present copies on this pixmap. */ static Bool vmwgfx_is_present_hw(PixmapPtr pixmap) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); return (vpix->dirty_present != NULL); } static void vmwgfx_check_hw_contents(struct vmwgfx_saa *vsaa, struct vmwgfx_saa_pixmap *vpix, RegionPtr region, Bool *has_dirty_hw, Bool *has_valid_hw) { RegionRec intersection; if (!vpix->hw) { *has_dirty_hw = FALSE; *has_valid_hw = FALSE; return; } if (!region) { *has_dirty_hw = REGION_NOTEMPTY(vsaa->pScreen, &vpix->base.dirty_hw); *has_valid_hw = !REGION_NOTEMPTY(vsaa->pScreen, &vpix->base.dirty_shadow); return; } REGION_NULL(vsaa->pScreen, &intersection); REGION_INTERSECT(vsaa->pScreen, &intersection, &vpix->base.dirty_hw, region); *has_dirty_hw = REGION_NOTEMPTY(vsaa->pScreen, &intersection); REGION_INTERSECT(vsaa->pScreen, &intersection, &vpix->base.dirty_shadow, region); *has_valid_hw = !REGION_NOTEMPTY(vsaa->pScreen, &intersection); REGION_UNINIT(vsaa->pScreen, &intersection); } Bool vmwgfx_create_hw(struct vmwgfx_saa *vsaa, PixmapPtr pixmap) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); struct xa_surface *hw; uint32_t new_flags; if (!vsaa->xat) return FALSE; if (vpix->hw) return TRUE; new_flags = (vpix->xa_flags & ~vpix->staging_remove_flags) | vpix->staging_add_flags; hw = xa_surface_create(vsaa->xat, pixmap->drawable.width, pixmap->drawable.height, 0, xa_type_other, vpix->staging_format, new_flags); if (hw == NULL) return FALSE; vpix->xa_flags = new_flags; if (!vmwgfx_pixmap_add_damage(pixmap)) goto out_no_damage; /* * Even if we don't have a GMR yet, indicate that when needed it * should be created. */ vpix->hw = hw; vpix->backing |= VMWGFX_PIX_SURFACE; vmwgfx_pixmap_free_storage(vpix); return TRUE; out_no_damage: xa_surface_destroy(hw); return FALSE; } Bool vmwgfx_hw_validate(PixmapPtr pixmap, RegionPtr region) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen)); struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); RegionRec intersection; if (!vmwgfx_pixmap_present_readback(vsaa, pixmap, region)) return FALSE; REGION_NULL(vsaa->pScreen, &intersection); REGION_COPY(vsaa->pScreen, &intersection, &spix->dirty_shadow); if (vpix->dirty_present) REGION_UNION(vsaa->pScreen, &intersection, vpix->dirty_present, &spix->dirty_shadow); if (spix->damage && REGION_NOTEMPTY(vsaa->pScreen, &intersection)) { RegionPtr upload = &intersection; /* * Check whether we need to upload from GMR. */ if (region) { REGION_INTERSECT(vsaa->pScreen, &intersection, region, &intersection); upload = &intersection; } if (REGION_NOTEMPTY(vsaa->pScreen, upload)) { Bool ret = vmwgfx_upload_to_hw(&vsaa->driver, pixmap, upload); if (ret) { REGION_SUBTRACT(vsaa->pScreen, &spix->dirty_shadow, &spix->dirty_shadow, upload); if (vpix->dirty_present) REGION_SUBTRACT(vsaa->pScreen, vpix->dirty_present, vpix->dirty_present, upload); } else { REGION_UNINIT(vsaa->pScreen, &intersection); return FALSE; } } } REGION_UNINIT(vsaa->pScreen, &intersection); return TRUE; } static Bool vmwgfx_copy_prepare(struct saa_driver *driver, PixmapPtr src_pixmap, PixmapPtr dst_pixmap, int dx, int dy, int alu, RegionPtr src_reg, uint32_t plane_mask) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); struct vmwgfx_saa_pixmap *src_vpix; struct vmwgfx_saa_pixmap *dst_vpix; Bool has_dirty_hw; Bool has_valid_hw; if (!vsaa->xat || !SAA_PM_IS_SOLID(&dst_pixmap->drawable, plane_mask) || alu != GXcopy) return FALSE; src_vpix = vmwgfx_saa_pixmap(src_pixmap); dst_vpix = vmwgfx_saa_pixmap(dst_pixmap); vmwgfx_check_hw_contents(vsaa, src_vpix, src_reg, &has_dirty_hw, &has_valid_hw); if (vmwgfx_is_present_hw(dst_pixmap) && src_vpix->backing & VMWGFX_PIX_SURFACE) { if (!has_dirty_hw && !has_valid_hw) return FALSE; if (!vmwgfx_hw_accel_validate(src_pixmap, 0, 0, 0, src_reg)) return FALSE; if (vmwgfx_present_prepare(vsaa, src_vpix, dst_vpix)) { vsaa->present_copy = TRUE; return TRUE; } return FALSE; } vsaa->present_copy = FALSE; if (src_vpix != dst_vpix) { /* * Use hardware acceleration either if source is partially only * in hardware, or if source is entirely in hardware and destination * has a hardware surface. */ if (!has_dirty_hw && !(has_valid_hw && (dst_vpix->hw != NULL))) return FALSE; /* * Determine surface formats. */ if (src_vpix->base.src_format == 0) { if (!vmwgfx_hw_accel_stage(src_pixmap, 0, XA_FLAG_RENDER_TARGET, 0)) return FALSE; } else { if (PICT_FORMAT_TYPE(src_vpix->base.src_format) != PICT_TYPE_ARGB || !vmwgfx_hw_composite_src_stage(src_pixmap, src_vpix->base.src_format)) return FALSE; } if (dst_vpix->base.dst_format == 0) { if (!vmwgfx_hw_accel_stage(dst_pixmap, 0, XA_FLAG_RENDER_TARGET, 0)) return FALSE; } else { if (PICT_FORMAT_TYPE(dst_vpix->base.dst_format) != PICT_TYPE_ARGB || !vmwgfx_hw_composite_dst_stage(dst_pixmap, dst_vpix->base.dst_format)) return FALSE; } /* * Create hardware surfaces. */ if (!vmwgfx_hw_commit(src_pixmap)) return FALSE; if (!vmwgfx_hw_commit(dst_pixmap)) return FALSE; /* * Migrate data. */ if (!vmwgfx_hw_validate(src_pixmap, src_reg)) { xa_copy_done(vsaa->xa_ctx); return FALSE; } /* * Setup copy state. */ if (xa_copy_prepare(vsaa->xa_ctx, dst_vpix->hw, src_vpix->hw) != XA_ERR_NONE) return FALSE; return TRUE; } return FALSE; } static void vmwgfx_present_done(struct vmwgfx_saa *vsaa) { ScreenPtr pScreen = vsaa->pScreen; struct vmwgfx_saa_pixmap *dst_vpix = vsaa->dst_vpix; (void) pScreen; if (!vsaa->diff_valid) return; (void) vmwgfx_present(vsaa->drm_fd, dst_vpix->fb_id, vsaa->xdiff, vsaa->ydiff, &vsaa->present_region, vsaa->src_handle); REGION_TRANSLATE(pScreen, &vsaa->present_region, vsaa->xdiff, vsaa->ydiff); REGION_UNION(pScreen, dst_vpix->present_damage, dst_vpix->present_damage, &vsaa->present_region); vsaa->diff_valid = FALSE; REGION_UNINIT(pScreen, &vsaa->present_region); } static void vmwgfx_present_copy(struct vmwgfx_saa *vsaa, int src_x, int src_y, int dst_x, int dst_y, int w, int h) { int xdiff = dst_x - src_x; int ydiff = dst_y - src_y; BoxRec box; RegionRec reg; if (vsaa->diff_valid && ((xdiff != vsaa->xdiff) || (ydiff != vsaa->ydiff))) (void) vmwgfx_present_done(vsaa); if (!vsaa->diff_valid) { vsaa->xdiff = xdiff; vsaa->ydiff = ydiff; vsaa->diff_valid = TRUE; } box.x1 = src_x; box.x2 = src_x + w; box.y1 = src_y; box.y2 = src_y + h; REGION_INIT(pScreen, ®, &box, 1); REGION_UNION(pScreen, &vsaa->present_region, &vsaa->present_region, ®); REGION_UNINIT(pScreen, ®); } static void vmwgfx_copy(struct saa_driver *driver, int src_x, int src_y, int dst_x, int dst_y, int w, int h) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); if (vsaa->present_copy) { vmwgfx_present_copy(vsaa, src_x, src_y, dst_x, dst_y, w, h); return; } xa_copy(vsaa->xa_ctx, dst_x, dst_y, src_x, src_y, w, h); } static void vmwgfx_copy_done(struct saa_driver *driver) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); if (vsaa->present_copy) { vmwgfx_present_done(vsaa); return; } xa_copy_done(vsaa->xa_ctx); } static Bool vmwgfx_composite_prepare(struct saa_driver *driver, CARD8 op, PicturePtr src_pict, PicturePtr mask_pict, PicturePtr dst_pict, PixmapPtr src_pix, PixmapPtr mask_pix, PixmapPtr dst_pix, RegionPtr src_region, RegionPtr mask_region, RegionPtr dst_region) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); struct vmwgfx_saa_pixmap *src_vpix; struct vmwgfx_saa_pixmap *dst_vpix; struct vmwgfx_saa_pixmap *mask_vpix; Bool tmp_valid_hw; Bool dirty_hw; Bool valid_hw; RegionRec empty; struct xa_composite *xa_comp; REGION_NULL(pScreen, &empty); /* * First we define our migration policy. We accelerate only if there * are dirty hw regions to be read or if all source data is * available in hw, and the destination has a hardware surface. */ dst_vpix = vmwgfx_saa_pixmap(dst_pix); valid_hw = (dst_vpix->hw != NULL); if (saa_op_reads_destination(op)) { vmwgfx_check_hw_contents(vsaa, dst_vpix, dst_region, &dirty_hw, &tmp_valid_hw); valid_hw = (valid_hw && tmp_valid_hw); } else { dirty_hw = FALSE; dst_region = ∅ } if (src_pix && !dirty_hw) { src_vpix = vmwgfx_saa_pixmap(src_pix); vmwgfx_check_hw_contents(vsaa, src_vpix, src_region, &dirty_hw, &tmp_valid_hw); valid_hw = (valid_hw && tmp_valid_hw); } if (mask_pict && mask_pix && !dirty_hw) { mask_vpix = vmwgfx_saa_pixmap(mask_pix); vmwgfx_check_hw_contents(vsaa, mask_vpix, mask_region, &dirty_hw, &tmp_valid_hw); valid_hw = (valid_hw && tmp_valid_hw); } /* * In rendercheck mode we try to accelerate all supported * composite operations. */ if (!valid_hw && !dirty_hw && !vsaa->rendercheck) goto out_err; /* * Then, setup most of the XA composite state (except hardware surfaces) * and check whether XA can accelerate. */ xa_comp = vmwgfx_xa_setup_comp(vsaa->vcomp, op, src_pict, mask_pict, dst_pict); if (!xa_comp) goto out_err; if (xa_composite_check_accelerated(xa_comp) != XA_ERR_NONE) goto out_err; /* * Check that we can create the needed hardware surfaces. */ if (src_pix && !vmwgfx_hw_composite_src_stage(src_pix, src_pict->format)) goto out_err; if (mask_pict && mask_pix && !vmwgfx_hw_composite_src_stage(mask_pix, mask_pict->format)) goto out_err; if (!vmwgfx_hw_composite_dst_stage(dst_pix, dst_pict->format)) goto out_err; /* * Seems OK. Commit the changes, creating hardware surfaces. */ if (src_pix && !vmwgfx_hw_commit(src_pix)) goto out_err; if (mask_pict && mask_pix && !vmwgfx_hw_commit(mask_pix)) goto out_err; if (!vmwgfx_hw_commit(dst_pix)) goto out_err; /* * Update the XA state with our hardware surfaces and * surface formats */ if (!vmwgfx_xa_update_comp(xa_comp, src_pix, mask_pix, dst_pix)) goto out_err; /* * Migrate data to surfaces. */ if (src_pix && src_region && !vmwgfx_hw_validate(src_pix, NULL)) goto out_err; if (mask_pict && mask_pix && mask_region && !vmwgfx_hw_validate(mask_pix, NULL)) goto out_err; if (dst_region && !vmwgfx_hw_validate(dst_pix, NULL)) goto out_err; /* * Bind the XA state. This must be done after data migration, since * migration may change the hardware surfaces. */ if (xa_composite_prepare(vsaa->xa_ctx, xa_comp)) goto out_err; return TRUE; out_err: return FALSE; } static void vmwgfx_composite(struct saa_driver *driver, int src_x, int src_y, int mask_x, int mask_y, int dst_x, int dst_y, int width, int height) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); xa_composite_rect(vsaa->xa_ctx, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); } static void vmwgfx_composite_done(struct saa_driver *driver) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); xa_composite_done(vsaa->xa_ctx); } static void vmwgfx_takedown(struct saa_driver *driver) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); if (vsaa->vcomp) vmwgfx_free_composite(vsaa->vcomp); free(vsaa); } /* * This function call originates from the damage layer (outside SAA) * to indicate that an operation is complete, and that damage is being * processed. */ static void vmwgfx_operation_complete(struct saa_driver *driver, PixmapPtr pixmap) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); ScrnInfoPtr pScrn = xf86ScreenToScrn(vsaa->pScreen); /* * Make dri2 drawables up to date, or add them to the flush list * executed at glxWaitX(). Currently glxWaitX() is broken, so * we flush immediately, unless we're VT-switched away, in which * case a flush would deadlock in the kernel. */ if (vpix->hw && vpix->hw_is_dri2_fronts) { if (1 && pScrn->vtSema && vmwgfx_upload_to_hw(driver, pixmap, &spix->dirty_shadow)) { REGION_EMPTY(vsaa->pScreen, &spix->dirty_shadow); return; } if (WSBMLISTEMPTY(&vpix->sync_x_head)) WSBMLISTADDTAIL(&vpix->sync_x_head, &vsaa->sync_x_list); } } /* * This function is called by SAA to indicate that SAA has * dirtied a region of a pixmap, either as hw (accelerated) or as * !hw (not accelerated). */ static Bool vmwgfx_dirty(struct saa_driver *driver, PixmapPtr pixmap, Bool hw, RegionPtr damage) { struct vmwgfx_saa *vsaa = to_vmwgfx_saa(driver); struct saa_pixmap *spix = saa_get_saa_pixmap(pixmap); struct vmwgfx_saa_pixmap *vpix = to_vmwgfx_saa_pixmap(spix); /* * Return if this is not a scanout pixmap. */ if (WSBMLISTEMPTY(&vpix->scanout_list)) return TRUE; #if 0 /* * This code can be enabled to immediately upload scanout sw * contents to the hw surface. Otherwise this is done * just before we call the kms update function for the hw * surface. */ if (vsaa->only_hw_presents) { if (!hw && !vmwgfx_upload_to_hw(&vsaa->driver, pixmap, damage)) return FALSE; REGION_SUBTRACT(&vsaa->pScreen, &spix->dirty_shadow, &spix->dirty_shadow, damage); hw = TRUE; } #endif /* * Is the new scanout damage hw or sw? */ if (hw) { /* * Dump pending present into present tracking region. */ if (vpix->dirty_present && REGION_NOTEMPTY(vsaa->pScreen, vpix->present_damage)) { REGION_UNION(vsaa->pScreen, vpix->dirty_present, vpix->dirty_present, damage); REGION_EMPTY(vsaa->pScreen, vpix->present_damage); } else { if (REGION_NOTEMPTY(vsaa->pScreen, vpix->pending_update)) { RegionRec reg; REGION_NULL(vsaa->pScreen, ®); REGION_INTERSECT(vsaa->pScreen, ®, vpix->pending_update, damage); if (REGION_NOTEMPTY(vsaa->pScreen, ®)) vsaa->present_flush(vsaa->pScreen); REGION_UNINIT(pScreen, ®); } REGION_UNION(vsaa->pScreen, vpix->pending_present, vpix->pending_present, damage); if (vpix->dirty_present) REGION_SUBTRACT(vsaa->pScreen, vpix->dirty_present, vpix->dirty_present, damage); } } else { if (REGION_NOTEMPTY(vsaa->pScreen, vpix->pending_present)) { RegionRec reg; REGION_NULL(vsaa->pScreen, ®); REGION_INTERSECT(vsaa->pScreen, ®, vpix->pending_present, damage); if (REGION_NOTEMPTY(vsaa->pScreen, ®)) vsaa->present_flush(vsaa->pScreen); REGION_UNINIT(pScreen, ®); } REGION_UNION(vsaa->pScreen, vpix->pending_update, vpix->pending_update, damage); if (vpix->dirty_present) REGION_SUBTRACT(vsaa->pScreen, vpix->dirty_present, vpix->dirty_present, damage); } return TRUE; } static const struct saa_driver vmwgfx_saa_driver = { .saa_major = SAA_VERSION_MAJOR, .saa_minor = SAA_VERSION_MINOR, .pixmap_size = sizeof(struct vmwgfx_saa_pixmap), .damage = vmwgfx_dirty, .operation_complete = vmwgfx_operation_complete, .download_from_hw = vmwgfx_download_from_hw, .release_from_cpu = vmwgfx_release_from_cpu, .sync_for_cpu = vmwgfx_sync_for_cpu, .map = vmwgfx_map, .unmap = vmwgfx_unmap, .create_pixmap = vmwgfx_create_pixmap, .destroy_pixmap = vmwgfx_destroy_pixmap, .modify_pixmap_header = vmwgfx_modify_pixmap_header, .copy_prepare = vmwgfx_copy_prepare, .copy = vmwgfx_copy, .copy_done = vmwgfx_copy_done, .composite_prepare = vmwgfx_composite_prepare, .composite = vmwgfx_composite, .composite_done = vmwgfx_composite_done, .takedown = vmwgfx_takedown, }; Bool vmwgfx_saa_init(ScreenPtr pScreen, int drm_fd, struct xa_tracker *xat, void (*present_flush)(ScreenPtr pScreen), Bool direct_presents, Bool only_hw_presents, Bool rendercheck) { struct vmwgfx_saa *vsaa; vsaa = calloc(1, sizeof(*vsaa)); if (!vsaa) return FALSE; if (xat == NULL) { direct_presents = FALSE; only_hw_presents = FALSE; } vsaa->pScreen = pScreen; vsaa->xat = xat; if (xat) vsaa->xa_ctx = xa_context_default(xat); vsaa->drm_fd = drm_fd; vsaa->present_flush = present_flush; vsaa->can_optimize_dma = FALSE; vsaa->use_present_opt = direct_presents; vsaa->only_hw_presents = only_hw_presents; vsaa->rendercheck = rendercheck; WSBMINITLISTHEAD(&vsaa->sync_x_list); vsaa->driver = vmwgfx_saa_driver; vsaa->vcomp = vmwgfx_alloc_composite(); if (!vsaa->vcomp) vsaa->driver.composite_prepare = NULL; if (!saa_driver_init(pScreen, &vsaa->driver)) goto out_no_saa; return TRUE; out_no_saa: free(vsaa); return FALSE; } /* * ************************************************************************* * Scanout functions. * These do not strictly belong here, but we choose to hide the scanout * pixmap private data in the saa pixmaps. Might want to revisit this. */ /* * Make sure we flush / update this scanout on next update run. */ void vmwgfx_scanout_refresh(PixmapPtr pixmap) { ScreenPtr pScreen = pixmap->drawable.pScreen; struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); BoxRec box; (void) pScreen; box.x1 = 0; box.y1 = 0; box.x2 = pixmap->drawable.width; box.y2 = pixmap->drawable.height; REGION_RESET(vsaa->pScreen, vpix->pending_present, &box); if (vpix->dirty_present) REGION_SUBTRACT(vsaa->pScreen, vpix->pending_present, vpix->pending_present, vpix->dirty_present); REGION_SUBTRACT(vsaa->pScreen, vpix->pending_present, vpix->pending_present, &vpix->base.dirty_shadow); REGION_COPY(vsaa->pScreen, vpix->pending_update, &vpix->base.dirty_shadow); } /* * Take a "scanout reference" on a pixmap. If this is the first scanout * reference, allocate resources needed for scanout, like proper * damage tracking and kms fbs. */ uint32_t vmwgfx_scanout_ref(struct vmwgfx_screen_entry *entry) { PixmapPtr pixmap = entry->pixmap; struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen)); struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); if (WSBMLISTEMPTY(&vpix->scanout_list)) { uint32_t handle, dummy; unsigned int depth; if (vsaa->only_hw_presents) { /* * The KMS fb will be a HW surface. Create it, add damage * and get the handle. */ if (!vmwgfx_hw_accel_validate(pixmap, 0, XA_FLAG_SCANOUT, 0, NULL)) goto out_err; if (xa_surface_handle(vpix->hw, &handle, &dummy) != 0) goto out_err; depth = xa_format_depth(xa_surface_format(vpix->hw)); } else { /* * The KMS fb will be a Guest Memory Region. Create it, * add damage and get the handle. */ if (!vmwgfx_pixmap_create_gmr(vsaa, pixmap)) goto out_err; handle = vpix->gmr->handle; depth = pixmap->drawable.depth; } if (!vmwgfx_pixmap_add_present(pixmap, vsaa->use_present_opt)) goto out_no_present; if (drmModeAddFB(vsaa->drm_fd, pixmap->drawable.width, pixmap->drawable.height, depth, pixmap->drawable.bitsPerPixel, pixmap->devKind, handle, &vpix->fb_id) != 0) goto out_no_fb;; } pixmap->refcnt += 1; WSBMLISTADDTAIL(&entry->scanout_head, &vpix->scanout_list); return vpix->fb_id; out_no_fb: vmwgfx_pixmap_remove_present(vpix); out_no_present: vmwgfx_pixmap_remove_damage(pixmap); out_err: vpix->fb_id = -1; return -1; } /* * Free a "scanout reference" on a pixmap. If this was the last scanout * reference, free pixmap resources needed for scanout, like * damage tracking and kms fbs. */ void vmwgfx_scanout_unref(struct vmwgfx_screen_entry *entry) { struct vmwgfx_saa *vsaa; struct vmwgfx_saa_pixmap *vpix; PixmapPtr pixmap = entry->pixmap; if (!pixmap) return; vsaa = to_vmwgfx_saa(saa_get_driver(pixmap->drawable.pScreen)); vpix = vmwgfx_saa_pixmap(pixmap); WSBMLISTDELINIT(&entry->scanout_head); if (WSBMLISTEMPTY(&vpix->scanout_list)) { REGION_EMPTY(vsaa->pScreen, vpix->pending_update); drmModeRmFB(vsaa->drm_fd, vpix->fb_id); vpix->fb_id = -1; vmwgfx_pixmap_present_readback(vsaa, pixmap, NULL); vmwgfx_pixmap_remove_present(vpix); vmwgfx_pixmap_remove_damage(pixmap); } entry->pixmap = NULL; pixmap->drawable.pScreen->DestroyPixmap(pixmap); }