diff options
Diffstat (limited to 'src/vivante_accel.c')
-rw-r--r-- | src/vivante_accel.c | 1619 |
1 files changed, 1619 insertions, 0 deletions
diff --git a/src/vivante_accel.c b/src/vivante_accel.c new file mode 100644 index 0000000..f9144bb --- /dev/null +++ b/src/vivante_accel.c @@ -0,0 +1,1619 @@ +/* + * Vivante GPU Acceleration Xorg driver + * + * Written by Russell King, 2012, derived in part from the + * Intel xorg X server driver. + * + * Notes: + * * For a window, the drawable inside the window structure has an + * x and y position for the underlying pixmap. + * * Composite clips have the drawable position already included. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> + +#ifdef HAVE_DIX_CONFIG_H +#include "dix-config.h" +#endif +#include "fb.h" +#include "gcstruct.h" +#include "xf86.h" + +#include "vivante_accel.h" +#include "vivante_unaccel.h" +#include "vivante_utils.h" + +#include <gc_hal_raster.h> +#include <gc_hal_enum.h> +#include <gc_hal.h> + +static CARD32 get_first_pixel(DrawablePtr pDraw) +{ + union { CARD32 c32; CARD16 c16; CARD8 c8; char c; } pixel; + + pDraw->pScreen->GetImage(pDraw, 0, 0, 1, 1, ZPixmap, ~0, &pixel.c); + + switch (pDraw->bitsPerPixel) { + case 32: + return pixel.c32; + case 16: + return pixel.c16; + case 8: + case 4: + case 1: + return pixel.c8; + default: + assert(0); + } +} + +static void vivante_disable_alpha_blend(struct vivante *vivante) +{ +#ifdef RENDER + /* If alpha blending was enabled, disable it now */ + if (vivante->alpha_blend_enabled) { + vivante->alpha_blend_enabled = FALSE; + gco2D_DisableAlphaBlend(vivante->e2d); + } +#endif +} + + + +struct vivante_batch { + struct list node; + struct list head; + uint32_t index; + int32_t serial; + int32_t *current; +}; + +static void vivante_batch_destroy(struct vivante_batch *batch) +{ + struct vivante_pixmap *vp, *vn; + + /* Unlink all pixmaps that this batch is connected to */ + list_for_each_entry_safe(vp, vn, &batch->head, batch_node) { + vp->batch = NULL; + list_del(&vp->batch_node); + } + + list_del(&batch->node); + free(batch); +} + +static void vivante_batch_reap(struct vivante *vivante) +{ + struct vivante_batch *batch, *n; + + list_for_each_entry_safe(batch, n, &vivante->batch_list, node) { + if (*batch->current == batch->serial) { +#ifdef DEBUG_BATCH + dbg("batch %p: reaping at %08x\n", + batch, *batch->current); +#endif + vivante_batch_destroy(batch); + } + } +} + +static void __vivante_batch_wait(struct vivante_batch *batch) +{ +#ifdef DEBUG_BATCH + dbg("batch %p: waiting: %08x %08x\n", + batch, *batch->current, batch->serial); +#endif + while (*batch->current != batch->serial) + usleep(5); + vivante_batch_destroy(batch); +} + +/* + * If the pixmap is part of a batch which is not the current batch, + * wait for its batch to indicate operations are complete on it. + */ +static void +vivante_batch_wait(struct vivante *vivante, struct vivante_pixmap *vPix) +{ + struct vivante_batch *batch = vPix->batch; + + if (batch && batch != vivante->batch) + __vivante_batch_wait(batch); +} + +/* + * Issue and wait for all outstanding GPU activity for this pixmap to + * complete. Essentially, that means if this pixmap is attached to a + * batch, it is busy, and if the batch is the current batch, we need + * to commit the current batch of operations. + */ +void +vivante_batch_wait_commit(struct vivante *vivante, struct vivante_pixmap *vPix) +{ + struct vivante_batch *batch = vPix->batch; + + if (batch) { + if (batch == vivante->batch) + vivante_commit(vivante, TRUE); + __vivante_batch_wait(batch); + } +} + +static Bool vivante_batch_new(struct vivante *vivante) +{ + struct vivante_batch *batch; + int32_t serial; + + vivante_batch_reap(vivante); + + serial = vivante->batch_serial + 1; + if (serial <= 0) + serial = 1; + vivante->batch_serial = serial; + + batch = malloc(sizeof *batch); + if (batch) { + uint16_t i = vivante->batch_idx; + + batch->index = i; + batch->serial = serial; + batch->current = vivante->batch_ptr + i; + *batch->current = -1; + list_init(&batch->head); + + i += 1; + if (i >= vivante->batch_idx_max) + i = 0; + + vivante->batch_idx = i; + } + + vivante->batch = batch; + + return batch ? TRUE : FALSE; +} + +/* Add the pixmap to the current batch, if not already added */ +static void +vivante_batch_add(struct vivante *vivante, struct vivante_pixmap *vPix) +{ + struct vivante_batch *batch = vPix->batch; + + if (!batch) { + vPix->batch = batch = vivante->batch; + list_add(&vPix->batch_node, &batch->head); +#ifdef DEBUG_BATCH + dbg("Allocated batch %p for vPix %p\n", batch, vPix); +#endif + vivante->need_commit = TRUE; + } + assert(vPix->batch == vivante->batch); +} + +/* Add the batch to the GPU operations right at the very end of the GPU ops */ +static void vivante_batch_commit(struct vivante *vivante) +{ + struct vivante_batch *batch = vivante->batch; + uint32_t col = batch->serial; + uint32_t handle = vivante->batch_handle; + gceSTATUS err; + gcsRECT rect; + +#define BATCH_PITCH 64 +#define BATCH_WIDTH (BATCH_PITCH / sizeof(uint32_t)) + + rect.left = batch->index & (BATCH_WIDTH - 1); + rect.top = batch->index / BATCH_WIDTH; + rect.right = rect.left + 1; + rect.bottom = rect.top + 1; + +#ifdef DEBUG_BATCH + dbg("batch %p: current %08x next %08x handle %08x index %04x rect [%u,%u,%u,%u]\n", + batch, *batch->current, col, handle, batch->index, + rect.left, rect.top, rect.right, rect.bottom); +#endif + + vivante_disable_alpha_blend(vivante); + + err = gco2D_LoadSolidBrush(vivante->e2d, gcvSURF_A8R8G8B8, 0, col, ~0ULL); + if (err != gcvSTATUS_OK) + goto error; + + err = gco2D_SetClipping(vivante->e2d, &rect); + if (err != gcvSTATUS_OK) + goto error; + + err = gco2D_SetTarget(vivante->e2d, handle, BATCH_PITCH, + gcvSURF_0_DEGREE, 0); + if (err != gcvSTATUS_OK) + goto error; + + err = gco2D_Blit(vivante->e2d, 1, &rect, 0xf0, 0xf0, gcvSURF_A8R8G8B8); + if (err != gcvSTATUS_OK) + goto error; + + list_append(&batch->node, &vivante->batch_list); + vivante->batch = NULL; + return; + + error: + vivante_error(vivante, "batch blit", err); +} + + +enum gpuid { + GPU2D_Source, + GPU2D_Target, +}; + +static Bool +gal_prepare_gpu(struct vivante *vivante, struct vivante_pixmap *vPix, + enum gpuid id) +{ + gceSTATUS err; + +#ifdef DEBUG_CHECK_DRAWABLE_USE + if (vPix->in_use) { + fprintf(stderr, "Trying to accelerate: %p %p %u\n", + vPix, vPix->bo, vPix->in_use); + return FALSE; + } +#endif + + /* + * If we don't have a batch already in place, then add one now. + * This gives us a chance to error out and fallback to CPU based + * blit if this allocation fails. + */ + if (!vivante->batch && !vivante_batch_new(vivante)) { + xf86DrvMsg(vivante->scrnIndex, X_ERROR, + "[vivante] %s failed\n", "batch allocation"); + return FALSE; + } + + vivante_batch_wait(vivante, vPix); + + if (!vivante_map_gpu(vivante, vPix)) + return FALSE; + + switch (id) { + case GPU2D_Target: + err = gco2D_SetTarget(vivante->e2d, vPix->handle, vPix->pitch, + gcvSURF_0_DEGREE, 0); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_SetTarget", err); + return FALSE; + } + break; + + case GPU2D_Source: +#if 0 + err = gco2D_SetColorSourceAdvanced(vivante->e2d, vSrc->handle, + vSrc->pitch, vSrc->format, gcvSURF_0_DEGREE, + vSrc->width, vSrc->height, gcvFALSE); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_SetColourSourceAdvanced", err); + return FALSE; + } +#endif + break; + } + return TRUE; +} + +void vivante_commit(struct vivante *vivante, Bool stall) +{ + gceSTATUS err; + + if (vivante->batch) + vivante_batch_commit(vivante); + + err = gco2D_Flush(vivante->e2d); + if (err != gcvSTATUS_OK) + vivante_error(vivante, "Flush", err); + + err = gcoHAL_Commit(vivante->hal, stall ? gcvTRUE : gcvFALSE); + if (err != gcvSTATUS_OK) + vivante_error(vivante, "Commit", err); + + vivante->need_commit = FALSE; +} + + + + + +/* + * All operations must respect clips and planemask + * Colors: fgcolor and bgcolor are indexes into the colormap + * PolyLine, PolySegment, PolyRect, PolyArc: + * line width (pixels, 0=1pix), line style, cap style, join style + * FillPolygon, PolyFillRect, PolyFillArc: + * fill rule, fill style + * fill style: + * a solid foreground color, a transparent stipple,a n opaque stipple, + * or a tile. + * Stipples are bitmaps where the 1 bits represent that the foreground + * color is written, and 0 bits represent that either the pixel is left + * alone (transparent) or that the background color is written (opaque). + * A tile is a pixmap of the full depth of the GC that is applied in its + * full glory to all areas. + * + * The stipple and tile patterns can be any rectangular size, although + * some implementations will be faster for certain sizes such as 8x8 + * or 32x32. + * + * + * 0 = Black, 1 = !src & !dst, 2 = !src & dst, 3 = !src + * 4 = src & !dst, 5 = !dst, 6 = src ^ dst, 7 = !src | !dst + * 8 = src & dst, 9 = !src ^ dst, a = dst, b = !src | dst + * c = src, d = src | !dst, e = src | dst, f = White + * + * high nibble: brush color bit is 1 + * low nibble: brush color bit is 0 + * + * fgrop: used when mask bit is 1 + * bgrop: used when mask bit is 0 + * mask (in brush): is an 8x8 mask: LSB is top line, LS bit righthand-most + */ +static const gctUINT8 vivante_fill_rop[] = { + /* GXclear */ 0x00, // ROP_BLACK, + /* GXand */ 0xa0, // ROP_BRUSH_AND_DST, + /* GXandReverse */ 0x50, // ROP_BRUSH_AND_NOT_DST, + /* GXcopy */ 0xf0, // ROP_BRUSH, + /* GXandInverted */ 0x0a, // ROP_NOT_BRUSH_AND_DST, + /* GXnoop */ 0xaa, // ROP_DST, + /* GXxor */ 0x5a, // ROP_BRUSH_XOR_DST, + /* GXor */ 0xfa, // ROP_BRUSH_OR_DST, + /* GXnor */ 0x05, // ROP_NOT_BRUSH_AND_NOT_DST, + /* GXequiv */ 0xa5, // ROP_NOT_BRUSH_XOR_DST, + /* GXinvert */ 0x55, // ROP_NOT_DST, + /* GXorReverse */ 0xf5, // ROP_BRUSH_OR_NOT_DST, + /* GXcopyInverted */ 0x0f, // ROP_NOT_BRUSH, + /* GXorInverted */ 0xaf, // ROP_NOT_BRUSH_OR_DST, + /* GXnand */ 0x5f, // ROP_NOT_BRUSH_OR_NOT_DST, + /* GXset */ 0xff // ROP_WHITE +}; + +/* + * Generic solid-like blit fill - takes a set of boxes, and fills them + * according to the clips in the GC. + */ +static Bool vivante_fill(struct vivante *vivante, struct vivante_pixmap *vPix, + GCPtr pGC, RegionPtr region) +{ + const BoxRec *pBox, *b; + unsigned nBox, chunk; + gceSTATUS err; + gctUINT32 fg; + gctUINT8 rop; + gcsRECT *rects, *r, clip; + + if (RegionBroken(region)) { + xf86DrvMsg(vivante->scrnIndex, X_ERROR, + "[vivante] %s: broken region\n", __FUNCTION__); + return FALSE; + } + + nBox = RegionNumRects(region); + pBox = RegionRects(region); + + chunk = vivante->max_rect_count; + if (nBox < chunk) + chunk = nBox; + + rects = malloc(chunk * sizeof *rects); + if (!rects) { + xf86DrvMsg(vivante->scrnIndex, X_ERROR, + "[vivante] %s: %s failed\n", __FUNCTION__, "malloc rects"); + return FALSE; + } + + if (!gal_prepare_gpu(vivante, vPix, GPU2D_Target)) { + free(rects); + return FALSE; + } + + vivante_disable_alpha_blend(vivante); + + RectBox(&clip, RegionExtents(region), 0, 0); + err = gco2D_SetClipping(vivante->e2d, &clip); + if (err) { + vivante_error(vivante, "gco2D_SetClipping", err); + free(rects); + return FALSE; + } + + if (pGC->fillStyle == FillTiled) { + if (pGC->tileIsPixel) + fg = pGC->tile.pixel; + else + fg = get_first_pixel(&pGC->tile.pixmap->drawable); + } else { + fg = pGC->fgPixel; + } + err = gco2D_LoadSolidBrush(vivante->e2d, vPix->format, 0, fg, ~0ULL); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_LoadSolidBrush", err); + free(rects); + return FALSE; + } + + rop = vivante_fill_rop[pGC->alu]; + b = pBox; + while (nBox) { + unsigned i; + + if (nBox < chunk) + chunk = nBox; + + for (i = 0, r = rects; i < chunk; i++, r++, b++) + RectBox(r, b, 0, 0); + + err = gco2D_Blit(vivante->e2d, chunk, rects, rop, rop, vPix->format); + if (err) + break; + + nBox -= chunk; + } + free(rects); + + if (err != gcvSTATUS_OK) + vivante_error(vivante, "Blit", err); +// gcoHAL_Commit(vivante->hal, FALSE); + + vivante_batch_add(vivante, vPix); + + return TRUE; +} + + +static const gctUINT8 vivante_copy_rop[] = { + /* GXclear */ 0x00, // ROP_BLACK, + /* GXand */ 0x88, // ROP_DST_AND_SRC, + /* GXandReverse */ 0x44, // ROP_SRC_AND_NOT_DST, + /* GXcopy */ 0xcc, // ROP_SRC, + /* GXandInverted */ 0x22, // ROP_NOT_SRC_AND_DST, + /* GXnoop */ 0xaa, // ROP_DST, + /* GXxor */ 0x66, // ROP_DST_XOR_SRC, + /* GXor */ 0xee, // ROP_DST_OR_SRC, + /* GXnor */ 0x11, // ROP_NOT_SRC_AND_NOT_DST, + /* GXequiv */ 0x99, // ROP_NOT_SRC_XOR_DST, + /* GXinvert */ 0x55, // ROP_NOT_DST, + /* GXorReverse */ 0xdd, // ROP_SRC_OR_NOT_DST, + /* GXcopyInverted */ 0x33, // ROP_NOT_SRC, + /* GXorInverted */ 0xbb, // ROP_NOT_SRC_OR_DST, + /* GXnand */ 0x77, // ROP_NOT_SRC_OR_NOT_DST, + /* GXset */ 0xff // ROP_WHITE +}; + +static gceSTATUS +vivante_blit_copy(struct vivante *vivante, GCPtr pGC, const BoxRec *total, + const BoxRec *pbox, int nbox, + int src_off_x, int src_off_y, int dst_off_x, int dst_off_y, + gceSURF_FORMAT format) +{ + gctUINT8 rop = vivante_copy_rop[pGC ? pGC->alu : GXcopy]; + gceSTATUS err = gcvSTATUS_OK; + + for (; nbox; nbox--, pbox++) { + BoxRec clipped; + gcsRECT src, dst; + + if (BoxClip(&clipped, total, pbox)) + continue; + + RectBox(&src, &clipped, src_off_x, src_off_y); + RectBox(&dst, &clipped, dst_off_x, dst_off_y); + + err = gco2D_SetClipping(vivante->e2d, &dst); + if (err != gcvSTATUS_OK) + break; + + err = gco2D_BatchBlit(vivante->e2d, 1, &src, &dst, rop, rop, format); + if (err != gcvSTATUS_OK) + break; + } + + return err; +} + + + +Bool vivante_accel_FillSpans(DrawablePtr pDrawable, GCPtr pGC, int n, + DDXPointPtr ppt, int *pwidth, int fSorted) +{ + struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen); + struct vivante_pixmap *vPix; + PixmapPtr pPix; + BoxPtr pBox, p; + RegionRec region; + int i, off_x, off_y; + Bool ret, overlap; + + pPix = vivante_drawable_pixmap_deltas(pDrawable, &off_x, &off_y); + vPix = vivante_get_pixmap_priv(pPix); + if (!vPix) + return FALSE; + + pBox = malloc(n * sizeof *pBox); + if (!pBox) + return FALSE; + + for (i = n, p = pBox; i; i--, p++, ppt++, pwidth++) { + p->x1 = ppt->x + pDrawable->x; + p->x2 = ppt->x + *pwidth; + p->y1 = ppt->y + pDrawable->y; + p->y2 = ppt->y + 1; + } + + /* Convert the boxes to a region */ + RegionInitBoxes(®ion, pBox, n); + free(pBox); + + if (!fSorted) + RegionValidate(®ion, &overlap); + + /* Intersect them with the clipping region */ + RegionIntersect(®ion, ®ion, fbGetCompositeClip(pGC)); + + /* Translate them for the drawable offset */ + RegionTranslate(®ion, off_x, off_y); + + ret = vivante_fill(vivante, vPix, pGC, ®ion); + + RegionUninit(®ion); + + return ret; +} + +Bool vivante_accel_PutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, + int x, int y, int w, int h, int leftPad, int format, char *bits) +{ + struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen); + struct vivante_pixmap *vPix; + RegionPtr pClip = fbGetCompositeClip(pGC); + PixmapPtr pPix; + BoxRec total; + unsigned pitch, size; + int dst_off_x, dst_off_y, off, src_off_x, src_off_y; + gctPOINTER info; + gctUINT32 addr; + gceSTATUS err; + char *buf = bits; + + if (format != ZPixmap) + return FALSE; + + pPix = vivante_drawable_pixmap_deltas(pDrawable, &dst_off_x, &dst_off_y); + vPix = vivante_get_pixmap_priv(pPix); + if (!vPix) + return FALSE; + + pitch = PixmapBytePad(w, depth); + + /* + * If the image is not appropriately aligned on each scanline, align + * it - it's cheaper to align it here, than to fall back and copy it + * manually to the scanout buffer. It is unfortunate that we can't + * tell the X server/clients about this restriction. + */ + if (pitch & 15) { + unsigned i, new_pitch = (pitch + 15) & ~15; + + buf = malloc(new_pitch * h); + if (!buf) + return FALSE; + + for (i = 0; i < h; i++) { + memcpy(buf + new_pitch * i, bits + pitch * i, pitch); + memset(buf + new_pitch * i + pitch, 0, new_pitch - pitch); + } + + pitch = new_pitch; + } + + size = pitch * h; + + err = gcoOS_MapUserMemory(vivante->os, buf, size, &info, &addr); + if (err) + return FALSE; + + /* Get the 'X' offset required to align the supplied data */ + off = addr & VIVANTE_ALIGN_MASK; + + if (!gal_prepare_gpu(vivante, vPix, GPU2D_Target)) + goto unmap; + + vivante_disable_alpha_blend(vivante); + + err = gco2D_SetColorSourceAdvanced(vivante->e2d, addr - off, pitch, + vPix->format, gcvSURF_0_DEGREE, w, h, gcvFALSE); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "SetColorSourceAdvanced", err); + goto unmap; + } + + err = gco2D_LoadSolidBrush(vivante->e2d, vPix->format, 0, 0, ~0ULL); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "LoadSolidBrush", err); + goto unmap; + } + + total.x1 = x; + total.y1 = y; + total.x2 = x + w; + total.y2 = y + h; + src_off_x = -x + off * 8 / BitsPerPixel(depth); + src_off_y = -y; + + err = vivante_blit_copy(vivante, pGC, &total, REGION_RECTS(pClip), + REGION_NUM_RECTS(pClip), src_off_x, src_off_y, + dst_off_x, dst_off_y, vPix->format); + if (err != gcvSTATUS_OK) + vivante_error(vivante, "Blit", err); + + vivante_batch_add(vivante, vPix); + + /* Ask for the memory to be unmapped upon completion */ + gcoHAL_ScheduleUnmapUserMemory(vivante->hal, info, size, addr, buf); + + /* We have to wait for this blit to finish... */ + vivante_batch_wait_commit(vivante, vPix); + + /* And free the buffer we may have allocated */ + if (buf != bits) + free(buf); + + return TRUE; + + unmap: + gcoOS_UnmapUserMemory(vivante->os, bits, size, info, addr); + + return FALSE; +} + +void vivante_accel_CopyNtoN(DrawablePtr pSrc, DrawablePtr pDst, + GCPtr pGC, BoxPtr pBox, int nBox, int dx, int dy, Bool reverse, + Bool upsidedown, Pixel bitPlane, void *closure) +{ + struct vivante *vivante = vivante_get_screen_priv(pDst->pScreen); + struct vivante_pixmap *vSrc, *vDst; + PixmapPtr pixSrc, pixDst; + int dst_off_x, dst_off_y, src_off_x, src_off_y; + BoxRec limits; + gceSTATUS err; + const char *what; + + if (vivante->force_fallback) + goto fallback; + + /* Get the source and destination pixmaps and offsets */ + pixSrc = vivante_drawable_pixmap_deltas(pSrc, &src_off_x, &src_off_y); + pixDst = vivante_drawable_pixmap_deltas(pDst, &dst_off_x, &dst_off_y); + + vSrc = vivante_get_pixmap_priv(pixSrc); + vDst = vivante_get_pixmap_priv(pixDst); + if (!vSrc || !vDst) + goto fallback; + + /* Include the copy delta on the source */ + src_off_x += dx; + src_off_y += dy; + + /* Calculate the overall limits */ + limits.x1 = -min(src_off_x, dst_off_x); + limits.y1 = -min(src_off_y, dst_off_y); + limits.x2 = min(pixSrc->drawable.width - src_off_x, pixDst->drawable.width - dst_off_x); + limits.y2 = min(pixSrc->drawable.height - src_off_y, pixDst->drawable.height - dst_off_y); + + /* Right, we're all good to go */ + if (!gal_prepare_gpu(vivante, vDst, GPU2D_Target) || + !gal_prepare_gpu(vivante, vSrc, GPU2D_Source)) + goto fallback; + + vivante_disable_alpha_blend(vivante); + + err = gco2D_LoadSolidBrush(vivante->e2d, vDst->format, 0, 0, ~0ULL); + if (err != gcvSTATUS_OK) { + what = "LoadSolidBrush"; + goto fallback_msg; + } + + err = gco2D_SetColorSourceAdvanced(vivante->e2d, vSrc->handle, + vSrc->pitch, vSrc->format, gcvSURF_0_DEGREE, + vSrc->width, vSrc->height, gcvFALSE); + if (err != gcvSTATUS_OK) { + what = "SetColorSourceAdvanced"; + goto fallback_msg; + } + + /* Submit the blit operations */ + err = vivante_blit_copy(vivante, pGC, &limits, pBox, nBox, + src_off_x, src_off_y, + dst_off_x, dst_off_y, vDst->format); + if (err != gcvSTATUS_OK) + vivante_error(vivante, "Blit", err); + +// gcoHAL_Commit(vivante->hal, FALSE); + + vivante_batch_add(vivante, vSrc); + vivante_batch_add(vivante, vDst); + + return; + + fallback_msg: + vivante_error(vivante, what, err); + fallback: + vivante_unaccel_CopyNtoN(pSrc, pDst, pGC, pBox, nBox, dx, dy, reverse, + upsidedown, bitPlane, closure); +} + +Bool vivante_accel_PolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, + int npt, DDXPointPtr ppt) +{ + struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen); + struct vivante_pixmap *vPix; + PixmapPtr pPix; + BoxPtr pBox; + RegionRec region; + int i, off_x, off_y; + Bool ret, overlap; + + pPix = vivante_drawable_pixmap_deltas(pDrawable, &off_x, &off_y); + vPix = vivante_get_pixmap_priv(pPix); + if (!vPix) + return FALSE; + + pBox = malloc(npt * sizeof *pBox); + if (!pBox) + return FALSE; + + for (i = 0; i < npt; i++) { + pBox[i].x1 = ppt[i].x + pDrawable->x; + pBox[i].y1 = ppt[i].y + pDrawable->y; + if (i > 0 && mode == CoordModePrevious) { + pBox[i].x1 += ppt[i - 1].x; + pBox[i].y1 += ppt[i - 1].y; + } + pBox[i].x2 = pBox[i].x1 + 1; + pBox[i].y2 = pBox[i].y1 + 1; + } + + /* Convert the boxes to a region */ + RegionInitBoxes(®ion, pBox, npt); + free(pBox); + + RegionValidate(®ion, &overlap); + + /* Intersect them with the clipping region */ + RegionIntersect(®ion, ®ion, fbGetCompositeClip(pGC)); + + /* Translate them for the drawable offset */ + RegionTranslate(®ion, off_x, off_y); + + ret = vivante_fill(vivante, vPix, pGC, ®ion); + + RegionUninit(®ion); + + return ret; +} + +Bool vivante_accel_PolyFillRectSolid(DrawablePtr pDrawable, GCPtr pGC, int n, + xRectangle * prect) +{ + struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen); + struct vivante_pixmap *vPix; + PixmapPtr pPix; + RegionPtr rects; + int off_x, off_y; + Bool ret; + + pPix = vivante_drawable_pixmap_deltas(pDrawable, &off_x, &off_y); + vPix = vivante_get_pixmap_priv(pPix); + if (!vPix) + return FALSE; + + /* Convert the rectangles to a region */ + rects = RegionFromRects(n, prect, CT_UNSORTED); + + /* Translate them for the drawable position */ + RegionTranslate(rects, pDrawable->x, pDrawable->y); + + /* Intersect them with the clipping region */ + RegionIntersect(rects, rects, fbGetCompositeClip(pGC)); + + if (RegionNumRects(rects)) { + /* Translate them for the drawable offset */ + RegionTranslate(rects, off_x, off_y); + + ret = vivante_fill(vivante, vPix, pGC, rects); + } else { + ret = TRUE; + } + + RegionUninit(rects); + RegionDestroy(rects); + + return ret; +} + +Bool vivante_accel_PolyFillRectTiled(DrawablePtr pDrawable, GCPtr pGC, int n, + xRectangle * prect) +{ + struct vivante *vivante = vivante_get_screen_priv(pDrawable->pScreen); + struct vivante_pixmap *vPix, *vTile; + PixmapPtr pPix, pTile = pGC->tile.pixmap; + RegionPtr rects; + int off_x, off_y, nbox; + Bool ret; + + pPix = vivante_drawable_pixmap_deltas(pDrawable, &off_x, &off_y); + vPix = vivante_get_pixmap_priv(pPix); + vTile = vivante_get_pixmap_priv(pTile); + if (!vPix || !vTile) + return FALSE; + + /* Convert the rectangles to a region */ + rects = RegionFromRects(n, prect, CT_UNSORTED); + + /* Translate them for the drawable position */ + RegionTranslate(rects, pDrawable->x, pDrawable->y); + + /* Intersect them with the clipping region */ + RegionIntersect(rects, rects, fbGetCompositeClip(pGC)); + + nbox = RegionNumRects(rects); + if (nbox) { + int tile_w, tile_h; + BoxPtr pBox; + gceSTATUS err = gcvSTATUS_OK; + + /* Translate them for the drawable offset */ + RegionTranslate(rects, off_x, off_y); + + ret = FALSE; + + /* Right, we're all good to go */ + if (!gal_prepare_gpu(vivante, vPix, GPU2D_Target) || + !gal_prepare_gpu(vivante, vTile, GPU2D_Source)) + goto fallback; + + vivante_disable_alpha_blend(vivante); + + err = gco2D_LoadSolidBrush(vivante->e2d, vPix->format, 0, 0, ~0ULL); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "LoadSolidBrush", err); + goto fallback; + } + + err = gco2D_SetColorSourceAdvanced(vivante->e2d, vTile->handle, + vTile->pitch, vTile->format, gcvSURF_0_DEGREE, + vTile->width, vTile->height, gcvFALSE); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "SetColorSourceAdvanced", err); + goto fallback; + } + + /* Calculate the tile offset from the rect coords */ + off_x += pDrawable->x + pGC->patOrg.x; + off_y += pDrawable->y + pGC->patOrg.y; + + tile_w = pTile->drawable.width; + tile_h = pTile->drawable.height; + + pBox = RegionRects(rects); + while (nbox--) { + int dst_y, height, tile_y; + gcsRECT clip; + gctUINT8 rop = vivante_copy_rop[pGC ? pGC->alu : GXcopy]; + + RectBox(&clip, pBox, 0, 0); + + err = gco2D_SetClipping(vivante->e2d, &clip); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "SetClipping", err); + break; + } + + dst_y = pBox->y1; + height = pBox->y2 - dst_y; + modulus(dst_y - off_y, tile_h, tile_y); + + while (height > 0) { + int dst_x, width, tile_x, h; + + dst_x = pBox->x1; + width = pBox->x2 - dst_x; + modulus(dst_x - off_x, tile_w, tile_x); + + h = tile_h - tile_y; + if (h > height) + h = height; + height -= h; + + while (width > 0) { + gcsRECT dst, src; + int w; + + w = tile_w - tile_x; + if (w > width) + w = width; + width -= w; + + src.left = tile_x; + src.top = tile_y; + src.right = tile_x + w; + src.bottom = tile_y + h; + dst.left = dst_x; + dst.top = dst_y; + dst.right = dst_x + w; + dst.bottom = dst_y + h; + + err = gco2D_BatchBlit(vivante->e2d, 1, &src, &dst, rop, rop, vPix->format); + if (err) + break; + + dst_x += w; + tile_x = 0; + } + if (err) + break; + dst_y += h; + tile_y = 0; + } + if (err) + break; + pBox++; + } + vivante_batch_add(vivante, vTile); + vivante_batch_add(vivante, vPix); + ret = err == 0 ? TRUE : FALSE; + } else { + ret = TRUE; + } + + fallback: + RegionUninit(rects); + RegionDestroy(rects); + + return ret; +} + +#ifdef RENDER +#include "mipict.h" +#include "fbpict.h" +static Bool transform_is_integer_translation(PictTransformPtr t, int *tx, int *ty) +{ + if (t == NULL) { + *tx = *ty = 0; + return TRUE; + } + + if (t->matrix[0][0] != IntToxFixed(1) || + t->matrix[0][1] != 0 || + t->matrix[1][0] != 0 || + t->matrix[1][1] != IntToxFixed(1) || + t->matrix[2][0] != 0 || + t->matrix[2][1] != 0 || + t->matrix[2][2] != IntToxFixed(1)) + return FALSE; + + if (xFixedFrac(t->matrix[0][2]) != 0 || + xFixedFrac(t->matrix[1][2]) != 0) + return FALSE; + + *tx = xFixedToInt(t->matrix[0][2]); + *ty = xFixedToInt(t->matrix[1][2]); + + return TRUE; +} + +static Bool drawable_contains(DrawablePtr drawable, int x, int y, int w, int h) +{ + if (x < 0 || y < 0 || x + w > drawable->width || y + h > drawable->height) + return FALSE; + + return TRUE; +} + +static void adjust_repeat(PicturePtr pPict, int x, int y, unsigned w, unsigned h) +{ + int tx, ty; + + if (pPict->pDrawable && + pPict->repeat != RepeatNone && + pPict->filter != PictFilterConvolution && + transform_is_integer_translation(pPict->transform, &tx, &ty) && + (pPict->pDrawable->width > 1 || pPict->pDrawable->height > 1) && + drawable_contains(pPict->pDrawable, x + tx, y + ty, w, h)) { +//fprintf(stderr, "%s: removing repeat on %p\n", __FUNCTION__, pPict); + pPict->repeat = RepeatNone; + } +} + +struct vivante_blend_op { + gceSURF_BLEND_FACTOR_MODE src_blend; + gceSURF_BLEND_FACTOR_MODE dst_blend; +}; + +static const struct vivante_blend_op vivante_composite_op[] = { +#define OP(op,s,d) \ + [PictOp##op] = { \ + .src_blend = gcvSURF_BLEND_##s, \ + .dst_blend = gcvSURF_BLEND_##d, \ + } + OP(Clear, ZERO, ZERO), + OP(Src, ONE, ZERO), + OP(Dst, ZERO, ONE), + OP(Over, ONE, INVERSED), + OP(OverReverse, INVERSED, ONE), + OP(In, STRAIGHT, ZERO), + OP(InReverse, ZERO, STRAIGHT), + OP(Out, INVERSED, ZERO), + OP(OutReverse, ZERO, INVERSED), + OP(Atop, STRAIGHT, INVERSED), + OP(AtopReverse, INVERSED, STRAIGHT), + OP(Xor, INVERSED, INVERSED), + OP(Add, ONE, ONE), +// OP(Saturate, SRC_ALPHA_SATURATED, ZERO ), +#undef OP +}; + +static const struct vivante_blend_op vivante_mask_op = { + .src_blend = gcvSURF_BLEND_ZERO, // Mask + .dst_blend = gcvSURF_BLEND_STRAIGHT, // Source +}; + +static Bool vivante_fill_single(struct vivante *vivante, + struct vivante_pixmap *vPix, gcsRECT_PTR rect, gceSURF_FORMAT format, + uint32_t colour) +{ + gceSTATUS err; + + if (!gal_prepare_gpu(vivante, vPix, GPU2D_Target)) + return FALSE; + + vivante_disable_alpha_blend(vivante); + + err = gco2D_LoadSolidBrush(vivante->e2d, format, 0, colour, ~0ULL); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_LoadSolidBrush", err); + return FALSE; + } + + err = gco2D_SetClipping(vivante->e2d, rect); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_SetClipping", err); + return FALSE; + } + + err = gco2D_Blit(vivante->e2d, 1, rect, 0xf0, 0xf0, format); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_Blit", err); + return FALSE; + } + + vivante_batch_add(vivante, vPix); + + return TRUE; +} + +#if 0 +static void BoxCopy(gcsRECT_PTR src, gcsRECT_PTR dst, int xSrc, int ySrc, + int xDst, int yDst, int w, int h) +{ + src->left = xSrc; + src->top = ySrc; + src->right = xSrc + w; + src->bottom = ySrc + h; + + dst->left = xDst; + dst->top = yDst; + dst->right = xDst + w; + dst->bottom = yDst + h; +} +#endif + +static Bool vivante_blend(struct vivante *vivante, gcsRECT_PTR clip, + const struct vivante_blend_op *blend, + struct vivante_pixmap *vDst, gceSURF_FORMAT fDst, gcsRECT_PTR rDst, + struct vivante_pixmap *vSrc, gceSURF_FORMAT fSrc, gcsRECT_PTR rSrc, + unsigned nRect) +{ + gceSTATUS err; + + if (!gal_prepare_gpu(vivante, vDst, GPU2D_Target) || + !gal_prepare_gpu(vivante, vSrc, GPU2D_Source)) + return FALSE; + + if (!blend) { + vivante_disable_alpha_blend(vivante); + } else { + err = gco2D_EnableAlphaBlendAdvanced(vivante->e2d, + gcvSURF_PIXEL_ALPHA_STRAIGHT, gcvSURF_PIXEL_ALPHA_STRAIGHT, + gcvSURF_GLOBAL_ALPHA_OFF, gcvSURF_GLOBAL_ALPHA_OFF, + blend->src_blend, blend->dst_blend); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_EnableAlphaBlendAdvanced", err); + return FALSE; + } + vivante->alpha_blend_enabled = TRUE; + } + + err = gco2D_SetColorSourceAdvanced(vivante->e2d, vSrc->handle, + vSrc->pitch, fSrc, gcvSURF_0_DEGREE, + vSrc->width, vSrc->height, gcvFALSE); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_SetColorSourceAdvanced", err); + return FALSE; + } + + err = gco2D_SetClipping(vivante->e2d, clip); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_SetClipping", err); + return FALSE; + } + + err = gco2D_BatchBlit(vivante->e2d, nRect, rSrc, rDst, 0xcc, 0xcc, fDst); + if (err != gcvSTATUS_OK) { + vivante_error(vivante, "gco2D_BatchBlit", err); + return FALSE; + } + + vivante_batch_add(vivante, vDst); + vivante_batch_add(vivante, vSrc); + + return TRUE; +} + +/* + * If we're filling a solid + * surface, force it to have alpha; it may be used in combination + * with a mask. Otherwise, we ask for the plain source format, + * with or without alpha, and convert later when copying. + */ +static struct vivante_pixmap *vivante_acquire_src(struct vivante *vivante, + PicturePtr pict, int x, int y, int w, int h, gcsRECT_PTR clip, + PixmapPtr pix, struct vivante_pixmap *vTemp, + gceSURF_FORMAT *fout, INT16 *xout, INT16 *yout) +{ + PixmapPtr pPixmap; + struct vivante_pixmap *vSrc; + DrawablePtr drawable = pict->pDrawable; + uint32_t colour; + int tx, ty, ox, oy; + Bool fill = FALSE; + + if (drawable == NULL) { + SourcePict *src = pict->pSourcePict; + if (src && src->type == SourcePictTypeSolidFill) { + colour = src->solidFill.color; + fill = TRUE; + } else { + return NULL; + } + } else if (drawable->width == 1 && drawable->height == 1 && + pict->repeat != RepeatNone) { + colour = get_first_pixel(pict->pDrawable); + fill = TRUE; + } + + if (fill) { + *xout = 0; + *yout = 0; + *fout = vivante_format(PICT_a8r8g8b8, FALSE); + if (PICT_FORMAT_A(pict->format) == 0) + colour |= 0xff000000; + if (!vivante_fill_single(vivante, vTemp, clip, *fout, colour)) + return NULL; + + return vTemp; + } + + pPixmap = vivante_drawable_pixmap_deltas(pict->pDrawable, &ox, &oy); + vSrc = vivante_get_pixmap_priv(pPixmap); + if (!vSrc) + return NULL; + + if (pict->repeat == RepeatNone && + transform_is_integer_translation(pict->transform, &tx, &ty)) { + *xout = ox + x + tx + drawable->x; + *yout = ox + y + ty + drawable->y; + *fout = vivante_format(pict->format, FALSE); + } else { + PictFormatPtr f; + PicturePtr dest; + int err; + + f = PictureMatchFormat(drawable->pScreen, pict->pFormat->depth, + PICT_a8r8g8b8); + if (!f) + return NULL; + + dest = CreatePicture(0, &pix->drawable, f, 0, 0, serverClient, &err); + if (!dest) + return NULL; + ValidatePicture(dest); + + vivante_unaccel_Composite(PictOpSrc, pict, NULL, dest, x, y, 0, 0, 0, 0, w, h); + FreePicture(dest, 0); + *xout = 0; + *yout = 0; + *fout = vivante_format(PICT_a8r8g8b8, FALSE); + vSrc = vTemp; + } + + return vSrc; +} + +int vivante_accel_Composite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, + PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, + INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) +{ + struct vivante *vivante = vivante_get_screen_priv(pDst->pDrawable->pScreen); + struct vivante_pixmap *vDst, *vSrc, *vMask, *vTemp = NULL; + ScreenPtr pScreen = pDst->pDrawable->pScreen; + PixmapPtr pPixmap, pPixTemp = NULL; + RegionRec region; + gceSURF_FORMAT fDst, fSrc; + gcsRECT clipTemp; + gcsRECT clip; + int oDst_x, oDst_y; + + if (pDst->alphaMap || pSrc->alphaMap || + (pMask && (pMask->alphaMap || pMask->componentAlpha))) { +// fprintf(stderr, "%s: D:%s S:%s M:%s%s\n", __FUNCTION__, +// pDst->alphaMap ? "AM" : "", pSrc->alphaMap ? "AM" : "", +// pMask && pMask->alphaMap ? "AM" : "", +// pMask && pMask->componentAlpha ? "CA" : ""); + return FALSE; + } + + /* If we can't do the op, there's no point going any further */ + if (op >= ARRAY_SIZE(vivante_composite_op)) + return FALSE; + + /* The destination pixmap must have a bo */ + pPixmap = vivante_drawable_pixmap_deltas(pDst->pDrawable, &oDst_x, &oDst_y); + vDst = vivante_get_pixmap_priv(pPixmap); + if (!vDst) + return FALSE; + +#if 0 +fprintf(stderr, "%s: i: op 0x%02x src=%p,%d,%d mask=%p,%d,%d dst=%p,%d,%d %ux%u\n", + __FUNCTION__, op, pSrc, xSrc, ySrc, pMask, xMask, yMask, + pDst, xDst, yDst, width, height); +#endif + + memset(®ion, 0, sizeof(region)); + + /* Remove repeat on source or mask if useless */ + adjust_repeat(pSrc, xSrc, ySrc, width, height); + if (pMask) { + adjust_repeat(pMask, xMask, yMask, width, height); + + /* We don't handle mask repeats (yet) */ + if (pMask->repeat != RepeatNone) + goto fallback; + } + + /* + * Convert the source/destination coordinates according to the + * position of the drawables against the backing buffer. + */ + if (pMask && pMask->pDrawable) { + xMask += pMask->pDrawable->x; + yMask += pMask->pDrawable->y; + } + xDst += pDst->pDrawable->x; + yDst += pDst->pDrawable->y; + + /* + * Compute the regions to be composited. This provides us with the + * rectangles which need to be composited at each stage, where the + * rectangle coordinates are based on the destination image. + * + * Clips are interesting. A picture composite clip has the drawable + * position included in it. A picture client clip does not. + * + * The clip region below is calculated by beginning with the box + * xDst,yDst,xDst+width,yDst+width, and then intersecting that with + * the destination composite clips. Therefore, xDst,yDst must + * contain the drawable position. + * + * The source and mask pictures are then factored in, intersecting + * their client clips (which doesn't have a drawable position) with + * the current set of clips from the destination, first translating + * them by (xDst - xSrc),(yDst - ySrc). + * + * However, the X unaccelerated fb layer ignores any clips in the + * source and mask. So... we ignore them here too. + */ + if (!miComputeCompositeRegion(®ion, pSrc, NULL, pDst, xSrc, ySrc, + 0, 0, xDst, yDst, width, height)) + return TRUE; + + /* + * Compute the temporary image clipping box, which is the + * clipping region extents without the destination offset. + */ + RectBox(&clipTemp, RegionExtents(®ion), -xDst, -yDst); + + /* + * Get a temporary pixmap. We don't really know yet whether we're + * going to use it or not. + */ + pPixTemp = pScreen->CreatePixmap(pScreen, width, height, + pDst->pDrawable->depth, 0); + if (!pPixTemp) + goto failed; + + vTemp = vivante_get_pixmap_priv(pPixTemp); + if (!vTemp) + goto failed; + + /* + * Get the source. The source image will be described by vSrc with + * offset xSrc/ySrc. This may or may not be the temporary image, and + * fSrc describes its format, including whether the alpha channel is + * valid. + */ + if (op == PictOpClear) { + fSrc = vivante_format(pSrc->format, TRUE); + if (!vivante_fill_single(vivante, vTemp, &clipTemp, fSrc, 0)) + goto failed; + vSrc = vTemp; + xSrc = 0; + ySrc = 0; + } else { + vSrc = vivante_acquire_src(vivante, pSrc, xSrc, ySrc, + width, height, + &clipTemp, pPixTemp, vTemp, + &fSrc, &xSrc, &ySrc); + if (!vSrc) + goto failed; + } + +//vivante_batch_wait_commit(vivante, vSrc); +//dump_vPix(buf, vivante, vSrc, 1, "A-ISRC%02.2x-%p", op, pSrc); + +#if 0 +#define C(p,e) ((p) ? (p)->e : 0) +fprintf(stderr, "%s: 0: OP 0x%02x src=%p[%p,%p,%u,%ux%u]x%dy%d mask=%p[%p,%u,%ux%u]x%dy%d dst=%p[%p]x%dy%d %ux%u\n", + __FUNCTION__, op, + pSrc, pSrc->transform, pSrc->pDrawable, pSrc->repeat, C(pSrc->pDrawable, width), C(pSrc->pDrawable, height), xSrc, ySrc, + pMask, C(pMask, pDrawable), C(pMask, repeat), C(C(pMask, pDrawable), width), C(C(pMask, pDrawable), height), xMask, yMask, + pDst, pDst->pDrawable, xDst, yDst, + width, height); +} +#endif + + /* + * If we have a mask, handle it. We deal with the mask by doing a + * InReverse operation. However, note that the source may already + * be in the temporary buffer. Also note that the temporary buffer + * must have valid alpha upon completion of this operation for the + * subsequent final blend to work. + * + * If vTemp != vSrc + * vTemp <= vSrc (if non-alpha, + max alpha) + * vTemp <= vTemp BlendOp(In) vMask + * vSrc = vTemp + */ + if (pMask) { + PixmapPtr pPixMask; + gcsRECT rsrc, rdst; + int oMask_x, oMask_y; + gceSURF_FORMAT fMask; + + pPixMask = vivante_drawable_pixmap_deltas(pMask->pDrawable, &oMask_x, &oMask_y); + vMask = vivante_get_pixmap_priv(pPixMask); + if (!vMask) + goto failed; + + oMask_x += xMask; + oMask_y += yMask; +//dump_vPix(buf, vivante, vMask, 1, "A-MASK%02.2x-%p", op, pMask); + rdst.left = 0; + rdst.top = 0; + rdst.right = width; + rdst.bottom = height; + + if (vTemp != vSrc) { + gceSURF_FORMAT fTemp; + + /* Copy Source to Temp */ + rsrc.left = xSrc; + rsrc.top = ySrc; + rsrc.right = xSrc + width; + rsrc.bottom = ySrc + height; + + /* + * The source may not have alpha, but we need the + * temporary pixmap to have alpha. Try to convert + * while copying. (If this doesn't work, use OR + * in the brush with maximum alpha value.) + */ + fTemp = vivante_format(pSrc->format, TRUE); + + if (!vivante_blend(vivante, &clipTemp, NULL, + vTemp, fTemp, &rdst, + vSrc, fSrc, &rsrc, 1)) + goto failed; +//vivante_batch_wait_commit(vivante, vTemp); +//dump_vPix(buf, vivante, vTemp, 1, "A-TMSK%02.2x-%p", op, pMask); + fSrc = fTemp; + } + + rsrc.left = oMask_x; + rsrc.top = oMask_y; + rsrc.right = oMask_x + width; + rsrc.bottom = oMask_y + height; + + fMask = vivante_format(pMask->format, FALSE); + +#if 0 +if (pMask && pMask->pDrawable) + fprintf(stderr, "%s: src %d,%d,%d,%d %d,%d %u (%x)\n", + __FUNCTION__, pMask->pDrawable->x, pMask->pDrawable->y, + pMask->pDrawable->x + pMask->pDrawable->width, pMask->pDrawable->y + pMask->pDrawable->height, + xMask, yMask, fMask, pMask->format); +#endif + + if (!vivante_blend(vivante, &clipTemp, &vivante_mask_op, + vTemp, fSrc, &rdst, + vMask, fMask, &rsrc, + 1)) + goto failed; + + vSrc = vTemp; + xSrc = 0; + ySrc = 0; + } + + /* Get the Vivante destination format early */ + fDst = vivante_format(pDst->format, FALSE); + +//vivante_batch_wait_commit(vivante, vSrc); +//dump_vPix(buf, vivante, vSrc, 1, "A-TSRC%02.2x-%p", op, pSrc); + + if (1) { + gcsRECT *rects, *rsrc, *rdst; + int i, nrects; + + xSrc -= xDst; + ySrc -= yDst; + +#if 0 +fprintf(stderr, "%s: dst %d,%d,%d,%d %d,%d %u (%x) bo %p\n", + __FUNCTION__, pDst->pDrawable->x, pDst->pDrawable->y, + pDst->pDrawable->x + pDst->pDrawable->width, pDst->pDrawable->y + pDst->pDrawable->height, + xDst, yDst, fDst, pDst->format, vDst->bo); +#endif + + nrects = REGION_NUM_RECTS(®ion); + rects = malloc(sizeof(*rects) * nrects * 2); + if (!rects) { +fprintf(stderr, "%s: malloc fail\n", __FUNCTION__); + RegionUninit(®ion); + goto failed; + } + + for (i = 0, rsrc = rects, rdst = rsrc + nrects; + i < nrects; + i++, rsrc++, rdst++) { + RectBox(rsrc, REGION_RECTS(®ion) + i, xSrc, ySrc); + RectBox(rdst, REGION_RECTS(®ion) + i, oDst_x, oDst_y); +//fprintf(stderr, "%s: rect %d,%d,%d,%d -> %d,%d,%d,%d\n", __FUNCTION__, +// rsrc->left, rsrc->top, rsrc->right, rsrc->bottom, +// rdst->left, rdst->top, rdst->right, rdst->bottom); + } + + RectBox(&clip, RegionExtents(®ion), oDst_x, oDst_y); + RegionUninit(®ion); +//fprintf(stderr, "%s: clip: %d,%d,%d,%d\n", __FUNCTION__, clip.left, clip.top, clip.right, clip.bottom); + + rsrc = rects; + rdst = rsrc + nrects; +//vivante_batch_wait_commit(vivante, vSrc); +//dump_vPix(buf, vivante, vSrc, 1, "A-FSRC%02.2x-%p", op, pSrc); +//dump_vPix(buf, vivante, vDst, 1, "A-FDST%02.2x-%p", op, pDst); + if (!vivante_blend(vivante, &clip, &vivante_composite_op[op], + vDst, fDst, rdst, + vSrc, fSrc, rsrc, nrects)) { + free(rects); + goto failed; + } + + free(rects); + +//vivante_batch_wait_commit(vivante, vDst); +//fprintf(stderr, "%s: success\n", __FUNCTION__); +//dump_vPix(buf, vivante, vDst, PICT_FORMAT_A(pDst->format) != 0, "A-DEST%02.2x-%p", op, pDst); } + goto done; + } + + fallback: +#if 0 +#define C(p,e) ((p) ? (p)->e : 0) +fprintf(stderr, "%s: op 0x%02x src=%p[%p,%u,%ux%u]x%dy%d mask=%p[%p,%u,%ux%u]x%dy%d dst=%p[%p]x%dy%d %ux%u\n", + __FUNCTION__, op, + pSrc, pSrc->pDrawable, pSrc->repeat, C(pSrc->pDrawable, width), C(pSrc->pDrawable, height), xSrc, ySrc, + pMask, C(pMask, pDrawable), C(pMask, repeat), C(C(pMask, pDrawable), width), C(C(pMask, pDrawable), height), xMask, yMask, + pDst, pDst->pDrawable, xDst, yDst, + width, height); +#endif + + failed: + RegionUninit(®ion); + + if (pPixTemp) { + ScreenPtr pScreen = pPixTemp->drawable.pScreen; + pScreen->DestroyPixmap(pPixTemp); + } + return FALSE; + + done: + if (pPixTemp) { + ScreenPtr pScreen = pPixTemp->drawable.pScreen; + pScreen->DestroyPixmap(pPixTemp); + } + return TRUE; +} +#endif + +Bool vivante_accel_init(struct vivante *vivante) +{ + gceCHIPMODEL model; + gctUINT32 rev, feat, minfeat; + gceSTATUS ret; + + ret = gcoOS_Construct(gcvNULL, &vivante->os); + if (ret != gcvSTATUS_OK) { + xf86DrvMsg(vivante->scrnIndex, X_ERROR, + "vivante: unable to construct %s object: %s\n", + "OS", vivante_strerror(ret)); + return FALSE; + } + + ret = gcoHAL_Construct(gcvNULL, vivante->os, &vivante->hal); + if (ret != gcvSTATUS_OK) { + xf86DrvMsg(vivante->scrnIndex, X_ERROR, + "vivante: unable to construct %s object: %s\n", + "HAL", vivante_strerror(ret)); + return FALSE; + } + + ret = gcoHAL_QueryChipIdentity(vivante->hal, &model, &rev, &feat, &minfeat); + if (ret != gcvSTATUS_OK) + return FALSE; + + ret = gcoHAL_Get2DEngine(vivante->hal, &vivante->e2d); + if (ret != gcvSTATUS_OK) { + xf86DrvMsg(vivante->scrnIndex, X_ERROR, + "vivante: unable to construct %s object: %s\n", + "2d engine", vivante_strerror(ret)); + return FALSE; + } + + xf86DrvMsg(vivante->scrnIndex, X_PROBED, + "Vivante GC%x GPU revision %x\n", model, rev); + + vivante->max_rect_count = gco2D_GetMaximumRectCount(); + + return TRUE; +} + +void vivante_accel_shutdown(struct vivante *vivante) +{ + if (vivante->hal) { + gcoHAL_Commit(vivante->hal, gcvTRUE); + gcoHAL_Destroy(vivante->hal); + } + if (vivante->os) + gcoOS_Destroy(vivante->os); +} |