/* * Copyright (c) 2016 Freescale Semiconductor, Inc. * Copyright © 2012 Intel Corporation * Copyright © 2015 Collabora, Ltd. * Copyright 2018 NXP * * 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, sublicense, 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 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * 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. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "g2d-renderer.h" #include "vertex-clipping.h" #include "linux-dmabuf.h" #include "linux-dmabuf-unstable-v1-server-protocol.h" #include "linux-explicit-synchronization.h" #include "shared/fd-util.h" #include "shared/helpers.h" #include "shared/platform.h" #define BUFFER_DAMAGE_COUNT 3 #define ALIGN_TO_16(a) (((a) + 15) & ~15) #define ALIGN_TO_64(a) (((a) + 63) & ~63) static PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL; #ifndef PFNEGLUPDATEWAYLANDBUFFERWL typedef EGLBoolean (EGLAPIENTRYP PFNEGLUPDATEWAYLANDBUFFERWL)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute); #endif struct wl_viv_buffer { struct wl_resource *resource; gcoSURF surface; gctINT32 width; gctINT32 height; gctINT32 format; gctUINT alignedWidth; gctUINT alignedHeight; gctUINT32 physical[3]; gctUINT32 gpuBaseAddr; gceTILING tiling; gctINT32 fd; gctUINT32 ts_addr; gctUINT32 fc_enabled; gctUINT32 fcValue; gctUINT32 fcValueUpper; gctUINT32 compressed; gctUINT32 tileStatus_enabled; }; typedef struct _g2dRECT { int left; int top; int right; int bottom; } g2dRECT; struct fb_screeninfo { struct fb_var_screeninfo varinfo; struct fb_fix_screeninfo fixinfo; unsigned int x_resolution; unsigned int y_resolution; size_t buffer_length; /* length of frame buffer memory in bytes */ size_t physical; size_t stride; size_t stride_bytes; enum g2d_format pixel_format; /* frame buffer pixel format */ int fb_fd; }; struct g2d_output_state { int current_buffer; pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT]; struct g2d_surfaceEx *renderSurf; int nNumBuffers; int activebuffer; struct g2d_surfaceEx offscreenSurface; struct g2d_buf *offscreen_buf; struct fb_screeninfo fb_info; struct fb_screeninfo *mirror_fb_info; struct g2d_surfaceEx *mirrorSurf; struct g2d_surfaceEx *drm_hw_buffer; int directBlit; int clone_display_num; int width; int height; }; struct g2d_surface_state { float color[4]; struct weston_buffer_reference buffer_ref; struct weston_buffer_release_reference buffer_release_ref; int pitch; /* in pixels */ int attached; pixman_region32_t texture_damage; struct g2d_surfaceEx g2d_surface; struct g2d_buf *shm_buf; struct g2d_buf *dma_buf; int shm_buf_length; int shm_buf_clrcolor; int set_shm_buf_clrcolor; int bpp; struct weston_surface *surface; struct wl_listener surface_destroy_listener; struct wl_listener renderer_destroy_listener; }; struct g2d_renderer { struct weston_renderer base; struct wl_signal destroy_signal; #ifdef ENABLE_EGL NativeDisplayType display; EGLDisplay egl_display; struct wl_display *wl_display; PFNEGLBINDWAYLANDDISPLAYWL bind_display; PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display; PFNEGLUPDATEWAYLANDBUFFERWL update_buffer; #endif void *handle; int use_drm; }; static int g2d_renderer_create_surface(struct weston_surface *surface); static inline struct g2d_surface_state * get_surface_state(struct weston_surface *surface) { if (!surface->renderer_state) g2d_renderer_create_surface(surface); return (struct g2d_surface_state *)surface->renderer_state; } static inline struct g2d_renderer * get_renderer(struct weston_compositor *ec) { return (struct g2d_renderer *)ec->renderer; } #define max(a, b) (((a) > (b)) ? (a) : (b)) #define min(a, b) (((a) > (b)) ? (b) : (a)) /* * Compute the boundary vertices of the intersection of the global coordinate * aligned rectangle 'rect', and an arbitrary quadrilateral produced from * 'surf_rect' when transformed from surface coordinates into global coordinates. * The vertices are written to 'ex' and 'ey', and the return value is the * number of vertices. Vertices are produced in clockwise winding order. * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero * polygon area. */ static int calculate_edges(struct weston_view *ev, pixman_box32_t *rect, pixman_box32_t *surf_rect, float *ex, float *ey) { struct clip_context ctx; int i, n; float min_x, max_x, min_y, max_y; struct polygon8 surf = { { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 }, { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 }, 4 }; ctx.clip.x1 = rect->x1; ctx.clip.y1 = rect->y1; ctx.clip.x2 = rect->x2; ctx.clip.y2 = rect->y2; /* transform surface to screen space: */ for (i = 0; i < surf.n; i++) weston_view_to_global_float(ev, surf.x[i], surf.y[i], &surf.x[i], &surf.y[i]); /* find bounding box: */ min_x = max_x = surf.x[0]; min_y = max_y = surf.y[0]; for (i = 1; i < surf.n; i++) { min_x = min(min_x, surf.x[i]); max_x = max(max_x, surf.x[i]); min_y = min(min_y, surf.y[i]); max_y = max(max_y, surf.y[i]); } /* First, simple bounding box check to discard early transformed * surface rects that do not intersect with the clip region: */ if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) || (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1)) return 0; /* Simple case, bounding box edges are parallel to surface edges, * there will be only four edges. We just need to clip the surface * vertices to the clip rect bounds: */ if (!ev->transform.enabled) return clip_simple(&ctx, &surf, ex, ey); /* Transformed case: use a general polygon clipping algorithm to * clip the surface rectangle with each side of 'rect'. * The algorithm is Sutherland-Hodgman, as explained in * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm * but without looking at any of that code. */ n = clip_transformed(&ctx, &surf, ex, ey); if (n < 3) return 0; return n; } static void calculate_rect_with_transform(int surfaceWidth, int surfaceHeight, uint32_t transform, g2dRECT *rect) { g2dRECT tmp = *rect; switch (transform) { case WL_OUTPUT_TRANSFORM_NORMAL: default: break; case WL_OUTPUT_TRANSFORM_270: rect->right = surfaceWidth - tmp.top; rect->left = rect->right - (tmp.bottom - tmp.top); rect->top = tmp.left; rect->bottom = rect->top + (tmp.right - tmp.left); break; case WL_OUTPUT_TRANSFORM_90: rect->left = tmp.top; rect->right = rect->left + (tmp.bottom - tmp.top); rect->bottom = surfaceHeight - tmp.left; rect->top = rect->bottom - (tmp.right - tmp.left); break; case WL_OUTPUT_TRANSFORM_180: rect->left = surfaceWidth - tmp.right; rect->right = rect->left + (tmp.right - tmp.left); rect->bottom = surfaceHeight - tmp.top; rect->top = rect->bottom - (tmp.bottom - tmp.top); break; } } static enum g2d_rotation convert_transform_to_rot(uint32_t transform) { enum g2d_rotation rot; switch(transform) { case WL_OUTPUT_TRANSFORM_NORMAL: default: rot = G2D_ROTATION_0; break; case WL_OUTPUT_TRANSFORM_90: /*For source rotation*/ rot = G2D_ROTATION_90; break; case WL_OUTPUT_TRANSFORM_270: rot = G2D_ROTATION_270; break; case WL_OUTPUT_TRANSFORM_180: rot = G2D_ROTATION_180; break; } return rot; } static inline struct g2d_output_state * get_output_state(struct weston_output *output) { return (struct g2d_output_state *)output->renderer_state; } static void g2d_getG2dTiling(IN gceTILING tiling, enum g2d_tiling* g2dTiling) { switch(tiling) { case gcvLINEAR: *g2dTiling = G2D_LINEAR; break; case gcvTILED: *g2dTiling = G2D_TILED; break; case gcvSUPERTILED: *g2dTiling = G2D_SUPERTILED; break; default: weston_log("Error in function %s\n", __func__); break; } } static void g2d_getG2dFormat(IN gceSURF_FORMAT Format, enum g2d_format* g2dFormat) { switch(Format) { case gcvSURF_R5G6B5: *g2dFormat = G2D_RGB565; break; case gcvSURF_A8B8G8R8: *g2dFormat = G2D_RGBA8888; break; case gcvSURF_X8B8G8R8: *g2dFormat = G2D_RGBA8888; break; case gcvSURF_A8R8G8B8: *g2dFormat = G2D_BGRA8888; break; case gcvSURF_X8R8G8B8: *g2dFormat = G2D_BGRX8888; break; case gcvSURF_B5G6R5: *g2dFormat = G2D_BGR565; break; case gcvSURF_B8G8R8A8: *g2dFormat = G2D_ARGB8888; break; case gcvSURF_R8G8B8A8: *g2dFormat = G2D_ABGR8888; break; case gcvSURF_B8G8R8X8: *g2dFormat = G2D_XRGB8888; break; case gcvSURF_R8G8B8X8: *g2dFormat = G2D_XBGR8888; break; case gcvSURF_NV12: *g2dFormat = G2D_NV12; break; case gcvSURF_NV21: *g2dFormat = G2D_NV21; break; case gcvSURF_I420: *g2dFormat = G2D_I420; break; case gcvSURF_YV12: *g2dFormat = G2D_YV12; break; case gcvSURF_YUY2: *g2dFormat = G2D_YUYV; break; case gcvSURF_YVYU: *g2dFormat = G2D_YVYU; break; case gcvSURF_UYVY: *g2dFormat = G2D_UYVY; break; case gcvSURF_VYUY: *g2dFormat = G2D_VYUY; break; case gcvSURF_NV16: *g2dFormat = G2D_NV16; break; case gcvSURF_NV61: *g2dFormat = G2D_NV61; break; default: weston_log("Error in function %s, Format not supported\n", __func__); break; } } static int g2d_getG2dFormat_from_pixman(IN pixman_format_code_t Format, enum g2d_format* g2dFormat) { switch(Format) { case PIXMAN_r5g6b5: *g2dFormat = G2D_RGB565; break; case PIXMAN_a8b8g8r8: *g2dFormat = G2D_RGBA8888; break; case PIXMAN_x8b8g8r8: *g2dFormat = G2D_RGBA8888; break; case PIXMAN_a8r8g8b8: *g2dFormat = G2D_BGRA8888; break; case PIXMAN_x8r8g8b8 : *g2dFormat = G2D_BGRX8888; break; case PIXMAN_b5g6r5: *g2dFormat = G2D_BGR565; break; case PIXMAN_b8g8r8a8: *g2dFormat = G2D_ARGB8888; break; case PIXMAN_r8g8b8a8: *g2dFormat = G2D_ABGR8888; break; case PIXMAN_b8g8r8x8: *g2dFormat = G2D_XRGB8888; break; case PIXMAN_r8g8b8x8: *g2dFormat = G2D_XBGR8888; break; case PIXMAN_yv12: *g2dFormat = G2D_YV12; break; case PIXMAN_yuy2: *g2dFormat = G2D_YUYV; break; default: weston_log("Error in function %s, Format(%d) not supported\n", __func__, Format); return -1; } return 0; } static void printG2dSurfaceInfo(struct g2d_surfaceEx* g2dSurface, const char* msg) { weston_log("%s physicAddr = %x left = %d right = %d top=%d bottom=%d stride= %d tiling = %d, format=%d \n", msg, g2dSurface->base.planes[0], g2dSurface->base.left, g2dSurface->base.right, g2dSurface->base.top, g2dSurface->base.bottom, g2dSurface->base.stride, g2dSurface->tiling, g2dSurface->base.format); } static int get_g2dSurface(struct wl_viv_buffer *buffer, struct g2d_surfaceEx *g2dSurface) { if(buffer->width <= 0 || buffer->height <= 0) { weston_log("invalid EGL buffer in function %s\n", __func__); return -EINVAL; } g2d_getG2dFormat(buffer->format, &g2dSurface->base.format); g2d_getG2dTiling(buffer->tiling, &g2dSurface->tiling); g2dSurface->base.planes[0] = buffer->physical[0] + buffer->gpuBaseAddr; g2dSurface->base.planes[1] = buffer->physical[1] + buffer->gpuBaseAddr; g2dSurface->base.planes[2] = buffer->physical[2] + buffer->gpuBaseAddr; g2dSurface->base.left = 0; g2dSurface->base.top = 0; g2dSurface->base.right = buffer->width; g2dSurface->base.bottom = buffer->height; g2dSurface->base.stride = buffer->alignedWidth; g2dSurface->base.width = buffer->width; g2dSurface->base.height = buffer->height; g2dSurface->base.rot = G2D_ROTATION_0; if(buffer->ts_addr && buffer->tileStatus_enabled) { g2dSurface->tiling |= G2D_TILED_STATUS; g2dSurface->ts.ts_addr = buffer->ts_addr; g2dSurface->ts.fc_enabled = buffer->fc_enabled; g2dSurface->ts.fc_value = buffer->fcValue; g2dSurface->ts.fc_value_upper = buffer->fcValueUpper; } return 0; } static void g2d_SetSurfaceRect(struct g2d_surfaceEx* g2dSurface, g2dRECT* rect) { if(g2dSurface && rect) { g2dSurface->base.left = rect->left; g2dSurface->base.top = rect->top; g2dSurface->base.right = rect->right; g2dSurface->base.bottom = rect->bottom; } } #define _hasAlpha(format) (format==G2D_RGBA8888 || format==G2D_BGRA8888 \ || format==G2D_ARGB8888 || format==G2D_ABGR8888) static int g2d_blit_surface(void *handle, struct g2d_surfaceEx * srcG2dSurface, struct g2d_surfaceEx *dstG2dSurface, g2dRECT *srcRect, g2dRECT *dstRect) { g2d_SetSurfaceRect(srcG2dSurface, srcRect); g2d_SetSurfaceRect(dstG2dSurface, dstRect); srcG2dSurface->base.blendfunc = G2D_ONE; dstG2dSurface->base.blendfunc = G2D_ONE_MINUS_SRC_ALPHA; if(!(_hasAlpha(srcG2dSurface->base.format))){ g2d_disable(handle, G2D_BLEND); } if(g2d_blitEx(handle, srcG2dSurface, dstG2dSurface)){ printG2dSurfaceInfo(srcG2dSurface, "SRC:"); printG2dSurfaceInfo(dstG2dSurface, "DST:"); return -1; } return 0; } static void g2d_flip_surface(struct weston_output *output) { struct g2d_output_state *go = get_output_state(output); go->fb_info.varinfo.yoffset = go->activebuffer * go->fb_info.y_resolution; if(ioctl(go->fb_info.fb_fd, FBIOPAN_DISPLAY, &(go->fb_info.varinfo)) < 0) { weston_log("FBIOPAN_DISPLAY Failed\n"); } go->activebuffer = (go->activebuffer + 1) % go->nNumBuffers; } static void copy_to_framebuffer(struct weston_output *output, pixman_region32_t* output_damage) { struct g2d_renderer *gr = get_renderer(output->compositor); struct g2d_output_state *go = get_output_state(output); g2dRECT clipRect = {0}; if((!go->directBlit && go->nNumBuffers == 1) || (go->clone_display_num)) { clipRect.left = output_damage->extents.x1; clipRect.top = output_damage->extents.y1; clipRect.right = output_damage->extents.x2; clipRect.bottom = output_damage->extents.y2; if(clipRect.left >= go->offscreenSurface.base.width) { clipRect.left -= go->offscreenSurface.base.width; clipRect.right -= go->offscreenSurface.base.width; } } if(!go->directBlit && go->nNumBuffers == 1) { g2dRECT srcRect = {0, 0, go->offscreenSurface.base.width, go->offscreenSurface.base.height}; g2dRECT dstrect = srcRect; clipRect = srcRect; g2d_set_clipping(gr->handle, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); g2d_blit_surface(gr->handle, &go->offscreenSurface, &go->renderSurf[go->activebuffer], &srcRect, &dstrect); } if(go->clone_display_num) { int i = 0; for(i = 0; i < go->clone_display_num; i++) { g2dRECT srcRect = {0, 0, go->renderSurf[go->activebuffer].base.width, go->renderSurf[go->activebuffer].base.height}; g2dRECT dstrect = {0, 0, go->mirrorSurf[i].base.width, go->mirrorSurf[i].base.height}; if(go->directBlit || go->nNumBuffers > 1) { g2d_blit_surface(gr->handle, &go->renderSurf[go->activebuffer], &go->mirrorSurf[i], &srcRect, &dstrect); } else { g2d_blit_surface(gr->handle, &go->offscreenSurface, &go->mirrorSurf[i], &srcRect, &dstrect); } } } g2d_finish(gr->handle); if(go->nNumBuffers > 1) { g2d_flip_surface(output); } } static int is_view_visible(struct weston_view *view) { /* Return false, if surface is guaranteed to be totally obscured. */ int ret; pixman_region32_t unocc; pixman_region32_init(&unocc); pixman_region32_subtract(&unocc, &view->transform.boundingbox, &view->clip); ret = pixman_region32_not_empty(&unocc); pixman_region32_fini(&unocc); return ret; } static void use_output(struct weston_output *output) { struct weston_compositor *compositor = output->compositor; struct weston_view *view; struct g2d_output_state *go = get_output_state(output); int visibleViews=0; int fullscreenViews=0; if(go->nNumBuffers == 1) { wl_list_for_each_reverse(view, &compositor->view_list, link) if (view->plane == &compositor->primary_plane && is_view_visible(view)) { visibleViews++; if(view->surface->width == go->width && view->surface->height == go->height) { pixman_box32_t *bb_rects; int nbb=0; bb_rects = pixman_region32_rectangles(&view->transform.boundingbox, &nbb); if(nbb == 1) if(bb_rects[0].x1 == 0 && bb_rects[0].y1 ==0) fullscreenViews++; } } go->directBlit = ((visibleViews == 1) || (fullscreenViews > 1)); } } static void g2d_clip_rects(enum wl_output_transform transform, g2dRECT *srcRect, g2dRECT *dstrect, int dstWidth, int dstHeight) { int srcWidth = srcRect->right - srcRect->left; int srcHeight = srcRect->bottom - srcRect->top; float scale_v = 1.0f; float scale_h = 1.0f; if(transform == WL_OUTPUT_TRANSFORM_90 || transform == WL_OUTPUT_TRANSFORM_270) { scale_h = (float)srcHeight / (dstrect->right - dstrect->left); scale_v = (float)srcWidth / (dstrect->bottom - dstrect->top); } else { scale_h = (float)srcWidth / (dstrect->right - dstrect->left); scale_v = (float)srcHeight / (dstrect->bottom - dstrect->top); } switch (transform) { case WL_OUTPUT_TRANSFORM_NORMAL: if(dstrect->left < 0) { srcRect->left += floorf((float)(-dstrect->left) * scale_h); dstrect->left = 0; if(srcRect->left >= srcRect->right) return; } if(dstrect->right > dstWidth) { srcRect->right -= floorf((float)(dstrect->right - dstWidth) * scale_h); dstrect->right = dstWidth; if(srcRect->right <= srcRect->left) return; } if(dstrect->top < 0) { srcRect->top += floorf((float)(-dstrect->top) * scale_v); dstrect->top = 0; if(srcRect->top >= srcRect->bottom) return; } if(dstrect->bottom > dstHeight) { srcRect->bottom -= floorf((float)(dstrect->bottom - dstHeight) * scale_v); dstrect->bottom = dstHeight; if(srcRect->bottom < 0) return; } break; case WL_OUTPUT_TRANSFORM_270: if(dstrect->left < 0) { srcRect->bottom -= floorf((float)(-dstrect->left) * scale_h); dstrect->left = 0; if(srcRect->top >= srcRect->bottom) return; } if(dstrect->bottom > dstHeight) { srcRect->right -= floorf((float)(dstrect->bottom - dstHeight) * scale_v); dstrect->bottom = dstHeight; if(srcRect->right < 0) return; } if(dstrect->top < 0) { srcRect->left += floorf((float)(-dstrect->top) * scale_h); dstrect->top = 0; if(srcRect->left > srcRect->right) return; } break; case WL_OUTPUT_TRANSFORM_90: if(dstrect->left < 0) { srcRect->top += floorf((float)(-dstrect->left) * scale_h); dstrect->left = 0; if(srcRect->top >= srcRect->bottom) return; } if(dstrect->top < 0) { srcRect->right -= floorf((float)(-dstrect->top) * scale_v); dstrect->top = 0; if(srcRect->left >= srcRect->right) return; } if(dstrect->bottom > dstHeight) { srcRect->left += floorf((float)(dstrect->bottom - dstHeight) * scale_v); dstrect->bottom = dstHeight; if(srcRect->right <= srcRect->left) return; } if(dstrect->right > dstWidth) { srcRect->bottom -= floorf((float)(dstrect->right - dstWidth) * scale_h); dstrect->right = dstWidth; if(srcRect->bottom <= srcRect->top) return; } break; case WL_OUTPUT_TRANSFORM_180: if(dstrect->left < 0) { srcRect->right -= floorf((float)(-dstrect->left) * scale_h); dstrect->left = 0; if(srcRect->left >= srcRect->right) return; } if(dstrect->right > dstWidth) { srcRect->left += floorf((float)(dstrect->right - dstWidth) * scale_h); dstrect->right = dstWidth; if(srcRect->right <= srcRect->left) return; } if(dstrect->top < 0) { srcRect->bottom -= floorf((float)(-dstrect->top) * scale_v); dstrect->top = 0; if(srcRect->top >= srcRect->bottom) return; } break; default: break; } } static int g2d_renderer_read_pixels(struct weston_output *output, pixman_format_code_t format, void *pixels, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { struct g2d_surfaceEx dstSurface; struct g2d_surfaceEx *srcSurface; struct g2d_output_state *go = get_output_state(output); struct g2d_renderer *gr = get_renderer(output->compositor); struct g2d_buf *read_buf = NULL; enum g2d_format dst_format; g2dRECT srcRect = {x, y, x + width, y + height}; g2dRECT dstRect = {0, 0, width, height}; if( g2d_getG2dFormat_from_pixman(format, &dst_format)) return -1; read_buf = g2d_alloc(width * height * 4, 0); if( !read_buf) return -1; if(go->drm_hw_buffer && gr->use_drm) { srcSurface = go->drm_hw_buffer; } else { if(go->nNumBuffers > 1 || go->directBlit) { srcSurface = &go->renderSurf[go->activebuffer]; } else { srcSurface = &go->offscreenSurface; } } dstSurface.base.planes[0] = read_buf->buf_paddr; dstSurface.base.format = dst_format; dstSurface.base.width = width; dstSurface.base.height = height; dstSurface.base.stride = width; dstSurface.base.rot = G2D_FLIP_V; if(g2d_blit_surface(gr->handle, srcSurface, &dstSurface, &srcRect, &dstRect)) { g2d_free(read_buf); return -1; } g2d_finish(gr->handle); memcpy(pixels, read_buf->buf_vaddr, width * height * PIXMAN_FORMAT_BPP(format)/8); g2d_free(read_buf); return 0; } static int g2d_int_from_double(double d) { return wl_fixed_to_int(wl_fixed_from_double(d)); } static void repaint_region(struct weston_view *ev, struct weston_output *output, struct g2d_output_state *go, pixman_region32_t *region, pixman_region32_t *surf_region){ struct g2d_renderer *gr = get_renderer(ev->surface->compositor); struct g2d_surface_state *gs = get_surface_state(ev->surface); pixman_box32_t *rects, *surf_rects, *bb_rects; int i, j, nrects, nsurf, nbb=0; g2dRECT srcRect = {0}; g2dRECT dstrect = {0}; g2dRECT clipRect = {0}; int dstWidth = 0; int dstHeight = 0; struct g2d_surfaceEx *dstsurface; struct g2d_surfaceEx srcsurface = gs->g2d_surface; uint32_t view_transform = ev->surface->buffer_viewport.buffer.transform; int src_x = wl_fixed_to_int (ev->surface->buffer_viewport.buffer.src_x); int src_y = wl_fixed_to_int (ev->surface->buffer_viewport.buffer.src_y); int src_width = wl_fixed_to_int (ev->surface->buffer_viewport.buffer.src_width); int src_height = wl_fixed_to_int (ev->surface->buffer_viewport.buffer.src_height); int scale = ev->surface->buffer_viewport.buffer.scale; if (ev->alpha < 1.0) { /* Skip the render for global alpha, a workaround to disable the fade effect, it created garbage info in the sequence test.*/ return; } bb_rects = pixman_region32_rectangles(&ev->transform.boundingbox, &nbb); if(!gs->attached || nbb <= 0) { return; } rects = pixman_region32_rectangles(region, &nrects); surf_rects = pixman_region32_rectangles(surf_region, &nsurf); if(src_width != -1 && src_width > 0 && src_x >=0 && src_y >= 0 && src_x < gs->g2d_surface.base.width && src_y < gs->g2d_surface.base.height) { srcRect.left = src_x * scale; srcRect.top = src_y * scale; srcRect.right = min (gs->g2d_surface.base.width, (src_x + src_width) * scale); srcRect.bottom = min (gs->g2d_surface.base.height, (src_y + src_height) * scale); } else { srcRect.left = srcsurface.base.left; srcRect.top = srcsurface.base.top; srcRect.right = srcsurface.base.right; srcRect.bottom = srcsurface.base.bottom; } if(go->drm_hw_buffer && gr->use_drm) { dstsurface = go->drm_hw_buffer; } else { if(go->nNumBuffers > 1 || go->directBlit) { dstsurface = &go->renderSurf[go->activebuffer]; } else { dstsurface = &go->offscreenSurface; } } dstWidth = dstsurface->base.width; dstHeight = dstsurface->base.height; /*Calculate the destrect once for all*/ dstrect.left = bb_rects[0].x1; dstrect.top = bb_rects[0].y1; dstrect.right = bb_rects[0].x2; dstrect.bottom = bb_rects[0].y2; /*Multi display support*/ if(output->x > 0) { dstrect.left = dstrect.left - output->x; dstrect.right = dstrect.right - output->x; } calculate_rect_with_transform(dstsurface->base.width, dstsurface->base.height, output->transform, &dstrect); if(view_transform != output->transform) { g2d_clip_rects(output->transform, &srcRect, &dstrect, dstWidth, dstHeight); srcsurface.base.rot = convert_transform_to_rot(output->transform); } else { /* if the view is transformed by compositor, we only need handle input crop * according to dst rect */ g2d_clip_rects(WL_OUTPUT_TRANSFORM_NORMAL, &srcRect, &dstrect, dstWidth, dstHeight); } for (i = 0; i < nrects; i++) { pixman_box32_t *rect = &rects[i]; gctFLOAT min_x, max_x, min_y, max_y; for (j = 0; j < nsurf; j++) { pixman_box32_t *surf_rect = &surf_rects[j]; gctFLOAT ex[8], ey[8]; /* edge points in screen space */ int n; int m=0; n = calculate_edges(ev, rect, surf_rect, ex, ey); if (n < 3) continue; min_x = max_x = ex[0]; min_y = max_y = ey[0]; for (m = 1; m < n; m++) { min_x = min(min_x, ex[m]); max_x = max(max_x, ex[m]); min_y = min(min_y, ey[m]); max_y = max(max_y, ey[m]); } clipRect.left = g2d_int_from_double(min_x); clipRect.top = g2d_int_from_double(min_y); clipRect.right = g2d_int_from_double(max_x); clipRect.bottom = g2d_int_from_double(max_y); if(output->x > 0) { clipRect.left = clipRect.left - output->x; clipRect.right = clipRect.right - output->x; } /* Need compute the clip rect with transform */ calculate_rect_with_transform(dstsurface->base.width, dstsurface->base.height, output->transform, &clipRect); if(clipRect.left >= clipRect.right || clipRect.top >= clipRect.bottom) { return; } g2d_set_clipping(gr->handle, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); if(gs->g2d_surface.base.width == 1 && gs->g2d_surface.base.height == 1 && gs->set_shm_buf_clrcolor ==1) { struct g2d_surface tmp_surface = dstsurface->base; memcpy(&tmp_surface.left, &clipRect.left, sizeof(clipRect)); tmp_surface.clrcolor = gs->shm_buf_clrcolor; g2d_clear(gr->handle, &tmp_surface); } else { g2d_blit_surface(gr->handle, &srcsurface, dstsurface, &srcRect, &dstrect); } } } } static int sync_wait(int fd, int timeout) { struct pollfd fds; int ret; if (fd < 0) { errno = EINVAL; return -1; } fds.fd = fd; fds.events = POLLIN; do { ret = poll(&fds, 1, timeout); if (ret > 0) { if (fds.revents & (POLLERR | POLLNVAL)) { errno = EINVAL; return -1; } return 0; } else if (ret == 0) { errno = ETIME; return -1; } } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); return ret; } static int ensure_surface_buffer_is_ready(struct g2d_renderer *gr, struct g2d_surface_state *gs) { int ret = 0; struct weston_surface *surface = gs->surface; struct weston_buffer *buffer = gs->buffer_ref.buffer; if (!buffer) return 0; if(!wl_shm_buffer_get(buffer->resource) && !linux_dmabuf_buffer_get(buffer->resource)){ /*Update vivBuffer and set g2d surface */ struct wl_viv_buffer *vivBuffer = wl_resource_get_user_data(buffer->resource); if (gr->update_buffer) gr->update_buffer(gr->egl_display, (void *)buffer->resource, EGL_WAYLAND_BUFFER_WL); ret = get_g2dSurface(vivBuffer, &gs->g2d_surface); if (ret < 0) return ret; } if (surface->acquire_fence_fd < 0) return 0; ret = sync_wait(surface->acquire_fence_fd, 2000); if (ret < 0 && errno == ETIME){ /* Print a warning. */ weston_log("%s: Warning: wait for fence fd=%d", __func__, surface->acquire_fence_fd); /* Wait for ever. */ ret = sync_wait(surface->acquire_fence_fd, -1); } return ret; } static void draw_view(struct weston_view *ev, struct weston_output *output, pixman_region32_t *damage) /* in global coordinates */ { struct weston_compositor *ec = ev->surface->compositor; struct g2d_output_state *go = get_output_state(output); struct g2d_surface_state *gs = get_surface_state(ev->surface); struct g2d_renderer *gr = get_renderer(ec); /* repaint bounding region in global coordinates: */ pixman_region32_t repaint; /* opaque region in surface coordinates: */ pixman_region32_t surface_opaque; /* non-opaque region in surface coordinates: */ pixman_region32_t surface_blend; pixman_region32_init(&repaint); pixman_region32_intersect(&repaint, &ev->transform.boundingbox, damage); pixman_region32_subtract(&repaint, &repaint, &ev->clip); if (!pixman_region32_not_empty(&repaint)) goto out; if (ensure_surface_buffer_is_ready(gr, gs) < 0) goto out; /* blended region is whole surface minus opaque region: */ pixman_region32_init_rect(&surface_blend, 0, 0, ev->surface->width, ev->surface->height); if (ev->geometry.scissor_enabled) pixman_region32_intersect(&surface_blend, &surface_blend, &ev->geometry.scissor); pixman_region32_subtract(&surface_blend, &surface_blend, &ev->surface->opaque); /* XXX: Should we be using ev->transform.opaque here? */ pixman_region32_init(&surface_opaque); if (ev->geometry.scissor_enabled) pixman_region32_intersect(&surface_opaque, &ev->surface->opaque, &ev->geometry.scissor); else pixman_region32_copy(&surface_opaque, &ev->surface->opaque); if (pixman_region32_not_empty(&surface_opaque)) { if (ev->alpha < 1.0) { g2d_enable(gr->handle, G2D_BLEND); g2d_enable(gr->handle, G2D_GLOBAL_ALPHA); gs->g2d_surface.base.global_alpha = ev->alpha * 0xFF; } repaint_region(ev, output, go, &repaint, &surface_opaque); g2d_disable(gr->handle, G2D_GLOBAL_ALPHA); g2d_disable(gr->handle, G2D_BLEND); } if (pixman_region32_not_empty(&surface_blend)) { g2d_enable(gr->handle, G2D_BLEND); if (ev->alpha < 1.0) { g2d_enable(gr->handle, G2D_GLOBAL_ALPHA); gs->g2d_surface.base.global_alpha = ev->alpha * 0xFF; } repaint_region(ev, output, go, &repaint, &surface_blend); g2d_disable(gr->handle, G2D_GLOBAL_ALPHA); g2d_disable(gr->handle, G2D_BLEND); } pixman_region32_fini(&surface_blend); pixman_region32_fini(&surface_opaque); out: pixman_region32_fini(&repaint); } static void repaint_views(struct weston_output *output, pixman_region32_t *damage) { struct weston_compositor *compositor = output->compositor; struct weston_view *view; wl_list_for_each_reverse(view, &compositor->view_list, link) if (view->plane == &compositor->primary_plane) draw_view(view, output, damage); } static void output_get_damage(struct weston_output *output, pixman_region32_t *buffer_damage) { struct g2d_output_state *go = get_output_state(output); int i; for (i = 0; i < BUFFER_DAMAGE_COUNT; i++) pixman_region32_union(buffer_damage, buffer_damage, &go->buffer_damage[i]); } static void output_rotate_damage(struct weston_output *output, pixman_region32_t *output_damage) { struct g2d_output_state *go = get_output_state(output); go->current_buffer = (go->current_buffer + 1) % BUFFER_DAMAGE_COUNT; pixman_region32_copy(&go->buffer_damage[go->current_buffer], output_damage); } #if G2D_VERSION_MAJOR >= 2 && defined(BUILD_DRM_COMPOSITOR) static void g2d_update_buffer_release_fences(struct weston_compositor *compositor, int fence_fd) { struct weston_view *view; wl_list_for_each_reverse(view, &compositor->view_list, link) { struct g2d_surface_state *gs; struct weston_buffer_release *buffer_release; if (view->plane != &compositor->primary_plane) continue; gs = get_surface_state(view->surface); buffer_release = gs->buffer_release_ref.buffer_release; if(!buffer_release) { continue; } /* If we have a buffer_release then it means we support fences, * and we should be able to create the release fence. If we * can't, something has gone horribly wrong, so disconnect the * client. */ if (fence_fd == -1) { fd_clear(&buffer_release->fence_fd); continue; } fd_update(&buffer_release->fence_fd, dup(fence_fd)); } } #endif static void g2d_renderer_repaint_output(struct weston_output *output, pixman_region32_t *output_damage) { struct weston_compositor *compositor = output->compositor; struct g2d_renderer *gr = get_renderer(compositor); pixman_region32_t buffer_damage, total_damage; #if G2D_VERSION_MAJOR >= 2 && defined(BUILD_DRM_COMPOSITOR) struct g2d_output_state *go = get_output_state(output); #endif int fence_fd = -1; use_output(output); pixman_region32_init(&total_damage); pixman_region32_init(&buffer_damage); output_get_damage(output, &buffer_damage); output_rotate_damage(output, output_damage); pixman_region32_union(&total_damage, &buffer_damage, output_damage); repaint_views(output, &total_damage); pixman_region32_fini(&total_damage); pixman_region32_fini(&buffer_damage); #if G2D_VERSION_MAJOR >= 2 && defined(BUILD_DRM_COMPOSITOR) fence_fd = g2d_create_fence_fd(gr->handle); g2d_update_buffer_release_fences(compositor, fence_fd); fd_clear(&go->drm_hw_buffer->reserved[0]); go->drm_hw_buffer->reserved[0] = fence_fd; #endif if(fence_fd == -1) g2d_finish(gr->handle); wl_signal_emit(&output->frame_signal, output_damage); if(!gr->use_drm) copy_to_framebuffer(output, output_damage); } static void g2d_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) { struct wl_viv_buffer *vivBuffer = wl_resource_get_user_data(buffer->resource); buffer->width = vivBuffer->width; buffer->height = vivBuffer->height; } static void g2d_renderer_copy_shm_buffer(struct g2d_surface_state *gs, struct weston_buffer *buffer) { int alignedWidth = ALIGN_TO_16(buffer->width); int height = 0; uint8_t *src = wl_shm_buffer_get_data(buffer->shm_buffer); uint8_t *dst = gs->shm_buf->buf_vaddr; int bpp = gs->bpp; int plane_size[3] = {0,}; int src_plane_offset[3] = {0,}; int dst_plane_offset[3] = {0,}; int uv_src_stride = 0; int uv_dst_stride = 0; int n_planes = 0; int i, j; switch (wl_shm_buffer_get_format(buffer->shm_buffer)) { case WL_SHM_FORMAT_XRGB8888: case WL_SHM_FORMAT_ARGB8888: case WL_SHM_FORMAT_RGB565: n_planes = 1; height = buffer->height; plane_size[0] = wl_shm_buffer_get_stride(buffer->shm_buffer)*buffer->height; break; case WL_SHM_FORMAT_YUYV: n_planes = 1; height = ALIGN_TO_16(buffer->height); plane_size[0] = wl_shm_buffer_get_stride(buffer->shm_buffer)*buffer->height; break; case WL_SHM_FORMAT_NV12: n_planes = 2; height = ALIGN_TO_16(buffer->height); plane_size[0] = wl_shm_buffer_get_stride(buffer->shm_buffer)*buffer->height; plane_size[1] = wl_shm_buffer_get_stride(buffer->shm_buffer)*buffer->height / 2; src_plane_offset[1] = plane_size[0]; dst_plane_offset[1] = alignedWidth * height; uv_src_stride = wl_shm_buffer_get_stride(buffer->shm_buffer); uv_dst_stride = alignedWidth; break; case WL_SHM_FORMAT_YUV420: n_planes = 3; height = ALIGN_TO_16(buffer->height); plane_size[0] = wl_shm_buffer_get_stride(buffer->shm_buffer)*buffer->height; plane_size[1] = wl_shm_buffer_get_stride(buffer->shm_buffer)*buffer->height / 4; plane_size[2] = plane_size[1]; src_plane_offset[1] = plane_size[0]; src_plane_offset[2] = plane_size[0] + plane_size[1]; dst_plane_offset[1] = alignedWidth * height; dst_plane_offset[2] = dst_plane_offset[1] + alignedWidth * height / 4; uv_src_stride = wl_shm_buffer_get_stride(buffer->shm_buffer) / 2; uv_dst_stride = alignedWidth / 2; break; default: weston_log("warning: copy shm buffer meet unknown format: %08x\n", wl_shm_buffer_get_format(buffer->shm_buffer)); return; } wl_shm_buffer_begin_access(buffer->shm_buffer); if(alignedWidth == buffer->width && height == buffer->height) { for (i = 0; i < n_planes; i++) memcpy (dst + dst_plane_offset[i], src + src_plane_offset[i], plane_size[i]); } else { int src_stride = wl_shm_buffer_get_stride(buffer->shm_buffer); int dst_stride = alignedWidth * bpp; /* copy the 1st plane */ for (i = 0; i < buffer->height; i++) { memcpy(dst + dst_plane_offset[0] + dst_stride * i, src + src_plane_offset[0] + src_stride * i, src_stride); } /* copy the rest plane */ for (i = 1; i < n_planes; i++) { for (j = 0; j < buffer->height / 2; j++) { memcpy(dst + dst_plane_offset[i] + uv_dst_stride * j, src + src_plane_offset[i] + uv_src_stride * j, uv_src_stride); } } /* Some garbage was found in 6dl board, This workaround will use * g2d_clear instead of g2d_blit for this special source */ if(buffer->width == 1 && buffer->height == 1 && wl_shm_buffer_get_format(buffer->shm_buffer) == WL_SHM_FORMAT_ARGB8888) { uint8_t *clrcolor = gs->shm_buf->buf_vaddr; gs->shm_buf_clrcolor = (clrcolor[0] << 16) | (clrcolor[1] << 8) | (clrcolor[2]) | (clrcolor[3] << 24); gs->set_shm_buf_clrcolor = 1; } } wl_shm_buffer_end_access(buffer->shm_buffer); } static void g2d_renderer_flush_damage(struct weston_surface *surface) { struct g2d_surface_state *gs = get_surface_state(surface); struct weston_buffer *buffer = gs->buffer_ref.buffer; struct weston_view *view; int texture_used; pixman_region32_union(&gs->texture_damage, &gs->texture_damage, &surface->damage); if (!buffer) return; texture_used = 0; wl_list_for_each(view, &surface->views, surface_link) { if (view->plane == &surface->compositor->primary_plane) { texture_used = 1; break; } } if (!texture_used) return; if (!pixman_region32_not_empty(&gs->texture_damage)) goto done; if(wl_shm_buffer_get(buffer->resource)) { g2d_renderer_copy_shm_buffer(gs, buffer); } else { g2d_renderer_attach_egl(surface, buffer); } done: pixman_region32_fini(&gs->texture_damage); pixman_region32_init(&gs->texture_damage); weston_buffer_reference(&gs->buffer_ref, NULL); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); } static void g2d_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, struct wl_shm_buffer *shm_buffer) { struct g2d_surface_state *gs = get_surface_state(es); int buffer_length = 0; int alloc_new_buff = 1; int alignedWidth = 0; int height = 0; enum g2d_format g2dFormat = 0; buffer->shm_buffer = shm_buffer; buffer->width = wl_shm_buffer_get_width(shm_buffer); buffer->height = wl_shm_buffer_get_height(shm_buffer); alignedWidth = ALIGN_TO_16(buffer->width); switch (wl_shm_buffer_get_format(shm_buffer)) { case WL_SHM_FORMAT_XRGB8888: g2dFormat = G2D_BGRX8888; gs->bpp = 4; break; case WL_SHM_FORMAT_ARGB8888: g2dFormat = G2D_BGRA8888; gs->bpp = 4; break; case WL_SHM_FORMAT_RGB565: g2dFormat = G2D_RGB565; gs->bpp = 2; break; case WL_SHM_FORMAT_YUYV: g2dFormat = G2D_YUYV; height = ALIGN_TO_16(buffer->height); buffer_length = alignedWidth * height * 2; gs->bpp = 2; break; case WL_SHM_FORMAT_YUV420: g2dFormat = G2D_I420; height = ALIGN_TO_16(buffer->height); buffer_length = alignedWidth * height * 3/2; gs->bpp = 1; break; case WL_SHM_FORMAT_NV12: g2dFormat = G2D_NV12; height = ALIGN_TO_16(buffer->height); buffer_length = alignedWidth * height * 3/2; gs->bpp = 1; break; default: weston_log("warning: unknown shm buffer format: %08x\n", wl_shm_buffer_get_format(shm_buffer)); return; } if (height == 0) height = buffer->height; if (buffer_length == 0) buffer_length = alignedWidth * buffer->height * gs->bpp; /* Only allocate a new g2d buff if it is larger than existing one.*/ gs->shm_buf_length = buffer_length; if(gs->shm_buf && gs->shm_buf->buf_size > buffer_length) { alloc_new_buff = 0; } if(alloc_new_buff) { if(gs->shm_buf) g2d_free(gs->shm_buf); gs->shm_buf = g2d_alloc(buffer_length, 0); gs->g2d_surface.base.planes[0] = gs->shm_buf->buf_paddr; gs->g2d_surface.base.planes[1] = gs->g2d_surface.base.planes[0] + alignedWidth * height; gs->g2d_surface.base.planes[2] = gs->g2d_surface.base.planes[1] + alignedWidth * height / 4; } gs->g2d_surface.base.left = 0; gs->g2d_surface.base.top = 0; gs->g2d_surface.base.right = buffer->width; gs->g2d_surface.base.bottom = buffer->height; gs->g2d_surface.base.stride = alignedWidth; gs->g2d_surface.base.width = buffer->width; gs->g2d_surface.base.height = height; gs->g2d_surface.base.rot = G2D_ROTATION_0; gs->g2d_surface.base.clrcolor = 0xFF400000; gs->g2d_surface.tiling = G2D_LINEAR; gs->g2d_surface.base.format = g2dFormat; } static void g2d_renderer_get_g2dformat_from_dmabuf(uint32_t dmaformat, enum g2d_format *g2dFormat, int *bpp) { switch (dmaformat) { case DRM_FORMAT_ARGB8888: *g2dFormat = G2D_BGRA8888; *bpp = 4; break; case DRM_FORMAT_ABGR8888: *g2dFormat = G2D_RGBA8888; *bpp = 4; break; case DRM_FORMAT_XRGB8888: *g2dFormat = G2D_BGRX8888; *bpp = 4; break; case DRM_FORMAT_RGB565: *g2dFormat = G2D_RGB565; *bpp = 2; break; case DRM_FORMAT_YUYV: *g2dFormat = G2D_YUYV; *bpp = 2; break; case DRM_FORMAT_NV12: *g2dFormat = G2D_NV12; *bpp = 1; break; case DRM_FORMAT_YUV420: *g2dFormat = G2D_I420; *bpp = 1; break; default: *g2dFormat = -1; weston_log("warning: unknown dmabuf buffer format: %08x\n", dmaformat); break; } } static void g2d_renderer_attach_dmabuf(struct weston_surface *es, struct weston_buffer *buffer, struct linux_dmabuf_buffer *dmabuf) { struct g2d_surface_state *gs = get_surface_state(es); int alignedWidth = 0, alignedHeight = 0; enum g2d_format g2dFormat; gctUINT32 *paddr; int i = 0; int bpp = 1; buffer->width = dmabuf->attributes.width; buffer->height = dmabuf->attributes.height; if(dmabuf->attributes.modifier[0] == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED) { alignedWidth = ALIGN_TO_64(buffer->width); alignedHeight = ALIGN_TO_64(buffer->height); } else { alignedWidth = ALIGN_TO_16(buffer->width); alignedHeight = ALIGN_TO_16(buffer->height); } g2d_renderer_get_g2dformat_from_dmabuf(dmabuf->attributes.format, &g2dFormat, &bpp); if (g2dFormat < 0) return; paddr = (gctUINT32 *)linux_dmabuf_buffer_get_user_data(dmabuf); for (i = 0; i < dmabuf->attributes.n_planes; i++) { gs->g2d_surface.base.planes[i] = paddr[i] + dmabuf->attributes.offset[i]; } gs->g2d_surface.base.left = 0; gs->g2d_surface.base.top = 0; gs->g2d_surface.base.right = buffer->width; gs->g2d_surface.base.bottom = buffer->height; gs->g2d_surface.base.width = alignedWidth; gs->g2d_surface.base.height = alignedHeight; gs->g2d_surface.base.rot = G2D_ROTATION_0; if (dmabuf->attributes.modifier[0] == DRM_FORMAT_MOD_AMPHION_TILED) { gs->g2d_surface.base.stride = dmabuf->attributes.stride[0]; gs->g2d_surface.tiling = G2D_AMPHION_TILED; } else if(dmabuf->attributes.modifier[0] == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED){ gs->g2d_surface.base.stride = alignedWidth; gs->g2d_surface.tiling = G2D_SUPERTILED; } else { gs->g2d_surface.base.stride = dmabuf->attributes.stride[0] / bpp; gs->g2d_surface.tiling = G2D_LINEAR; } gs->g2d_surface.base.format = g2dFormat; } static void g2d_renderer_query_dmabuf_formats(struct weston_compositor *wc, int **formats, int *num_formats) { int num; static const int dma_formats[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB565, DRM_FORMAT_YUYV, DRM_FORMAT_NV12, DRM_FORMAT_YUV420, }; num = ARRAY_LENGTH(dma_formats); *formats = calloc(num, sizeof(int)); memcpy(*formats, dma_formats, num * sizeof(int)); *num_formats = num; } static void g2d_renderer_query_dmabuf_modifiers(struct weston_compositor *wc, int format, uint64_t **modifiers, int *num_modifiers) { *num_modifiers = 1; *modifiers = calloc(*num_modifiers, sizeof(uint64_t)); (*modifiers)[0] = DRM_FORMAT_MOD_LINEAR; } static void free_paddr_buf (struct linux_dmabuf_buffer *buffer) { gctUINT32 * paddr = (gctUINT32 *)buffer->user_data; if (paddr) free (paddr); } static bool g2d_renderer_import_dmabuf(struct weston_compositor *wc, struct linux_dmabuf_buffer *dmabuf) { struct g2d_buf *g2dBuf = NULL; enum g2d_format g2dFormat; gctUINT32 *paddr = NULL; int i = 0, dmafd = -1; int bpp = 1; if (!dmabuf) return false; g2d_renderer_get_g2dformat_from_dmabuf(dmabuf->attributes.format, &g2dFormat, &bpp); if (g2dFormat < 0) return false; paddr = malloc (sizeof (gctUINT32) * dmabuf->attributes.n_planes); if (!paddr) return false; for (i = 0; i < dmabuf->attributes.n_planes; i++) { dmafd = dup(dmabuf->attributes.fd[i]); if (dmafd == -1) goto failed; if (g2dBuf) g2d_free(g2dBuf); g2dBuf = g2d_buf_from_fd(dmafd); if(!g2dBuf) goto failed; paddr[i] = g2dBuf->buf_paddr; close(dmafd); } if(!g2dBuf) return false; else g2d_free(g2dBuf); linux_dmabuf_buffer_set_user_data(dmabuf, (void *)paddr, free_paddr_buf); return true; failed: if (paddr) free(paddr); if(g2dBuf) g2d_free(g2dBuf); if (dmafd != -1) close(dmafd); return false; } static void g2d_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { struct g2d_surface_state *gs = get_surface_state(es); struct wl_shm_buffer *shm_buffer; struct linux_dmabuf_buffer *dmabuf; weston_buffer_reference(&gs->buffer_ref, buffer); weston_buffer_release_reference(&gs->buffer_release_ref, es->buffer_release_ref.buffer_release); if (!buffer) { gs->attached = 0; return; } shm_buffer = wl_shm_buffer_get(buffer->resource); if(shm_buffer) { g2d_renderer_attach_shm(es, buffer, shm_buffer); } else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource))) { g2d_renderer_attach_dmabuf(es, buffer, dmabuf); } else { g2d_renderer_attach_egl(es, buffer); } gs->attached = 1; } static void surface_state_destroy(struct g2d_surface_state *gs, struct g2d_renderer *gr) { wl_list_remove(&gs->surface_destroy_listener.link); wl_list_remove(&gs->renderer_destroy_listener.link); if(gs->surface) gs->surface->renderer_state = NULL; if(gs->shm_buf) { g2d_free(gs->shm_buf); gs->shm_buf = NULL; } if(gs->dma_buf) { g2d_free(gs->dma_buf); gs->dma_buf = NULL; } weston_buffer_reference(&gs->buffer_ref, NULL); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); free(gs); } static void surface_state_handle_surface_destroy(struct wl_listener *listener, void *data) { struct g2d_surface_state *gs; struct g2d_renderer *gr; gs = container_of(listener, struct g2d_surface_state, surface_destroy_listener); gr = get_renderer(gs->surface->compositor); surface_state_destroy(gs, gr); } static void surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data) { struct g2d_surface_state *gs; struct g2d_renderer *gr; gr = data; gs = container_of(listener, struct g2d_surface_state, renderer_destroy_listener); surface_state_destroy(gs, gr); } static int g2d_renderer_create_surface(struct weston_surface *surface) { struct g2d_surface_state *gs; struct g2d_renderer *gr = get_renderer(surface->compositor); gs = zalloc(sizeof *gs); if (gs == NULL) return -1; /* A buffer is never attached to solid color surfaces, yet * they still go through texcoord computations. Do not divide * by zero there. */ gs->pitch = 1; gs->surface = surface; pixman_region32_init(&gs->texture_damage); surface->renderer_state = gs; gs->surface_destroy_listener.notify = surface_state_handle_surface_destroy; wl_signal_add(&surface->destroy_signal, &gs->surface_destroy_listener); gs->renderer_destroy_listener.notify = surface_state_handle_renderer_destroy; wl_signal_add(&gr->destroy_signal, &gs->renderer_destroy_listener); if (surface->buffer_ref.buffer) { g2d_renderer_attach(surface, surface->buffer_ref.buffer); g2d_renderer_flush_damage(surface); } return 0; } static void g2d_renderer_surface_set_color(struct weston_surface *surface, float red, float green, float blue, float alpha) { struct g2d_surface_state *gs = get_surface_state(surface); gs->color[0] = red; gs->color[1] = green; gs->color[2] = blue; gs->color[3] = alpha; } /* create use-g2d-renderer */ static void create_g2d_file(struct weston_output *output) { char *dir, *path; FILE *fp = NULL; dir = getenv("XDG_RUNTIME_DIR"); path = malloc(strlen(dir) + 40); strcpy(path, dir); strcat(path, "/use-g2d-renderer"); fp = fopen(path, "w"); if(fp) { weston_log("Create File %s\n", path); if(output->transform == WL_OUTPUT_TRANSFORM_90 || output->transform == WL_OUTPUT_TRANSFORM_270) { fprintf(fp, "transform=1\n"); } fclose(fp); } free(path); } static void remove_g2d_file() { char *dir, *path; dir = getenv("XDG_RUNTIME_DIR"); path = malloc(strlen(dir) + 40); strcpy(path, dir); strcat(path, "/use-g2d-renderer"); remove(path); free(path); } static void g2d_renderer_output_destroy(struct weston_output *output) { struct g2d_output_state *go = get_output_state(output); int i; for (i = 0; i < 2; i++) { pixman_region32_fini(&go->buffer_damage[i]); } if(go->offscreen_buf) { g2d_free(go->offscreen_buf); go->offscreen_buf = NULL; } if(go->fb_info.fb_fd) { close(go->fb_info.fb_fd); go->fb_info.fb_fd = 0; } if(go->renderSurf) { free(go->renderSurf); go->renderSurf = NULL; } for (i = 0; i < go->clone_display_num; i++) { if(go->mirror_fb_info[i].fb_fd) { close(go->mirror_fb_info[i].fb_fd); go->mirror_fb_info[i].fb_fd = 0; } } if(go->mirrorSurf) { free(go->mirrorSurf); go->mirrorSurf = NULL; } if(go->mirror_fb_info) { free(go->mirror_fb_info); go->mirror_fb_info = NULL; } fd_clear(&go->drm_hw_buffer->reserved[0]); remove_g2d_file(); free(go); } static void g2d_renderer_destroy(struct weston_compositor *ec) { struct g2d_renderer *gr = get_renderer(ec); wl_signal_emit(&gr->destroy_signal, gr); g2d_close(gr->handle); #ifdef ENABLE_EGL if(gr->bind_display) gr->bind_display(gr->egl_display, gr->wl_display); eglTerminate(gr->egl_display); if(!gr->use_drm) fbDestroyDisplay(gr->display); #endif free(ec->renderer); ec->renderer = NULL; } static int g2d_renderer_create(struct weston_compositor *ec) { struct g2d_renderer *gr; gr = calloc(1, sizeof *gr); if (gr == NULL) return -1; gr->base.read_pixels = g2d_renderer_read_pixels; gr->base.repaint_output = g2d_renderer_repaint_output; gr->base.flush_damage = g2d_renderer_flush_damage; gr->base.attach = g2d_renderer_attach; gr->base.surface_set_color = g2d_renderer_surface_set_color; gr->base.destroy = g2d_renderer_destroy; gr->base.import_dmabuf = g2d_renderer_import_dmabuf; gr->base.query_dmabuf_formats = g2d_renderer_query_dmabuf_formats; gr->base.query_dmabuf_modifiers = g2d_renderer_query_dmabuf_modifiers; ec->renderer = &gr->base; #ifdef ENABLE_EGL gr->bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL"); gr->unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL"); gr->update_buffer = (void *) eglGetProcAddress("eglUpdateWaylandBufferWL"); if (!get_platform_display) { get_platform_display = (void *) eglGetProcAddress( "eglGetPlatformDisplayEXT"); } ec->capabilities |= WESTON_CAP_EXPLICIT_SYNC; #endif if(g2d_open(&gr->handle)) { weston_log("g2d_open fail.\n"); return -1; } ec->capabilities |= WESTON_CAP_ROTATION_ANY; ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP; ec->capabilities |= WESTON_CAP_VIEW_CLIP_MASK; ec->read_format = PIXMAN_a8r8g8b8; wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_YUV420); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_NV12); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_YUYV); wl_signal_init(&gr->destroy_signal); return 0; } static int g2d_drm_display_create(struct weston_compositor *ec, void *native_window) { struct g2d_renderer *gr; if(g2d_renderer_create(ec) < 0) { weston_log("g2d_renderer_create faile.\n"); return -1; } #ifdef ENABLE_EGL gr = get_renderer(ec); gr->wl_display = ec->wl_display; if(get_platform_display) gr->egl_display = get_platform_display(EGL_PLATFORM_GBM_KHR, native_window, NULL); if(gr->bind_display) gr->bind_display(gr->egl_display, gr->wl_display); #endif gr->use_drm = 1; return 0; } static void g2d_renderer_output_set_buffer(struct weston_output *output, struct g2d_surfaceEx *buffer) { struct g2d_output_state *go = get_output_state(output); go->drm_hw_buffer = buffer; } static int g2d_renderer_get_surface_fence_fd(struct g2d_surfaceEx *buffer) { return buffer->reserved[0]; } static int g2d_drm_renderer_output_create(struct weston_output *output) { struct g2d_output_state *go; int i = 0; go = zalloc(sizeof *go); if (go == NULL) return -1; output->renderer_state = go; for (i = 0; i < BUFFER_DAMAGE_COUNT; i++) pixman_region32_init(&go->buffer_damage[i]); create_g2d_file(output); return 0; } static int drm_create_g2d_image(struct g2d_surfaceEx* g2dSurface, enum g2d_format g2dFormat, void *vaddr, int w, int h, int stride, int size, int dmafd) { struct g2d_buf * buffer = NULL; buffer = g2d_buf_from_fd(dmafd); if (!buffer) return -1; buffer->buf_vaddr = vaddr; buffer->buf_size = size; g2dSurface->base.planes[0] = buffer->buf_paddr; g2dSurface->base.left = 0; g2dSurface->base.top = 0; g2dSurface->base.right = w; g2dSurface->base.bottom = h; g2dSurface->base.stride = w; g2dSurface->base.width = w; g2dSurface->base.height = h; g2dSurface->base.format = g2dFormat; g2dSurface->base.rot = G2D_ROTATION_0; g2dSurface->base.clrcolor = 0xFF400000; g2dSurface->tiling = G2D_LINEAR; g2dSurface->reserved[0] = -1; return 0; } static int calculate_g2d_format(struct fb_var_screeninfo *varinfo, enum g2d_format *g2dFormat) { /* Get the color format. */ switch (varinfo->green.length) { case 6: *g2dFormat= G2D_RGB565; break; case 8: if (varinfo->blue.offset == 0) { *g2dFormat = (varinfo->transp.length == 0) ? G2D_BGRX8888 : G2D_BGRA8888; } else { *g2dFormat = (varinfo->transp.length == 0) ? G2D_RGBX8888 : G2D_RGBA8888; } break; default: *g2dFormat = -1; break; } return 0; } static int get_G2dSurface_from_screeninfo(struct fb_screeninfo *info, struct g2d_surfaceEx* g2dSurface) { if(info && g2dSurface) { g2dSurface->base.planes[0] = info->physical; g2dSurface->base.left = 0; g2dSurface->base.top = 0; g2dSurface->base.right = info->x_resolution; g2dSurface->base.bottom = info->y_resolution; g2dSurface->base.stride = info->stride; g2dSurface->base.width = info->x_resolution; g2dSurface->base.height = info->y_resolution; g2dSurface->base.format = info->pixel_format; g2dSurface->base.rot = G2D_ROTATION_0; g2dSurface->base.clrcolor = 0xFF400000; g2dSurface->tiling = G2D_LINEAR; return 0; } return -1; } static int fb_query_screen_info(struct g2d_output_state *output, int fd, struct fb_screeninfo *info) { struct g2d_output_state *go = output; struct fb_var_screeninfo *varinfo = &info->varinfo; struct fb_fix_screeninfo *fixinfo = &info->fixinfo; /* Probe the device for screen information. */ if (ioctl(fd, FBIOGET_VSCREENINFO, varinfo) < 0) { return -1; } if(go->nNumBuffers > 1){ varinfo->yres_virtual = varinfo->yres * go->nNumBuffers; if (ioctl(fd, FBIOPUT_VSCREENINFO, varinfo) < 0) return -1; } if (ioctl(fd, FBIOGET_FSCREENINFO, fixinfo) < 0 || ioctl(fd, FBIOGET_VSCREENINFO, varinfo) < 0){ return -1; } /* Store the pertinent data. */ info->x_resolution = varinfo->xres; info->y_resolution = varinfo->yres; info->physical = fixinfo->smem_start; info->buffer_length = fixinfo->smem_len; info->stride = fixinfo->line_length / (varinfo->bits_per_pixel >> 3); info->stride_bytes = fixinfo->line_length; calculate_g2d_format(varinfo, &info->pixel_format); if (info->pixel_format < 0) { weston_log("Frame buffer uses an unsupported format.\n"); return -1; } return 0; } static int fb_frame_buffer_open(struct g2d_output_state *output, const char *fb_dev, struct fb_screeninfo *screen_info) { /* Open the frame buffer device. */ screen_info->fb_fd = open(fb_dev, O_RDWR | O_CLOEXEC); if (screen_info->fb_fd < 0) { weston_log("Failed to open frame buffer device%s \n", fb_dev); return -1; } /* Grab the screen info. */ if (fb_query_screen_info(output, screen_info->fb_fd, screen_info) < 0) { weston_log("Failed to get frame buffer info \n"); close(screen_info->fb_fd); return -1; } return 0; } static void getBufferNumber(struct g2d_output_state *go) { char *p = NULL; p = getenv("FB_MULTI_BUFFER"); if (p == gcvNULL) { go->nNumBuffers = BUFFER_DAMAGE_COUNT; } else { go->nNumBuffers = atoi(p); if (go->nNumBuffers < 2) { go->nNumBuffers = 1; } else if(go->nNumBuffers >= BUFFER_DAMAGE_COUNT) { go->nNumBuffers = BUFFER_DAMAGE_COUNT; go->activebuffer = 1; } } weston_log("The number of the Framebuffer: %d\n", go->nNumBuffers); } static int g2d_renderer_surface_create(struct g2d_output_state *go, struct g2d_renderer *gr, const char *device) { int i = 0; int offset = 0; weston_log("Opend device=%s\n", device); if(fb_frame_buffer_open(go, device, &go->fb_info) < 0) { weston_log("Open frame buffer failed.\n"); return -1; } go->renderSurf = zalloc(sizeof(struct g2d_surfaceEx) * go->nNumBuffers); offset = go->fb_info.stride_bytes * go->fb_info.y_resolution; for(i = 0; i < go->nNumBuffers; i++) { get_G2dSurface_from_screeninfo(&go->fb_info, &go->renderSurf[i]); go->renderSurf[i].base.planes[0] = go->fb_info.physical + (offset * i); g2d_clear(gr->handle, &go->renderSurf[i].base); } if(go->nNumBuffers == 1) { go->offscreenSurface = (go->renderSurf[go->activebuffer]); go->offscreen_buf = g2d_alloc(go->fb_info.buffer_length, 0); go->offscreenSurface.base.planes[0] = go->offscreen_buf->buf_paddr; g2d_clear(gr->handle, &go->offscreenSurface.base); } return 0; } static int g2d_fbdev_renderer_output_create(struct weston_output *output, struct wl_display *wl_display, const char *device) { struct g2d_renderer *gr = get_renderer(output->compositor); struct g2d_output_state *go; int i; int clone_display_num = 0; int count = 0; int k=0, dispCount = 0; char displays[5][32]; weston_log("g2d_renderer_output_create device=%s\n", device); count = strlen(device); if(count > 0) { for(i= 0; i < count; i++) { if(device[i] == ',') { displays[dispCount][k] = '\0'; dispCount++; k = 0; continue; } else if(device[i] != ' ') { displays[dispCount][k++] = device[i]; } } displays[dispCount][k] = '\0'; clone_display_num = dispCount++; weston_log("clone_display_num = %d\n", clone_display_num); } else { weston_log("Invalid device name\n"); return -1; } go = zalloc(sizeof *go); if (go == NULL) return -1; go->clone_display_num = clone_display_num; output->renderer_state = go; #ifdef ENABLE_EGL gr->wl_display = wl_display; gr->display = fbGetDisplay(wl_display); gr->egl_display = eglGetDisplay(gr->display); if(gr->bind_display) gr->bind_display(gr->egl_display, wl_display); #endif getBufferNumber(go); if(g2d_renderer_surface_create(go, gr, displays[0]) < 0) { weston_log("Create Render surface failed.\n"); return -1; } if(go->clone_display_num) { go->mirrorSurf = zalloc(sizeof(struct g2d_surfaceEx) * clone_display_num); go->mirror_fb_info = zalloc(sizeof(struct fb_screeninfo) * clone_display_num); if(go->mirrorSurf == NULL || go->mirror_fb_info == NULL) return -1; for(i = 0; i < clone_display_num; i++) { if(fb_frame_buffer_open(go, displays[i + 1], &go->mirror_fb_info[i]) < 0) { weston_log("Open frame buffer failed.\n"); return -1; } get_G2dSurface_from_screeninfo(&go->mirror_fb_info[i], &go->mirrorSurf[i]); go->mirrorSurf[i].base.planes[0] = go->mirror_fb_info[i].physical; g2d_clear(gr->handle, &go->mirrorSurf[i].base); } } g2d_finish(gr->handle); if(go->renderSurf->base.format == G2D_RGB565) g2d_enable(gr->handle, G2D_DITHER); for (i = 0; i < BUFFER_DAMAGE_COUNT; i++) pixman_region32_init(&go->buffer_damage[i]); create_g2d_file(output); return 0; } WL_EXPORT struct g2d_renderer_interface g2d_renderer_interface = { .create = g2d_renderer_create, .drm_display_create = g2d_drm_display_create, .drm_output_create = g2d_drm_renderer_output_create, .fbdev_output_create = g2d_fbdev_renderer_output_create, .create_g2d_image = drm_create_g2d_image, .output_set_buffer = g2d_renderer_output_set_buffer, .output_destroy = g2d_renderer_output_destroy, .get_surface_fence_fd = g2d_renderer_get_surface_fence_fd, };