diff options
author | marha <marha@users.sourceforge.net> | 2014-01-26 20:05:50 +0100 |
---|---|---|
committer | marha <marha@users.sourceforge.net> | 2014-01-26 20:10:14 +0100 |
commit | 30af30b78075159fce477ae99cc72540133714d0 (patch) | |
tree | 1028af42bd030d09bf9c9cb6085665300326abc6 /xorg-server/glamor | |
parent | 775780ea274e6602c2d64de33a98ee35979cc330 (diff) | |
download | vcxsrv-30af30b78075159fce477ae99cc72540133714d0.tar.gz vcxsrv-30af30b78075159fce477ae99cc72540133714d0.tar.bz2 vcxsrv-30af30b78075159fce477ae99cc72540133714d0.zip |
xserver randrproto libxtrans fontconfig libxcb xcb-proto mesa git update 26 Jan 2014
xserver commit c1ce807d9f18f215332d7eeb844e8c640f71c53c
libxcb commit e7263931aff3e3450dc938ad465a7577f943549f
libxcb/xcb-proto commit d898fd39ad6c82207eb78666b2daad982dd757b5
randrproto commit a4a6694c059d74247c16527eef4a0ec9f56bbef6
libxtrans commit e1e6121a1638d43d9929589b4723da2b38cb6b44
fontconfig commit e2b406053c2937799da8636c56b72a77998bcab0
mesa commit 07149f0252c52b4ac58b6df4e307fd786b49b490
Diffstat (limited to 'xorg-server/glamor')
42 files changed, 20996 insertions, 0 deletions
diff --git a/xorg-server/glamor/Makefile.am b/xorg-server/glamor/Makefile.am new file mode 100644 index 000000000..2fd521f11 --- /dev/null +++ b/xorg-server/glamor/Makefile.am @@ -0,0 +1,71 @@ +lib_LTLIBRARIES = libglamor.la + +if GLAMOR_GLES2 +libglamor_la_LIBADD = $(GLESV2_LIBS) +else +libglamor_la_LIBADD = $(GL_LIBS) +endif + +AM_CFLAGS = $(CWARNFLAGS) $(XORG_CFLAGS) $(LIBDRM_CFLAGS) + +libglamor_la_LDFLAGS = -version-info 0:0:0 + +libglamor_la_SOURCES = \ + compat-api.h \ + compiler.h \ + glamor.c \ + glamor_copyarea.c \ + glamor_copywindow.c \ + glamor_core.c \ + glamor_debug.h \ + glamor_gl_dispatch.h \ + glamor_fill.c \ + glamor_fillspans.c \ + glamor_getspans.c \ + glamor_glext.h \ + glamor_glyphs.c \ + glamor_polyfillrect.c \ + glamor_polylines.c \ + glamor_putimage.c \ + glamor_setspans.c \ + glamor_render.c \ + glamor_gradient.c \ + glamor_trapezoid.c \ + glamor_tile.c \ + glamor_triangles.c\ + glamor_addtraps.c\ + glamor_getimage.c\ + glamor_copyplane.c\ + glamor_glyphblt.c\ + glamor_polyops.c\ + glamor_priv.h\ + glamor_pixmap.c\ + glamor_largepixmap.c\ + glamor_picture.c\ + glamor_window.c\ + glamor_gl_dispatch.c\ + glamor_fbo.c\ + glamor_compositerects.c\ + glamor_xv.c\ + glamor_utils.h\ + glamor.h\ + glapi.h + +sdk_HEADERS = glamor.h + +if EGL +LIBGLAMOREGL = libglamoregl.la +module_LTLIBRARIES = $(LIBGLAMOREGL) +libglamoregl_la_DEPENDENCIES = libglamor.la +libglamoregl_la_LDFLAGS = -avoid-version -module +libglamoregl_la_LIBADD = $(EGL_LIBS) $(GLX_SYS_LIBS) $(GBM_LIBS) libglamor.la +libglamoregl_la_SOURCES = glamor_eglmodule.c glamor_egl.c +libglamoregl_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(GLX_DEFINES) \ + $(LIBDRM_CFLAGS) \ + $(EGL_CFLAGS) \ + $(GBM_CFLAGS) +endif + + diff --git a/xorg-server/glamor/compat-api.h b/xorg-server/glamor/compat-api.h new file mode 100644 index 000000000..1608478f8 --- /dev/null +++ b/xorg-server/glamor/compat-api.h @@ -0,0 +1,107 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * 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. + * + * Author: Dave Airlie <airlied@redhat.com> + */ + +/* this file provides API compat between server post 1.13 and pre it, + it should be reused inside as many drivers as possible */ +#ifndef COMPAT_API_H +#define COMPAT_API_H + +#ifndef GLYPH_HAS_GLYPH_PICTURE_ACCESSOR +#define GetGlyphPicture(g, s) GlyphPicture((g))[(s)->myNum] +#define SetGlyphPicture(g, s, p) GlyphPicture((g))[(s)->myNum] = p +#endif + +#ifndef XF86_HAS_SCRN_CONV +#define xf86ScreenToScrn(s) xf86Screens[(s)->myNum] +#define xf86ScrnToScreen(s) screenInfo.screens[(s)->scrnIndex] +#endif + +#ifndef XF86_SCRN_INTERFACE + +#define SCRN_ARG_TYPE int +#define SCRN_INFO_PTR(arg1) ScrnInfoPtr scrn = xf86Screens[(arg1)] + +#define SCREEN_ARG_TYPE int +#define SCREEN_PTR(arg1) ScreenPtr screen = screenInfo.screens[(arg1)] + +#define SCREEN_INIT_ARGS_DECL int scrnIndex, ScreenPtr screen, int argc, char **argv + +#define BLOCKHANDLER_ARGS_DECL int arg, pointer blockData, pointer timeout, pointer read_mask +#define BLOCKHANDLER_ARGS arg, blockData, timeout, read_mask + +#define WAKEUPHANDLER_ARGS_DECL int arg, pointer wakeupData, unsigned long result, pointer read_mask +#define WAKEUPHANDLER_ARGS arg, wakeupData, result, read_mask + +#define CLOSE_SCREEN_ARGS_DECL int scrnIndex, ScreenPtr screen +#define CLOSE_SCREEN_ARGS scrnIndex, screen + +#define ADJUST_FRAME_ARGS_DECL int arg, int x, int y, int flags +#define ADJUST_FRAME_ARGS(arg, x, y) (arg)->scrnIndex, x, y, 0 + +#define SWITCH_MODE_ARGS_DECL int arg, DisplayModePtr mode, int flags +#define SWITCH_MODE_ARGS(arg, m) (arg)->scrnIndex, m, 0 + +#define FREE_SCREEN_ARGS_DECL int arg, int flags +#define FREE_SCREEN_ARGS arg, flags + +#define VT_FUNC_ARGS_DECL int arg, int flags +#define VT_FUNC_ARGS(flags) scrn->scrnIndex, (flags) + +#define XF86_ENABLEDISABLEFB_ARG(x) ((x)->scrnIndex) + +#else +#define SCRN_ARG_TYPE ScrnInfoPtr +#define SCRN_INFO_PTR(arg1) ScrnInfoPtr scrn = (arg1) + +#define SCREEN_ARG_TYPE ScreenPtr +#define SCREEN_PTR(arg1) ScreenPtr screen = (arg1) + +#define SCREEN_INIT_ARGS_DECL ScreenPtr screen, int argc, char **argv + +#define BLOCKHANDLER_ARGS_DECL ScreenPtr arg, pointer timeout, pointer read_mask +#define BLOCKHANDLER_ARGS arg, timeout, read_mask + +#define WAKEUPHANDLER_ARGS_DECL ScreenPtr arg, unsigned long result, pointer read_mask +#define WAKEUPHANDLER_ARGS arg, result, read_mask + +#define CLOSE_SCREEN_ARGS_DECL ScreenPtr screen +#define CLOSE_SCREEN_ARGS screen + +#define ADJUST_FRAME_ARGS_DECL ScrnInfoPtr arg, int x, int y +#define ADJUST_FRAME_ARGS(arg, x, y) arg, x, y + +#define SWITCH_MODE_ARGS_DECL ScrnInfoPtr arg, DisplayModePtr mode +#define SWITCH_MODE_ARGS(arg, m) arg, m + +#define FREE_SCREEN_ARGS_DECL ScrnInfoPtr arg +#define FREE_SCREEN_ARGS arg + +#define VT_FUNC_ARGS_DECL ScrnInfoPtr arg +#define VT_FUNC_ARGS(flags) scrn + +#define XF86_ENABLEDISABLEFB_ARG(x) (x) + +#endif +#endif diff --git a/xorg-server/glamor/compiler.h b/xorg-server/glamor/compiler.h new file mode 100644 index 000000000..fa2895976 --- /dev/null +++ b/xorg-server/glamor/compiler.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Intel Corporation + * + * 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. + * + * Authors: + * Chris Wilson <chris@chris-wilson.co.uk> + * + * Copied from sna + * + */ + +#ifndef _GLAMOR_COMPILER_H_ +#define _GLAMOR_COMPILER_H_ + +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#define likely(expr) (__builtin_expect (!!(expr), 1)) +#define unlikely(expr) (__builtin_expect (!!(expr), 0)) +#define noinline __attribute__((noinline)) +#define fastcall __attribute__((regparm(3))) +#define must_check __attribute__((warn_unused_result)) +#define constant __attribute__((const)) +#else +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#define noinline +#define fastcall +#define must_check +#define constant +#endif + +#ifdef HAVE_VALGRIND +#define VG(x) x +#else +#define VG(x) +#endif + +#define VG_CLEAR(s) VG(memset(&s, 0, sizeof(s))) + +#define COMPILE_TIME_ASSERT(E) ((void)sizeof(char[1 - 2*!(E)])) + +#endif /* _SNA_COMPILER_H_ */ diff --git a/xorg-server/glamor/glamor.c b/xorg-server/glamor/glamor.c new file mode 100644 index 000000000..93d3c5e72 --- /dev/null +++ b/xorg-server/glamor/glamor.c @@ -0,0 +1,628 @@ +/* + * Copyright © 2008 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + +/** @file glamor.c + * This file covers the initialization and teardown of glamor, and has various + * functions not responsible for performing rendering. + */ + +#include <stdlib.h> + +#include "glamor_priv.h" + +static DevPrivateKeyRec glamor_screen_private_key_index; +DevPrivateKey glamor_screen_private_key = &glamor_screen_private_key_index; +static DevPrivateKeyRec glamor_pixmap_private_key_index; +DevPrivateKey glamor_pixmap_private_key = &glamor_pixmap_private_key_index; + +/** + * glamor_get_drawable_pixmap() returns a backing pixmap for a given drawable. + * + * @param drawable the drawable being requested. + * + * This function returns the backing pixmap for a drawable, whether it is a + * redirected window, unredirected window, or already a pixmap. Note that + * coordinate translation is needed when drawing to the backing pixmap of a + * redirected window, and the translation coordinates are provided by calling + * exaGetOffscreenPixmap() on the drawable. + */ +PixmapPtr +glamor_get_drawable_pixmap(DrawablePtr drawable) +{ + if (drawable->type == DRAWABLE_WINDOW) + return drawable-> + pScreen->GetWindowPixmap((WindowPtr) drawable); + else + return (PixmapPtr) drawable; +} + +_X_EXPORT void +glamor_set_pixmap_type(PixmapPtr pixmap, glamor_pixmap_type_t type) +{ + glamor_pixmap_private *pixmap_priv; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(pixmap->drawable.pScreen); + + pixmap_priv = dixLookupPrivate(&pixmap->devPrivates, + glamor_pixmap_private_key); + if (pixmap_priv == NULL) { + pixmap_priv = calloc(sizeof(*pixmap_priv), 1); + glamor_set_pixmap_private(pixmap, pixmap_priv); + pixmap_priv->base.pixmap = pixmap; + pixmap_priv->base.glamor_priv = glamor_priv; + } + pixmap_priv->type = type; +} + +_X_EXPORT void +glamor_set_pixmap_texture(PixmapPtr pixmap, unsigned int tex) +{ + ScreenPtr screen = pixmap->drawable.pScreen; + glamor_pixmap_private *pixmap_priv; + glamor_screen_private *glamor_priv; + glamor_pixmap_fbo *fbo; + GLenum format; + + glamor_priv = glamor_get_screen_private(screen); + pixmap_priv = glamor_get_pixmap_private(pixmap); + + if (pixmap_priv->base.fbo) { + fbo = glamor_pixmap_detach_fbo(pixmap_priv); + glamor_destroy_fbo(fbo); + } + + gl_iformat_for_depth(pixmap->drawable.depth, &format); + fbo = glamor_create_fbo_from_tex(glamor_priv, pixmap->drawable.width, + pixmap->drawable.height, + format, tex, 0); + + if (fbo == NULL) { + ErrorF("XXX fail to create fbo.\n"); + return; + } + + glamor_pixmap_attach_fbo(pixmap, fbo); +} + +void +glamor_set_screen_pixmap(PixmapPtr screen_pixmap, PixmapPtr *back_pixmap) +{ + glamor_pixmap_private *pixmap_priv; + glamor_screen_private *glamor_priv; + + glamor_priv = glamor_get_screen_private(screen_pixmap->drawable.pScreen); + pixmap_priv = glamor_get_pixmap_private(screen_pixmap); + glamor_priv->screen_fbo = pixmap_priv->base.fbo->fb; + + pixmap_priv->base.fbo->width = screen_pixmap->drawable.width; + pixmap_priv->base.fbo->height = screen_pixmap->drawable.height; + + glamor_priv->back_pixmap = back_pixmap; +} + +PixmapPtr +glamor_create_pixmap(ScreenPtr screen, int w, int h, int depth, + unsigned int usage) +{ + PixmapPtr pixmap; + glamor_pixmap_type_t type = GLAMOR_TEXTURE_ONLY; + glamor_pixmap_private *pixmap_priv; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_pixmap_fbo *fbo; + int pitch; + GLenum format; + + if (w > 32767 || h > 32767) + return NullPixmap; + + if ((usage == GLAMOR_CREATE_PIXMAP_CPU + || (usage == CREATE_PIXMAP_USAGE_GLYPH_PICTURE && w <= 64 && h <= 64) + || (w == 0 && h == 0) + || !glamor_check_pixmap_fbo_depth(depth)) + || (!GLAMOR_TEXTURED_LARGE_PIXMAP && + !glamor_check_fbo_size(glamor_priv, w, h))) + return fbCreatePixmap(screen, w, h, depth, usage); + else + pixmap = fbCreatePixmap(screen, 0, 0, depth, usage); + + pixmap_priv = calloc(1, sizeof(*pixmap_priv)); + + if (!pixmap_priv) { + fbDestroyPixmap(pixmap); + return fbCreatePixmap(screen, w, h, depth, usage); + } + glamor_set_pixmap_private(pixmap, pixmap_priv); + + if (usage == GLAMOR_CREATE_PIXMAP_MAP) + type = GLAMOR_MEMORY_MAP; + + pixmap_priv->base.pixmap = pixmap; + pixmap_priv->base.glamor_priv = glamor_priv; + + gl_iformat_for_depth(depth, &format); + + pitch = (((w * pixmap->drawable.bitsPerPixel + 7) / 8) + 3) & ~3; + screen->ModifyPixmapHeader(pixmap, w, h, 0, 0, pitch, NULL); + + if (type == GLAMOR_MEMORY_MAP || glamor_check_fbo_size(glamor_priv, w, h)) { + pixmap_priv->type = type; + fbo = glamor_create_fbo(glamor_priv, w, h, format, usage); + } + else { + DEBUGF("Create LARGE pixmap %p width %d height %d\n", pixmap, w, h); + pixmap_priv->type = GLAMOR_TEXTURE_LARGE; + fbo = glamor_create_fbo_array(glamor_priv, w, h, format, usage, + glamor_priv->max_fbo_size, + glamor_priv->max_fbo_size, + pixmap_priv); + } + + if (fbo == NULL) { + fbDestroyPixmap(pixmap); + free(pixmap_priv); + return fbCreatePixmap(screen, w, h, depth, usage); + } + + glamor_pixmap_attach_fbo(pixmap, fbo); + + return pixmap; +} + +void +glamor_destroy_textured_pixmap(PixmapPtr pixmap) +{ + if (pixmap->refcnt == 1) { + glamor_pixmap_private *pixmap_priv; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (pixmap_priv != NULL) + glamor_pixmap_destroy_fbo(pixmap_priv); + } +} + +Bool +glamor_destroy_pixmap(PixmapPtr pixmap) +{ + glamor_screen_private + *glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); + if (glamor_priv->dri3_enabled) + glamor_egl_destroy_textured_pixmap(pixmap); + else + glamor_destroy_textured_pixmap(pixmap); + return fbDestroyPixmap(pixmap); +} + +void +glamor_block_handler(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_gl_dispatch *dispatch; + + dispatch = glamor_get_dispatch(glamor_priv); + glamor_priv->tick++; + dispatch->glFlush(); + glamor_fbo_expire(glamor_priv); + glamor_put_dispatch(glamor_priv); + if (glamor_priv->state == RENDER_STATE + && glamor_priv->render_idle_cnt++ > RENDER_IDEL_MAX) { + glamor_priv->state = IDLE_STATE; + glamor_priv->render_idle_cnt = 0; + } +} + +static void +_glamor_block_handler(void *data, OSTimePtr timeout, + void *last_select_mask) +{ + glamor_screen_private *glamor_priv = data; + glamor_gl_dispatch *dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glFlush(); + glamor_put_dispatch(glamor_priv); +} + +static void +_glamor_wakeup_handler(void *data, int result, void *last_select_mask) +{ +} + +static void +glamor_set_debug_level(int *debug_level) +{ + char *debug_level_string; + debug_level_string = getenv("GLAMOR_DEBUG"); + if (debug_level_string + && sscanf(debug_level_string, "%d", debug_level) == 1) + return; + *debug_level = 0; +} + +int glamor_debug_level; + +/** Set up glamor for an already-configured GL context. */ +Bool +glamor_init(ScreenPtr screen, unsigned int flags) +{ + glamor_screen_private *glamor_priv; + int gl_version; + +#ifdef RENDER + PictureScreenPtr ps = GetPictureScreenIfSet(screen); +#endif + if (flags & ~GLAMOR_VALID_FLAGS) { + ErrorF("glamor_init: Invalid flags %x\n", flags); + return FALSE; + } + glamor_priv = calloc(1, sizeof(*glamor_priv)); + if (glamor_priv == NULL) + return FALSE; + + if (flags & GLAMOR_INVERTED_Y_AXIS) { + glamor_priv->yInverted = 1; + } else + glamor_priv->yInverted = 0; + + if (!dixRegisterPrivateKey + (glamor_screen_private_key, PRIVATE_SCREEN, 0)) { + LogMessage(X_WARNING, + "glamor%d: Failed to allocate screen private\n", + screen->myNum); + goto fail; + } + + glamor_set_screen_private(screen, glamor_priv); + + if (!dixRegisterPrivateKey + (glamor_pixmap_private_key, PRIVATE_PIXMAP, 0)) { + LogMessage(X_WARNING, + "glamor%d: Failed to allocate pixmap private\n", + screen->myNum); + goto fail;; + } + + gl_version = glamor_gl_get_version(); + +#ifndef GLAMOR_GLES2 + if (gl_version < GLAMOR_GL_VERSION_ENCODE(1, 3)) { + ErrorF("Require OpenGL version 1.3 or latter.\n"); + goto fail; + } +#else + if (gl_version < GLAMOR_GL_VERSION_ENCODE(2, 0)) { + ErrorF("Require Open GLES2.0 or latter.\n"); + goto fail; + } +#endif + + glamor_gl_dispatch_init(screen, &glamor_priv->_dispatch, gl_version); + +#ifdef GLAMOR_GLES2 + if (!glamor_gl_has_extension("GL_EXT_texture_format_BGRA8888")) { + ErrorF("GL_EXT_texture_format_BGRA8888 required\n"); + goto fail; + } +#endif + + glamor_priv->has_pack_invert = + glamor_gl_has_extension("GL_MESA_pack_invert"); + glamor_priv->has_fbo_blit = + glamor_gl_has_extension("GL_EXT_framebuffer_blit"); + glamor_priv->_dispatch.glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, + &glamor_priv->max_fbo_size); +#ifdef MAX_FBO_SIZE + glamor_priv->max_fbo_size = MAX_FBO_SIZE; +#endif + + glamor_set_debug_level(&glamor_debug_level); + +#ifdef GLAMOR_GLES2 + glamor_priv->gl_flavor = GLAMOR_GL_ES2; +#else + glamor_priv->gl_flavor = GLAMOR_GL_DESKTOP; +#endif + /* If we are using egl screen, call egl screen init to + * register correct close screen function. */ + if (flags & GLAMOR_USE_EGL_SCREEN) + glamor_egl_screen_init(screen); + + glamor_priv->saved_procs.close_screen = screen->CloseScreen; + screen->CloseScreen = glamor_close_screen; + + if (flags & GLAMOR_USE_SCREEN) { + if (!RegisterBlockAndWakeupHandlers(_glamor_block_handler, + _glamor_wakeup_handler, + glamor_priv)) { + goto fail; + } + + glamor_priv->saved_procs.create_gc = screen->CreateGC; + screen->CreateGC = glamor_create_gc; + + glamor_priv->saved_procs.create_pixmap = screen->CreatePixmap; + screen->CreatePixmap = glamor_create_pixmap; + + glamor_priv->saved_procs.destroy_pixmap = screen->DestroyPixmap; + screen->DestroyPixmap = glamor_destroy_pixmap; + + glamor_priv->saved_procs.get_spans = screen->GetSpans; + screen->GetSpans = glamor_get_spans; + + glamor_priv->saved_procs.get_image = screen->GetImage; + screen->GetImage = glamor_get_image; + + glamor_priv->saved_procs.change_window_attributes = + screen->ChangeWindowAttributes; + screen->ChangeWindowAttributes = + glamor_change_window_attributes; + + glamor_priv->saved_procs.copy_window = screen->CopyWindow; + screen->CopyWindow = glamor_copy_window; + + glamor_priv->saved_procs.bitmap_to_region = + screen->BitmapToRegion; + screen->BitmapToRegion = glamor_bitmap_to_region; + } +#ifdef RENDER + if (flags & GLAMOR_USE_PICTURE_SCREEN) { + glamor_priv->saved_procs.composite = ps->Composite; + ps->Composite = glamor_composite; + + + glamor_priv->saved_procs.trapezoids = ps->Trapezoids; + ps->Trapezoids = glamor_trapezoids; + + + glamor_priv->saved_procs.triangles = ps->Triangles; + ps->Triangles = glamor_triangles; + + glamor_priv->saved_procs.addtraps = ps->AddTraps; + ps->AddTraps = glamor_add_traps; + + } + + glamor_priv->saved_procs.composite_rects = ps->CompositeRects; + ps->CompositeRects = glamor_composite_rectangles; + + glamor_priv->saved_procs.glyphs = ps->Glyphs; + ps->Glyphs = glamor_glyphs; + + glamor_priv->saved_procs.unrealize_glyph = ps->UnrealizeGlyph; + ps->UnrealizeGlyph = glamor_glyph_unrealize; + + glamor_priv->saved_procs.create_picture = ps->CreatePicture; + ps->CreatePicture = glamor_create_picture; + + glamor_priv->saved_procs.set_window_pixmap = screen->SetWindowPixmap; + screen->SetWindowPixmap = glamor_set_window_pixmap; + + glamor_priv->saved_procs.destroy_picture = ps->DestroyPicture; + ps->DestroyPicture = glamor_destroy_picture; + glamor_init_composite_shaders(screen); +#endif + glamor_init_pixmap_fbo(screen); + glamor_init_solid_shader(screen); + glamor_init_tile_shader(screen); +#ifdef GLAMOR_TRAPEZOID_SHADER + glamor_init_trapezoid_shader(screen); +#endif + glamor_init_putimage_shaders(screen); + glamor_init_finish_access_shaders(screen); +#ifdef GLAMOR_GRADIENT_SHADER + glamor_init_gradient_shader(screen); +#endif +#ifdef GLAMOR_XV + glamor_init_xv_shader(screen); +#endif + glamor_pixmap_init(screen); + + glamor_priv->flags = flags; + glamor_priv->screen = screen; + + return TRUE; + + fail: + free(glamor_priv); + glamor_set_screen_private(screen, NULL); + return FALSE; +} + +static void +glamor_release_screen_priv(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + + glamor_priv = glamor_get_screen_private(screen); +#ifdef GLAMOR_XV + glamor_fini_xv_shader(screen); +#endif +#ifdef RENDER + glamor_fini_composite_shaders(screen); +#endif + glamor_fini_pixmap_fbo(screen); + glamor_fini_solid_shader(screen); + glamor_fini_tile_shader(screen); +#ifdef GLAMOR_TRAPEZOID_SHADER + glamor_fini_trapezoid_shader(screen); +#endif + glamor_fini_putimage_shaders(screen); + glamor_fini_finish_access_shaders(screen); +#ifdef GLAMOR_GRADIENT_SHADER + glamor_fini_gradient_shader(screen); +#endif + glamor_pixmap_fini(screen); + free(glamor_priv); + + glamor_set_screen_private(screen, NULL); +} + +_X_EXPORT void +glamor_set_pixmap_private(PixmapPtr pixmap, glamor_pixmap_private *priv) +{ + glamor_pixmap_private *old_priv; + glamor_pixmap_fbo *fbo; + + old_priv = dixGetPrivate(&pixmap->devPrivates, glamor_pixmap_private_key); + + if (priv) { + assert(old_priv == NULL); + } else { + if (old_priv == NULL) + return; + fbo = glamor_pixmap_detach_fbo(old_priv); + glamor_purge_fbo(fbo); + free(old_priv); + } + + dixSetPrivate(&pixmap->devPrivates, + glamor_pixmap_private_key, + priv); +} + +Bool +glamor_close_screen(CLOSE_SCREEN_ARGS_DECL) +{ + glamor_screen_private *glamor_priv; + PixmapPtr screen_pixmap; + int flags; +#ifdef RENDER + PictureScreenPtr ps = GetPictureScreenIfSet(screen); +#endif + glamor_priv = glamor_get_screen_private(screen); + flags = glamor_priv->flags; + glamor_glyphs_fini(screen); + screen->CloseScreen = glamor_priv->saved_procs.close_screen; + if (flags & GLAMOR_USE_SCREEN) { + + screen->CreateGC = glamor_priv->saved_procs.create_gc; + screen->CreatePixmap = glamor_priv->saved_procs.create_pixmap; + screen->DestroyPixmap = glamor_priv->saved_procs.destroy_pixmap; + screen->GetSpans = glamor_priv->saved_procs.get_spans; + screen->ChangeWindowAttributes = + glamor_priv->saved_procs.change_window_attributes; + screen->CopyWindow = glamor_priv->saved_procs.copy_window; + screen->BitmapToRegion = glamor_priv->saved_procs.bitmap_to_region; + } +#ifdef RENDER + if (ps && (flags & GLAMOR_USE_PICTURE_SCREEN)) { + + ps->Composite = glamor_priv->saved_procs.composite; + ps->Trapezoids = glamor_priv->saved_procs.trapezoids; + ps->Triangles = glamor_priv->saved_procs.triangles; + ps->CreatePicture = glamor_priv->saved_procs.create_picture; + } + ps->CompositeRects = glamor_priv->saved_procs.composite_rects; + ps->Glyphs = glamor_priv->saved_procs.glyphs; + ps->UnrealizeGlyph = glamor_priv->saved_procs.unrealize_glyph; + screen->SetWindowPixmap = glamor_priv->saved_procs.set_window_pixmap; +#endif + screen_pixmap = screen->GetScreenPixmap(screen); + glamor_set_pixmap_private(screen_pixmap, NULL); + if (glamor_priv->back_pixmap && *glamor_priv->back_pixmap) + glamor_set_pixmap_private(*glamor_priv->back_pixmap, NULL); + + glamor_release_screen_priv(screen); + + return screen->CloseScreen(CLOSE_SCREEN_ARGS); +} + + +void +glamor_fini(ScreenPtr screen) +{ + /* Do nothing currently. */ +} + +void glamor_enable_dri3(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_priv->dri3_enabled = TRUE; +} + +Bool glamor_is_dri3_support_enabled(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + return glamor_priv->dri3_enabled; +} + +int +glamor_dri3_fd_from_pixmap (ScreenPtr screen, + PixmapPtr pixmap, + CARD16 *stride, + CARD32 *size) +{ + glamor_pixmap_private *pixmap_priv; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(pixmap->drawable.pScreen); + + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (pixmap_priv == NULL || !glamor_priv->dri3_enabled) + return -1; + switch (pixmap_priv->type) + { + case GLAMOR_TEXTURE_DRM: + case GLAMOR_TEXTURE_ONLY: + glamor_pixmap_ensure_fbo(pixmap, GL_RGBA, 0); + return glamor_egl_dri3_fd_name_from_tex(screen, + pixmap, + pixmap_priv->base.fbo->tex, + FALSE, + stride, + size); + default: break; + } + return -1; +} + +int +glamor_dri3_name_from_pixmap (PixmapPtr pixmap) +{ + glamor_pixmap_private *pixmap_priv; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(pixmap->drawable.pScreen); + + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (pixmap_priv == NULL || !glamor_priv->dri3_enabled) + return -1; + switch (pixmap_priv->type) + { + case GLAMOR_TEXTURE_DRM: + case GLAMOR_TEXTURE_ONLY: + glamor_pixmap_ensure_fbo(pixmap, GL_RGBA, 0); + return glamor_egl_dri3_fd_name_from_tex(pixmap->drawable.pScreen, + pixmap, + pixmap_priv->base.fbo->tex, + TRUE, + NULL, + NULL); + default: break; + } + return -1; +} diff --git a/xorg-server/glamor/glamor.h b/xorg-server/glamor/glamor.h new file mode 100644 index 000000000..1bb48ed74 --- /dev/null +++ b/xorg-server/glamor/glamor.h @@ -0,0 +1,432 @@ +/* + * Copyright © 2008 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + +#ifndef GLAMOR_H +#define GLAMOR_H + +#include <scrnintstr.h> +#include <xf86.h> +#include <xf86str.h> +#include <pixmapstr.h> +#include <gcstruct.h> +#include <picturestr.h> +#include <fb.h> +#include <fbpict.h> +#include <xf86xv.h> +/* + * glamor_pixmap_type : glamor pixmap's type. + * @MEMORY: pixmap is in memory. + * @TEXTURE_DRM: pixmap is in a texture created from a DRM buffer. + * @SEPARATE_TEXTURE: The texture is created from a DRM buffer, but + * the format is incompatible, so this type of pixmap + * will never fallback to DDX layer. + * @DRM_ONLY: pixmap is in a external DRM buffer. + * @TEXTURE_ONLY: pixmap is in an internal texture. + */ +typedef enum glamor_pixmap_type { + GLAMOR_MEMORY, + GLAMOR_MEMORY_MAP, + GLAMOR_TEXTURE_DRM, + GLAMOR_SEPARATE_TEXTURE, + GLAMOR_DRM_ONLY, + GLAMOR_TEXTURE_ONLY, + GLAMOR_TEXTURE_LARGE, + GLAMOR_TEXTURE_PACK +} glamor_pixmap_type_t; + +#define GLAMOR_EGL_EXTERNAL_BUFFER 3 +#define GLAMOR_INVERTED_Y_AXIS 1 +#define GLAMOR_USE_SCREEN (1 << 1) +#define GLAMOR_USE_PICTURE_SCREEN (1 << 2) +#define GLAMOR_USE_EGL_SCREEN (1 << 3) +#define GLAMOR_VALID_FLAGS (GLAMOR_INVERTED_Y_AXIS \ + | GLAMOR_USE_SCREEN \ + | GLAMOR_USE_PICTURE_SCREEN \ + | GLAMOR_USE_EGL_SCREEN) + +/* @glamor_init: Initialize glamor internal data structure. + * + * @screen: Current screen pointer. + * @flags: Please refer the flags description above. + * + * @GLAMOR_INVERTED_Y_AXIS: + * set 1 means the GL env's origin (0,0) is at top-left. + * EGL/DRM platform is an example need to set this bit. + * glx platform's origin is at bottom-left thus need to + * clear this bit. + * + * @GLAMOR_USE_SCREEN: + * If running in an pre-existing X environment, and the + * gl context is GLX, then you should set this bit and + * let the glamor to handle all the screen related + * functions such as GC ops and CreatePixmap/DestroyPixmap. + * + * @GLAMOR_USE_PICTURE_SCREEN: + * If don't use any other underlying DDX driver to handle + * the picture related rendering functions, please set this + * bit on. Otherwise, clear this bit. And then it is the DDX + * driver's responsibility to determine how/when to jump to + * glamor's picture compositing path. + * + * @GLAMOR_USE_EGL_SCREEN: + * If you are using EGL layer, then please set this bit + * on, otherwise, clear it. + * + * This function initializes necessary internal data structure + * for glamor. And before calling into this function, the OpenGL + * environment should be ready. Should be called before any real + * glamor rendering or texture allocation functions. And should + * be called after the DDX's screen initialization or at the last + * step of the DDX's screen initialization. + */ +extern _X_EXPORT Bool glamor_init(ScreenPtr screen, unsigned int flags); +extern _X_EXPORT void glamor_fini(ScreenPtr screen); + +/* This function is used to free the glamor private screen's + * resources. If the DDX driver is not set GLAMOR_USE_SCREEN, + * then, DDX need to call this function at proper stage, if + * it is the xorg DDX driver,then it should be called at free + * screen stage not the close screen stage. The reason is after + * call to this function, the xorg DDX may need to destroy the + * screen pixmap which must be a glamor pixmap and requires + * the internal data structure still exist at that time. + * Otherwise, the glamor internal structure will not be freed.*/ +#ifndef XF86_SCRN_INTERFACE +extern _X_EXPORT Bool glamor_close_screen(int scrnIndex, ScreenPtr screen); +#else +extern _X_EXPORT Bool glamor_close_screen(ScreenPtr screen); +#endif + + +/* Let glamor to know the screen's fbo. The low level + * driver should already assign a tex + * to this pixmap through the set_pixmap_texture. */ +extern _X_EXPORT void glamor_set_screen_pixmap(PixmapPtr screen_pixmap, PixmapPtr *back_pixmap); + +/* @glamor_glyphs_init: Initialize glyphs internal data structures. + * + * @pScreen: Current screen pointer. + * + * This function must be called after the glamor_init and the texture + * can be allocated. An example is to call it when create the screen + * resources at DDX layer. + */ +extern _X_EXPORT Bool glamor_glyphs_init(ScreenPtr pScreen); + +extern _X_EXPORT void glamor_set_pixmap_texture(PixmapPtr pixmap, + unsigned int tex); + +extern _X_EXPORT void glamor_set_pixmap_type(PixmapPtr pixmap, glamor_pixmap_type_t type); +extern _X_EXPORT void glamor_destroy_textured_pixmap(PixmapPtr pixmap); +extern _X_EXPORT void glamor_block_handler(ScreenPtr screen); +extern _X_EXPORT PixmapPtr glamor_create_pixmap(ScreenPtr screen, int w, int h, int depth, + unsigned int usage); + +extern _X_EXPORT void glamor_egl_screen_init(ScreenPtr screen); + +extern _X_EXPORT void glamor_egl_make_current(ScreenPtr screen); +extern _X_EXPORT void glamor_egl_restore_context(ScreenPtr screen); + +/* @glamor_egl_exchange_buffers: Exchange the underlying buffers(KHR image,fbo). + * + * @front: front pixmap. + * @back: back pixmap. + * + * Used by the DRI2 page flip. This function will exchange the KHR images and + * fbos of the two pixmaps. + * */ +extern _X_EXPORT void glamor_egl_exchange_buffers(PixmapPtr front, PixmapPtr back); + +extern _X_EXPORT void glamor_pixmap_exchange_fbos(PixmapPtr front, PixmapPtr back); + +/* The DDX is not supposed to call these three functions */ +extern _X_EXPORT void glamor_enable_dri3(ScreenPtr screen); +extern _X_EXPORT unsigned int glamor_egl_create_argb8888_based_texture(ScreenPtr screen, int w, int h); +extern _X_EXPORT int glamor_egl_dri3_fd_name_from_tex(ScreenPtr, PixmapPtr, unsigned int, Bool, CARD16*, CARD32*); + +/* @glamor_is_dri3_support_enabled: Returns if DRI3 support is enabled. + * + * @screen: Current screen pointer. + * + * To have DRI3 support enabled, glamor and glamor_egl need to be initialized, + * and glamor_egl_init_textured_pixmap need to be called. glamor also + * has to be compiled with gbm support. + * The EGL layer need to have the following extensions working: + * .EGL_KHR_gl_texture_2D_image + * .EGL_EXT_image_dma_buf_import + * If DRI3 support is not enabled, the following helpers will return an error. + * */ +extern _X_EXPORT Bool glamor_is_dri3_support_enabled(ScreenPtr screen); + +/* @glamor_dri3_fd_from_pixmap: DRI3 helper to get a dma-buf fd from a pixmap. + * + * @screen: Current screen pointer. + * @pixmap: The pixmap from which we want the fd. + * @stride, @size: Pointers to fill the stride and size of the + * buffer associated to the fd. + * + * the pixmap and the buffer associated by the fd will share the same + * content. + * Returns the fd on success, -1 on error. + * */ +extern _X_EXPORT int glamor_dri3_fd_from_pixmap (ScreenPtr screen, + PixmapPtr pixmap, + CARD16 *stride, + CARD32 *size); + +/* @glamor_dri3_name_from_pixmap: helper to get an gem name from a pixmap. + * + * @pixmap: The pixmap from which we want the gem name. + * + * the pixmap and the buffer associated by the gem name will share the same + * content. This function can be used by the DDX to support DRI2, but needs + * glamor DRI3 support to be activated. + * Returns the name on success, -1 on error. + * */ +extern _X_EXPORT int glamor_dri3_name_from_pixmap (PixmapPtr pixmap); + +/* @glamor_egl_dri3_pixmap_from_fd: DRI3 helper to get a pixmap from a dma-buf fd. + * + * @screen: Current screen pointer. + * @fd: The dma-buf fd to import. + * @width: The width of the buffer. + * @height: The height of the buffer. + * @stride: The stride of the buffer. + * @depth: The depth of the buffer. + * @bpp: The number of bpp of the buffer. + * + * Returns a valid pixmap if the import succeeded, else NULL. + * */ +extern _X_EXPORT PixmapPtr glamor_egl_dri3_pixmap_from_fd (ScreenPtr screen, + int fd, + CARD16 width, + CARD16 height, + CARD16 stride, + CARD8 depth, + CARD8 bpp); + +#ifdef GLAMOR_FOR_XORG + +#define GLAMOR_EGL_MODULE_NAME "glamoregl" + +/* @glamor_egl_init: Initialize EGL environment. + * + * @scrn: Current screen info pointer. + * @fd: Current drm fd. + * + * This function creates and intialize EGL contexts. + * Should be called from DDX's preInit function. + * Return TRUE if success, otherwise return FALSE. + * */ +extern _X_EXPORT Bool glamor_egl_init(ScrnInfoPtr scrn, int fd); + +/* @glamor_egl_init_textured_pixmap: Initialization for textured pixmap allocation. + * + * @screen: Current screen pointer. + * + * This function must be called before any textured pixmap's creation including + * the screen pixmap. Could be called from DDX's screenInit function after the calling + * to glamor_init.. + */ +extern _X_EXPORT Bool glamor_egl_init_textured_pixmap(ScreenPtr screen); + +/* @glamor_egl_create_textured_screen: Create textured screen pixmap. + * + * @screen: screen pointer to be processed. + * @handle: screen pixmap's BO handle. + * @stride: screen pixmap's stride in bytes. + * + * This function is similar with the create_textured_pixmap. As the + * screen pixmap is a special, we handle it separately in this function. + */ +extern _X_EXPORT Bool glamor_egl_create_textured_screen(ScreenPtr screen, + int handle, + int stride); + +/* @glamor_egl_create_textured_screen_ext: + * + * extent one parameter to track the pointer of the DDX layer's back pixmap. + * We need this pointer during the closing screen stage. As before back to + * the DDX's close screen, we have to free all the glamor related resources. + */ +extern _X_EXPORT Bool glamor_egl_create_textured_screen_ext(ScreenPtr screen, + int handle, + int stride, + PixmapPtr *back_pixmap); + +/* + * @glamor_egl_create_textured_pixmap: Try to create a textured pixmap from + * a BO handle. + * + * @pixmap: The pixmap need to be processed. + * @handle: The BO's handle attached to this pixmap at DDX layer. + * @stride: Stride in bytes for this pixmap. + * + * This function try to create a texture from the handle and attach + * the texture to the pixmap , thus glamor can render to this pixmap + * as well. Return true if successful, otherwise return FALSE. + */ +extern _X_EXPORT Bool glamor_egl_create_textured_pixmap(PixmapPtr pixmap, + int handle, + int stride); + +/* + * @glamor_egl_create_textured_pixmap_from_bo: Try to create a textured pixmap + * from a gbm_bo. + * + * @pixmap: The pixmap need to be processed. + * @bo: a pointer on a gbm_bo structure attached to this pixmap at DDX layer. + * + * This function is similar to glamor_egl_create_textured_pixmap. + */ +extern _X_EXPORT Bool + glamor_egl_create_textured_pixmap_from_gbm_bo(PixmapPtr pixmap, + void *bo); + +#endif + +extern _X_EXPORT void glamor_egl_destroy_textured_pixmap(PixmapPtr pixmap); + +extern _X_EXPORT int glamor_create_gc(GCPtr gc); + +extern _X_EXPORT void glamor_validate_gc(GCPtr gc, unsigned long changes, DrawablePtr drawable); +/* Glamor rendering/drawing functions with XXX_nf. + * nf means no fallback within glamor internal if possible. If glamor + * fail to accelerate the operation, glamor will return a false, and the + * caller need to implement fallback method. Return a true means the + * rendering request get done successfully. */ +extern _X_EXPORT Bool glamor_fill_spans_nf(DrawablePtr drawable, + GCPtr gc, + int n, DDXPointPtr points, + int *widths, int sorted); + +extern _X_EXPORT Bool glamor_poly_fill_rect_nf(DrawablePtr drawable, + GCPtr gc, + int nrect, + xRectangle * prect); + +extern _X_EXPORT Bool glamor_put_image_nf(DrawablePtr drawable, + GCPtr gc, int depth, int x, int y, + int w, int h, int left_pad, + int image_format, char *bits); + +extern _X_EXPORT Bool glamor_copy_n_to_n_nf(DrawablePtr src, + DrawablePtr dst, + GCPtr gc, + BoxPtr box, + int nbox, + int dx, + int dy, + Bool reverse, + Bool upsidedown, Pixel bitplane, + void *closure); + +extern _X_EXPORT Bool glamor_composite_nf(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + INT16 x_source, + INT16 y_source, + INT16 x_mask, + INT16 y_mask, + INT16 x_dest, INT16 y_dest, + CARD16 width, CARD16 height); + +extern _X_EXPORT Bool glamor_trapezoids_nf(CARD8 op, + PicturePtr src, PicturePtr dst, + PictFormatPtr mask_format, + INT16 x_src, INT16 y_src, + int ntrap, xTrapezoid * traps); + +extern _X_EXPORT Bool glamor_glyphs_nf(CARD8 op, + PicturePtr src, + PicturePtr dst, + PictFormatPtr mask_format, + INT16 x_src, + INT16 y_src, int nlist, + GlyphListPtr list, GlyphPtr * glyphs); + +extern _X_EXPORT Bool glamor_triangles_nf(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, INT16 ySrc, + int ntris, xTriangle * tris); + + +extern _X_EXPORT void glamor_glyph_unrealize(ScreenPtr screen, GlyphPtr glyph); + +extern _X_EXPORT Bool glamor_set_spans_nf(DrawablePtr drawable, GCPtr gc, char *src, + DDXPointPtr points, int *widths, int n, int sorted); + +extern _X_EXPORT Bool glamor_get_spans_nf(DrawablePtr drawable, int wmax, + DDXPointPtr points, int *widths, int count, char *dst); + +extern _X_EXPORT Bool glamor_composite_rects_nf (CARD8 op, + PicturePtr pDst, + xRenderColor *color, + int nRect, + xRectangle *rects); + +extern _X_EXPORT Bool glamor_get_image_nf(DrawablePtr pDrawable, int x, int y, int w, int h, + unsigned int format, unsigned long planeMask, char *d); + +extern _X_EXPORT Bool glamor_add_traps_nf(PicturePtr pPicture, + INT16 x_off, + INT16 y_off, int ntrap, xTrap * traps); + +extern _X_EXPORT Bool glamor_copy_plane_nf(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty, + unsigned long bitPlane, RegionPtr *pRegion); + +extern _X_EXPORT Bool glamor_image_glyph_blt_nf(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase); + +extern _X_EXPORT Bool glamor_poly_glyph_blt_nf(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase); + +extern _X_EXPORT Bool glamor_push_pixels_nf(GCPtr pGC, PixmapPtr pBitmap, + DrawablePtr pDrawable, int w, int h, int x, int y); + +extern _X_EXPORT Bool glamor_poly_point_nf(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr ppt); + +extern _X_EXPORT Bool glamor_poly_segment_nf(DrawablePtr pDrawable, GCPtr pGC, int nseg, + xSegment *pSeg); + +extern _X_EXPORT Bool glamor_poly_line_nf(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr ppt); + +extern _X_EXPORT Bool glamor_poly_lines_nf(DrawablePtr drawable, GCPtr gc, int mode, int n, + DDXPointPtr points); + +extern _X_EXPORT XF86VideoAdaptorPtr glamor_xv_init(ScreenPtr pScreen, int num_texture_ports); + +#endif /* GLAMOR_H */ diff --git a/xorg-server/glamor/glamor_addtraps.c b/xorg-server/glamor/glamor_addtraps.c new file mode 100644 index 000000000..ac852963c --- /dev/null +++ b/xorg-server/glamor/glamor_addtraps.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + * + */ + +#include "glamor_priv.h" + +static Bool +_glamor_add_traps(PicturePtr pPicture, + INT16 x_off, + INT16 y_off, int ntrap, xTrap * traps, + Bool fallback) +{ + if (!fallback + && ( !pPicture->pDrawable + || glamor_ddx_fallback_check_pixmap(pPicture->pDrawable))) + return FALSE; + + if (glamor_prepare_access_picture(pPicture, GLAMOR_ACCESS_RW)) { + fbAddTraps(pPicture, x_off, y_off, ntrap, traps); + glamor_finish_access_picture(pPicture, GLAMOR_ACCESS_RW); + } + + return TRUE; +} + +void +glamor_add_traps(PicturePtr pPicture, + INT16 x_off, + INT16 y_off, int ntrap, xTrap * traps) +{ + _glamor_add_traps(pPicture, x_off, y_off, ntrap, traps, TRUE); +} + +Bool +glamor_add_traps_nf(PicturePtr pPicture, + INT16 x_off, + INT16 y_off, int ntrap, xTrap * traps) +{ + return _glamor_add_traps(pPicture, x_off, y_off, ntrap, traps, FALSE); +} + diff --git a/xorg-server/glamor/glamor_compositerects.c b/xorg-server/glamor/glamor_compositerects.c new file mode 100644 index 000000000..1a5769958 --- /dev/null +++ b/xorg-server/glamor/glamor_compositerects.c @@ -0,0 +1,278 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + * original author is Chris Wilson at sna. + * + */ + +#include "glamor_priv.h" +#include "mipict.h" +#include "damage.h" + +/** @file glamor_compositerects. + * + * compositeRects acceleration implementation + */ + +static int16_t bound(int16_t a, uint16_t b) +{ + int v = (int)a + (int)b; + if (v > MAXSHORT) + return MAXSHORT; + return v; +} + +static Bool +_pixman_region_init_clipped_rectangles(pixman_region16_t *region, + unsigned int num_rects, + xRectangle *rects, + int tx, int ty, + BoxPtr extents) +{ + pixman_box16_t stack_boxes[64], *boxes = stack_boxes; + pixman_bool_t ret; + unsigned int i, j; + + if (num_rects > ARRAY_SIZE(stack_boxes)) { + boxes = malloc(sizeof(pixman_box16_t) * num_rects); + if (boxes == NULL) + return FALSE; + } + + for (i = j = 0; i < num_rects; i++) { + boxes[j].x1 = rects[i].x + tx; + if (boxes[j].x1 < extents->x1) + boxes[j].x1 = extents->x1; + + boxes[j].y1 = rects[i].y + ty; + if (boxes[j].y1 < extents->y1) + boxes[j].y1 = extents->y1; + + boxes[j].x2 = bound(rects[i].x + tx, rects[i].width); + if (boxes[j].x2 > extents->x2) + boxes[j].x2 = extents->x2; + + boxes[j].y2 = bound(rects[i].y + ty, rects[i].height); + if (boxes[j].y2 > extents->y2) + boxes[j].y2 = extents->y2; + + if (boxes[j].x2 > boxes[j].x1 && boxes[j].y2 > boxes[j].y1) + j++; + } + + ret = FALSE; + if (j) + ret = pixman_region_init_rects(region, boxes, j); + + if (boxes != stack_boxes) + free(boxes); + + DEBUGF("%s: nrects=%d, region=(%d, %d), (%d, %d) x %d\n", + __FUNCTION__, num_rects, + region->extents.x1, region->extents.y1, + region->extents.x2, region->extents.y2, + j); + return ret; +} + + +void +glamor_composite_rectangles(CARD8 op, + PicturePtr dst, + xRenderColor *color, + int num_rects, + xRectangle *rects) +{ + PixmapPtr pixmap; + struct glamor_pixmap_private *priv; + pixman_region16_t region; + pixman_box16_t *boxes; + int dst_x, dst_y; + int num_boxes; + PicturePtr source = NULL; + Bool need_free_region = FALSE; + + DEBUGF("%s(op=%d, %08x x %d [(%d, %d)x(%d, %d) ...])\n", + __FUNCTION__, op, + (color->alpha >> 8 << 24) | + (color->red >> 8 << 16) | + (color->green >> 8 << 8) | + (color->blue >> 8 << 0), + num_rects, + rects[0].x, rects[0].y, rects[0].width, rects[0].height); + + if (!num_rects) + return; + + if (region_is_empty(dst->pCompositeClip)) { + DEBUGF("%s: empty clip, skipping\n", __FUNCTION__); + return; + } + + if ((color->red|color->green|color->blue|color->alpha) <= 0x00ff) { + switch (op) { + case PictOpOver: + case PictOpOutReverse: + case PictOpAdd: + return; + case PictOpInReverse: + case PictOpSrc: + op = PictOpClear; + break; + case PictOpAtopReverse: + op = PictOpOut; + break; + case PictOpXor: + op = PictOpOverReverse; + break; + } + } + if (color->alpha <= 0x00ff) { + switch (op) { + case PictOpOver: + case PictOpOutReverse: + return; + case PictOpInReverse: + op = PictOpClear; + break; + case PictOpAtopReverse: + op = PictOpOut; + break; + case PictOpXor: + op = PictOpOverReverse; + break; + } + } else if (color->alpha >= 0xff00) { + switch (op) { + case PictOpOver: + op = PictOpSrc; + break; + case PictOpInReverse: + return; + case PictOpOutReverse: + op = PictOpClear; + break; + case PictOpAtopReverse: + op = PictOpOverReverse; + break; + case PictOpXor: + op = PictOpOut; + break; + } + } + DEBUGF("%s: converted to op %d\n", __FUNCTION__, op); + + if (!_pixman_region_init_clipped_rectangles(®ion, + num_rects, rects, + dst->pDrawable->x, + dst->pDrawable->y, + &dst->pCompositeClip->extents)) + { + DEBUGF("%s: allocation failed for region\n", __FUNCTION__); + return; + } + + pixmap = glamor_get_drawable_pixmap(dst->pDrawable); + priv = glamor_get_pixmap_private(pixmap); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(priv)) + goto fallback; + if (dst->alphaMap) { + DEBUGF("%s: fallback, dst has an alpha-map\n", __FUNCTION__); + goto fallback; + } + + need_free_region = TRUE; + + DEBUGF("%s: drawable extents (%d, %d),(%d, %d) x %d\n", + __FUNCTION__, + RegionExtents(®ion)->x1, RegionExtents(®ion)->y1, + RegionExtents(®ion)->x2, RegionExtents(®ion)->y2, + RegionNumRects(®ion)); + + if (dst->pCompositeClip->data && + (!pixman_region_intersect(®ion, ®ion, dst->pCompositeClip) || + region_is_empty(®ion))) { + DEBUGF("%s: zero-intersection between rectangles and clip\n", + __FUNCTION__); + pixman_region_fini(®ion); + return; + } + + DEBUGF("%s: clipped extents (%d, %d),(%d, %d) x %d\n", + __FUNCTION__, + RegionExtents(®ion)->x1, RegionExtents(®ion)->y1, + RegionExtents(®ion)->x2, RegionExtents(®ion)->y2, + RegionNumRects(®ion)); + + glamor_get_drawable_deltas(dst->pDrawable, pixmap, &dst_x, &dst_y); + pixman_region_translate(®ion, dst_x, dst_y); + + DEBUGF("%s: pixmap +(%d, %d) extents (%d, %d),(%d, %d)\n", + __FUNCTION__, dst_x, dst_y, + RegionExtents(®ion)->x1, RegionExtents(®ion)->y1, + RegionExtents(®ion)->x2, RegionExtents(®ion)->y2); + + + boxes = pixman_region_rectangles(®ion, &num_boxes); + if (op == PictOpSrc || op == PictOpClear) { + CARD32 pixel; + if (op == PictOpClear) + pixel = 0; + else + miRenderColorToPixel(dst->pFormat, color, &pixel); + glamor_solid_boxes(pixmap, boxes, num_boxes, pixel); + + goto done; + } else { + if (likely(priv->type != GLAMOR_TEXTURE_LARGE)) { + int error; + + source = CreateSolidPicture(0, color, &error); + if (!source) + goto done; + if (glamor_composite_clipped_region(op, source, + NULL, dst, + NULL, NULL, priv, + ®ion, + 0,0,0,0,0,0)) + goto done; + } + } +fallback: + miCompositeRects(op, dst, color, num_rects, rects); +done: + /* XXX xserver-1.8: CompositeRects is not tracked by Damage, so we must + * manually append the damaged regions ourselves. + */ + DamageRegionAppend(&pixmap->drawable, ®ion); + DamageRegionProcessPending(&pixmap->drawable); + + if (need_free_region) + pixman_region_fini(®ion); + if (source) + FreePicture(source, 0); + return; +} diff --git a/xorg-server/glamor/glamor_copyarea.c b/xorg-server/glamor/glamor_copyarea.c new file mode 100644 index 000000000..4e6f953d2 --- /dev/null +++ b/xorg-server/glamor/glamor_copyarea.c @@ -0,0 +1,676 @@ +/* + * Copyright © 2008 Intel Corporation + * Copyright © 1998 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + */ + +#include "glamor_priv.h" + +/** @file glamor_copyarea.c + * + * GC CopyArea implementation + */ +#ifndef GLAMOR_GLES2 +static Bool +glamor_copy_n_to_n_fbo_blit(DrawablePtr src, + DrawablePtr dst, + GCPtr gc, BoxPtr box, int nbox, int dx, int dy) +{ + ScreenPtr screen = dst->pScreen; + PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst); + PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src); + glamor_pixmap_private *src_pixmap_priv, *dst_pixmap_priv; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_gl_dispatch *dispatch; + int dst_x_off, dst_y_off, src_x_off, src_y_off, i; + int fbo_x_off, fbo_y_off; + int src_fbo_x_off, src_fbo_y_off; + + if (!glamor_priv->has_fbo_blit) { + glamor_delayed_fallback(screen, + "no EXT_framebuffer_blit\n"); + return FALSE; + } + src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); + dst_pixmap_priv = glamor_get_pixmap_private(dst_pixmap); + + if (gc) { + if (gc->alu != GXcopy) { + glamor_delayed_fallback(screen, "non-copy ALU\n"); + return FALSE; + } + } + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(src_pixmap_priv)) { + glamor_delayed_fallback(screen, "no src fbo\n"); + return FALSE; + } + + if (glamor_set_destination_pixmap(dst_pixmap)) + return FALSE; + + pixmap_priv_get_fbo_off(dst_pixmap_priv, &fbo_x_off, &fbo_y_off); + pixmap_priv_get_fbo_off(src_pixmap_priv, &src_fbo_x_off, &src_fbo_y_off); + + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, + src_pixmap_priv->base.fbo->fb); + glamor_get_drawable_deltas(dst, dst_pixmap, &dst_x_off, + &dst_y_off); + glamor_get_drawable_deltas(src, src_pixmap, &src_x_off, + &src_y_off); + dst_x_off += fbo_x_off; + dst_y_off += fbo_y_off; + src_y_off += dy + src_fbo_y_off; + src_x_off += src_fbo_x_off; + + for (i = 0; i < nbox; i++) { + if (glamor_priv->yInverted) { + dispatch->glBlitFramebuffer((box[i].x1 + dx + + src_x_off), + (box[i].y1 + + src_y_off), + (box[i].x2 + dx + + src_x_off), + (box[i].y2 + + src_y_off), + (box[i].x1 + + dst_x_off), + (box[i].y1 + + dst_y_off), + (box[i].x2 + + dst_x_off), + (box[i].y2 + + dst_y_off), + GL_COLOR_BUFFER_BIT, + GL_NEAREST); + } else { + int flip_dst_y1 = + dst_pixmap->drawable.height - (box[i].y2 + + dst_y_off); + int flip_dst_y2 = + dst_pixmap->drawable.height - (box[i].y1 + + dst_y_off); + int flip_src_y1 = + src_pixmap->drawable.height - (box[i].y2 + + src_y_off); + int flip_src_y2 = + src_pixmap->drawable.height - (box[i].y1 + + src_y_off); + + dispatch->glBlitFramebuffer(box[i].x1 + dx + + src_x_off, + flip_src_y1, + box[i].x2 + dx + + src_x_off, + flip_src_y2, + box[i].x1 + + dst_x_off, + flip_dst_y1, + box[i].x2 + + dst_x_off, + flip_dst_y2, + GL_COLOR_BUFFER_BIT, + GL_NEAREST); + } + } + glamor_put_dispatch(glamor_priv); + glamor_priv->state = BLIT_STATE; + return TRUE; +} +#endif + +static Bool +glamor_copy_n_to_n_textured(DrawablePtr src, + DrawablePtr dst, + GCPtr gc, BoxPtr box, int nbox, int dx, int dy) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(dst->pScreen); + glamor_gl_dispatch *dispatch; + PixmapPtr src_pixmap = glamor_get_drawable_pixmap(src); + PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(dst); + int i; + float vertices[8], texcoords[8]; + glamor_pixmap_private *src_pixmap_priv; + glamor_pixmap_private *dst_pixmap_priv; + int src_x_off, src_y_off, dst_x_off, dst_y_off; + enum glamor_pixmap_status src_status = GLAMOR_NONE; + GLfloat dst_xscale, dst_yscale, src_xscale, src_yscale; + + src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); + dst_pixmap_priv = glamor_get_pixmap_private(dst_pixmap); + + if (!src_pixmap_priv->base.gl_fbo) { +#ifndef GLAMOR_PIXMAP_DYNAMIC_UPLOAD + glamor_delayed_fallback(dst->pScreen, "src has no fbo.\n"); + return FALSE; +#else + src_status = glamor_upload_pixmap_to_texture(src_pixmap); + if (src_status != GLAMOR_UPLOAD_DONE) + return FALSE; + + src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); +#endif + } + + + pixmap_priv_get_dest_scale(dst_pixmap_priv, &dst_xscale, &dst_yscale); + pixmap_priv_get_scale(src_pixmap_priv, &src_xscale, &src_yscale); + + glamor_get_drawable_deltas(dst, dst_pixmap, &dst_x_off, + &dst_y_off); + + dispatch = glamor_get_dispatch(glamor_priv); + + + glamor_set_destination_pixmap_priv_nc(dst_pixmap_priv); + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), + vertices); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); + + glamor_get_drawable_deltas(src, src_pixmap, &src_x_off, + &src_y_off); + dx += src_x_off; + dy += src_y_off; + + dispatch->glActiveTexture(GL_TEXTURE0); + dispatch->glBindTexture(GL_TEXTURE_2D, + src_pixmap_priv->base.fbo->tex); +#ifndef GLAMOR_GLES2 + dispatch->glEnable(GL_TEXTURE_2D); + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_BORDER); + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_BORDER); +#endif + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, + GL_FLOAT, GL_FALSE, + 2 * sizeof(float), + texcoords); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glUseProgram(glamor_priv->finish_access_prog[0]); + dispatch->glUniform1i(glamor_priv->finish_access_revert[0], + REVERT_NONE); + dispatch->glUniform1i(glamor_priv->finish_access_swap_rb[0], + SWAP_NONE_UPLOADING); + + for (i = 0; i < nbox; i++) { + + glamor_set_normalize_vcoords(dst_pixmap_priv, + dst_xscale, dst_yscale, + box[i].x1 + dst_x_off, + box[i].y1 + dst_y_off, + box[i].x2 + dst_x_off, + box[i].y2 + dst_y_off, + glamor_priv->yInverted, + vertices); + + glamor_set_normalize_tcoords(src_pixmap_priv, + src_xscale, + src_yscale, + box[i].x1 + dx, + box[i].y1 + dy, + box[i].x2 + dx, + box[i].y2 + dy, + glamor_priv->yInverted, + texcoords); + dispatch->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); +#ifndef GLAMOR_GLES2 + dispatch->glDisable(GL_TEXTURE_2D); +#endif + dispatch->glUseProgram(0); + /* The source texture is bound to a fbo, we have to flush it here. */ + glamor_put_dispatch(glamor_priv); + glamor_priv->state = RENDER_STATE; + glamor_priv->render_idle_cnt = 0; + return TRUE; +} + +static Bool +__glamor_copy_n_to_n(DrawablePtr src, + DrawablePtr dst, + GCPtr gc, + BoxPtr box, + int nbox, + int dx, + int dy, + Bool reverse, + Bool upsidedown, Pixel bitplane, + void *closure) +{ + PixmapPtr dst_pixmap, src_pixmap, temp_pixmap = NULL; + DrawablePtr temp_src = src; + glamor_pixmap_private *dst_pixmap_priv, *src_pixmap_priv; + glamor_screen_private *glamor_priv; + BoxRec bound; + ScreenPtr screen; + int temp_dx = dx; + int temp_dy = dy; + int src_x_off, src_y_off, dst_x_off, dst_y_off; + int i; + int overlaped = 0; + Bool ret = FALSE; + + dst_pixmap = glamor_get_drawable_pixmap(dst); + dst_pixmap_priv = glamor_get_pixmap_private(dst_pixmap); + src_pixmap = glamor_get_drawable_pixmap(src); + src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); + screen = dst_pixmap->drawable.pScreen; + glamor_priv = glamor_get_screen_private(dst->pScreen); + glamor_get_drawable_deltas(src, src_pixmap, &src_x_off, + &src_y_off); + + glamor_get_drawable_deltas(dst, dst_pixmap, &dst_x_off, + &dst_y_off); + + if (src_pixmap_priv->base.fbo + && src_pixmap_priv->base.fbo->fb == dst_pixmap_priv->base.fbo->fb) { + int x_shift = abs(src_x_off - dx - dst_x_off); + int y_shift = abs(src_y_off - dy - dst_y_off); + for (i = 0; i < nbox; i++) { + if (x_shift < abs(box[i].x2 - box[i].x1) + && y_shift < abs(box[i].y2 - box[i].y1)) { + overlaped = 1; + break; + } + } + } + DEBUGF("Copy %d %d %dx%d dx %d dy %d from %p to %p \n", + box[0].x1, box[0].y1, + box[0].x2 - box[0].x1, box[0].y2 - box[0].y1, + dx, dy, + src_pixmap, dst_pixmap); +#ifndef GLAMOR_GLES2 + if (!overlaped && + (glamor_priv->state != RENDER_STATE + || !src_pixmap_priv->base.gl_tex || !dst_pixmap_priv->base.gl_tex) + && glamor_copy_n_to_n_fbo_blit(src, dst, gc, box, nbox, dx, + dy)) { + ret = TRUE; + goto done; + } +#endif + glamor_calculate_boxes_bound(&bound, box, nbox); + + /* Overlaped indicate the src and dst are the same pixmap. */ + if (overlaped || (!GLAMOR_PIXMAP_PRIV_HAS_FBO(src_pixmap_priv) + && (((bound.x2 - bound.x1) * (bound.y2 - bound.y1) + * 4 > + src_pixmap->drawable.width * + src_pixmap->drawable.height) + || !(glamor_check_fbo_size(glamor_priv, + src_pixmap->drawable.width, + src_pixmap->drawable.height))))) { + + temp_pixmap = glamor_create_pixmap(screen, + bound.x2 - bound.x1, + bound.y2 - bound.y1, + src_pixmap-> + drawable.depth, + overlaped ? 0 : + GLAMOR_CREATE_PIXMAP_CPU); + assert(bound.x2 - bound.x1 <= glamor_priv->max_fbo_size); + assert(bound.y2 - bound.y1 <= glamor_priv->max_fbo_size); + if (!temp_pixmap) + goto done; + glamor_translate_boxes(box, nbox, -bound.x1, -bound.y1); + temp_src = &temp_pixmap->drawable; + + if (overlaped) + glamor_copy_n_to_n_textured(src, temp_src, gc, box, + nbox, + temp_dx + bound.x1, + temp_dy + bound.y1); + else + fbCopyNtoN(src, temp_src, gc, box, nbox, + temp_dx + bound.x1, temp_dy + bound.y1, + reverse, upsidedown, bitplane, closure); + glamor_translate_boxes(box, nbox, bound.x1, bound.y1); + temp_dx = -bound.x1; + temp_dy = -bound.y1; + } else { + temp_dx = dx; + temp_dy = dy; + temp_src = src; + } + + if (glamor_copy_n_to_n_textured + (temp_src, dst, gc, box, nbox, temp_dx, temp_dy)) { + ret = TRUE; + } +done: + if (temp_src != src) + glamor_destroy_pixmap(temp_pixmap); + return ret; +} + +static Bool +_glamor_copy_n_to_n(DrawablePtr src, + DrawablePtr dst, + GCPtr gc, + BoxPtr box, + int nbox, + int dx, + int dy, + Bool reverse, + Bool upsidedown, Pixel bitplane, + void *closure, Bool fallback) +{ + PixmapPtr dst_pixmap, src_pixmap; + glamor_pixmap_private *dst_pixmap_priv, *src_pixmap_priv; + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + BoxPtr extent; + RegionRec region; + int src_x_off, src_y_off, dst_x_off, dst_y_off; + Bool ok = FALSE; + int force_clip = 0; + + if (nbox == 0) + return TRUE; + dst_pixmap = glamor_get_drawable_pixmap(dst); + dst_pixmap_priv = glamor_get_pixmap_private(dst_pixmap); + src_pixmap = glamor_get_drawable_pixmap(src); + src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); + + glamor_priv = glamor_get_screen_private(dst->pScreen); + + DEBUGF("Copy %d %d %dx%d dx %d dy %d from %p to %p \n", + box[0].x1, box[0].y1, + box[0].x2 - box[0].x1, box[0].y2 - box[0].y1, + dx, dy, + src_pixmap, dst_pixmap); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dst_pixmap_priv)) + goto fall_back; + + if (gc) { + if (!glamor_set_planemask(dst_pixmap, gc->planemask)) + goto fall_back; + dispatch = glamor_get_dispatch(glamor_priv); + if (!glamor_set_alu(dispatch, gc->alu)) { + glamor_put_dispatch(glamor_priv); + goto fail; + } + glamor_put_dispatch(glamor_priv); + } + + if (!src_pixmap_priv) { + glamor_set_pixmap_type(src_pixmap, GLAMOR_MEMORY); + src_pixmap_priv = glamor_get_pixmap_private(src_pixmap); + } + + glamor_get_drawable_deltas(src, src_pixmap, &src_x_off, + &src_y_off); + glamor_get_drawable_deltas(dst, dst_pixmap, &dst_x_off, + &dst_y_off); + + RegionInitBoxes(®ion, box, nbox); + extent = RegionExtents(®ion); + + if (!glamor_check_fbo_size(glamor_priv, + extent->x2 - extent->x1, extent->y2 - extent->y1) + && (src_pixmap_priv->type == GLAMOR_MEMORY + || (src_pixmap_priv == dst_pixmap_priv))) { + force_clip = 1; + } + + if (force_clip || dst_pixmap_priv->type == GLAMOR_TEXTURE_LARGE + || src_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + glamor_pixmap_clipped_regions *clipped_dst_regions; + int n_dst_region, i, j; + PixmapPtr temp_source_pixmap; + glamor_pixmap_private *temp_source_priv = NULL; + + RegionTranslate(®ion, dst_x_off, dst_y_off); + if (!force_clip) + clipped_dst_regions = glamor_compute_clipped_regions(dst_pixmap_priv, + ®ion, &n_dst_region, 0, + reverse, upsidedown); + else + clipped_dst_regions = glamor_compute_clipped_regions_ext(dst_pixmap_priv, + ®ion, &n_dst_region, + glamor_priv->max_fbo_size, + glamor_priv->max_fbo_size, + reverse, upsidedown); + for(i = 0; i < n_dst_region; i++) + { + int n_src_region; + glamor_pixmap_clipped_regions *clipped_src_regions; + BoxPtr current_boxes; + int n_current_boxes; + + SET_PIXMAP_FBO_CURRENT(dst_pixmap_priv, clipped_dst_regions[i].block_idx); + + temp_source_pixmap = NULL; + if (src_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + RegionTranslate(clipped_dst_regions[i].region, + -dst_x_off + src_x_off + dx, -dst_y_off + src_y_off + dy); + clipped_src_regions = glamor_compute_clipped_regions(src_pixmap_priv, + clipped_dst_regions[i].region, + &n_src_region, 0, + reverse, upsidedown); + DEBUGF("Source is large pixmap.\n"); + for (j = 0; j < n_src_region; j++) + { + if (src_pixmap_priv != dst_pixmap_priv) + SET_PIXMAP_FBO_CURRENT(src_pixmap_priv, clipped_src_regions[j].block_idx); + else if (src_pixmap_priv == dst_pixmap_priv && + clipped_src_regions[j].block_idx != clipped_dst_regions[i].block_idx) { + /* source and the dest are the same, but need different block_idx. + * we create a empty pixmap and fill the required source fbo and box to + * it. It's a little hacky, but avoid extra copy. */ + temp_source_pixmap = glamor_create_pixmap(src->pScreen, 0, 0, + src->depth, 0); + if (!temp_source_pixmap) { + ok = FALSE; + goto fail; + } + src->pScreen->ModifyPixmapHeader(temp_source_pixmap, + src_pixmap->drawable.width, + src_pixmap->drawable.height, + 0, 0, src_pixmap->devKind, NULL); + temp_source_priv = glamor_get_pixmap_private(temp_source_pixmap); + *temp_source_priv = *src_pixmap_priv; + temp_source_priv->large.box = src_pixmap_priv->large.box_array[clipped_src_regions[j].block_idx]; + temp_source_priv->base.fbo = src_pixmap_priv->large.fbo_array[clipped_src_regions[j].block_idx]; + temp_source_priv->base.pixmap = temp_source_pixmap; + } + assert(temp_source_pixmap || !(src_pixmap_priv == dst_pixmap_priv + && (clipped_src_regions[j].block_idx != clipped_dst_regions[i].block_idx))); + + RegionTranslate(clipped_src_regions[j].region, + -src_x_off - dx, + -src_y_off - dy); + current_boxes = RegionRects(clipped_src_regions[j].region); + n_current_boxes = RegionNumRects(clipped_src_regions[j].region); + DEBUGF("dst pixmap fbo idx %d src pixmap fbo idx %d \n", + clipped_dst_regions[i].block_idx, + clipped_src_regions[j].block_idx); + DEBUGF("Copy %d %d %d %d dx %d dy %d from %p to %p \n", + current_boxes[0].x1, current_boxes[0].y1, + current_boxes[0].x2, current_boxes[0].y2, + dx, dy, src_pixmap, dst_pixmap); + if (!temp_source_pixmap) + ok = __glamor_copy_n_to_n(src, dst, gc, current_boxes, + n_current_boxes, dx, dy, reverse, + upsidedown, bitplane, closure); + else { + ok = __glamor_copy_n_to_n(&temp_source_pixmap->drawable, + dst, gc, current_boxes, + n_current_boxes, dx, dy, reverse, + upsidedown, bitplane, closure); + temp_source_priv->type = GLAMOR_MEMORY; + temp_source_priv->base.fbo = NULL; + glamor_destroy_pixmap(temp_source_pixmap); + temp_source_pixmap = NULL; + } + + RegionDestroy(clipped_src_regions[j].region); + if (!ok) { + assert(0); + goto fail; + } + } + + if (n_src_region == 0) + ok = TRUE; + free(clipped_src_regions); + } else { + RegionTranslate(clipped_dst_regions[i].region, + - dst_x_off, + - dst_y_off); + current_boxes = RegionRects(clipped_dst_regions[i].region); + n_current_boxes = RegionNumRects(clipped_dst_regions[i].region); + + DEBUGF("dest pixmap fbo idx %d \n", + clipped_dst_regions[i].block_idx); + DEBUGF("Copy %d %d %d %d dx %d dy %d from %p to %p \n", + current_boxes[0].x1, current_boxes[0].y1, + current_boxes[0].x2, current_boxes[0].y2, + dx, dy, src_pixmap, dst_pixmap); + + ok = __glamor_copy_n_to_n(src, dst, gc, current_boxes, + n_current_boxes, dx, dy, reverse, + upsidedown, bitplane, closure); + + } + RegionDestroy(clipped_dst_regions[i].region); + } + if (n_dst_region == 0) + ok = TRUE; + free(clipped_dst_regions); + RegionUninit(®ion); + } else { + ok = __glamor_copy_n_to_n(src, dst, gc, box, nbox, dx, dy, + reverse, upsidedown, bitplane, + closure); + } + +fail: + dispatch = glamor_get_dispatch(glamor_priv); + glamor_set_alu(dispatch, GXcopy); + glamor_put_dispatch(glamor_priv); + + if (ok) + return TRUE; +fall_back: + if (!fallback + && glamor_ddx_fallback_check_pixmap(src) + && glamor_ddx_fallback_check_pixmap(dst)) + goto done; + + if (src_pixmap_priv->type == GLAMOR_DRM_ONLY + || dst_pixmap_priv->type == GLAMOR_DRM_ONLY) { + LogMessage(X_WARNING, + "Access a DRM only pixmap is not allowed within glamor.\n"); + return TRUE; + } + glamor_report_delayed_fallbacks(src->pScreen); + glamor_report_delayed_fallbacks(dst->pScreen); + + glamor_fallback("from %p to %p (%c,%c)\n", src, dst, + glamor_get_drawable_location(src), + glamor_get_drawable_location(dst)); + + if (glamor_prepare_access(dst, GLAMOR_ACCESS_RW)) { + if (dst == src + || glamor_prepare_access(src, GLAMOR_ACCESS_RO)) { + fbCopyNtoN(src, dst, gc, box, nbox, + dx, dy, reverse, upsidedown, bitplane, + closure); + if (dst != src) + glamor_finish_access(src, GLAMOR_ACCESS_RO); + } + glamor_finish_access(dst, GLAMOR_ACCESS_RW); + } + ok = TRUE; + + done: + glamor_clear_delayed_fallbacks(src->pScreen); + glamor_clear_delayed_fallbacks(dst->pScreen); + return ok; +} + +RegionPtr +glamor_copy_area(DrawablePtr src, DrawablePtr dst, GCPtr gc, + int srcx, int srcy, int width, int height, int dstx, + int dsty) +{ + RegionPtr region; + region = miDoCopy(src, dst, gc, + srcx, srcy, width, height, + dstx, dsty, glamor_copy_n_to_n, 0, NULL); + + return region; +} + +void +glamor_copy_n_to_n(DrawablePtr src, + DrawablePtr dst, + GCPtr gc, + BoxPtr box, + int nbox, + int dx, + int dy, + Bool reverse, + Bool upsidedown, Pixel bitplane, + void *closure) +{ + _glamor_copy_n_to_n(src, dst, gc, box, nbox, dx, + dy, reverse, upsidedown, bitplane, closure, TRUE); +} + +Bool +glamor_copy_n_to_n_nf(DrawablePtr src, + DrawablePtr dst, + GCPtr gc, + BoxPtr box, + int nbox, + int dx, + int dy, + Bool reverse, + Bool upsidedown, Pixel bitplane, + void *closure) +{ + return _glamor_copy_n_to_n(src, dst, gc, box, nbox, dx, + dy, reverse, upsidedown, bitplane, closure, FALSE); +} + diff --git a/xorg-server/glamor/glamor_copyplane.c b/xorg-server/glamor/glamor_copyplane.c new file mode 100644 index 000000000..3f2652ac7 --- /dev/null +++ b/xorg-server/glamor/glamor_copyplane.c @@ -0,0 +1,72 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + * + */ + +#include "glamor_priv.h" + +static Bool +_glamor_copy_plane(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty, + unsigned long bitPlane, RegionPtr *pRegion, Bool fallback) +{ + if (!fallback + && glamor_ddx_fallback_check_gc(pGC) + && glamor_ddx_fallback_check_pixmap(pSrc) + && glamor_ddx_fallback_check_pixmap(pDst)) + goto fail; + + glamor_prepare_access(pDst, GLAMOR_ACCESS_RW); + glamor_prepare_access(pSrc, GLAMOR_ACCESS_RO); + *pRegion = fbCopyPlane(pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty, bitPlane); + glamor_finish_access(pSrc, GLAMOR_ACCESS_RO); + glamor_finish_access(pDst, GLAMOR_ACCESS_RW); + return TRUE; + + fail: + return FALSE; +} + +RegionPtr +glamor_copy_plane(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty, + unsigned long bitPlane) +{ + RegionPtr ret; + _glamor_copy_plane(pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty, bitPlane, &ret, TRUE); + return ret; +} + +Bool +glamor_copy_plane_nf(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty, + unsigned long bitPlane, RegionPtr *pRegion) +{ + return _glamor_copy_plane(pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty, bitPlane, pRegion, FALSE); +} diff --git a/xorg-server/glamor/glamor_copywindow.c b/xorg-server/glamor/glamor_copywindow.c new file mode 100644 index 000000000..b181ff529 --- /dev/null +++ b/xorg-server/glamor/glamor_copywindow.c @@ -0,0 +1,58 @@ +/* + * Copyright © 2008 Intel Corporation + * Copyright © 1998 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "glamor_priv.h" + +/** @file glamor_copywindow.c + * + * Screen CopyWindow implementation. + */ + +void +glamor_copy_window(WindowPtr win, DDXPointRec old_origin, + RegionPtr src_region) +{ + RegionRec dst_region; + int dx, dy; + PixmapPtr pixmap = win->drawable.pScreen->GetWindowPixmap(win); + + dx = old_origin.x - win->drawable.x; + dy = old_origin.y - win->drawable.y; + REGION_TRANSLATE(win->drawable.pScreen, src_region, -dx, -dy); + + REGION_INIT(win->drawable.pScreen, &dst_region, NullBox, 0); + + REGION_INTERSECT(win->drawable.pScreen, &dst_region, + &win->borderClip, src_region); +#ifdef COMPOSITE + if (pixmap->screen_x || pixmap->screen_y) + REGION_TRANSLATE(win->drawable.pScreen, &dst_region, + -pixmap->screen_x, -pixmap->screen_y); +#endif + + miCopyRegion(&pixmap->drawable, &pixmap->drawable, + NULL, &dst_region, dx, dy, glamor_copy_n_to_n, 0, + NULL); + + REGION_UNINIT(win->drawable.pScreen, &dst_region); +} diff --git a/xorg-server/glamor/glamor_core.c b/xorg-server/glamor/glamor_core.c new file mode 100644 index 000000000..eb1a08d43 --- /dev/null +++ b/xorg-server/glamor/glamor_core.c @@ -0,0 +1,612 @@ +/* + * Copyright © 2001 Keith Packard + * Copyright © 2008 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +/** @file glamor_core.c + * + * This file covers core X rendering in glamor. + */ + +#include <stdlib.h> + +#include "glamor_priv.h" + +const Bool +glamor_get_drawable_location(const DrawablePtr drawable) +{ + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + glamor_pixmap_private *pixmap_priv = + glamor_get_pixmap_private(pixmap); + glamor_screen_private *glamor_priv = + glamor_get_screen_private(drawable->pScreen); + if (pixmap_priv == NULL || pixmap_priv->base.gl_fbo == 0) + return 'm'; + if (pixmap_priv->base.fbo->fb == glamor_priv->screen_fbo) + return 's'; + else + return 'f'; +} + +GLint +glamor_compile_glsl_prog(glamor_gl_dispatch * dispatch, GLenum type, + const char *source) +{ + GLint ok; + GLint prog; + + prog = dispatch->glCreateShader(type); + dispatch->glShaderSource(prog, 1, (const GLchar **) &source, NULL); + dispatch->glCompileShader(prog); + dispatch->glGetShaderiv(prog, GL_COMPILE_STATUS, &ok); + if (!ok) { + GLchar *info; + GLint size; + + dispatch->glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &size); + info = malloc(size); + if (info) { + dispatch->glGetShaderInfoLog(prog, size, NULL, info); + ErrorF("Failed to compile %s: %s\n", + type == GL_FRAGMENT_SHADER ? "FS" : "VS", info); + ErrorF("Program source:\n%s", source); + free(info); + } else + ErrorF("Failed to get shader compilation info.\n"); + FatalError("GLSL compile failure\n"); + } + + return prog; +} + +void +glamor_link_glsl_prog(glamor_gl_dispatch * dispatch, GLint prog) +{ + GLint ok; + + dispatch->glLinkProgram(prog); + dispatch->glGetProgramiv(prog, GL_LINK_STATUS, &ok); + if (!ok) { + GLchar *info; + GLint size; + + dispatch->glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size); + info = malloc(size); + + dispatch->glGetProgramInfoLog(prog, size, NULL, info); + ErrorF("Failed to link: %s\n", info); + FatalError("GLSL link failure\n"); + } +} + + +Bool +glamor_prepare_access(DrawablePtr drawable, glamor_access_t access) +{ + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + return glamor_download_pixmap_to_cpu(pixmap, access); +} + +/* + * When downloading a unsupported color format to CPU memory, + we need to shuffle the color elements and then use a supported + color format to read it back to CPU memory. + + For an example, the picture's format is PICT_b8g8r8a8, + Then the expecting color layout is as below (little endian): + 0 1 2 3 : address + a r g b + + Now the in GLES2 the supported color format is GL_RGBA, type is + GL_UNSIGNED_TYPE, then we need to shuffle the fragment + color as : + frag_color = sample(texture).argb; + before we use glReadPixel to get it back. + + For the uploading process, the shuffle is a revert shuffle. + We still use GL_RGBA, GL_UNSIGNED_BYTE to upload the color + to a texture, then let's see + 0 1 2 3 : address + a r g b : correct colors + R G B A : GL_RGBA with GL_UNSIGNED_BYTE + + Now we need to shuffle again, the mapping rule is + r = G, g = B, b = A, a = R. Then the uploading shuffle is as + below: + frag_color = sample(texture).gbar; +*/ + +void +glamor_init_finish_access_shaders(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + const char *vs_source = + "attribute vec4 v_position;\n" + "attribute vec4 v_texcoord0;\n" + "varying vec2 source_texture;\n" + "void main()\n" + "{\n" + " gl_Position = v_position;\n" + " source_texture = v_texcoord0.xy;\n" "}\n"; + + const char *common_source = + GLAMOR_DEFAULT_PRECISION + "varying vec2 source_texture;\n" + "uniform sampler2D sampler;\n" + "uniform int revert;\n" + "uniform int swap_rb;\n" + + "#define REVERT_NONE 0\n" + "#define REVERT_NORMAL 1\n" + "#define SWAP_NONE_DOWNLOADING 0\n" + "#define SWAP_DOWNLOADING 1\n" + "#define SWAP_UPLOADING 2\n" + "#define SWAP_NONE_UPLOADING 3\n"; + + const char *fs_source = + "void main()\n" + "{\n" + " if (revert == REVERT_NONE) \n" + " { \n" + " if ((swap_rb != SWAP_NONE_DOWNLOADING) && (swap_rb != SWAP_NONE_UPLOADING)) \n" + " gl_FragColor = texture2D(sampler, source_texture).bgra;\n" + " else \n" + " gl_FragColor = texture2D(sampler, source_texture).rgba;\n" + " } \n" + " else \n" + " { \n" + " if (swap_rb == SWAP_DOWNLOADING) \n" + " gl_FragColor = texture2D(sampler, source_texture).argb;\n" + " else if (swap_rb == SWAP_NONE_DOWNLOADING)\n" + " gl_FragColor = texture2D(sampler, source_texture).abgr;\n" + " else if (swap_rb == SWAP_UPLOADING)\n" + " gl_FragColor = texture2D(sampler, source_texture).gbar;\n" + " else if (swap_rb == SWAP_NONE_UPLOADING)\n" + " gl_FragColor = texture2D(sampler, source_texture).abgr;\n" + " } \n" "}\n"; + + const char *set_alpha_source = + "void main()\n" + "{\n" + " if (revert == REVERT_NONE) \n" + " { \n" + " if ((swap_rb != SWAP_NONE_DOWNLOADING) && (swap_rb != SWAP_NONE_UPLOADING)) \n" + " gl_FragColor = vec4(texture2D(sampler, source_texture).bgr, 1);\n" + " else \n" + " gl_FragColor = vec4(texture2D(sampler, source_texture).rgb, 1);\n" + " } \n" + " else \n" + " { \n" + " if (swap_rb == SWAP_DOWNLOADING) \n" + " gl_FragColor = vec4(1, texture2D(sampler, source_texture).rgb);\n" + " else if (swap_rb == SWAP_NONE_DOWNLOADING)\n" + " gl_FragColor = vec4(1, texture2D(sampler, source_texture).bgr);\n" + " else if (swap_rb == SWAP_UPLOADING)\n" + " gl_FragColor = vec4(texture2D(sampler, source_texture).gba, 1);\n" + " else if (swap_rb == SWAP_NONE_UPLOADING)\n" + " gl_FragColor = vec4(texture2D(sampler, source_texture).abg, 1);\n" + " } \n" + "}\n"; + GLint fs_prog, vs_prog, avs_prog, set_alpha_prog; + GLint sampler_uniform_location; + char *source; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + glamor_priv->finish_access_prog[0] = dispatch->glCreateProgram(); + glamor_priv->finish_access_prog[1] = dispatch->glCreateProgram(); + + vs_prog = glamor_compile_glsl_prog(dispatch, GL_VERTEX_SHADER, + vs_source); + + XNFasprintf(&source, "%s%s", common_source, fs_source); + fs_prog = glamor_compile_glsl_prog(dispatch, GL_FRAGMENT_SHADER, + source); + free(source); + + dispatch->glAttachShader(glamor_priv->finish_access_prog[0], + vs_prog); + dispatch->glAttachShader(glamor_priv->finish_access_prog[0], + fs_prog); + + avs_prog = glamor_compile_glsl_prog(dispatch, GL_VERTEX_SHADER, + vs_source); + + XNFasprintf(&source, "%s%s", common_source, set_alpha_source); + set_alpha_prog = glamor_compile_glsl_prog(dispatch, GL_FRAGMENT_SHADER, + source); + free(source); + + dispatch->glAttachShader(glamor_priv->finish_access_prog[1], + avs_prog); + dispatch->glAttachShader(glamor_priv->finish_access_prog[1], + set_alpha_prog); + + dispatch->glBindAttribLocation(glamor_priv->finish_access_prog[0], + GLAMOR_VERTEX_POS, "v_position"); + dispatch->glBindAttribLocation(glamor_priv->finish_access_prog[0], + GLAMOR_VERTEX_SOURCE, + "v_texcoord0"); + glamor_link_glsl_prog(dispatch, + glamor_priv->finish_access_prog[0]); + + dispatch->glBindAttribLocation(glamor_priv->finish_access_prog[1], + GLAMOR_VERTEX_POS, "v_position"); + dispatch->glBindAttribLocation(glamor_priv->finish_access_prog[1], + GLAMOR_VERTEX_SOURCE, + "v_texcoord0"); + glamor_link_glsl_prog(dispatch, + glamor_priv->finish_access_prog[1]); + + glamor_priv->finish_access_revert[0] = + dispatch-> + glGetUniformLocation(glamor_priv->finish_access_prog[0], + "revert"); + + glamor_priv->finish_access_swap_rb[0] = + dispatch-> + glGetUniformLocation(glamor_priv->finish_access_prog[0], + "swap_rb"); + sampler_uniform_location = + dispatch-> + glGetUniformLocation(glamor_priv->finish_access_prog[0], + "sampler"); + dispatch->glUseProgram(glamor_priv->finish_access_prog[0]); + dispatch->glUniform1i(sampler_uniform_location, 0); + dispatch->glUniform1i(glamor_priv->finish_access_revert[0], 0); + dispatch->glUniform1i(glamor_priv->finish_access_swap_rb[0], 0); + dispatch->glUseProgram(0); + + glamor_priv->finish_access_revert[1] = + dispatch-> + glGetUniformLocation(glamor_priv->finish_access_prog[1], + "revert"); + glamor_priv->finish_access_swap_rb[1] = + dispatch-> + glGetUniformLocation(glamor_priv->finish_access_prog[1], + "swap_rb"); + sampler_uniform_location = + dispatch-> + glGetUniformLocation(glamor_priv->finish_access_prog[1], + "sampler"); + dispatch->glUseProgram(glamor_priv->finish_access_prog[1]); + dispatch->glUniform1i(glamor_priv->finish_access_revert[1], 0); + dispatch->glUniform1i(sampler_uniform_location, 0); + dispatch->glUniform1i(glamor_priv->finish_access_swap_rb[1], 0); + dispatch->glUseProgram(0); + glamor_put_dispatch(glamor_priv); +} + +void +glamor_fini_finish_access_shaders(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glDeleteProgram(glamor_priv->finish_access_prog[0]); + dispatch->glDeleteProgram(glamor_priv->finish_access_prog[1]); + glamor_put_dispatch(glamor_priv); +} + +void +glamor_finish_access(DrawablePtr drawable, glamor_access_t access_mode) +{ + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + glamor_pixmap_private *pixmap_priv = + glamor_get_pixmap_private(pixmap); + glamor_screen_private *glamor_priv = + glamor_get_screen_private(drawable->pScreen); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO_DOWNLOADED(pixmap_priv)) + return; + + if (access_mode != GLAMOR_ACCESS_RO) { + glamor_restore_pixmap_to_texture(pixmap); + } + + if (pixmap_priv->base.fbo->pbo != 0 && pixmap_priv->base.fbo->pbo_valid) { + glamor_gl_dispatch *dispatch; + + assert(glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP); + + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + dispatch->glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + dispatch->glDeleteBuffers(1, &pixmap_priv->base.fbo->pbo); + glamor_put_dispatch(glamor_priv); + + pixmap_priv->base.fbo->pbo_valid = FALSE; + pixmap_priv->base.fbo->pbo = 0; + } else { + free(pixmap->devPrivate.ptr); + } + + if (pixmap_priv->type == GLAMOR_TEXTURE_DRM) + pixmap->devKind = pixmap_priv->base.drm_stride; + + if (pixmap_priv->base.gl_fbo == GLAMOR_FBO_DOWNLOADED) + pixmap_priv->base.gl_fbo = GLAMOR_FBO_NORMAL; + + pixmap->devPrivate.ptr = NULL; +} + + +/** + * Calls uxa_prepare_access with UXA_PREPARE_SRC for the tile, if that is the + * current fill style. + * + * Solid doesn't use an extra pixmap source, so we don't worry about them. + * Stippled/OpaqueStippled are 1bpp and can be in fb, so we should worry + * about them. + */ +Bool +glamor_prepare_access_gc(GCPtr gc) +{ + if (gc->stipple) { + if (!glamor_prepare_access + (&gc->stipple->drawable, GLAMOR_ACCESS_RO)) + return FALSE; + } + if (gc->fillStyle == FillTiled) { + if (!glamor_prepare_access(&gc->tile.pixmap->drawable, + GLAMOR_ACCESS_RO)) { + if (gc->stipple) + glamor_finish_access(&gc-> + stipple->drawable, + GLAMOR_ACCESS_RO); + return FALSE; + } + } + return TRUE; +} + +/** + * Finishes access to the tile in the GC, if used. + */ +void +glamor_finish_access_gc(GCPtr gc) +{ + if (gc->fillStyle == FillTiled) + glamor_finish_access(&gc->tile.pixmap->drawable, GLAMOR_ACCESS_RO); + if (gc->stipple) + glamor_finish_access(&gc->stipple->drawable, GLAMOR_ACCESS_RO); +} + +Bool +glamor_stipple(PixmapPtr pixmap, PixmapPtr stipple, + int x, int y, int width, int height, + unsigned char alu, unsigned long planemask, + unsigned long fg_pixel, unsigned long bg_pixel, + int stipple_x, int stipple_y) +{ + glamor_fallback("stubbed out stipple depth %d\n", + pixmap->drawable.depth); + return FALSE; +} + +GCOps glamor_gc_ops = { + .FillSpans = glamor_fill_spans, + .SetSpans = glamor_set_spans, + .PutImage = glamor_put_image, + .CopyArea = glamor_copy_area, + .CopyPlane = glamor_copy_plane, + .PolyPoint = glamor_poly_point, + .Polylines = glamor_poly_lines, + .PolySegment = glamor_poly_segment, + .PolyRectangle = miPolyRectangle, + .PolyArc = miPolyArc, + .FillPolygon = miFillPolygon, + .PolyFillRect = glamor_poly_fill_rect, + .PolyFillArc = miPolyFillArc, + .PolyText8 = miPolyText8, + .PolyText16 = miPolyText16, + .ImageText8 = miImageText8, + .ImageText16 = miImageText16, + .ImageGlyphBlt = glamor_image_glyph_blt, //miImageGlyphBlt, + .PolyGlyphBlt = glamor_poly_glyph_blt, //miPolyGlyphBlt, + .PushPixels = glamor_push_pixels, //miPushPixels, +}; + +/** + * uxa_validate_gc() sets the ops to glamor's implementations, which may be + * accelerated or may sync the card and fall back to fb. + */ +void +glamor_validate_gc(GCPtr gc, unsigned long changes, DrawablePtr drawable) +{ + /* fbValidateGC will do direct access to pixmaps if the tiling has changed. + * Preempt fbValidateGC by doing its work and masking the change out, so + * that we can do the Prepare/finish_access. + */ +#ifdef FB_24_32BIT + if ((changes & GCTile) && fbGetRotatedPixmap(gc)) { + gc->pScreen->DestroyPixmap(fbGetRotatedPixmap(gc)); + fbGetRotatedPixmap(gc) = 0; + } + + if (gc->fillStyle == FillTiled) { + PixmapPtr old_tile, new_tile; + + old_tile = gc->tile.pixmap; + if (old_tile->drawable.bitsPerPixel != + drawable->bitsPerPixel) { + new_tile = fbGetRotatedPixmap(gc); + if (!new_tile || + new_tile->drawable.bitsPerPixel != + drawable->bitsPerPixel) { + if (new_tile) + gc->pScreen->DestroyPixmap + (new_tile); + /* fb24_32ReformatTile will do direct access of a newly- + * allocated pixmap. + */ + glamor_fallback + ("GC %p tile FB_24_32 transformat %p.\n", + gc, old_tile); + + if (glamor_prepare_access + (&old_tile->drawable, + GLAMOR_ACCESS_RO)) { + new_tile = + fb24_32ReformatTile + (old_tile, + drawable->bitsPerPixel); + glamor_finish_access + (&old_tile->drawable, GLAMOR_ACCESS_RO); + } + } + if (new_tile) { + fbGetRotatedPixmap(gc) = old_tile; + gc->tile.pixmap = new_tile; + changes |= GCTile; + } + } + } +#endif + if (changes & GCTile) { + if (!gc->tileIsPixel) { + glamor_pixmap_private *pixmap_priv = + glamor_get_pixmap_private(gc->tile.pixmap); + if ((!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + && FbEvenTile(gc->tile.pixmap->drawable.width * + drawable->bitsPerPixel)) { + glamor_fallback + ("GC %p tile changed %p.\n", gc, + gc->tile.pixmap); + if (glamor_prepare_access + (&gc->tile.pixmap->drawable, + GLAMOR_ACCESS_RW)) { + fbPadPixmap(gc->tile.pixmap); + glamor_finish_access + (&gc->tile.pixmap->drawable, GLAMOR_ACCESS_RW); + } + } + } + /* Mask out the GCTile change notification, now that we've done FB's + * job for it. + */ + changes &= ~GCTile; + } + + if (changes & GCStipple && gc->stipple) { + /* We can't inline stipple handling like we do for GCTile because + * it sets fbgc privates. + */ + if (glamor_prepare_access + (&gc->stipple->drawable, GLAMOR_ACCESS_RW)) { + fbValidateGC(gc, changes, drawable); + glamor_finish_access(&gc->stipple->drawable, GLAMOR_ACCESS_RW); + } + } else { + fbValidateGC(gc, changes, drawable); + } + + gc->ops = &glamor_gc_ops; +} + +static GCFuncs glamor_gc_funcs = { + glamor_validate_gc, + miChangeGC, + miCopyGC, + miDestroyGC, + miChangeClip, + miDestroyClip, + miCopyClip +}; + +/** + * exaCreateGC makes a new GC and hooks up its funcs handler, so that + * exaValidateGC() will get called. + */ +int +glamor_create_gc(GCPtr gc) +{ + if (!fbCreateGC(gc)) + return FALSE; + + gc->funcs = &glamor_gc_funcs; + + return TRUE; +} + +RegionPtr +glamor_bitmap_to_region(PixmapPtr pixmap) +{ + RegionPtr ret; + glamor_fallback("pixmap %p \n", pixmap); + if (!glamor_prepare_access(&pixmap->drawable, GLAMOR_ACCESS_RO)) + return NULL; + ret = fbPixmapToRegion(pixmap); + glamor_finish_access(&pixmap->drawable, GLAMOR_ACCESS_RO); + return ret; +} + +/* Borrow from cairo. */ +Bool +glamor_gl_has_extension(const char *extension) +{ + const char *pext; + int ext_len; + ext_len = strlen(extension); + + pext = (const char*)glGetString(GL_EXTENSIONS); + + if (pext == NULL || extension == NULL) + return FALSE; + + while ((pext = strstr(pext, extension)) != NULL) { + if (pext[ext_len] == ' ' || pext[ext_len] == '\0') + return TRUE; + pext += ext_len; + } + return FALSE; +} + +int +glamor_gl_get_version(void) +{ + int major, minor; + const char *version = (const char *) glGetString(GL_VERSION); + const char *dot = version == NULL ? NULL : strchr(version, '.'); + const char *major_start = dot; + + /* Sanity check */ + if (dot == NULL || dot == version || *(dot + 1) == '\0') { + major = 0; + minor = 0; + } else { + /* Find the start of the major version in the string */ + while (major_start > version && *major_start != ' ') + --major_start; + major = strtol(major_start, NULL, 10); + minor = strtol(dot + 1, NULL, 10); + } + + return GLAMOR_GL_VERSION_ENCODE(major, minor); +} diff --git a/xorg-server/glamor/glamor_debug.h b/xorg-server/glamor/glamor_debug.h new file mode 100644 index 000000000..f0c969b11 --- /dev/null +++ b/xorg-server/glamor/glamor_debug.h @@ -0,0 +1,116 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + * + */ + +#ifndef __GLAMOR_DEBUG_H__ +#define __GLAMOR_DEBUG_H__ + + +#define GLAMOR_DELAYED_STRING_MAX 64 + +#define GLAMOR_DEBUG_NONE 0 +#define GLAMOR_DEBUG_UNIMPL 0 +#define GLAMOR_DEBUG_FALLBACK 1 +#define GLAMOR_DEBUG_TEXTURE_DOWNLOAD 2 +#define GLAMOR_DEBUG_TEXTURE_DYNAMIC_UPLOAD 3 + +extern void +AbortServer(void) + _X_NORETURN; + +#define GLAMOR_PANIC(_format_, ...) \ + do { \ + LogMessageVerb(X_NONE, 0, "Glamor Fatal Error" \ + " at %32s line %d: " _format_ "\n", \ + __FUNCTION__, __LINE__, \ + ##__VA_ARGS__ ); \ + exit(1); \ + } while(0) + + + + +#define __debug_output_message(_format_, _prefix_, ...) \ + LogMessageVerb(X_NONE, 0, \ + "%32s:\t" _format_ , \ + /*_prefix_,*/ \ + __FUNCTION__, \ + ##__VA_ARGS__) + +#define glamor_debug_output(_level_, _format_,...) \ + do { \ + if (glamor_debug_level >= _level_) \ + __debug_output_message(_format_, \ + "Glamor debug", \ + ##__VA_ARGS__); \ + } while(0) + + +#define glamor_fallback(_format_,...) \ + do { \ + if (glamor_debug_level >= GLAMOR_DEBUG_FALLBACK) \ + __debug_output_message(_format_, \ + "Glamor fallback", \ + ##__VA_ARGS__);} while(0) + + + +#define glamor_delayed_fallback(_screen_, _format_,...) \ + do { \ + if (glamor_debug_level >= GLAMOR_DEBUG_FALLBACK) { \ + glamor_screen_private *_glamor_priv_; \ + _glamor_priv_ = glamor_get_screen_private(_screen_); \ + _glamor_priv_->delayed_fallback_pending = 1; \ + snprintf(_glamor_priv_->delayed_fallback_string, \ + GLAMOR_DELAYED_STRING_MAX, \ + "glamor delayed fallback: \t%s " _format_ , \ + __FUNCTION__, ##__VA_ARGS__); } } while(0) + + +#define glamor_clear_delayed_fallbacks(_screen_) \ + do { \ + if (glamor_debug_level >= GLAMOR_DEBUG_FALLBACK) { \ + glamor_screen_private *_glamor_priv_; \ + _glamor_priv_ = glamor_get_screen_private(_screen_); \ + _glamor_priv_->delayed_fallback_pending = 0; } } while(0) + +#define glamor_report_delayed_fallbacks(_screen_) \ + do { \ + if (glamor_debug_level >= GLAMOR_DEBUG_FALLBACK) { \ + glamor_screen_private *_glamor_priv_; \ + _glamor_priv_ = glamor_get_screen_private(_screen_); \ + LogMessageVerb(X_INFO, 0, "%s", \ + _glamor_priv_->delayed_fallback_string); \ + _glamor_priv_->delayed_fallback_pending = 0; } } while(0) + +#define DEBUGF(str, ...) do {} while(0) +//#define DEBUGF(str, ...) ErrorF(str, ##__VA_ARGS__) +#define DEBUGRegionPrint(x) do {} while (0) +//#define DEBUGRegionPrint RegionPrint + + +#endif diff --git a/xorg-server/glamor/glamor_egl.c b/xorg-server/glamor/glamor_egl.c new file mode 100644 index 000000000..ff4c0bdd9 --- /dev/null +++ b/xorg-server/glamor/glamor_egl.c @@ -0,0 +1,860 @@ +/* + * Copyright © 2010 Intel Corporation. + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GLAMOR_FOR_XORG +#include <xorg-server.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <xf86.h> +#include <xf86drm.h> +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES +#define EGL_DISPLAY_NO_X_MESA + +#ifdef GLAMOR_HAS_GBM +#include <gbm.h> +#include <drm_fourcc.h> +#endif + +#if GLAMOR_GLES2 +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#else +#include <GL/gl.h> +#endif + +#define MESA_EGL_NO_X11_HEADERS +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include "glamor.h" +#include "compat-api.h" +#include "glamor_gl_dispatch.h" +#ifdef GLX_USE_SHARED_DISPATCH +#include "glapi.h" +#endif + +static const char glamor_name[] = "glamor"; + +static DevPrivateKeyRec glamor_egl_pixmap_private_key_index; +DevPrivateKey glamor_egl_pixmap_private_key = &glamor_egl_pixmap_private_key_index; + +static void +glamor_identify(int flags) +{ + xf86Msg(X_INFO, "%s: OpenGL accelerated X.org driver based.\n", + glamor_name); +} + +struct glamor_egl_screen_private { + EGLDisplay display; + EGLContext context; + EGLint major, minor; + + CreateScreenResourcesProcPtr CreateScreenResources; + CloseScreenProcPtr CloseScreen; + int fd; + EGLImageKHR front_image; + PixmapPtr *back_pixmap; + int cpp; +#ifdef GLAMOR_HAS_GBM + struct gbm_device *gbm; +#endif + int has_gem; + void *glamor_context; + void *current_context; + int gl_context_depth; + int dri3_capable; + + PFNEGLCREATEIMAGEKHRPROC egl_create_image_khr; + PFNEGLDESTROYIMAGEKHRPROC egl_destroy_image_khr; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC egl_image_target_texture2d_oes; + struct glamor_gl_dispatch *dispatch; + CloseScreenProcPtr saved_close_screen; + xf86FreeScreenProc *saved_free_screen; +}; + +int xf86GlamorEGLPrivateIndex = -1; + +static struct glamor_egl_screen_private * +glamor_egl_get_screen_private(ScrnInfoPtr scrn) +{ + return (struct glamor_egl_screen_private *) + scrn->privates[xf86GlamorEGLPrivateIndex].ptr; +} +#ifdef GLX_USE_SHARED_DISPATCH +_X_EXPORT void +glamor_egl_make_current(ScreenPtr screen) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl = + glamor_egl_get_screen_private(scrn); + + if (glamor_egl->gl_context_depth++) + return; + + GET_CURRENT_CONTEXT(glamor_egl->current_context); + + if (glamor_egl->glamor_context != glamor_egl->current_context) { + eglMakeCurrent(glamor_egl->display, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (!eglMakeCurrent(glamor_egl->display, + EGL_NO_SURFACE, EGL_NO_SURFACE, + glamor_egl->context)) { + FatalError("Failed to make EGL context current\n"); + } + } +} + +_X_EXPORT void +glamor_egl_restore_context(ScreenPtr screen) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl = + glamor_egl_get_screen_private(scrn); + + if (--glamor_egl->gl_context_depth) + return; + + if (glamor_egl->current_context && + glamor_egl->glamor_context != glamor_egl->current_context) + SET_CURRENT_CONTEXT(glamor_egl->current_context); +} +#else +#define glamor_egl_make_current(x) +#define glamor_egl_restore_context(s) +#endif + +static EGLImageKHR +_glamor_egl_create_image(struct glamor_egl_screen_private *glamor_egl, + int width, int height, int stride, int name, int depth) +{ + EGLImageKHR image; + EGLint attribs[] = { + EGL_WIDTH, 0, + EGL_HEIGHT, 0, + EGL_DRM_BUFFER_STRIDE_MESA, 0, + EGL_DRM_BUFFER_FORMAT_MESA, + EGL_DRM_BUFFER_FORMAT_ARGB32_MESA, + EGL_DRM_BUFFER_USE_MESA, + EGL_DRM_BUFFER_USE_SHARE_MESA | + EGL_DRM_BUFFER_USE_SCANOUT_MESA, + EGL_NONE + }; + attribs[1] = width; + attribs[3] = height; + attribs[5] = stride; + if (depth != 32 && depth != 24) + return EGL_NO_IMAGE_KHR; + image = glamor_egl->egl_create_image_khr(glamor_egl->display, + glamor_egl->context, + EGL_DRM_BUFFER_MESA, + (void *) (uintptr_t)name, attribs); + if (image == EGL_NO_IMAGE_KHR) + return EGL_NO_IMAGE_KHR; + + + return image; +} + +static int +glamor_get_flink_name(int fd, int handle, int *name) +{ + struct drm_gem_flink flink; + flink.handle = handle; + if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) < 0) + return FALSE; + *name = flink.name; + return TRUE; +} + +static Bool +glamor_create_texture_from_image(struct glamor_egl_screen_private + *glamor_egl, + EGLImageKHR image, GLuint * texture) +{ + glamor_egl->dispatch->glGenTextures(1, texture); + glamor_egl->dispatch->glBindTexture(GL_TEXTURE_2D, *texture); + glamor_egl->dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glamor_egl->dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + + (glamor_egl->egl_image_target_texture2d_oes) (GL_TEXTURE_2D, + image); + glamor_egl->dispatch->glBindTexture(GL_TEXTURE_2D, 0); + return TRUE; +} + +unsigned int +glamor_egl_create_argb8888_based_texture(ScreenPtr screen, + int w, + int h) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl; + EGLImageKHR image; + GLuint texture; +#ifdef GLAMOR_HAS_DRI3_SUPPORT + struct gbm_bo *bo; + EGLNativePixmapType native_pixmap; + glamor_egl = glamor_egl_get_screen_private(scrn); + bo = gbm_bo_create (glamor_egl->gbm, w, h, GBM_FORMAT_ARGB8888, + GBM_BO_USE_RENDERING | + GBM_BO_USE_SCANOUT); + if (!bo) + return 0; + + /* If the following assignment raises an error or a warning + * then that means EGLNativePixmapType is not struct gbm_bo * + * on your platform: This code won't work and you should not + * compile with dri3 support enabled */ + native_pixmap = bo; + + image = glamor_egl->egl_create_image_khr(glamor_egl->display, + EGL_NO_CONTEXT, + EGL_NATIVE_PIXMAP_KHR, + native_pixmap, NULL); + gbm_bo_destroy(bo); + if (image == EGL_NO_IMAGE_KHR) + return 0; + glamor_create_texture_from_image(glamor_egl, image, &texture); + glamor_egl->egl_destroy_image_khr(glamor_egl->display, image); + + return texture; +#else + return 0; /* this path should never happen */ +#endif +} + +Bool +glamor_egl_create_textured_screen(ScreenPtr screen, int handle, int stride) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl; + PixmapPtr screen_pixmap; + + glamor_egl = glamor_egl_get_screen_private(scrn); + screen_pixmap = screen->GetScreenPixmap(screen); + + if (!glamor_egl_create_textured_pixmap(screen_pixmap, handle, stride)) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, "Failed to create textured screen."); + return FALSE; + } + + glamor_egl->front_image = dixLookupPrivate(&screen_pixmap->devPrivates, + glamor_egl_pixmap_private_key); + glamor_set_screen_pixmap(screen_pixmap, glamor_egl->back_pixmap); + return TRUE; +} + +Bool +glamor_egl_create_textured_screen_ext(ScreenPtr screen, + int handle, + int stride, + PixmapPtr *back_pixmap) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl; + + glamor_egl = glamor_egl_get_screen_private(scrn); + + glamor_egl->back_pixmap = back_pixmap; + if (!glamor_egl_create_textured_screen(screen, handle, stride)) + return FALSE; + return TRUE; +} + +static Bool +glamor_egl_check_has_gem(int fd) +{ + struct drm_gem_flink flink; + flink.handle = 0; + + ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink); + if (errno == ENOENT || errno == EINVAL) + return TRUE; + return FALSE; +} + +Bool +glamor_egl_create_textured_pixmap(PixmapPtr pixmap, int handle, int stride) +{ + ScreenPtr screen = pixmap->drawable.pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl; + EGLImageKHR image; + GLuint texture; + int name; + Bool ret = FALSE; + + glamor_egl = glamor_egl_get_screen_private(scrn); + + glamor_egl_make_current(screen); + if (glamor_egl->has_gem) { + if (!glamor_get_flink_name(glamor_egl->fd, handle, &name)) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Couldn't flink pixmap handle\n"); + glamor_set_pixmap_type(pixmap, GLAMOR_DRM_ONLY); + assert(0); + return FALSE; + } + } else + name = handle; + + image = _glamor_egl_create_image(glamor_egl, + pixmap->drawable.width, + pixmap->drawable.height, + ((stride * 8 + 7) / pixmap->drawable.bitsPerPixel), + name, + pixmap->drawable.depth); + if (image == EGL_NO_IMAGE_KHR) { + glamor_set_pixmap_type(pixmap, GLAMOR_DRM_ONLY); + goto done; + } + glamor_create_texture_from_image(glamor_egl, image, &texture); + glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM); + glamor_set_pixmap_texture(pixmap, texture); + dixSetPrivate(&pixmap->devPrivates, glamor_egl_pixmap_private_key, + image); + ret = TRUE; + +done: + glamor_egl_restore_context(screen); + return ret; +} + +Bool +glamor_egl_create_textured_pixmap_from_gbm_bo(PixmapPtr pixmap, void *bo) +{ + ScreenPtr screen = pixmap->drawable.pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl; + EGLImageKHR image; + GLuint texture; + Bool ret = FALSE; + + glamor_egl = glamor_egl_get_screen_private(scrn); + + glamor_egl_make_current(screen); + + image = glamor_egl->egl_create_image_khr(glamor_egl->display, + glamor_egl->context, + EGL_NATIVE_PIXMAP_KHR, + bo, NULL); + if (image == EGL_NO_IMAGE_KHR) { + glamor_set_pixmap_type(pixmap, GLAMOR_DRM_ONLY); + goto done; + } + glamor_create_texture_from_image(glamor_egl, image, &texture); + glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM); + glamor_set_pixmap_texture(pixmap, texture); + dixSetPrivate(&pixmap->devPrivates, glamor_egl_pixmap_private_key, + image); + ret = TRUE; + +done: + glamor_egl_restore_context(screen); + return ret; +} + +#ifdef GLAMOR_HAS_DRI3_SUPPORT +int glamor_get_fd_from_bo (int gbm_fd, struct gbm_bo *bo, int *fd); +void glamor_get_name_from_bo (int gbm_fd, struct gbm_bo *bo, int *name); +int +glamor_get_fd_from_bo (int gbm_fd, struct gbm_bo *bo, int *fd) +{ + union gbm_bo_handle handle; + struct drm_prime_handle args; + + handle = gbm_bo_get_handle(bo); + args.handle = handle.u32; + args.flags = DRM_CLOEXEC; + if (ioctl (gbm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args)) + return FALSE; + *fd = args.fd; + return TRUE; +} + +void +glamor_get_name_from_bo (int gbm_fd, struct gbm_bo *bo, int *name) +{ + union gbm_bo_handle handle; + + handle = gbm_bo_get_handle(bo); + if (!glamor_get_flink_name(gbm_fd, handle.u32, name)) + *name = -1; +} +#endif + +int glamor_egl_dri3_fd_name_from_tex (ScreenPtr screen, + PixmapPtr pixmap, + unsigned int tex, + Bool want_name, + CARD16 *stride, + CARD32 *size) +{ +#ifdef GLAMOR_HAS_DRI3_SUPPORT + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl; + EGLImageKHR image; + struct gbm_bo* bo; + int fd = -1; + + EGLint attribs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_GL_TEXTURE_LEVEL_KHR, 0, + EGL_NONE + }; + + glamor_egl = glamor_egl_get_screen_private(scrn); + + glamor_egl_make_current(screen); + + image = dixLookupPrivate(&pixmap->devPrivates, + glamor_egl_pixmap_private_key); + + if (image == EGL_NO_IMAGE_KHR || image == NULL) + { + image = glamor_egl->egl_create_image_khr(glamor_egl->display, + glamor_egl->context, + EGL_GL_TEXTURE_2D_KHR, + (EGLClientBuffer)(uintptr_t)tex, attribs); + if (image == EGL_NO_IMAGE_KHR) + goto failure; + + dixSetPrivate(&pixmap->devPrivates, + glamor_egl_pixmap_private_key, + image); + glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM); + } + + bo = gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_EGL_IMAGE, image, 0); + if (!bo) + goto failure; + + pixmap->devKind = gbm_bo_get_stride(bo); + + if (want_name) + { + if (glamor_egl->has_gem) + glamor_get_name_from_bo(glamor_egl->fd, bo, &fd); + } + else + { + if (glamor_get_fd_from_bo(glamor_egl->fd, bo, &fd)) + { + *stride = pixmap->devKind; + *size = pixmap->devKind * gbm_bo_get_height(bo); + } + } + + gbm_bo_destroy(bo); +failure: + glamor_egl_restore_context(screen); + return fd; +#else + return -1; +#endif +} + +PixmapPtr glamor_egl_dri3_pixmap_from_fd (ScreenPtr screen, + int fd, + CARD16 width, + CARD16 height, + CARD16 stride, + CARD8 depth, + CARD8 bpp) +{ +#ifdef GLAMOR_HAS_DRI3_SUPPORT + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl; + struct gbm_bo* bo; + EGLImageKHR image; + PixmapPtr pixmap; + Bool ret = FALSE; + EGLint attribs[] = { + EGL_WIDTH, 0, + EGL_HEIGHT, 0, + EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_ARGB8888, + EGL_DMA_BUF_PLANE0_FD_EXT, 0, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, + EGL_DMA_BUF_PLANE0_PITCH_EXT, 0, + EGL_NONE + }; + + glamor_egl = glamor_egl_get_screen_private(scrn); + + if (!glamor_egl->dri3_capable) + return NULL; + + if (bpp != 32 || !(depth == 24 || depth == 32) || width == 0 || height == 0) + return NULL; + + attribs[1] = width; + attribs[3] = height; + attribs[7] = fd; + attribs[11] = stride; + image = glamor_egl->egl_create_image_khr(glamor_egl->display, + EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + NULL, attribs); + + if (image == EGL_NO_IMAGE_KHR) + return NULL; + + /* EGL_EXT_image_dma_buf_import can impose restrictions on the + * usage of the image. Use gbm_bo to bypass the limitations. */ + + bo = gbm_bo_import(glamor_egl->gbm, GBM_BO_IMPORT_EGL_IMAGE, image, 0); + glamor_egl->egl_destroy_image_khr(glamor_egl->display, image); + + if (!bo) + return NULL; + + pixmap = screen->CreatePixmap(screen, 0, 0, depth, 0); + screen->ModifyPixmapHeader (pixmap, width, height, 0, 0, stride, NULL); + + ret = glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap, bo); + gbm_bo_destroy(bo); + + if (ret) + return pixmap; + else + { + screen->DestroyPixmap(pixmap); + return NULL; + } +#else + return NULL; +#endif +} + +static void +_glamor_egl_destroy_pixmap_image(PixmapPtr pixmap) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(pixmap->drawable.pScreen); + EGLImageKHR image; + struct glamor_egl_screen_private *glamor_egl = + glamor_egl_get_screen_private(scrn); + + image = dixLookupPrivate(&pixmap->devPrivates, + glamor_egl_pixmap_private_key); + if (image != EGL_NO_IMAGE_KHR && image != NULL) { + /* Before destroy an image which was attached to + * a texture. we must call glFlush to make sure the + * operation on that texture has been done.*/ + glamor_block_handler(pixmap->drawable.pScreen); + glamor_egl->egl_destroy_image_khr(glamor_egl->display, image); + dixSetPrivate(&pixmap->devPrivates, glamor_egl_pixmap_private_key, NULL); + } +} + +_X_EXPORT void +glamor_egl_exchange_buffers(PixmapPtr front, PixmapPtr back) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(front->drawable.pScreen); + struct glamor_egl_screen_private *glamor_egl = + glamor_egl_get_screen_private(scrn); + EGLImageKHR old_front_image; + EGLImageKHR new_front_image; + + glamor_pixmap_exchange_fbos(front, back); + new_front_image = dixLookupPrivate(&back->devPrivates, glamor_egl_pixmap_private_key); + old_front_image = dixLookupPrivate(&front->devPrivates, glamor_egl_pixmap_private_key); + dixSetPrivate(&front->devPrivates, glamor_egl_pixmap_private_key, new_front_image); + dixSetPrivate(&back->devPrivates, glamor_egl_pixmap_private_key, old_front_image); + glamor_set_pixmap_type(front, GLAMOR_TEXTURE_DRM); + glamor_set_pixmap_type(back, GLAMOR_TEXTURE_DRM); + glamor_egl->front_image = new_front_image; + +} + +void +glamor_egl_destroy_textured_pixmap(PixmapPtr pixmap) +{ + if (pixmap->refcnt == 1) + _glamor_egl_destroy_pixmap_image(pixmap); + glamor_destroy_textured_pixmap(pixmap); +} + +static Bool +glamor_egl_close_screen(CLOSE_SCREEN_ARGS_DECL) +{ + ScrnInfoPtr scrn; + struct glamor_egl_screen_private *glamor_egl; + PixmapPtr screen_pixmap; + EGLImageKHR back_image; + + scrn = xf86ScreenToScrn(screen); + glamor_egl = glamor_egl_get_screen_private(scrn); + screen_pixmap = screen->GetScreenPixmap(screen); + + glamor_egl->egl_destroy_image_khr(glamor_egl->display, glamor_egl->front_image); + dixSetPrivate(&screen_pixmap->devPrivates, glamor_egl_pixmap_private_key, NULL); + glamor_egl->front_image = NULL; + if (glamor_egl->back_pixmap && *glamor_egl->back_pixmap) { + back_image = dixLookupPrivate(&(*glamor_egl->back_pixmap)->devPrivates, + glamor_egl_pixmap_private_key); + if (back_image != NULL && back_image != EGL_NO_IMAGE_KHR) { + glamor_egl->egl_destroy_image_khr(glamor_egl->display, back_image); + dixSetPrivate(&(*glamor_egl->back_pixmap)->devPrivates, + glamor_egl_pixmap_private_key, NULL); + } + } + + screen->CloseScreen = glamor_egl->saved_close_screen; + + return screen->CloseScreen(CLOSE_SCREEN_ARGS); +} + +static Bool +glamor_egl_has_extension(struct glamor_egl_screen_private *glamor_egl, + const char *extension) +{ + const char *pext; + int ext_len; + + ext_len = strlen(extension); + pext = + (const char *) eglQueryString(glamor_egl->display, + EGL_EXTENSIONS); + if (pext == NULL || extension == NULL) + return FALSE; + while ((pext = strstr(pext, extension)) != NULL) { + if (pext[ext_len] == ' ' || pext[ext_len] == '\0') + return TRUE; + pext += ext_len; + } + return FALSE; +} + +void +glamor_egl_screen_init(ScreenPtr screen) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl = + glamor_egl_get_screen_private(scrn); + + glamor_egl->saved_close_screen = screen->CloseScreen; + screen->CloseScreen = glamor_egl_close_screen; +} + +static void +glamor_egl_free_screen(FREE_SCREEN_ARGS_DECL) +{ + ScrnInfoPtr scrn; + struct glamor_egl_screen_private *glamor_egl; +#ifndef XF86_SCRN_INTERFACE + scrn = xf86Screens[arg]; +#else + scrn = arg; +#endif + + glamor_egl = glamor_egl_get_screen_private(scrn); + if (glamor_egl != NULL) { + + eglMakeCurrent(glamor_egl->display, + EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); +#ifdef GLAMOR_HAS_GBM + if (glamor_egl->gbm) + gbm_device_destroy(glamor_egl->gbm); +#endif + scrn->FreeScreen = glamor_egl->saved_free_screen; + free(glamor_egl); + scrn->FreeScreen(FREE_SCREEN_ARGS); + } +} + +Bool +glamor_egl_init(ScrnInfoPtr scrn, int fd) +{ + struct glamor_egl_screen_private *glamor_egl; + const char *version; + EGLint config_attribs[] = { +#ifdef GLAMOR_GLES2 + EGL_CONTEXT_CLIENT_VERSION, 2, +#endif + EGL_NONE + }; + + glamor_identify(0); + glamor_egl = calloc(sizeof(*glamor_egl), 1); + if (glamor_egl == NULL) + return FALSE; + if (xf86GlamorEGLPrivateIndex == -1) + xf86GlamorEGLPrivateIndex = + xf86AllocateScrnInfoPrivateIndex(); + + scrn->privates[xf86GlamorEGLPrivateIndex].ptr = glamor_egl; + glamor_egl->fd = fd; +#ifdef GLAMOR_HAS_GBM + glamor_egl->gbm = gbm_create_device(glamor_egl->fd); + if (glamor_egl->gbm == NULL) { + ErrorF("couldn't get display device\n"); + return FALSE; + } + glamor_egl->display = eglGetDisplay(glamor_egl->gbm); +#else + glamor_egl->display = eglGetDisplay((EGLNativeDisplayType)(intptr_t)fd); +#endif + + glamor_egl->has_gem = glamor_egl_check_has_gem(fd); + +#ifndef GLAMOR_GLES2 + eglBindAPI(EGL_OPENGL_API); +#else + eglBindAPI(EGL_OPENGL_ES_API); +#endif + if (!eglInitialize + (glamor_egl->display, &glamor_egl->major, &glamor_egl->minor)) + { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "eglInitialize() failed\n"); + return FALSE; + } + + version = eglQueryString(glamor_egl->display, EGL_VERSION); + xf86Msg(X_INFO, "%s: EGL version %s:\n", glamor_name, version); + +#define GLAMOR_CHECK_EGL_EXTENSION(EXT) \ + if (!glamor_egl_has_extension(glamor_egl, "EGL_" #EXT)) { \ + ErrorF("EGL_" #EXT " required.\n"); \ + return FALSE; \ + } + +#define GLAMOR_CHECK_EGL_EXTENSIONS(EXT1, EXT2) \ + if (!glamor_egl_has_extension(glamor_egl, "EGL_" #EXT1) && \ + !glamor_egl_has_extension(glamor_egl, "EGL_" #EXT2)) { \ + ErrorF("EGL_" #EXT1 " or EGL_" #EXT2 " required.\n"); \ + return FALSE; \ + } + + GLAMOR_CHECK_EGL_EXTENSION(MESA_drm_image); + GLAMOR_CHECK_EGL_EXTENSION(KHR_gl_renderbuffer_image); +#ifdef GLAMOR_GLES2 + GLAMOR_CHECK_EGL_EXTENSIONS(KHR_surfaceless_context, KHR_surfaceless_gles2); +#else + GLAMOR_CHECK_EGL_EXTENSIONS(KHR_surfaceless_context, KHR_surfaceless_opengl); +#endif + +#ifdef GLAMOR_HAS_DRI3_SUPPORT + if (glamor_egl_has_extension(glamor_egl, "EGL_KHR_gl_texture_2D_image") && + glamor_egl_has_extension(glamor_egl, "EGL_EXT_image_dma_buf_import") ) + glamor_egl->dri3_capable = TRUE; +#endif + glamor_egl->egl_create_image_khr = (PFNEGLCREATEIMAGEKHRPROC) + eglGetProcAddress("eglCreateImageKHR"); + + glamor_egl->egl_destroy_image_khr = (PFNEGLDESTROYIMAGEKHRPROC) + eglGetProcAddress("eglDestroyImageKHR"); + + glamor_egl->egl_image_target_texture2d_oes = + (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) + eglGetProcAddress("glEGLImageTargetTexture2DOES"); + + if (!glamor_egl->egl_create_image_khr + || !glamor_egl->egl_image_target_texture2d_oes) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "eglGetProcAddress() failed\n"); + return FALSE; + } + + glamor_egl->context = eglCreateContext(glamor_egl->display, + NULL, EGL_NO_CONTEXT, + config_attribs); + if (glamor_egl->context == EGL_NO_CONTEXT) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Failed to create EGL context\n"); + return FALSE; + } + + if (!eglMakeCurrent(glamor_egl->display, + EGL_NO_SURFACE, EGL_NO_SURFACE, + glamor_egl->context)) { + xf86DrvMsg(scrn->scrnIndex, X_ERROR, + "Failed to make EGL context current\n"); + return FALSE; + } +#ifdef GLX_USE_SHARED_DISPATCH + GET_CURRENT_CONTEXT(glamor_egl->glamor_context); +#endif + glamor_egl->saved_free_screen = scrn->FreeScreen; + scrn->FreeScreen = glamor_egl_free_screen; +#ifdef GLAMOR_GLES2 + xf86DrvMsg(scrn->scrnIndex, X_INFO, "Using GLES2.\n"); +#ifdef GLX_USE_SHARED_DISPATCH + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Glamor is using GLES2 but GLX needs GL. " + "Indirect GLX may not work correctly.\n"); +#endif +#endif + return TRUE; +} + +Bool +glamor_egl_init_textured_pixmap(ScreenPtr screen) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl = + glamor_egl_get_screen_private(scrn); + if (!dixRegisterPrivateKey + (glamor_egl_pixmap_private_key, PRIVATE_PIXMAP, 0)) { + LogMessage(X_WARNING, + "glamor%d: Failed to allocate egl pixmap private\n", + screen->myNum); + return FALSE; + } + if (glamor_egl->dri3_capable) + glamor_enable_dri3(screen); + return TRUE; +} + +Bool +glamor_gl_dispatch_init(ScreenPtr screen, + struct glamor_gl_dispatch *dispatch, + int gl_version) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + struct glamor_egl_screen_private *glamor_egl = + glamor_egl_get_screen_private(scrn); + if (!glamor_gl_dispatch_init_impl + (dispatch, gl_version, (get_proc_address_t)eglGetProcAddress)) + return FALSE; + glamor_egl->dispatch = dispatch; + return TRUE; +} diff --git a/xorg-server/glamor/glamor_eglmodule.c b/xorg-server/glamor/glamor_eglmodule.c new file mode 100644 index 000000000..9a0dec9f2 --- /dev/null +++ b/xorg-server/glamor/glamor_eglmodule.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 1998 The XFree86 Project, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, 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 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 + * XFREE86 PROJECT 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. + * + * Except as contained in this notice, the name of the XFree86 Project shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from the + * XFree86 Project. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <xorg-server.h> +#define GLAMOR_FOR_XORG +#include <xf86Module.h> +#include "glamor.h" + +static XF86ModuleVersionInfo VersRec = { + GLAMOR_EGL_MODULE_NAME, + MODULEVENDORSTRING, + MODINFOSTRING1, + MODINFOSTRING2, + XORG_VERSION_CURRENT, + PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL, + ABI_CLASS_ANSIC, /* Only need the ansic layer */ + ABI_ANSIC_VERSION, + MOD_CLASS_NONE, + {0, 0, 0, 0} /* signature, to be patched into the file by a tool */ +}; + +_X_EXPORT XF86ModuleData glamoreglModuleData = { &VersRec, NULL, NULL }; diff --git a/xorg-server/glamor/glamor_fbo.c b/xorg-server/glamor/glamor_fbo.c new file mode 100644 index 000000000..d1b087ebe --- /dev/null +++ b/xorg-server/glamor/glamor_fbo.c @@ -0,0 +1,590 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + * + */ + +#include <stdlib.h> + +#include "glamor_priv.h" + +#define GLAMOR_CACHE_EXPIRE_MAX 100 + +#define GLAMOR_CACHE_DEFAULT 0 +#define GLAMOR_CACHE_EXACT_SIZE 1 + +//#define NO_FBO_CACHE 1 +#define FBO_CACHE_THRESHOLD (256*1024*1024) + +/* Loop from the tail to the head. */ +#define xorg_list_for_each_entry_reverse(pos, head, member) \ + for (pos = __container_of((head)->prev, pos, member); \ + &pos->member != (head); \ + pos = __container_of(pos->member.prev, pos, member)) + + +#define xorg_list_for_each_entry_safe_reverse(pos, tmp, head, member) \ + for (pos = __container_of((head)->prev, pos, member), \ + tmp = __container_of(pos->member.prev, pos, member); \ + &pos->member != (head); \ + pos = tmp, tmp = __container_of(pos->member.prev, tmp, member)) + +inline static int cache_wbucket(int size) +{ + int order = __fls(size / 32); + if (order >= CACHE_BUCKET_WCOUNT) + order = CACHE_BUCKET_WCOUNT - 1; + return order; +} + +inline static int cache_hbucket(int size) +{ + int order = __fls(size / 32); + if (order >= CACHE_BUCKET_HCOUNT) + order = CACHE_BUCKET_HCOUNT - 1; + return order; +} + +static glamor_pixmap_fbo * +glamor_pixmap_fbo_cache_get(glamor_screen_private *glamor_priv, + int w, int h, GLenum format, int flag) +{ + struct xorg_list *cache; + glamor_pixmap_fbo *fbo_entry, *ret_fbo = NULL; + int n_format; +#ifdef NO_FBO_CACHE + return NULL; +#else + n_format = cache_format(format); + if (n_format == -1) + return NULL; + cache = &glamor_priv->fbo_cache[n_format] + [cache_wbucket(w)] + [cache_hbucket(h)]; + if (!(flag & GLAMOR_CACHE_EXACT_SIZE)) { + xorg_list_for_each_entry(fbo_entry, cache, list) { + if (fbo_entry->width >= w && fbo_entry->height >= h) { + + DEBUGF("Request w %d h %d format %x \n", w, h, format); + DEBUGF("got cache entry %p w %d h %d fbo %d tex %d format %x\n", + fbo_entry, fbo_entry->width, fbo_entry->height, + fbo_entry->fb, fbo_entry->tex); + xorg_list_del(&fbo_entry->list); + ret_fbo = fbo_entry; + break; + } + } + } + else { + xorg_list_for_each_entry(fbo_entry, cache, list) { + if (fbo_entry->width == w && fbo_entry->height == h) { + + DEBUGF("Request w %d h %d format %x \n", w, h, format); + DEBUGF("got cache entry %p w %d h %d fbo %d tex %d format %x\n", + fbo_entry, fbo_entry->width, fbo_entry->height, + fbo_entry->fb, fbo_entry->tex, fbo_entry->format); + assert(format == fbo_entry->format); + xorg_list_del(&fbo_entry->list); + ret_fbo = fbo_entry; + break; + } + } + } + + if (ret_fbo) + glamor_priv->fbo_cache_watermark -= ret_fbo->width * ret_fbo->height; + + assert(glamor_priv->fbo_cache_watermark >= 0); + + return ret_fbo; +#endif +} + +void +glamor_purge_fbo(glamor_pixmap_fbo *fbo) +{ + glamor_gl_dispatch *dispatch = glamor_get_dispatch(fbo->glamor_priv); + if (fbo->fb) + dispatch->glDeleteFramebuffers(1, &fbo->fb); + if (fbo->tex) + dispatch->glDeleteTextures(1, &fbo->tex); + if (fbo->pbo) + dispatch->glDeleteBuffers(1, &fbo->pbo); + glamor_put_dispatch(fbo->glamor_priv); + + free(fbo); +} + +static void +glamor_pixmap_fbo_cache_put(glamor_pixmap_fbo *fbo) +{ + struct xorg_list *cache; + int n_format; + +#ifdef NO_FBO_CACHE + glamor_purge_fbo(fbo); + return; +#else + n_format = cache_format(fbo->format); + + if (fbo->fb == 0 || n_format == -1 + || fbo->glamor_priv->fbo_cache_watermark >= FBO_CACHE_THRESHOLD) { + fbo->glamor_priv->tick += GLAMOR_CACHE_EXPIRE_MAX; + glamor_fbo_expire(fbo->glamor_priv); + glamor_purge_fbo(fbo); + return; + } + + cache = &fbo->glamor_priv->fbo_cache[n_format] + [cache_wbucket(fbo->width)] + [cache_hbucket(fbo->height)]; + DEBUGF("Put cache entry %p to cache %p w %d h %d format %x fbo %d tex %d \n", fbo, cache, + fbo->width, fbo->height, fbo->format, fbo->fb, fbo->tex); + + fbo->glamor_priv->fbo_cache_watermark += fbo->width * fbo->height; + xorg_list_add(&fbo->list, cache); + fbo->expire = fbo->glamor_priv->tick + GLAMOR_CACHE_EXPIRE_MAX; +#endif +} + +static void +glamor_pixmap_ensure_fb(glamor_pixmap_fbo *fbo) +{ + glamor_gl_dispatch *dispatch; + int status; + + dispatch = glamor_get_dispatch(fbo->glamor_priv); + + if (fbo->fb == 0) + dispatch->glGenFramebuffers(1, &fbo->fb); + assert(fbo->tex != 0); + dispatch->glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb); + dispatch->glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, fbo->tex, + 0); + status = dispatch->glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + const char *str; + switch (status) { + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + str = "incomplete attachment"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + str = "incomplete/missing attachment"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + str = "incomplete draw buffer"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + str = "incomplete read buffer"; + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + str = "unsupported"; + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + str = "incomplete multiple"; + break; + default: + str = "unknown error"; + break; + } + + FatalError("destination is framebuffer incomplete: %s [%x]\n", + str, status); + } + glamor_put_dispatch(fbo->glamor_priv); +} + +glamor_pixmap_fbo * +glamor_create_fbo_from_tex(glamor_screen_private *glamor_priv, + int w, int h, GLenum format, GLint tex, int flag) +{ + glamor_pixmap_fbo *fbo; + + fbo = calloc(1, sizeof(*fbo)); + if (fbo == NULL) + return NULL; + + xorg_list_init(&fbo->list); + + fbo->tex = tex; + fbo->width = w; + fbo->height = h; + fbo->format = format; + fbo->glamor_priv = glamor_priv; + + if (flag == GLAMOR_CREATE_PIXMAP_MAP) { + glamor_gl_dispatch *dispatch; + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glGenBuffers(1, &fbo->pbo); + glamor_put_dispatch(glamor_priv); + goto done; + } + + if (flag != GLAMOR_CREATE_FBO_NO_FBO) + glamor_pixmap_ensure_fb(fbo); + +done: + return fbo; +} + + +void +glamor_fbo_expire(glamor_screen_private *glamor_priv) +{ + struct xorg_list *cache; + glamor_pixmap_fbo *fbo_entry, *tmp; + int i,j,k; + + for(i = 0; i < CACHE_FORMAT_COUNT; i++) + for(j = 0; j < CACHE_BUCKET_WCOUNT; j++) + for(k = 0; k < CACHE_BUCKET_HCOUNT; k++) { + cache = &glamor_priv->fbo_cache[i][j][k]; + xorg_list_for_each_entry_safe_reverse(fbo_entry, tmp, cache, list) { + if (GLAMOR_TICK_AFTER(fbo_entry->expire, glamor_priv->tick)) { + break; + } + + glamor_priv->fbo_cache_watermark -= fbo_entry->width * fbo_entry->height; + xorg_list_del(&fbo_entry->list); + DEBUGF("cache %p fbo %p expired %d current %d \n", cache, fbo_entry, + fbo_entry->expire, glamor_priv->tick); + glamor_purge_fbo(fbo_entry); + } + } + +} + +void +glamor_init_pixmap_fbo(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + int i,j,k; + + glamor_priv = glamor_get_screen_private(screen); + for(i = 0; i < CACHE_FORMAT_COUNT; i++) + for(j = 0; j < CACHE_BUCKET_WCOUNT; j++) + for(k = 0; k < CACHE_BUCKET_HCOUNT; k++) + { + xorg_list_init(&glamor_priv->fbo_cache[i][j][k]); + } + glamor_priv->fbo_cache_watermark = 0; +} + +void +glamor_fini_pixmap_fbo(ScreenPtr screen) +{ + struct xorg_list *cache; + glamor_screen_private *glamor_priv; + glamor_pixmap_fbo *fbo_entry, *tmp; + int i,j,k; + + glamor_priv = glamor_get_screen_private(screen); + for(i = 0; i < CACHE_FORMAT_COUNT; i++) + for(j = 0; j < CACHE_BUCKET_WCOUNT; j++) + for(k = 0; k < CACHE_BUCKET_HCOUNT; k++) + { + cache = &glamor_priv->fbo_cache[i][j][k]; + xorg_list_for_each_entry_safe_reverse(fbo_entry, tmp, cache, list) { + xorg_list_del(&fbo_entry->list); + glamor_purge_fbo(fbo_entry); + } + } +} + +void +glamor_destroy_fbo(glamor_pixmap_fbo *fbo) +{ + xorg_list_del(&fbo->list); + glamor_pixmap_fbo_cache_put(fbo); + +} + +static int +_glamor_create_tex(glamor_screen_private *glamor_priv, + int w, int h, GLenum format) +{ + glamor_gl_dispatch *dispatch; + unsigned int tex = 0; + + /* With dri3, we want to allocate ARGB8888 pixmaps only. + * Depending on the implementation, GL_RGBA might not + * give us ARGB8888. We ask glamor_egl to use get + * an ARGB8888 based texture for us. */ + if (glamor_priv->dri3_enabled && format == GL_RGBA) + { + tex = glamor_egl_create_argb8888_based_texture(glamor_priv->screen, + w, h); + } + if (!tex) + { + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glGenTextures(1, &tex); + dispatch->glBindTexture(GL_TEXTURE_2D, tex); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + dispatch->glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, + format, GL_UNSIGNED_BYTE, NULL); + glamor_put_dispatch(glamor_priv); + } + return tex; +} + +glamor_pixmap_fbo * +glamor_create_fbo(glamor_screen_private *glamor_priv, + int w, int h, + GLenum format, + int flag) +{ + glamor_pixmap_fbo *fbo; + GLint tex = 0; + int cache_flag; + + if (!glamor_check_fbo_size(glamor_priv, w, h)) + return NULL; + + if (flag == GLAMOR_CREATE_FBO_NO_FBO) + goto new_fbo; + + if (flag == GLAMOR_CREATE_PIXMAP_MAP) + goto no_tex; + + if (flag == GLAMOR_CREATE_PIXMAP_FIXUP) + cache_flag = GLAMOR_CACHE_EXACT_SIZE; + else + cache_flag = 0; + + fbo = glamor_pixmap_fbo_cache_get(glamor_priv, w, h, + format, cache_flag); + if (fbo) + return fbo; +new_fbo: + tex = _glamor_create_tex(glamor_priv, w, h, format); +no_tex: + fbo = glamor_create_fbo_from_tex(glamor_priv, w, h, format, tex, flag); + + return fbo; +} + +static glamor_pixmap_fbo * +_glamor_create_fbo_array(glamor_screen_private *glamor_priv, + int w, int h, GLenum format, int flag, + int block_w, int block_h, + glamor_pixmap_private *pixmap_priv, + int has_fbo) +{ + int block_wcnt; + int block_hcnt; + glamor_pixmap_fbo **fbo_array; + BoxPtr box_array; + int i,j; + glamor_pixmap_private_large_t *priv; + + priv = &pixmap_priv->large; + + block_wcnt = (w + block_w - 1) / block_w; + block_hcnt = (h + block_h - 1) / block_h; + + box_array = calloc(block_wcnt * block_hcnt, sizeof(box_array[0])); + if (box_array == NULL) + return NULL; + + fbo_array = calloc(block_wcnt * block_hcnt, sizeof(glamor_pixmap_fbo*)); + if (fbo_array == NULL) { + free(box_array); + return FALSE; + } + for(i = 0; i < block_hcnt; i++) + { + int block_y1, block_y2; + int fbo_w, fbo_h; + + block_y1 = i * block_h; + block_y2 = (block_y1 + block_h) > h ? h : (block_y1 + block_h); + fbo_h = block_y2 - block_y1; + + for (j = 0; j < block_wcnt; j++) + { + box_array[i * block_wcnt + j].x1 = j * block_w; + box_array[i * block_wcnt + j].y1 = block_y1; + box_array[i * block_wcnt + j].x2 = (j + 1) * block_w > w ? w : (j + 1) * block_w; + box_array[i * block_wcnt + j].y2 = block_y2; + fbo_w = box_array[i * block_wcnt + j].x2 - box_array[i * block_wcnt + j].x1; + if (!has_fbo) + fbo_array[i * block_wcnt + j] = glamor_create_fbo(glamor_priv, + fbo_w, fbo_h, format, + GLAMOR_CREATE_PIXMAP_FIXUP); + else + fbo_array[i * block_wcnt + j] = priv->base.fbo; + if (fbo_array[i * block_wcnt + j] == NULL) + goto cleanup; + } + } + + priv->box = box_array[0]; + priv->box_array = box_array; + priv->fbo_array = fbo_array; + priv->block_wcnt = block_wcnt; + priv->block_hcnt = block_hcnt; + return fbo_array[0]; + +cleanup: + for(i = 0; i < block_wcnt * block_hcnt; i++) + if ((fbo_array)[i]) + glamor_destroy_fbo((fbo_array)[i]); + free(box_array); + free(fbo_array); + return NULL; +} + + +/* Create a fbo array to cover the w*h region, by using block_w*block_h + * block.*/ +glamor_pixmap_fbo * +glamor_create_fbo_array(glamor_screen_private *glamor_priv, + int w, int h, GLenum format, int flag, + int block_w, int block_h, + glamor_pixmap_private *pixmap_priv) +{ + pixmap_priv->large.block_w = block_w; + pixmap_priv->large.block_h = block_h; + return _glamor_create_fbo_array(glamor_priv, w, h, format, flag, + block_w, block_h, pixmap_priv, 0); +} + +glamor_pixmap_fbo * +glamor_pixmap_detach_fbo(glamor_pixmap_private *pixmap_priv) +{ + glamor_pixmap_fbo *fbo; + + if (pixmap_priv == NULL) + return NULL; + + fbo = pixmap_priv->base.fbo; + if (fbo == NULL) + return NULL; + + pixmap_priv->base.fbo = NULL; + return fbo; +} + +/* The pixmap must not be attached to another fbo. */ +void +glamor_pixmap_attach_fbo(PixmapPtr pixmap, glamor_pixmap_fbo *fbo) +{ + glamor_pixmap_private *pixmap_priv; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + + if (pixmap_priv->base.fbo) + return; + + pixmap_priv->base.fbo = fbo; + + switch (pixmap_priv->type) { + case GLAMOR_TEXTURE_LARGE: + case GLAMOR_TEXTURE_ONLY: + case GLAMOR_TEXTURE_DRM: + pixmap_priv->base.gl_fbo = 1; + if (fbo->tex != 0) + pixmap_priv->base.gl_tex = 1; + else { + /* XXX For the Xephyr only, may be broken now.*/ + pixmap_priv->base.gl_tex = 0; + } + case GLAMOR_MEMORY_MAP: + pixmap->devPrivate.ptr = NULL; + break; + default: + break; + } +} + +void +glamor_pixmap_destroy_fbo(glamor_pixmap_private *priv) +{ + glamor_pixmap_fbo *fbo; + if (priv->type == GLAMOR_TEXTURE_LARGE) { + int i; + glamor_pixmap_private_large_t *large = &priv->large; + for(i = 0; i < large->block_wcnt * large->block_hcnt; i++) + glamor_destroy_fbo(large->fbo_array[i]); + free(large->fbo_array); + } else { + fbo = glamor_pixmap_detach_fbo(priv); + if (fbo) + glamor_destroy_fbo(fbo); + } + + free(priv); +} + +Bool +glamor_pixmap_ensure_fbo(PixmapPtr pixmap, GLenum format, int flag) +{ + glamor_screen_private *glamor_priv; + glamor_pixmap_private *pixmap_priv; + glamor_pixmap_fbo *fbo; + + glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (pixmap_priv->base.fbo == NULL) { + + fbo = glamor_create_fbo(glamor_priv, pixmap->drawable.width, + pixmap->drawable.height, + format, + flag); + if (fbo == NULL) + return FALSE; + + glamor_pixmap_attach_fbo(pixmap, fbo); + } else { + /* We do have a fbo, but it may lack of fb or tex. */ + if (!pixmap_priv->base.fbo->tex) + pixmap_priv->base.fbo->tex = _glamor_create_tex(glamor_priv, pixmap->drawable.width, + pixmap->drawable.height, format); + + if (flag != GLAMOR_CREATE_FBO_NO_FBO && pixmap_priv->base.fbo->fb == 0) + glamor_pixmap_ensure_fb(pixmap_priv->base.fbo); + } + + return TRUE; +} + +_X_EXPORT void +glamor_pixmap_exchange_fbos(PixmapPtr front, PixmapPtr back) +{ + glamor_pixmap_private *front_priv, *back_priv; + glamor_pixmap_fbo *temp_fbo; + + front_priv = glamor_get_pixmap_private(front); + back_priv = glamor_get_pixmap_private(back); + temp_fbo = front_priv->base.fbo; + front_priv->base.fbo = back_priv->base.fbo; + back_priv->base.fbo = temp_fbo; +} diff --git a/xorg-server/glamor/glamor_fill.c b/xorg-server/glamor/glamor_fill.c new file mode 100644 index 000000000..fbc87392e --- /dev/null +++ b/xorg-server/glamor/glamor_fill.c @@ -0,0 +1,364 @@ +/* + * Copyright © 2008 Intel Corporation + * Copyright © 1998 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + */ + +#include "glamor_priv.h" + +/** @file glamor_fillspans.c + * + * GC fill implementation, based loosely on fb_fill.c + */ +Bool +glamor_fill(DrawablePtr drawable, + GCPtr gc, int x, int y, int width, int height, Bool fallback) +{ + PixmapPtr dst_pixmap = glamor_get_drawable_pixmap(drawable); + int off_x, off_y; + PixmapPtr sub_pixmap = NULL; + glamor_access_t sub_pixmap_access; + DrawablePtr saved_drawable = NULL; + int saved_x = x, saved_y = y; + + glamor_get_drawable_deltas(drawable, dst_pixmap, &off_x, &off_y); + + switch (gc->fillStyle) { + case FillSolid: + if (!glamor_solid(dst_pixmap, + x + off_x, + y + off_y, + width, height, gc->alu, gc->planemask, + gc->fgPixel)) + goto fail; + break; + case FillStippled: + case FillOpaqueStippled: + if (!glamor_stipple(dst_pixmap, + gc->stipple, + x + off_x, + y + off_y, + width, + height, + gc->alu, + gc->planemask, + gc->fgPixel, + gc->bgPixel, gc->patOrg.x, + gc->patOrg.y)) + goto fail; + break; + case FillTiled: + if (!glamor_tile(dst_pixmap, + gc->tile.pixmap, + x + off_x, + y + off_y, + width, + height, + gc->alu, + gc->planemask, + x - drawable->x - gc->patOrg.x, + y - drawable->y - gc->patOrg.y)) + goto fail; + break; + } + return TRUE; + + fail: + if (!fallback) { + if (glamor_ddx_fallback_check_pixmap(&dst_pixmap->drawable) + && glamor_ddx_fallback_check_gc(gc)) + return FALSE; + } + /* Is it possible to set the access as WO? */ + + sub_pixmap_access = GLAMOR_ACCESS_RW; + + sub_pixmap = glamor_get_sub_pixmap(dst_pixmap, x + off_x, + y + off_y, width, height, + sub_pixmap_access); + + if (sub_pixmap != NULL) { + if (gc->fillStyle != FillSolid) { + gc->patOrg.x += (drawable->x - x); + gc->patOrg.y += (drawable->y - y); + } + saved_drawable = drawable; + drawable = &sub_pixmap->drawable; + saved_x = x; + saved_y = y; + x = 0; + y = 0; + } + if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RW)) { + if (glamor_prepare_access_gc(gc)) { + fbFill(drawable, gc, x, y, width, height); + glamor_finish_access_gc(gc); + } + glamor_finish_access(drawable, GLAMOR_ACCESS_RW); + } + + if (sub_pixmap != NULL) { + if (gc->fillStyle != FillSolid) { + gc->patOrg.x -= (saved_drawable->x - saved_x); + gc->patOrg.y -= (saved_drawable->y - saved_y); + } + + x = saved_x; + y = saved_y; + + glamor_put_sub_pixmap(sub_pixmap, dst_pixmap, + x + off_x, y + off_y, + width, height, sub_pixmap_access); + } + + return TRUE; +} + +void +glamor_init_solid_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + const char *solid_vs = + "attribute vec4 v_position;" + "void main()\n" "{\n" " gl_Position = v_position;\n" + "}\n"; + const char *solid_fs = + GLAMOR_DEFAULT_PRECISION "uniform vec4 color;\n" + "void main()\n" "{\n" " gl_FragColor = color;\n" "}\n"; + GLint fs_prog, vs_prog; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + glamor_priv->solid_prog = dispatch->glCreateProgram(); + vs_prog = glamor_compile_glsl_prog(dispatch, GL_VERTEX_SHADER, solid_vs); + fs_prog = glamor_compile_glsl_prog(dispatch, GL_FRAGMENT_SHADER, + solid_fs); + dispatch->glAttachShader(glamor_priv->solid_prog, vs_prog); + dispatch->glAttachShader(glamor_priv->solid_prog, fs_prog); + + dispatch->glBindAttribLocation(glamor_priv->solid_prog, + GLAMOR_VERTEX_POS, "v_position"); + glamor_link_glsl_prog(dispatch, glamor_priv->solid_prog); + + glamor_priv->solid_color_uniform_location = + dispatch->glGetUniformLocation(glamor_priv->solid_prog, + "color"); + glamor_put_dispatch(glamor_priv); +} + +void +glamor_fini_solid_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glDeleteProgram(glamor_priv->solid_prog); + glamor_put_dispatch(glamor_priv); +} + +static void +_glamor_solid_boxes(PixmapPtr pixmap, BoxPtr box, int nbox, float *color) +{ + ScreenPtr screen = pixmap->drawable.pScreen; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_pixmap_private *pixmap_priv = + glamor_get_pixmap_private(pixmap); + glamor_gl_dispatch *dispatch; + GLfloat xscale, yscale; + float vertices[32]; + float *pvertices = vertices; + int valid_nbox = ARRAY_SIZE(vertices); + + glamor_set_destination_pixmap_priv_nc(pixmap_priv); + + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glUseProgram(glamor_priv->solid_prog); + + dispatch->glUniform4fv(glamor_priv->solid_color_uniform_location, + 1, color); + + pixmap_priv_get_dest_scale(pixmap_priv, &xscale, &yscale); + + if (unlikely(nbox*4*2 > ARRAY_SIZE(vertices))) { + int allocated_box; + + if (nbox * 6 > GLAMOR_COMPOSITE_VBO_VERT_CNT) { + allocated_box = GLAMOR_COMPOSITE_VBO_VERT_CNT / 6; + } else + allocated_box = nbox; + pvertices = malloc(allocated_box * 4 * 2 * sizeof(float)); + if (pvertices) + valid_nbox = allocated_box; + else { + pvertices = vertices; + valid_nbox = ARRAY_SIZE(vertices) / (4*2); + } + } + + if (unlikely(nbox > 1)) + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glamor_priv->ebo); + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), + pvertices); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); + + while(nbox) { + int box_cnt, i; + float *valid_vertices; + valid_vertices = pvertices; + box_cnt = nbox > valid_nbox ? valid_nbox : nbox; + for (i = 0; i < box_cnt; i++) { + glamor_set_normalize_vcoords(pixmap_priv, xscale, yscale, + box[i].x1, box[i].y1, + box[i].x2, box[i].y2, + glamor_priv->yInverted, + valid_vertices); + valid_vertices += 4*2; + } + if (box_cnt == 1) + dispatch->glDrawArrays(GL_TRIANGLE_FAN, 0, box_cnt * 4); + else +#ifndef GLAMOR_GLES2 + dispatch->glDrawRangeElements(GL_TRIANGLES, + 0, + box_cnt * 4, + box_cnt * 6, + GL_UNSIGNED_SHORT, + NULL); +#else + dispatch->glDrawElements(GL_TRIANGLES, + box_cnt * 6, + GL_UNSIGNED_SHORT, + NULL); +#endif + nbox -= box_cnt; + box += box_cnt; + } + + if (pvertices != vertices) + free(pvertices); + + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glUseProgram(0); + glamor_put_dispatch(glamor_priv); + glamor_priv->state = RENDER_STATE; + glamor_priv->render_idle_cnt = 0; +} + +Bool +glamor_solid_boxes(PixmapPtr pixmap, + BoxPtr box, int nbox, + unsigned long fg_pixel) +{ + glamor_pixmap_private *pixmap_priv; + GLfloat color[4]; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + return FALSE; + + glamor_get_rgba_from_pixel(fg_pixel, + &color[0], + &color[1], + &color[2], + &color[3], format_for_pixmap(pixmap)); + + if (pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + RegionRec region; + int n_region; + glamor_pixmap_clipped_regions *clipped_regions; + int i; + + RegionInitBoxes(®ion, box, nbox); + clipped_regions = glamor_compute_clipped_regions(pixmap_priv, ®ion, &n_region, 0, 0, 0); + for(i = 0; i < n_region; i++) + { + BoxPtr inner_box; + int inner_nbox; + SET_PIXMAP_FBO_CURRENT(pixmap_priv, clipped_regions[i].block_idx); + + inner_box = RegionRects(clipped_regions[i].region); + inner_nbox = RegionNumRects(clipped_regions[i].region); + _glamor_solid_boxes(pixmap, inner_box, inner_nbox, color); + RegionDestroy(clipped_regions[i].region); + } + free(clipped_regions); + RegionUninit(®ion); + } else + _glamor_solid_boxes(pixmap, box, nbox, color); + + return TRUE; +} + +Bool +glamor_solid(PixmapPtr pixmap, int x, int y, int width, int height, + unsigned char alu, unsigned long planemask, + unsigned long fg_pixel) +{ + ScreenPtr screen = pixmap->drawable.pScreen; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_pixmap_private *pixmap_priv; + glamor_gl_dispatch *dispatch; + BoxRec box; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + return FALSE; + + if (!glamor_set_planemask(pixmap, planemask)) { + glamor_fallback + ("Failedto set planemask in glamor_solid.\n"); + return FALSE; + } + + dispatch = glamor_get_dispatch(glamor_priv); + if (!glamor_set_alu(dispatch, alu)) { + if (alu == GXclear) + fg_pixel = 0; + else { + glamor_fallback("unsupported alu %x\n", alu); + glamor_put_dispatch(glamor_priv); + return FALSE; + } + } + box.x1 = x; + box.y1 = y; + box.x2 = x + width; + box.y2 = y + height; + glamor_solid_boxes(pixmap, &box, 1, fg_pixel); + + glamor_set_alu(dispatch, GXcopy); + glamor_put_dispatch(glamor_priv); + + return TRUE; +} + diff --git a/xorg-server/glamor/glamor_fillspans.c b/xorg-server/glamor/glamor_fillspans.c new file mode 100644 index 000000000..35e881f61 --- /dev/null +++ b/xorg-server/glamor/glamor_fillspans.c @@ -0,0 +1,113 @@ +/* + * Copyright © 1998 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** @file glamor_fillspans.c + * + * FillSpans implementation, taken from fb_fillsp.c + */ +#include "glamor_priv.h" + +static Bool +_glamor_fill_spans(DrawablePtr drawable, + GCPtr gc, + int n, DDXPointPtr points, int *widths, int sorted, Bool fallback) +{ + DDXPointPtr ppt; + int nbox; + BoxPtr pbox; + int x1, x2, y; + RegionPtr pClip = fbGetCompositeClip(gc); + Bool ret = FALSE; + + if (gc->fillStyle != FillSolid && gc->fillStyle != FillTiled) + goto fail; + + ppt = points; + while (n--) { + x1 = ppt->x; + y = ppt->y; + x2 = x1 + (int) *widths; + ppt++; + widths++; + + nbox = REGION_NUM_RECTS(pClip); + pbox = REGION_RECTS(pClip); + while (nbox--) { + int real_x1 = x1, real_x2 = x2; + + if (real_x1 < pbox->x1) + real_x1 = pbox->x1; + + if (real_x2 > pbox->x2) + real_x2 = pbox->x2; + + if (real_x2 > real_x1 && pbox->y1 <= y && pbox->y2 > y) { + if (!glamor_fill(drawable, gc, real_x1, y, + real_x2 - real_x1, 1, fallback)) + goto fail; + } + pbox++; + } + } + ret = TRUE; + goto done; + +fail: + if (!fallback + && glamor_ddx_fallback_check_pixmap(drawable) + && glamor_ddx_fallback_check_gc(gc)) { + goto done; + } + glamor_fallback("to %p (%c)\n", drawable, + glamor_get_drawable_location(drawable)); + if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RW)) { + if (glamor_prepare_access_gc(gc)) { + fbFillSpans(drawable, gc, n, points, widths, + sorted); + glamor_finish_access_gc(gc); + } + glamor_finish_access(drawable, GLAMOR_ACCESS_RW); + } + ret = TRUE; + +done: + return ret; +} + + +void +glamor_fill_spans(DrawablePtr drawable, + GCPtr gc, + int n, DDXPointPtr points, int *widths, int sorted) +{ + _glamor_fill_spans(drawable, gc, n, points, widths, sorted, TRUE); +} + +Bool +glamor_fill_spans_nf(DrawablePtr drawable, + GCPtr gc, + int n, DDXPointPtr points, int *widths, int sorted) +{ + return _glamor_fill_spans(drawable, gc, n, points, widths, sorted, FALSE); +} + + diff --git a/xorg-server/glamor/glamor_getimage.c b/xorg-server/glamor/glamor_getimage.c new file mode 100644 index 000000000..5df576c45 --- /dev/null +++ b/xorg-server/glamor/glamor_getimage.c @@ -0,0 +1,101 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + * + */ + +#include "glamor_priv.h" + + +static Bool +_glamor_get_image(DrawablePtr drawable, int x, int y, int w, int h, + unsigned int format, unsigned long planeMask, char *d, + Bool fallback) +{ + PixmapPtr pixmap, sub_pixmap; + struct glamor_pixmap_private *pixmap_priv; + int x_off, y_off; + int stride; + void *data; + + pixmap = glamor_get_drawable_pixmap(drawable); + glamor_get_drawable_deltas(drawable, pixmap, &x_off, &y_off); + + if (format != ZPixmap) + goto fall_back; + pixmap = glamor_get_drawable_pixmap(drawable); + glamor_get_drawable_deltas(drawable, pixmap, &x_off, &y_off); + + if (!glamor_set_planemask(pixmap, planeMask)) { + glamor_fallback + ("Failedto set planemask in glamor_solid.\n"); + goto fall_back; + } + pixmap_priv = glamor_get_pixmap_private(pixmap); + + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + goto fall_back; + stride = PixmapBytePad(w, drawable->depth); + + x += drawable->x + x_off; + y += drawable->y + y_off; + + data = glamor_download_sub_pixmap_to_cpu(pixmap, x, y, w, h, stride, + d, 0, GLAMOR_ACCESS_RO); + if (data != NULL) { + assert(data == d); + return TRUE; + } +fall_back: + sub_pixmap = glamor_get_sub_pixmap(pixmap, x + x_off + drawable->x, + y + y_off + drawable->y, w, h, + GLAMOR_ACCESS_RO); + if (sub_pixmap) { + fbGetImage(&sub_pixmap->drawable, 0, 0, w, h, format, planeMask, d); + glamor_put_sub_pixmap(sub_pixmap, pixmap, + x + x_off + drawable->x, + y + y_off + drawable->y, + w, h, GLAMOR_ACCESS_RO); + } else + miGetImage(drawable, x, y, w, h, format, planeMask, d); + + return TRUE; +} + +void +glamor_get_image(DrawablePtr pDrawable, int x, int y, int w, int h, + unsigned int format, unsigned long planeMask, char *d) +{ + _glamor_get_image(pDrawable, x, y, w, h, format, planeMask, d, TRUE); +} + +Bool +glamor_get_image_nf(DrawablePtr pDrawable, int x, int y, int w, int h, + unsigned int format, unsigned long planeMask, char *d) +{ + return _glamor_get_image(pDrawable, x, y, w, + h, format, planeMask, d, FALSE); +} diff --git a/xorg-server/glamor/glamor_getspans.c b/xorg-server/glamor/glamor_getspans.c new file mode 100644 index 000000000..6d6c8e9a4 --- /dev/null +++ b/xorg-server/glamor/glamor_getspans.c @@ -0,0 +1,95 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + +#include "glamor_priv.h" + +static Bool +_glamor_get_spans(DrawablePtr drawable, + int wmax, + DDXPointPtr points, int *widths, int count, char *dst, + Bool fallback) +{ + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + glamor_pixmap_private *pixmap_priv = + glamor_get_pixmap_private(pixmap); + int i; + uint8_t *readpixels_dst = (uint8_t *) dst; + void *data; + int x_off, y_off; + Bool ret = FALSE; + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + goto fail; + + glamor_get_drawable_deltas(drawable, pixmap, &x_off, &y_off); + for (i = 0; i < count; i++) { + data = glamor_download_sub_pixmap_to_cpu(pixmap, points[i].x + x_off, + points[i].y + y_off, widths[i], 1, + PixmapBytePad(widths[i], drawable->depth), + readpixels_dst, 0, GLAMOR_ACCESS_RO); + assert(data == readpixels_dst); + readpixels_dst += PixmapBytePad(widths[i], drawable->depth); + } + + ret = TRUE; + goto done; +fail: + + if (!fallback + && glamor_ddx_fallback_check_pixmap(drawable)) + goto done; + + ret = TRUE; + if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RO)) { + fbGetSpans(drawable, wmax, points, widths, count, dst); + glamor_finish_access(drawable, GLAMOR_ACCESS_RO); + } +done: + return ret; +} + +void +glamor_get_spans(DrawablePtr drawable, + int wmax, + DDXPointPtr points, int *widths, int count, char *dst) +{ + _glamor_get_spans(drawable, wmax, points, + widths, count, dst, TRUE); +} + +Bool +glamor_get_spans_nf(DrawablePtr drawable, + int wmax, + DDXPointPtr points, int *widths, int count, char *dst) +{ + return _glamor_get_spans(drawable, wmax, points, + widths, count, dst, FALSE); +} + + + diff --git a/xorg-server/glamor/glamor_gl_dispatch.c b/xorg-server/glamor/glamor_gl_dispatch.c new file mode 100644 index 000000000..da99e2627 --- /dev/null +++ b/xorg-server/glamor/glamor_gl_dispatch.c @@ -0,0 +1,118 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + * + */ + +#include "glamor_priv.h" +#include <dlfcn.h> + +#define INIT_FUNC(dst,func_name,get) \ + dst->func_name = get(#func_name); \ + if (dst->func_name == NULL) { \ + dst->func_name = (void *)dlsym(NULL, #func_name); \ + if (dst->func_name == NULL) { \ + ErrorF("Failed to get function %s\n", #func_name);\ + goto fail; \ + } \ + } \ + +_X_EXPORT Bool +glamor_gl_dispatch_init_impl(struct glamor_gl_dispatch *dispatch, + int gl_version, + void *(*get_proc_address) (const char *)) +{ +#ifndef GLAMOR_GLES2 + INIT_FUNC(dispatch, glMatrixMode, get_proc_address); + INIT_FUNC(dispatch, glLoadIdentity, get_proc_address); + INIT_FUNC(dispatch, glRasterPos2i, get_proc_address); + INIT_FUNC(dispatch, glDrawPixels, get_proc_address); + INIT_FUNC(dispatch, glLogicOp, get_proc_address); + INIT_FUNC(dispatch, glMapBuffer, get_proc_address); + INIT_FUNC(dispatch, glMapBufferRange, get_proc_address); + INIT_FUNC(dispatch, glUnmapBuffer, get_proc_address); + INIT_FUNC(dispatch, glBlitFramebuffer, get_proc_address); + INIT_FUNC(dispatch, glDrawRangeElements, get_proc_address); +#endif + INIT_FUNC(dispatch, glViewport, get_proc_address); + INIT_FUNC(dispatch, glDrawArrays, get_proc_address); + INIT_FUNC(dispatch, glDrawElements, get_proc_address); + INIT_FUNC(dispatch, glReadPixels, get_proc_address); + INIT_FUNC(dispatch, glPixelStorei, get_proc_address); + INIT_FUNC(dispatch, glTexParameteri, get_proc_address); + INIT_FUNC(dispatch, glTexImage2D, get_proc_address); + INIT_FUNC(dispatch, glGenTextures, get_proc_address); + INIT_FUNC(dispatch, glDeleteTextures, get_proc_address); + INIT_FUNC(dispatch, glBindTexture, get_proc_address); + INIT_FUNC(dispatch, glTexSubImage2D, get_proc_address); + INIT_FUNC(dispatch, glFlush, get_proc_address); + INIT_FUNC(dispatch, glFinish, get_proc_address); + INIT_FUNC(dispatch, glGetIntegerv, get_proc_address); + INIT_FUNC(dispatch, glGetString, get_proc_address); + INIT_FUNC(dispatch, glScissor, get_proc_address); + INIT_FUNC(dispatch, glEnable, get_proc_address); + INIT_FUNC(dispatch, glDisable, get_proc_address); + INIT_FUNC(dispatch, glBlendFunc, get_proc_address); + INIT_FUNC(dispatch, glActiveTexture, get_proc_address); + INIT_FUNC(dispatch, glGenBuffers, get_proc_address); + INIT_FUNC(dispatch, glBufferData, get_proc_address); + INIT_FUNC(dispatch, glBindBuffer, get_proc_address); + INIT_FUNC(dispatch, glDeleteBuffers, get_proc_address); + INIT_FUNC(dispatch, glFramebufferTexture2D, get_proc_address); + INIT_FUNC(dispatch, glBindFramebuffer, get_proc_address); + INIT_FUNC(dispatch, glDeleteFramebuffers, get_proc_address); + INIT_FUNC(dispatch, glGenFramebuffers, get_proc_address); + INIT_FUNC(dispatch, glCheckFramebufferStatus, get_proc_address); + INIT_FUNC(dispatch, glVertexAttribPointer, get_proc_address); + INIT_FUNC(dispatch, glDisableVertexAttribArray, get_proc_address); + INIT_FUNC(dispatch, glEnableVertexAttribArray, get_proc_address); + INIT_FUNC(dispatch, glBindAttribLocation, get_proc_address); + INIT_FUNC(dispatch, glLinkProgram, get_proc_address); + INIT_FUNC(dispatch, glShaderSource, get_proc_address); + + INIT_FUNC(dispatch, glUseProgram, get_proc_address); + INIT_FUNC(dispatch, glUniform1i, get_proc_address); + INIT_FUNC(dispatch, glUniform1f, get_proc_address); + INIT_FUNC(dispatch, glUniform4f, get_proc_address); + INIT_FUNC(dispatch, glUniform4fv, get_proc_address); + INIT_FUNC(dispatch, glUniform1fv, get_proc_address); + INIT_FUNC(dispatch, glUniform2fv, get_proc_address); + INIT_FUNC(dispatch, glUniformMatrix3fv, get_proc_address); + INIT_FUNC(dispatch, glCreateProgram, get_proc_address); + INIT_FUNC(dispatch, glDeleteProgram, get_proc_address); + INIT_FUNC(dispatch, glCreateShader, get_proc_address); + INIT_FUNC(dispatch, glCompileShader, get_proc_address); + INIT_FUNC(dispatch, glAttachShader, get_proc_address); + INIT_FUNC(dispatch, glDeleteShader, get_proc_address); + INIT_FUNC(dispatch, glGetShaderiv, get_proc_address); + INIT_FUNC(dispatch, glGetShaderInfoLog, get_proc_address); + INIT_FUNC(dispatch, glGetProgramiv, get_proc_address); + INIT_FUNC(dispatch, glGetProgramInfoLog, get_proc_address); + INIT_FUNC(dispatch, glGetUniformLocation, get_proc_address); + + return TRUE; + fail: + return FALSE; +} diff --git a/xorg-server/glamor/glamor_gl_dispatch.h b/xorg-server/glamor/glamor_gl_dispatch.h new file mode 100644 index 000000000..76dadd49e --- /dev/null +++ b/xorg-server/glamor/glamor_gl_dispatch.h @@ -0,0 +1,138 @@ +typedef struct glamor_gl_dispatch { + /* Transformation functions */ + void (*glMatrixMode) (GLenum mode); + void (*glLoadIdentity) (void); + void (*glViewport) (GLint x, GLint y, GLsizei width, + GLsizei height); + /* Drawing functions */ + void (*glRasterPos2i) (GLint x, GLint y); + + /* Vertex Array */ + void (*glDrawArrays) (GLenum mode, GLint first, GLsizei count); + + /* Elements Array*/ + void (*glDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid * indices); + void (*glDrawRangeElements) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices); + + /* Raster functions */ + void (*glReadPixels) (GLint x, GLint y, + GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid * pixels); + + void (*glDrawPixels) (GLsizei width, GLsizei height, + GLenum format, GLenum type, + const GLvoid * pixels); + void (*glPixelStorei) (GLenum pname, GLint param); + /* Texture Mapping */ + + void (*glTexParameteri) (GLenum target, GLenum pname, GLint param); + void (*glTexImage2D) (GLenum target, GLint level, + GLint internalFormat, + GLsizei width, GLsizei height, + GLint border, GLenum format, GLenum type, + const GLvoid * pixels); + /* 1.1 */ + void (*glGenTextures) (GLsizei n, GLuint * textures); + void (*glDeleteTextures) (GLsizei n, const GLuint * textures); + void (*glBindTexture) (GLenum target, GLuint texture); + void (*glTexSubImage2D) (GLenum target, GLint level, + GLint xoffset, GLint yoffset, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + const GLvoid * pixels); + /* MISC */ + void (*glFlush) (void); + void (*glFinish) (void); + void (*glGetIntegerv) (GLenum pname, GLint * params); + const GLubyte *(*glGetString) (GLenum name); + void (*glScissor) (GLint x, GLint y, GLsizei width, + GLsizei height); + void (*glEnable) (GLenum cap); + void (*glDisable) (GLenum cap); + void (*glBlendFunc) (GLenum sfactor, GLenum dfactor); + void (*glLogicOp) (GLenum opcode); + + /* 1.3 */ + void (*glActiveTexture) (GLenum texture); + + /* GL Extentions */ + void (*glGenBuffers) (GLsizei n, GLuint * buffers); + void (*glBufferData) (GLenum target, GLsizeiptr size, + const GLvoid * data, GLenum usage); + GLvoid *(*glMapBuffer) (GLenum target, GLenum access); + GLvoid *(*glMapBufferRange) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); + GLboolean (*glUnmapBuffer) (GLenum target); + void (*glBindBuffer) (GLenum target, GLuint buffer); + void (*glDeleteBuffers) (GLsizei n, const GLuint * buffers); + + void (*glFramebufferTexture2D) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level); + void (*glBindFramebuffer) (GLenum target, GLuint framebuffer); + void (*glDeleteFramebuffers) (GLsizei n, + const GLuint * framebuffers); + void (*glGenFramebuffers) (GLsizei n, GLuint * framebuffers); + GLenum (*glCheckFramebufferStatus) (GLenum target); + void (*glBlitFramebuffer) (GLint srcX0, GLint srcY0, GLint srcX1, + GLint srcY1, GLint dstX0, GLint dstY0, + GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); + + void (*glVertexAttribPointer) (GLuint index, GLint size, + GLenum type, GLboolean normalized, + GLsizei stride, + const GLvoid * pointer); + void (*glDisableVertexAttribArray) (GLuint index); + void (*glEnableVertexAttribArray) (GLuint index); + void (*glBindAttribLocation) (GLuint program, GLuint index, + const GLchar * name); + + void (*glLinkProgram) (GLuint program); + void (*glShaderSource) (GLuint shader, GLsizei count, + const GLchar * *string, + const GLint * length); + void (*glUseProgram) (GLuint program); + void (*glUniform1i) (GLint location, GLint v0); + void (*glUniform1f) (GLint location, GLfloat v0); + void (*glUniform4f) (GLint location, GLfloat v0, GLfloat v1, + GLfloat v2, GLfloat v3); + void (*glUniform1fv) (GLint location, GLsizei count, + const GLfloat * value); + void (*glUniform2fv) (GLint location, GLsizei count, + const GLfloat * value); + void (*glUniform4fv) (GLint location, GLsizei count, + const GLfloat * value); + void (*glUniformMatrix3fv) (GLint location, GLsizei count, + GLboolean transpose, const GLfloat* value); + GLuint (*glCreateProgram) (void); + GLuint (*glDeleteProgram) (GLuint); + GLuint (*glCreateShader) (GLenum type); + void (*glCompileShader) (GLuint shader); + void (*glAttachShader) (GLuint program, GLuint shader); + void (*glDeleteShader) (GLuint shader); + void (*glGetShaderiv) (GLuint shader, GLenum pname, + GLint * params); + void (*glGetShaderInfoLog) (GLuint shader, GLsizei bufSize, + GLsizei * length, GLchar * infoLog); + void (*glGetProgramiv) (GLuint program, GLenum pname, + GLint * params); + void (*glGetProgramInfoLog) (GLuint program, GLsizei bufSize, + GLsizei * length, GLchar * infoLog); + GLint (*glGetUniformLocation) (GLuint program, + const GLchar * name); + +} glamor_gl_dispatch; + + +typedef void *(*get_proc_address_t) (const char *); + +_X_EXPORT Bool +glamor_gl_dispatch_init_impl(struct glamor_gl_dispatch *dispatch, + int gl_version, + get_proc_address_t get_proc_address); + + +_X_EXPORT Bool +glamor_gl_dispatch_init(ScreenPtr screen, + struct glamor_gl_dispatch *dispatch, + int gl_version); diff --git a/xorg-server/glamor/glamor_glext.h b/xorg-server/glamor/glamor_glext.h new file mode 100644 index 000000000..1f7206b99 --- /dev/null +++ b/xorg-server/glamor/glamor_glext.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2001 Keith Packard + * Copyright © 2008 Intel Corporation + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + + +#ifdef GLAMOR_GLES2 + +#define GL_BGRA GL_BGRA_EXT +#define GL_COLOR_INDEX 0x1900 +#define GL_BITMAP 0x1A00 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 + +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_CLAMP_TO_BORDER 0x812D + +#define GL_READ_WRITE 0x88BA +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 + +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 + +#define GL_PACK_INVERT_MESA 0x8758 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 + +#endif diff --git a/xorg-server/glamor/glamor_glyphblt.c b/xorg-server/glamor/glamor_glyphblt.c new file mode 100644 index 000000000..b55327c4b --- /dev/null +++ b/xorg-server/glamor/glamor_glyphblt.c @@ -0,0 +1,118 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + * + */ + +#include "glamor_priv.h" + +static Bool +_glamor_image_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase, Bool fallback) +{ + if (!fallback + && glamor_ddx_fallback_check_pixmap(pDrawable) + && glamor_ddx_fallback_check_gc(pGC)) + return FALSE; + + miImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + return TRUE; +} + +void +glamor_image_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase) +{ + _glamor_image_glyph_blt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase, TRUE); +} + +Bool +glamor_image_glyph_blt_nf(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase) +{ + return _glamor_image_glyph_blt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase, FALSE); +} + +static Bool +_glamor_poly_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase, Bool fallback) +{ + if (!fallback + && glamor_ddx_fallback_check_pixmap(pDrawable) + && glamor_ddx_fallback_check_gc(pGC)) + return FALSE; + + miPolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + return TRUE; +} + +void +glamor_poly_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase) +{ + _glamor_poly_glyph_blt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase, TRUE); +} + +Bool +glamor_poly_glyph_blt_nf(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase) +{ + return _glamor_poly_glyph_blt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase, FALSE); +} + +static Bool +_glamor_push_pixels(GCPtr pGC, PixmapPtr pBitmap, + DrawablePtr pDrawable, int w, int h, int x, int y, Bool fallback) +{ + if (!fallback + && glamor_ddx_fallback_check_pixmap(pDrawable) + && glamor_ddx_fallback_check_pixmap(&pBitmap->drawable) + && glamor_ddx_fallback_check_gc(pGC)) + return FALSE; + + miPushPixels(pGC, pBitmap, pDrawable, w, h, x, y); + return TRUE; +} + +void +glamor_push_pixels(GCPtr pGC, PixmapPtr pBitmap, + DrawablePtr pDrawable, int w, int h, int x, int y) +{ + _glamor_push_pixels(pGC, pBitmap, pDrawable, w, h, x, y, TRUE); +} + +Bool +glamor_push_pixels_nf(GCPtr pGC, PixmapPtr pBitmap, + DrawablePtr pDrawable, int w, int h, int x, int y) +{ + return _glamor_push_pixels(pGC, pBitmap, pDrawable, w, h, x, y, FALSE); +} + diff --git a/xorg-server/glamor/glamor_glyphs.c b/xorg-server/glamor/glamor_glyphs.c new file mode 100644 index 000000000..fc361df42 --- /dev/null +++ b/xorg-server/glamor/glamor_glyphs.c @@ -0,0 +1,1806 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * Partly based on code Copyright © 2000 SuSE, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Red Hat + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Owen Taylor <otaylor@fishsoup.net> + * Based on code by: Keith Packard + */ + +#include <stdlib.h> + +#include "glamor_priv.h" + +#include <mipict.h> + +#if DEBUG_GLYPH_CACHE +#define DBG_GLYPH_CACHE(a) ErrorF a +#else +#define DBG_GLYPH_CACHE(a) +#endif + +/* Width of the pixmaps we use for the caches; this should be less than + * max texture size of the driver; this may need to actually come from + * the driver. + */ + +/* Maximum number of glyphs we buffer on the stack before flushing + * rendering to the mask or destination surface. + */ +#define GLYPH_BUFFER_SIZE 1024 + +#define CACHE_PICTURE_SIZE 1024 +#define GLYPH_MIN_SIZE 8 +#define GLYPH_MAX_SIZE 64 +#define GLYPH_CACHE_SIZE ((CACHE_PICTURE_SIZE) * CACHE_PICTURE_SIZE / (GLYPH_MIN_SIZE * GLYPH_MIN_SIZE)) +#define MASK_CACHE_MAX_SIZE 32 +#define MASK_CACHE_WIDTH (CACHE_PICTURE_SIZE / MASK_CACHE_MAX_SIZE) +#define MASK_CACHE_MASK ((1LL << (MASK_CACHE_WIDTH)) - 1) + +typedef struct { + PicturePtr source; + glamor_composite_rect_t rects[GLYPH_BUFFER_SIZE + 4]; + int count; +} glamor_glyph_buffer_t; + +struct glamor_glyph { + glamor_glyph_cache_t *cache; + uint16_t x, y; + uint16_t size, pos; + unsigned long long left_x1_map, left_x2_map; + unsigned long long right_x1_map, right_x2_map; /* Use to check real intersect or not. */ + Bool has_edge_map; + Bool cached; +}; + +typedef enum { + GLAMOR_GLYPH_SUCCESS, /* Glyph added to render buffer */ + GLAMOR_GLYPH_FAIL, /* out of memory, etc */ + GLAMOR_GLYPH_NEED_FLUSH, /* would evict a glyph already in the buffer */ +} glamor_glyph_cache_result_t; + +#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0) +static DevPrivateKeyRec glamor_glyph_key; + +static inline struct glamor_glyph * +glamor_glyph_get_private(GlyphPtr glyph) +{ + return (struct glamor_glyph*)glyph->devPrivates; +} + +/* + * Mask cache is located at the corresponding cache picture's last row. + * and is deadicated for the mask picture when do the glyphs_via_mask. + * + * As we split the glyphs list according to its overlapped or non-overlapped, + * we can reduce the length of glyphs to do the glyphs_via_mask to 2 or 3 + * glyphs one time for most cases. Thus it give us a case to allocate a + * small portion of the corresponding cache directly as the mask picture. + * Then we can rendering the glyphs to this mask picture, and latter we + * can accumulate the second steps, composite the mask to the dest with + * the other non-overlapped glyphs's rendering process. + * Another major benefit is we now only need to clear a relatively small mask + * region then before. It also make us implement a bunch mask picture clearing + * algorithm to avoid too frequently small region clearing. + * + * If there is no any overlapping, this method will not get performance gain. + * If there is some overlapping, then this algorithm can get about 15% performance + * gain. + */ + +struct glamor_glyph_mask_cache_entry { + int idx; + int width; + int height; + int x; + int y; +}; + +static struct glamor_glyph_mask_cache { + PixmapPtr pixmap; + struct glamor_glyph_mask_cache_entry mcache[MASK_CACHE_WIDTH]; + unsigned int free_bitmap; + unsigned int cleared_bitmap; +}*mask_cache[GLAMOR_NUM_GLYPH_CACHE_FORMATS] = {NULL}; + +static void +clear_mask_cache_bitmap(struct glamor_glyph_mask_cache *maskcache, + unsigned int clear_mask_bits) +{ + unsigned int i = 0; + BoxRec box[MASK_CACHE_WIDTH]; + int box_cnt = 0; + + assert((clear_mask_bits & ~MASK_CACHE_MASK) == 0); + for(i = 0; i < MASK_CACHE_WIDTH;i++) + { + if (clear_mask_bits & (1 << i)) { + box[box_cnt].x1 = maskcache->mcache[i].x; + box[box_cnt].x2 = maskcache->mcache[i].x + MASK_CACHE_MAX_SIZE; + box[box_cnt].y1 = maskcache->mcache[i].y; + box[box_cnt].y2 = maskcache->mcache[i].y + MASK_CACHE_MAX_SIZE; + box_cnt++; + } + } + glamor_solid_boxes(maskcache->pixmap, box, box_cnt, 0); + maskcache->cleared_bitmap |= clear_mask_bits; +} + +static void +clear_mask_cache(struct glamor_glyph_mask_cache *maskcache) +{ + int x = 0; + int cnt = MASK_CACHE_WIDTH; + unsigned int i = 0; + struct glamor_glyph_mask_cache_entry *mce; + glamor_solid(maskcache->pixmap, 0, CACHE_PICTURE_SIZE, CACHE_PICTURE_SIZE, + MASK_CACHE_MAX_SIZE, GXcopy, 0xFFFFFFFF, 0); + mce = &maskcache->mcache[0]; + while(cnt--) { + mce->width = 0; + mce->height = 0; + mce->x = x; + mce->y = CACHE_PICTURE_SIZE; + mce->idx = i++; + x += MASK_CACHE_MAX_SIZE; + mce++; + } + maskcache->free_bitmap = MASK_CACHE_MASK; + maskcache->cleared_bitmap = MASK_CACHE_MASK; +} + +static int +find_continuous_bits(unsigned int bits, int bits_cnt, unsigned int *pbits_mask) +{ + int idx = 0; + unsigned int bits_mask; + bits_mask = ((1LL << bits_cnt) - 1); + + if (unlikely(bits_cnt > 56)) { + while(bits) { + if ((bits & bits_mask) == bits_mask) { + *pbits_mask = bits_mask << idx; + return idx; + } + bits >>= 1; + idx++; + } + } else { + idx = __fls(bits); + while(bits) { + unsigned int temp_bits; + temp_bits = bits_mask << (idx - bits_cnt + 1); + if ((bits & temp_bits) == temp_bits) { + *pbits_mask = temp_bits; + return (idx - bits_cnt + 1); + } + /* Find first zero. And clear the tested bit.*/ + bits &= ~(1LL<<idx); + idx = __fls(~bits); + bits &= ~((1LL << idx) - 1); + idx--; + } + } + return -1; +} + +static struct glamor_glyph_mask_cache_entry * +get_mask_cache(struct glamor_glyph_mask_cache *maskcache, int blocks) +{ + int free_cleared_bit, idx = -1; + int retry_cnt = 0; + unsigned int bits_mask = 0; + + if (maskcache->free_bitmap == 0) + return NULL; +retry: + free_cleared_bit = maskcache->free_bitmap & maskcache->cleared_bitmap; + if (free_cleared_bit && blocks == 1) { + idx = __fls(free_cleared_bit); + bits_mask = 1 << idx; + } else if (free_cleared_bit && blocks > 1) { + idx = find_continuous_bits(free_cleared_bit, blocks, &bits_mask); + } + + if (idx < 0) { + clear_mask_cache_bitmap(maskcache, maskcache->free_bitmap); + if (retry_cnt++ > 2) + return NULL; + goto retry; + } + + maskcache->cleared_bitmap &= ~bits_mask; + maskcache->free_bitmap &= ~bits_mask; + DEBUGF("get idx %d free %x clear %x \n", + idx, maskcache->free_bitmap, maskcache->cleared_bitmap); + return &maskcache->mcache[idx]; +} + +static void +put_mask_cache_bitmap(struct glamor_glyph_mask_cache *maskcache, + unsigned int bitmap) +{ + maskcache->free_bitmap |= bitmap; + DEBUGF("put bitmap %x free %x clear %x \n", + bitmap, maskcache->free_bitmap, maskcache->cleared_bitmap); +} + +static void +glamor_unrealize_glyph_caches(ScreenPtr pScreen) +{ + glamor_screen_private *glamor = glamor_get_screen_private(pScreen); + int i; + + if (!glamor->glyph_cache_initialized) + return; + + for (i = 0; i < GLAMOR_NUM_GLYPH_CACHE_FORMATS; i++) { + glamor_glyph_cache_t *cache = &glamor->glyphCaches[i]; + + if (cache->picture) + FreePicture(cache->picture, 0); + + if (cache->glyphs) + free(cache->glyphs); + + if (mask_cache[i]) + free(mask_cache[i]); + } + glamor->glyph_cache_initialized = FALSE; +} + +void +glamor_glyphs_fini(ScreenPtr pScreen) +{ + glamor_unrealize_glyph_caches(pScreen); +} + +/* All caches for a single format share a single pixmap for glyph storage, + * allowing mixing glyphs of different sizes without paying a penalty + * for switching between source pixmaps. (Note that for a size of font + * right at the border between two sizes, we might be switching for almost + * every glyph.) + * + * This function allocates the storage pixmap, and then fills in the + * rest of the allocated structures for all caches with the given format. + */ + +static Bool +glamor_realize_glyph_caches(ScreenPtr pScreen) +{ + glamor_screen_private *glamor = glamor_get_screen_private(pScreen); + unsigned int formats[] = { + PIXMAN_a8, + PIXMAN_a8r8g8b8, + }; + int i; + + if (glamor->glyph_cache_initialized) + return TRUE; + + glamor->glyph_cache_initialized = TRUE; + memset(glamor->glyphCaches, 0, sizeof(glamor->glyphCaches)); + + for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { + glamor_glyph_cache_t *cache = &glamor->glyphCaches[i]; + PixmapPtr pixmap; + PicturePtr picture; + XID component_alpha; + int depth = PIXMAN_FORMAT_DEPTH(formats[i]); + int error; + PictFormatPtr pPictFormat = + PictureMatchFormat(pScreen, depth, formats[i]); + if (!pPictFormat) + goto bail; + + /* Now allocate the pixmap and picture */ + pixmap = pScreen->CreatePixmap(pScreen, + CACHE_PICTURE_SIZE, + CACHE_PICTURE_SIZE + MASK_CACHE_MAX_SIZE, depth, + 0); + if (!pixmap) + goto bail; + + component_alpha = NeedsComponent(pPictFormat->format); + picture = CreatePicture(0, &pixmap->drawable, pPictFormat, + CPComponentAlpha, &component_alpha, + serverClient, &error); + + pScreen->DestroyPixmap(pixmap); + if (!picture) + goto bail; + + ValidatePicture(picture); + + cache->picture = picture; + cache->glyphs = calloc(sizeof(GlyphPtr), GLYPH_CACHE_SIZE); + if (!cache->glyphs) + goto bail; + + cache->evict = rand() % GLYPH_CACHE_SIZE; + mask_cache[i] = calloc(1, sizeof(*mask_cache[i])); + mask_cache[i]->pixmap = pixmap; + clear_mask_cache(mask_cache[i]); + } + assert(i == GLAMOR_NUM_GLYPH_CACHE_FORMATS); + + return TRUE; + + bail: + glamor_unrealize_glyph_caches(pScreen); + return FALSE; +} + + +Bool +glamor_glyphs_init(ScreenPtr pScreen) +{ + if (!dixRegisterPrivateKey(&glamor_glyph_key, + PRIVATE_GLYPH, sizeof(struct glamor_glyph))) + return FALSE; + + /* Skip pixmap creation if we don't intend to use it. */ + + return glamor_realize_glyph_caches(pScreen); +} + +/* The most efficient thing to way to upload the glyph to the screen + * is to use CopyArea; glamor pixmaps are always offscreen. + */ +static void +glamor_glyph_cache_upload_glyph(ScreenPtr screen, + glamor_glyph_cache_t * cache, + GlyphPtr glyph, int x, int y) +{ + PicturePtr pGlyphPicture = GlyphPicture(glyph)[screen->myNum]; + PixmapPtr pGlyphPixmap = (PixmapPtr) pGlyphPicture->pDrawable; + PixmapPtr pCachePixmap = (PixmapPtr) cache->picture->pDrawable; + PixmapPtr scratch; + BoxRec box; + GCPtr gc; + + gc = GetScratchGC(pCachePixmap->drawable.depth, screen); + if (!gc) + return; + + ValidateGC(&pCachePixmap->drawable, gc); + + scratch = pGlyphPixmap; + if (pGlyphPixmap->drawable.depth != pCachePixmap->drawable.depth) { + + scratch = glamor_create_pixmap(screen, + glyph->info.width, + glyph->info.height, + pCachePixmap-> + drawable.depth, 0); + if (scratch) { + PicturePtr picture; + int error; + + picture = + CreatePicture(0, + &scratch->drawable, + PictureMatchFormat + (screen, + pCachePixmap-> + drawable.depth, + cache->picture->format), + 0, NULL, serverClient, + &error); + if (picture) { + ValidatePicture(picture); + glamor_composite(PictOpSrc, + pGlyphPicture, + NULL, picture, + 0, 0, 0, 0, 0, + 0, + glyph->info.width, + glyph->info.height); + FreePicture(picture, 0); + } + } else { + scratch = pGlyphPixmap; + } + } + + box.x1 = x; + box.y1 = y; + box.x2 = x + glyph->info.width; + box.y2 = y + glyph->info.height; + glamor_copy_n_to_n_nf(&scratch->drawable, + &pCachePixmap->drawable, NULL, + &box, 1, + -x, -y, + FALSE, FALSE, 0, NULL); + if (scratch != pGlyphPixmap) + screen->DestroyPixmap(scratch); + + FreeScratchGC(gc); +} + + +void +glamor_glyph_unrealize(ScreenPtr screen, GlyphPtr glyph) +{ + struct glamor_glyph *priv; + + /* Use Lookup in case we have not attached to this glyph. */ + priv = glamor_glyph_get_private(glyph); + + if (priv->cached) + priv->cache->glyphs[priv->pos] = NULL; +} + +/* Cut and paste from render/glyph.c - probably should export it instead */ +static void +glamor_glyph_extents(int nlist, + GlyphListPtr list, GlyphPtr * glyphs, BoxPtr extents) +{ + int x1, x2, y1, y2; + int x, y, n; + + x1 = y1 = MAXSHORT; + x2 = y2 = MINSHORT; + x = y = 0; + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + list++; + while (n--) { + GlyphPtr glyph = *glyphs++; + int v; + + v = x - glyph->info.x; + if (v < x1) + x1 = v; + v += glyph->info.width; + if (v > x2) + x2 = v; + + v = y - glyph->info.y; + if (v < y1) + y1 = v; + v += glyph->info.height; + if (v > y2) + y2 = v; + + x += glyph->info.xOff; + y += glyph->info.yOff; + } + } + + extents->x1 = x1 < MINSHORT ? MINSHORT : x1; + extents->x2 = x2 > MAXSHORT ? MAXSHORT : x2; + extents->y1 = y1 < MINSHORT ? MINSHORT : y1; + extents->y2 = y2 > MAXSHORT ? MAXSHORT : y2; +} + +static void +glamor_glyph_priv_get_edge_map(GlyphPtr glyph, struct glamor_glyph *priv, + PicturePtr glyph_picture) +{ + PixmapPtr glyph_pixmap = (PixmapPtr) glyph_picture->pDrawable; + int j; + unsigned long long left_x1_map = 0, left_x2_map = 0; + unsigned long long right_x1_map = 0, right_x2_map = 0; + int bitsPerPixel; + int stride; + void *bits; + int width; + unsigned int left_x1_data = 0, left_x2_data = 0; + unsigned int right_x1_data = 0, right_x2_data = 0; + + bitsPerPixel = glyph_pixmap->drawable.bitsPerPixel; + stride = glyph_pixmap->devKind; + bits = glyph_pixmap->devPrivate.ptr; + width = glyph->info.width; + + if (glyph_pixmap->drawable.width < 2 + || !(glyph_pixmap->drawable.depth == 8 + || glyph_pixmap->drawable.depth == 1 + || glyph_pixmap->drawable.depth == 32)) { + priv->has_edge_map = FALSE; + return; + } + + left_x1_map = left_x2_map = 0; + right_x1_map = right_x2_map = 0; + + for(j = 0; j < glyph_pixmap->drawable.height; j++) + { + if (bitsPerPixel == 8) { + unsigned char *data; + data = (unsigned char*)((unsigned char*)bits + stride * j); + left_x1_data = *data++; + left_x2_data = *data; + data = (unsigned char*)((unsigned char*)bits + stride * j + width - 2); + right_x1_data = *data++; + right_x2_data = *data; + } else if (bitsPerPixel == 32) { + left_x1_data = *((unsigned int*)bits + stride/4 * j); + left_x2_data = *((unsigned int*)bits + stride/4 * j + 1); + right_x1_data = *((unsigned int*)bits + stride/4 * j + width - 2); + right_x2_data = *((unsigned int*)bits + stride/4 * j + width - 1); + } else if (bitsPerPixel == 1) { + unsigned char temp; + temp = *((unsigned char*)glyph_pixmap->devPrivate.ptr + + glyph_pixmap->devKind * j) & 0x3; + left_x1_data = temp & 0x1; + left_x2_data = temp & 0x2; + + temp = *((unsigned char*)glyph_pixmap->devPrivate.ptr + + glyph_pixmap->devKind * j + + (glyph_pixmap->drawable.width - 2)/8); + right_x1_data = temp + & (1 << ((glyph_pixmap->drawable.width - 2) % 8)); + temp = *((unsigned char*)glyph_pixmap->devPrivate.ptr + + glyph_pixmap->devKind * j + + (glyph_pixmap->drawable.width - 1)/8); + right_x2_data = temp + & (1 << ((glyph_pixmap->drawable.width - 1) % 8)); + } + left_x1_map |= (left_x1_data !=0) << j; + left_x2_map |= (left_x2_data !=0) << j; + right_x1_map |= (right_x1_data !=0) << j; + right_x2_map |= (right_x2_data !=0) << j; + } + + priv->left_x1_map = left_x1_map; + priv->left_x2_map = left_x2_map; + priv->right_x1_map = right_x1_map; + priv->right_x2_map = right_x2_map; + priv->has_edge_map = TRUE; + return; +} + +/** + * Returns TRUE if the glyphs in the lists intersect. Only checks based on + * bounding box, which appears to be good enough to catch most cases at least. + */ + +#define INTERSECTED_TYPE_MASK 1 +#define NON_INTERSECTED 0 +#define INTERSECTED 1 + +struct glamor_glyph_list { + int nlist; + GlyphListPtr list; + GlyphPtr *glyphs; + int type; +}; + +static Bool +glyph_new_fixed_list(struct glamor_glyph_list *fixed_list, + GlyphPtr *cur_glyphs, + GlyphPtr **head_glyphs, + GlyphListPtr cur_list, + int cur_pos, int cur_x, int cur_y, + int x1, int y1, int x2, int y2, + GlyphListPtr *head_list, + int *head_pos, + int *head_x, + int *head_y, + int *fixed_cnt, + int type, + BoxPtr prev_extents + ) +{ + int x_off = 0; + int y_off = 0; + int n_off = 0; + int list_cnt; + if (type == NON_INTERSECTED) { + if (x1 < prev_extents->x2 && x2 > prev_extents->x1 + && y1 < prev_extents->y2 && y2 > prev_extents->y1) + return FALSE; + x_off = (*(cur_glyphs-1))->info.xOff; + y_off = (*(cur_glyphs-1))->info.yOff; + n_off = 1; + } + + list_cnt = cur_list - *head_list + 1; + if (cur_pos <= n_off) { + DEBUGF("break at %d n_off %d\n", cur_pos, n_off); + list_cnt--; + if (cur_pos < n_off) { + /* we overlap with previous list's last glyph. */ + x_off += cur_list->xOff; + y_off += cur_list->yOff; + cur_list--; + cur_pos = cur_list->len; + if (cur_pos <= n_off) { + list_cnt--; + } + } + } + DEBUGF("got %d lists\n", list_cnt); + if (list_cnt != 0) { + fixed_list->list = malloc(list_cnt * sizeof(*cur_list)); + memcpy(fixed_list->list, *head_list, list_cnt * sizeof(*cur_list)); + fixed_list->list[0].xOff = *head_x; + fixed_list->list[0].yOff = *head_y; + fixed_list->glyphs = *head_glyphs; + fixed_list->type = type & INTERSECTED_TYPE_MASK; + fixed_list->nlist = list_cnt; + if (cur_list != *head_list) { + fixed_list->list[0].len = (*head_list)->len - *head_pos; + if (cur_pos != n_off) + fixed_list->list[list_cnt - 1].len = cur_pos - n_off; + } else + fixed_list->list[0].len = cur_pos - *head_pos - n_off; + (*fixed_cnt)++; + } + + if (type <= INTERSECTED) { + *head_list = cur_list; + *head_pos = cur_pos - n_off; + *head_x = cur_x - x_off; + *head_y = cur_y - y_off; + *head_glyphs = cur_glyphs - n_off; + } + return TRUE; +} + +/* + * This function detects glyph lists's overlapping. + * + * If check_fake_overlap is set, then it will check the glyph's left + * and right small boxes's real overlapping pixels. And if there is + * no real pixel overlapping, then it will not be treated as overlapped + * case. And we also can configured it to ignore less than 2 pixels + * overlappig. + * + * This function analyzes all the lists and split the list to multiple + * lists which are pure overlapped glyph lists or pure non-overlapped + * list if the overlapping only ocurr on the two adjacent glyphs. + * Otherwise, it return -1. + * + **/ + +static int +glamor_glyphs_intersect(int nlist, GlyphListPtr list, GlyphPtr * glyphs, + PictFormatShort mask_format, + ScreenPtr screen, Bool check_fake_overlap, + struct glamor_glyph_list * fixed_list, + int fixed_size) +{ + int x1, x2, y1, y2; + int n; + int x, y; + BoxPtr extents; + BoxRec prev_extents; + Bool first = TRUE, first_list = TRUE; + Bool need_free_list_region = FALSE; + Bool need_free_fixed_list = FALSE; + struct glamor_glyph *priv = NULL; + Bool in_non_intersected_list = -1; + GlyphListPtr head_list; + int head_x, head_y, head_pos; + int fixed_cnt = 0; + GlyphPtr *head_glyphs; + GlyphListPtr cur_list = list; + RegionRec list_region; + RegionRec current_region; + BoxRec current_box; + + if (nlist > 1) { + pixman_region_init(&list_region); + need_free_list_region = TRUE; + } + + pixman_region_init(¤t_region); + + extents = pixman_region_extents(¤t_region); + + x = 0; + y = 0; + x1 = x2 = y1 = y2 = 0; + n = 0; + extents->x1 = 0; + extents->y1 = 0; + extents->x2 = 0; + extents->y2 = 0; + + head_list = list; + DEBUGF("has %d lists.\n", nlist); + while (nlist--) { + BoxRec left_box, right_box = {0}; + Bool has_left_edge_box = FALSE, has_right_edge_box = FALSE; + Bool left_to_right; + struct glamor_glyph *left_priv = NULL, *right_priv = NULL; + + x += list->xOff; + y += list->yOff; + n = list->len; + left_to_right = TRUE; + cur_list = list++; + + if (unlikely(!first_list)) { + pixman_region_init_with_extents(¤t_region, extents); + pixman_region_union(&list_region, &list_region, ¤t_region); + first = TRUE; + } else { + head_list = cur_list; + head_pos = cur_list->len - n; + head_x = x; + head_y = y; + head_glyphs = glyphs; + } + + DEBUGF("current list %p has %d glyphs\n", cur_list, n); + while (n--) { + GlyphPtr glyph = *glyphs++; + + DEBUGF("the %dth glyph\n", cur_list->len - n - 1); + if (glyph->info.width == 0 + || glyph->info.height == 0) { + x += glyph->info.xOff; + y += glyph->info.yOff; + continue; + } + if (mask_format + && mask_format != GlyphPicture(glyph)[screen->myNum]->format) { + need_free_fixed_list = TRUE; + goto done; + } + + x1 = x - glyph->info.x; + if (x1 < MINSHORT) + x1 = MINSHORT; + y1 = y - glyph->info.y; + if (y1 < MINSHORT) + y1 = MINSHORT; + if (check_fake_overlap) + priv = glamor_glyph_get_private(glyph); + + x2 = x1 + glyph->info.width; + y2 = y1 + glyph->info.height; + + if (x2 > MAXSHORT) + x2 = MAXSHORT; + if (y2 > MAXSHORT) + y2 = MAXSHORT; + + if (first) { + extents->x1 = x1; + extents->y1 = y1; + extents->x2 = x2; + extents->y2 = y2; + + prev_extents = *extents; + + first = FALSE; + if (check_fake_overlap && priv + && priv->has_edge_map && glyph->info.yOff == 0) { + left_box.x1 = x1; + left_box.x2 = x1 + 1; + left_box.y1 = y1; + + right_box.x1 = x2 - 2; + right_box.x2 = x2 - 1; + right_box.y1 = y1; + left_priv = right_priv = priv; + has_left_edge_box = TRUE; + has_right_edge_box = TRUE; + } + } else { + if (unlikely(!first_list)) { + current_box.x1 = x1; + current_box.y1 = y1; + current_box.x2 = x2; + current_box.y2 = y2; + if (pixman_region_contains_rectangle(&list_region, ¤t_box) != PIXMAN_REGION_OUT) { + need_free_fixed_list = TRUE; + goto done; + } + } + + if (x1 < extents->x2 && x2 > extents->x1 + && y1 < extents->y2 + && y2 > extents->y1) { + + if (check_fake_overlap && (has_left_edge_box || has_right_edge_box) + && priv->has_edge_map && glyph->info.yOff == 0) { + int left_dx, right_dx; + unsigned long long intersected; + + left_dx = has_left_edge_box ? 1 : 0; + right_dx = has_right_edge_box ? 1 : 0; + if (x1 + 1 < extents->x2 - right_dx && x2 - 1 > extents->x1 + left_dx) + goto real_intersected; + + if (left_to_right && has_right_edge_box) { + if (x1 == right_box.x1) { + intersected = ((priv->left_x1_map & right_priv->right_x1_map) + | (priv->left_x2_map & right_priv->right_x2_map)); + if (intersected) + goto real_intersected; + } else if (x1 == right_box.x2) { + intersected = (priv->left_x1_map & right_priv->right_x2_map); + if (intersected) { + #ifdef GLYPHS_EDEGE_OVERLAP_LOOSE_CHECK + /* tolerate with two pixels overlap. */ + intersected &= ~(1<<__fls(intersected)); + if ((intersected & (intersected - 1))) + #endif + goto real_intersected; + } + } + } else if (!left_to_right && has_left_edge_box) { + if (x2 - 1 == left_box.x1) { + intersected = (priv->right_x2_map & left_priv->left_x1_map); + if (intersected) { + #ifdef GLYPHS_EDEGE_OVERLAP_LOOSE_CHECK + /* tolerate with two pixels overlap. */ + intersected &= ~(1<<__fls(intersected)); + if ((intersected & (intersected - 1))) + #endif + goto real_intersected; + } + } else if (x2 - 1 == right_box.x2) { + if ((priv->right_x1_map & left_priv->left_x1_map) + || (priv->right_x2_map & left_priv->left_x2_map)) + goto real_intersected; + } + } else { + if (x1 < extents->x2 && x1 + 2 > extents->x1) + goto real_intersected; + } + goto non_intersected; + } else { +real_intersected: + DEBUGF("overlap with previous glyph.\n"); + if (in_non_intersected_list == 1) { + if (fixed_cnt >= fixed_size) { + need_free_fixed_list = TRUE; + goto done; + } + if (!glyph_new_fixed_list(&fixed_list[fixed_cnt], + glyphs - 1, + &head_glyphs, + cur_list, + cur_list->len - (n + 1), x, y, + x1, y1, x2, y2, + &head_list, + &head_pos, + &head_x, + &head_y, &fixed_cnt, + NON_INTERSECTED, + &prev_extents + )){ + need_free_fixed_list = TRUE; + goto done; + } + } + + in_non_intersected_list = 0; + + } + } else { +non_intersected: + DEBUGF("doesn't overlap with previous glyph.\n"); + if (in_non_intersected_list == 0) { + if (fixed_cnt >= fixed_size) { + need_free_fixed_list = TRUE; + goto done; + } + if (!glyph_new_fixed_list(&fixed_list[fixed_cnt], + glyphs - 1, + &head_glyphs, + cur_list, + cur_list->len - (n + 1), x, y, + x1, y1, x2, y2, + &head_list, + &head_pos, + &head_x, + &head_y, &fixed_cnt, + INTERSECTED, + &prev_extents + )) { + need_free_fixed_list = TRUE; + goto done; + } + } + in_non_intersected_list = 1; + } + prev_extents = *extents; + } + + if (check_fake_overlap && priv + && priv->has_edge_map && glyph->info.yOff == 0) { + if (!has_left_edge_box || x1 < extents->x1) { + left_box.x1 = x1; + left_box.x2 = x1 + 1; + left_box.y1 = y1; + has_left_edge_box = TRUE; + left_priv = priv; + } + + if (!has_right_edge_box || x2 > extents->x2) { + right_box.x1 = x2 - 2; + right_box.x2 = x2 - 1; + right_box.y1 = y1; + has_right_edge_box = TRUE; + right_priv = priv; + } + } + + if (x1 < extents->x1) + extents->x1 = x1; + if (x2 > extents->x2) + extents->x2 = x2; + + if (y1 < extents->y1) + extents->y1 = y1; + if (y2 > extents->y2) + extents->y2 = y2; + + x += glyph->info.xOff; + y += glyph->info.yOff; + } + first_list = FALSE; + } + + if (in_non_intersected_list == 0 && fixed_cnt == 0) { + fixed_cnt = -1; + goto done; + } + + if ((in_non_intersected_list != -1 + || head_pos != n) && (fixed_cnt > 0)) { + if (fixed_cnt >= fixed_size) { + need_free_fixed_list = TRUE; + goto done; + } + if (!glyph_new_fixed_list(&fixed_list[fixed_cnt], + glyphs - 1, + &head_glyphs, + cur_list, + cur_list->len - (n + 1), x, y, + x1, y1, x2, y2, + &head_list, + &head_pos, + &head_x, + &head_y, &fixed_cnt, + (!in_non_intersected_list) | 0x80, + &prev_extents + )) { + need_free_fixed_list = TRUE; + goto done; + } + } + +done: + if (need_free_list_region) + pixman_region_fini(&list_region); + pixman_region_fini(¤t_region); + + if (need_free_fixed_list && fixed_cnt >= 0) { + while(fixed_cnt--) { + free(fixed_list[fixed_cnt].list); + } + } + + DEBUGF("Got %d fixed list \n", fixed_cnt); + return fixed_cnt; +} + +static inline unsigned int +glamor_glyph_size_to_count(int size) +{ + size /= GLYPH_MIN_SIZE; + return size * size; +} + +static inline unsigned int +glamor_glyph_count_to_mask(int count) +{ + return ~(count - 1); +} + +static inline unsigned int +glamor_glyph_size_to_mask(int size) +{ + return + glamor_glyph_count_to_mask(glamor_glyph_size_to_count(size)); +} + +static PicturePtr +glamor_glyph_cache(glamor_screen_private *glamor, GlyphPtr glyph, int *out_x, + int *out_y) +{ + ScreenPtr screen = glamor->screen; + PicturePtr glyph_picture = GlyphPicture(glyph)[screen->myNum]; + glamor_glyph_cache_t *cache = + &glamor->glyphCaches[PICT_FORMAT_RGB(glyph_picture->format) != + 0]; + struct glamor_glyph *priv = NULL, *evicted_priv = NULL; + int size, mask, pos, s; + + if (glyph->info.width > GLYPH_MAX_SIZE + || glyph->info.height > GLYPH_MAX_SIZE) + return NULL; + + for (size = GLYPH_MIN_SIZE; size <= GLYPH_MAX_SIZE; size *= 2) + if (glyph->info.width <= size + && glyph->info.height <= size) + break; + + s = glamor_glyph_size_to_count(size); + mask = glamor_glyph_count_to_mask(s); + pos = (cache->count + s - 1) & mask; + + priv = glamor_glyph_get_private(glyph); + if (pos < GLYPH_CACHE_SIZE) { + cache->count = pos + s; + } else { + for (s = size; s <= GLYPH_MAX_SIZE; s *= 2) { + int i = + cache->evict & glamor_glyph_size_to_mask(s); + GlyphPtr evicted = cache->glyphs[i]; + if (evicted == NULL) + continue; + + evicted_priv = glamor_glyph_get_private(evicted); + assert(evicted_priv->pos == i); + if (evicted_priv->size >= s) { + cache->glyphs[i] = NULL; + evicted_priv->cached = FALSE; + pos = cache->evict & + glamor_glyph_size_to_mask(size); + } else + evicted_priv = NULL; + break; + } + if (evicted_priv == NULL) { + int count = glamor_glyph_size_to_count(size); + mask = glamor_glyph_count_to_mask(count); + pos = cache->evict & mask; + for (s = 0; s < count; s++) { + GlyphPtr evicted = cache->glyphs[pos + s]; + if (evicted != NULL) { + + evicted_priv = + glamor_glyph_get_private + (evicted); + + assert(evicted_priv->pos == pos + s); + evicted_priv->cached = FALSE; + cache->glyphs[pos + s] = NULL; + } + } + + } + /* And pick a new eviction position */ + cache->evict = rand() % GLYPH_CACHE_SIZE; + } + + + cache->glyphs[pos] = glyph; + + priv->cache = cache; + priv->size = size; + priv->pos = pos; + s = pos / ((GLYPH_MAX_SIZE / GLYPH_MIN_SIZE) * + (GLYPH_MAX_SIZE / GLYPH_MIN_SIZE)); + priv->x = + s % (CACHE_PICTURE_SIZE / GLYPH_MAX_SIZE) * GLYPH_MAX_SIZE; + priv->y = + (s / (CACHE_PICTURE_SIZE / GLYPH_MAX_SIZE)) * GLYPH_MAX_SIZE; + for (s = GLYPH_MIN_SIZE; s < GLYPH_MAX_SIZE; s *= 2) { + if (pos & 1) + priv->x += s; + if (pos & 2) + priv->y += s; + pos >>= 2; + } + + glamor_glyph_cache_upload_glyph(screen, cache, glyph, priv->x, + priv->y); +#ifndef GLYPHS_NO_EDEGEMAP_OVERLAP_CHECK + if (priv->has_edge_map == FALSE && glyph->info.width >= 2) + glamor_glyph_priv_get_edge_map(glyph, priv, glyph_picture); +#endif + priv->cached = TRUE; + + *out_x = priv->x; + *out_y = priv->y; + return cache->picture; +} +typedef void (*glyphs_flush_func)(void * arg); +struct glyphs_flush_dst_arg { + CARD8 op; + PicturePtr src; + PicturePtr dst; + glamor_glyph_buffer_t * buffer; + int x_src,y_src; + int x_dst, y_dst; +}; + +static struct glyphs_flush_dst_arg dst_arg; +static struct glyphs_flush_mask_arg mask_arg; +static glamor_glyph_buffer_t dst_buffer; +static glamor_glyph_buffer_t mask_buffer; +unsigned long long mask_glyphs_cnt = 0; +unsigned long long dst_glyphs_cnt = 0; +#define GLYPHS_DST_MODE_VIA_MASK 0 +#define GLYPHS_DST_MODE_VIA_MASK_CACHE 1 +#define GLYPHS_DST_MODE_TO_DST 2 +#define GLYPHS_DST_MODE_MASK_TO_DST 3 + +struct glyphs_flush_mask_arg { + PicturePtr mask; + glamor_glyph_buffer_t *buffer; + struct glamor_glyph_mask_cache *maskcache; + unsigned int used_bitmap; +}; + +static void +glamor_glyphs_flush_mask(struct glyphs_flush_mask_arg *arg) +{ + if (arg->buffer->count>0) { +#ifdef RENDER + glamor_composite_glyph_rects(PictOpAdd, arg->buffer->source, + NULL, arg->mask, + arg->buffer->count, + arg->buffer->rects); +#endif + } + arg->buffer->count = 0; + arg->buffer->source = NULL; + +} + +static void +glamor_glyphs_flush_dst(struct glyphs_flush_dst_arg * arg) +{ + if (!arg->buffer) + return; + + if (mask_buffer.count > 0) { + glamor_glyphs_flush_mask(&mask_arg); + } + if (mask_arg.used_bitmap) { + put_mask_cache_bitmap(mask_arg.maskcache, mask_arg.used_bitmap); + mask_arg.used_bitmap = 0; + } + + if (arg->buffer->count > 0) { + glamor_composite_glyph_rects(arg->op, arg->src, + arg->buffer->source, arg->dst, + arg->buffer->count, + &arg->buffer->rects[0]); + arg->buffer->count = 0; + arg->buffer->source = NULL; + } +} + + +static glamor_glyph_cache_result_t +glamor_buffer_glyph(glamor_screen_private *glamor_priv, + glamor_glyph_buffer_t * buffer, + PictFormatShort format, + GlyphPtr glyph, struct glamor_glyph *priv, + int x_glyph, int y_glyph, + int dx, int dy, int w, int h, + int glyphs_dst_mode, + glyphs_flush_func glyphs_flush, void *flush_arg) +{ + ScreenPtr screen = glamor_priv->screen; + glamor_composite_rect_t *rect; + PicturePtr source; + int x, y; + glamor_glyph_cache_t *cache; + + if (glyphs_dst_mode != GLYPHS_DST_MODE_MASK_TO_DST) + priv = glamor_glyph_get_private(glyph); + + if (PICT_FORMAT_BPP(format) == 1) + format = PICT_a8; + + cache = + &glamor_priv->glyphCaches[PICT_FORMAT_RGB(format) != 0]; + + if (buffer->source + && buffer->source != cache->picture + && glyphs_flush) { + (*glyphs_flush)(flush_arg); + glyphs_flush = NULL; + } + + if (buffer->count == GLYPH_BUFFER_SIZE + && glyphs_flush) { + (*glyphs_flush)(flush_arg); + glyphs_flush = NULL; + } + + if (priv && priv->cached) { + rect = &buffer->rects[buffer->count++]; + rect->x_src = priv->x + dx; + rect->y_src = priv->y + dy; + if (buffer->source == NULL) + buffer->source = priv->cache->picture; + if (glyphs_dst_mode <= GLYPHS_DST_MODE_VIA_MASK_CACHE) + assert(priv->cache->glyphs[priv->pos] == glyph); + } else { + assert(glyphs_dst_mode != GLYPHS_DST_MODE_MASK_TO_DST); + if (glyphs_flush) + (*glyphs_flush)(flush_arg); + source = glamor_glyph_cache(glamor_priv, glyph, &x, &y); + + if (source != NULL) { + rect = &buffer->rects[buffer->count++]; + rect->x_src = x + dx; + rect->y_src = y + dy; + if (buffer->source == NULL) + buffer->source = source; + if (glyphs_dst_mode == GLYPHS_DST_MODE_VIA_MASK_CACHE) { + glamor_gl_dispatch *dispatch; + /* mode 1 means we are using global mask cache, + * thus we have to composite from the cache picture + * to the cache picture, we need a flush here to make + * sure latter we get the corret glyphs data.*/ + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glFlush(); + glamor_put_dispatch(glamor_priv); + } + } else { + /* Couldn't find the glyph in the cache, use the glyph picture directly */ + source = GlyphPicture(glyph)[screen->myNum]; + if (buffer->source + && buffer->source != source + && glyphs_flush) + (*glyphs_flush)(flush_arg); + buffer->source = source; + + rect = &buffer->rects[buffer->count++]; + rect->x_src = 0 + dx; + rect->y_src = 0 + dy; + } + priv = glamor_glyph_get_private(glyph); + } + + rect->x_dst = x_glyph; + rect->y_dst = y_glyph; + if (glyphs_dst_mode != GLYPHS_DST_MODE_MASK_TO_DST) { + rect->x_dst -= glyph->info.x; + rect->y_dst -= glyph->info.y; + } + rect->width = w; + rect->height = h; + if (glyphs_dst_mode > GLYPHS_DST_MODE_VIA_MASK_CACHE) { + rect->x_mask = rect->x_src; + rect->y_mask = rect->y_src; + rect->x_src = dst_arg.x_src + rect->x_dst - dst_arg.x_dst; + rect->y_src = dst_arg.y_src + rect->y_dst - dst_arg.y_dst; + } + + return GLAMOR_GLYPH_SUCCESS; +} + + +static void +glamor_buffer_glyph_clip(glamor_screen_private *glamor_priv, + BoxPtr rects, + int nrect, PictFormatShort format, + GlyphPtr glyph, struct glamor_glyph *priv, + int glyph_x, int glyph_y, + int glyph_dx, int glyph_dy, + int width, int height, + int glyphs_mode, + glyphs_flush_func flush_func, + void *arg + ) +{ + int i; + for (i = 0; i < nrect; i++) { + int dst_x, dst_y; + int dx, dy; + int x2, y2; + + dst_x = glyph_x - glyph_dx; + dst_y = glyph_y - glyph_dy; + x2 = dst_x + width; + y2 = dst_y + height; + dx = dy = 0; + if (rects[i].y1 >= y2) + break; + + if (dst_x < rects[i].x1) + dx = rects[i].x1 - dst_x, dst_x = rects[i].x1; + if (x2 > rects[i].x2) + x2 = rects[i].x2; + if (dst_y < rects[i].y1) + dy = rects[i].y1 - dst_y, dst_y = rects[i].y1; + if (y2 > rects[i].y2) + y2 = rects[i].y2; + if (dst_x < x2 && dst_y < y2) { + + glamor_buffer_glyph(glamor_priv, + &dst_buffer, + format, + glyph, priv, + dst_x + glyph_dx, + dst_y + glyph_dy, + dx, dy, + x2 - dst_x, y2 - dst_y, + glyphs_mode, + flush_func, + arg); + } + } +} + + +static void +glamor_glyphs_via_mask(CARD8 op, + PicturePtr src, + PicturePtr dst, + PictFormatPtr mask_format, + INT16 x_src, + INT16 y_src, + int nlist, GlyphListPtr list, GlyphPtr * glyphs, + Bool use_mask_cache) +{ + PixmapPtr mask_pixmap = 0; + PicturePtr mask; + ScreenPtr screen = dst->pDrawable->pScreen; + int width = 0, height = 0; + int x, y; + int x_dst = list->xOff, y_dst = list->yOff; + int n; + GlyphPtr glyph; + int error; + BoxRec extents = { 0, 0, 0, 0 }; + XID component_alpha; + glamor_screen_private *glamor_priv; + int need_free_mask = FALSE; + glamor_glyph_buffer_t buffer; + struct glyphs_flush_mask_arg arg; + glamor_glyph_buffer_t *pmask_buffer; + struct glyphs_flush_mask_arg *pmask_arg; + struct glamor_glyph_mask_cache_entry *mce = NULL; + struct glamor_glyph_mask_cache *maskcache; + glamor_glyph_cache_t *cache; + int glyphs_dst_mode; + + glamor_glyph_extents(nlist, list, glyphs, &extents); + + if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) + return; + glamor_priv = glamor_get_screen_private(screen); + width = extents.x2 - extents.x1; + height = extents.y2 - extents.y1; + + if (mask_format->depth == 1) { + PictFormatPtr a8Format = + PictureMatchFormat(screen, 8, PICT_a8); + + if (a8Format) + mask_format = a8Format; + } + + cache = &glamor_priv->glyphCaches + [PICT_FORMAT_RGB(mask_format->format) != 0]; + maskcache = mask_cache[PICT_FORMAT_RGB(mask_format->format) != 0]; + + x = -extents.x1; + y = -extents.y1; + if (!use_mask_cache + || width > (CACHE_PICTURE_SIZE/4) + || height > MASK_CACHE_MAX_SIZE) { +new_mask_pixmap: + mask_pixmap = glamor_create_pixmap(screen, width, height, + mask_format->depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!mask_pixmap) { + glamor_destroy_pixmap(mask_pixmap); + return; + } + glamor_solid(mask_pixmap, 0, 0, width, height, GXcopy, 0xFFFFFFFF, 0); + component_alpha = NeedsComponent(mask_format->format); + mask = CreatePicture(0, &mask_pixmap->drawable, + mask_format, CPComponentAlpha, + &component_alpha, serverClient, &error); + if (!mask) + return; + need_free_mask = TRUE; + pmask_arg = &arg; + pmask_buffer = &buffer; + pmask_buffer->count = 0; + pmask_buffer->source = NULL; + pmask_arg->used_bitmap = 0; + glyphs_dst_mode = GLYPHS_DST_MODE_VIA_MASK; + } else { + int retry_cnt = 0; +retry: + mce = get_mask_cache(maskcache, + (width + MASK_CACHE_MAX_SIZE - 1) / MASK_CACHE_MAX_SIZE); + + if (mce == NULL) { + glamor_glyphs_flush_dst(&dst_arg); + retry_cnt++; + if (retry_cnt > 2) { + assert(0); + goto new_mask_pixmap; + } + goto retry; + } + + mask = cache->picture; + x += mce->x; + y += mce->y; + mce->width = (width + MASK_CACHE_MAX_SIZE - 1) / MASK_CACHE_MAX_SIZE; + mce->height = 1; + if (mask_arg.mask && mask_arg.mask != mask + && mask_buffer.count != 0) + glamor_glyphs_flush_dst(&dst_arg); + pmask_arg = &mask_arg; + pmask_buffer = &mask_buffer; + pmask_arg->maskcache = maskcache; + glyphs_dst_mode = GLYPHS_DST_MODE_VIA_MASK_CACHE; + } + pmask_arg->mask = mask; + pmask_arg->buffer = pmask_buffer; + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + mask_glyphs_cnt += n; + while (n--) { + glyph = *glyphs++; + if (glyph->info.width > 0 + && glyph->info.height > 0) { + glyphs_flush_func flush_func; + void *temp_arg; + if (need_free_mask) { + if (pmask_buffer->count) + flush_func = (glyphs_flush_func)glamor_glyphs_flush_mask; + else + flush_func = NULL; + temp_arg = pmask_arg; + } else { + /* If we are using global mask cache, then we need to + * flush dst instead of mask. As some dst depends on the + * previous mask result. Just flush mask can't get all previous's + * overlapped glyphs.*/ + if (dst_buffer.count || mask_buffer.count) + flush_func = (glyphs_flush_func)glamor_glyphs_flush_dst; + else + flush_func = NULL; + temp_arg = &dst_arg; + } + glamor_buffer_glyph(glamor_priv, pmask_buffer, + mask_format->format, + glyph, NULL, x, y, + 0, 0, + glyph->info.width, glyph->info.height, + glyphs_dst_mode, + flush_func, + (void*)temp_arg); + } + x += glyph->info.xOff; + y += glyph->info.yOff; + } + list++; + } + + x = extents.x1; + y = extents.y1; + if (need_free_mask) { + glamor_glyphs_flush_mask(pmask_arg); + CompositePicture(op, + src, + mask, + dst, + x_src + x - x_dst, + y_src + y - y_dst, 0, 0, x, y, width, height); + FreePicture(mask, 0); + glamor_destroy_pixmap(mask_pixmap); + } else { + struct glamor_glyph priv; + glyphs_flush_func flush_func; + BoxPtr rects; + int nrect; + + priv.cache = cache; + priv.x = mce->x; + priv.y = mce->y; + priv.cached = TRUE; + rects = REGION_RECTS(dst->pCompositeClip); + nrect = REGION_NUM_RECTS(dst->pCompositeClip); + + pmask_arg->used_bitmap |= ((1 << mce->width) - 1) << mce->idx; + dst_arg.op = op; + dst_arg.src = src; + dst_arg.dst = dst; + dst_arg.buffer = &dst_buffer; + dst_arg.x_src = x_src; + dst_arg.y_src = y_src; + dst_arg.x_dst = x_dst; + dst_arg.y_dst = y_dst; + + if (dst_buffer.source == NULL) { + dst_buffer.source = cache->picture; + } else if (dst_buffer.source != cache->picture) { + glamor_glyphs_flush_dst(&dst_arg); + dst_buffer.source = cache->picture; + } + + x += dst->pDrawable->x; + y += dst->pDrawable->y; + + if (dst_buffer.count || mask_buffer.count) + flush_func = (glyphs_flush_func)glamor_glyphs_flush_dst; + else + flush_func = NULL; + + glamor_buffer_glyph_clip(glamor_priv, + rects, nrect, + mask_format->format, + NULL, &priv, + x, y, + 0, 0, + width, height, + GLYPHS_DST_MODE_MASK_TO_DST, + flush_func, + (void *)&dst_arg + ); + } +} + +static void +glamor_glyphs_to_dst(CARD8 op, + PicturePtr src, + PicturePtr dst, + INT16 x_src, + INT16 y_src, + int nlist, GlyphListPtr list, + GlyphPtr * glyphs) +{ + ScreenPtr screen = dst->pDrawable->pScreen; + int x = 0, y = 0; + int x_dst = list->xOff, y_dst = list->yOff; + int n; + GlyphPtr glyph; + BoxPtr rects; + int nrect; + glamor_screen_private *glamor_priv; + + rects = REGION_RECTS(dst->pCompositeClip); + nrect = REGION_NUM_RECTS(dst->pCompositeClip); + + glamor_priv = glamor_get_screen_private(screen); + + dst_arg.op = op; + dst_arg.src = src; + dst_arg.dst = dst; + dst_arg.buffer = &dst_buffer; + dst_arg.x_src = x_src; + dst_arg.y_src = y_src; + dst_arg.x_dst = x_dst; + dst_arg.y_dst = y_dst; + + x = dst->pDrawable->x; + y = dst->pDrawable->y; + + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + dst_glyphs_cnt += n; + while (n--) { + glyph = *glyphs++; + + if (glyph->info.width > 0 + && glyph->info.height > 0) { + glyphs_flush_func flush_func; + + if (dst_buffer.count || mask_buffer.count) + flush_func = (glyphs_flush_func)glamor_glyphs_flush_dst; + else + flush_func = NULL; + glamor_buffer_glyph_clip(glamor_priv, + rects, nrect, + (GlyphPicture(glyph)[screen->myNum])->format, + glyph, NULL, + x, y, + glyph->info.x, glyph->info.y, + glyph->info.width, glyph->info.height, + GLYPHS_DST_MODE_TO_DST, + flush_func, + (void *)&dst_arg + ); + } + + x += glyph->info.xOff; + y += glyph->info.yOff; + } + list++; + } +} +#define MAX_FIXED_SIZE +static void +glamor_glyphs_reset_buffer(glamor_glyph_buffer_t *buffer) +{ + buffer->count = 0; + buffer->source = NULL; +} + +static Bool +_glamor_glyphs(CARD8 op, + PicturePtr src, + PicturePtr dst, + PictFormatPtr mask_format, + INT16 x_src, + INT16 y_src, int nlist, GlyphListPtr list, + GlyphPtr * glyphs, Bool fallback) +{ + PictFormatShort format; + int fixed_size, fixed_cnt = 0; + struct glamor_glyph_list *fixed_list = NULL; + Bool need_free_list = FALSE; +#ifndef GLYPHS_NO_EDEGEMAP_OVERLAP_CHECK + Bool check_fake_overlap = TRUE; + if (!(op == PictOpOver + || op == PictOpAdd + || op == PictOpXor)) { + /* C = (0,0,0,0) D = glyphs , SRC = A, DEST = B (faked overlapped glyphs, overlapped with (0,0,0,0)). + * For those op, (A IN (C ADD D)) OP B != (A IN D) OP ((A IN C) OP B) + * or (A IN (D ADD C)) OP B != (A IN C) OP ((A IN D) OP B) + * We need to split the faked regions to three or two, and composite the disoverlapped small + * boxes one by one. For other Ops, it's safe to composite the whole box. */ + check_fake_overlap = FALSE; + } +#else + Bool check_fake_overlap = FALSE; +#endif + if (mask_format) + format = mask_format->depth << 24 | mask_format->format; + else + format = 0; + + fixed_size = 32; + glamor_glyphs_reset_buffer(&dst_buffer); + + if (!mask_format || (((nlist == 1 && list->len == 1) || op == PictOpAdd) + && (dst->format == ((mask_format->depth << 24) | mask_format->format)))) { + glamor_glyphs_to_dst(op, src, dst, x_src, y_src, nlist, + list, glyphs); + goto last_flush; + } + + glamor_glyphs_reset_buffer(&mask_buffer); + + /* We have mask_format. Need to check the real overlap or not.*/ + format = mask_format->depth << 24 | mask_format->format; + + fixed_list = calloc(fixed_size, sizeof(*fixed_list)); + if (unlikely(fixed_list == NULL)) + fixed_size = 0; + fixed_cnt = glamor_glyphs_intersect(nlist, list, glyphs, + format, dst->pDrawable->pScreen, + check_fake_overlap, + fixed_list, fixed_size); + if (fixed_cnt == 0) + mask_format = NULL; + need_free_list = TRUE; + + if (fixed_cnt <= 0) { + if (mask_format == NULL) { + glamor_glyphs_to_dst(op, src, dst, x_src, y_src, nlist, + list, glyphs); + goto last_flush; + } else { + glamor_glyphs_via_mask(op, src, dst, mask_format, + x_src, y_src, nlist, list, glyphs, + FALSE); + goto free_fixed_list; + } + } else { + + /* We have splitted the original list to serval list, some are overlapped + * and some are non-overlapped. For the non-overlapped, we render it to + * dst directly. For the overlapped, we render it to mask picture firstly, + * then render the mask to dst. If we can use mask cache which is in the + * glyphs cache's last row, we can accumulate the rendering of mask to dst + * with the other dst_buffer's rendering operations thus can reduce the call + * of glDrawElements. + * + * */ + struct glamor_glyph_list *saved_list; + + saved_list = fixed_list; + mask_arg.used_bitmap = 0; + while(fixed_cnt--) { + if (fixed_list->type == NON_INTERSECTED) { + glamor_glyphs_to_dst(op, src, dst, + x_src, y_src, + fixed_list->nlist, + fixed_list->list, + fixed_list->glyphs); + } + else + glamor_glyphs_via_mask(op, src, dst, + mask_format, x_src, y_src, + fixed_list->nlist, + fixed_list->list, + fixed_list->glyphs, TRUE); + + free(fixed_list->list); + fixed_list++; + } + free(saved_list); + need_free_list = FALSE; + } + +last_flush: + if (dst_buffer.count || mask_buffer.count) + glamor_glyphs_flush_dst(&dst_arg); +free_fixed_list: + if(need_free_list) { + assert(fixed_cnt <= 0); + free(fixed_list); + } + return TRUE; +} + +void +glamor_glyphs(CARD8 op, + PicturePtr src, + PicturePtr dst, + PictFormatPtr mask_format, + INT16 x_src, + INT16 y_src, int nlist, GlyphListPtr list, GlyphPtr * glyphs) +{ + _glamor_glyphs(op, src, dst, mask_format, x_src, + y_src, nlist, list, glyphs, TRUE); +} + +Bool +glamor_glyphs_nf(CARD8 op, + PicturePtr src, + PicturePtr dst, + PictFormatPtr mask_format, + INT16 x_src, + INT16 y_src, int nlist, + GlyphListPtr list, GlyphPtr * glyphs) +{ + return _glamor_glyphs(op, src, dst, mask_format, x_src, + y_src, nlist, list, glyphs, FALSE); +} + diff --git a/xorg-server/glamor/glamor_gradient.c b/xorg-server/glamor/glamor_gradient.c new file mode 100644 index 000000000..4abc82d74 --- /dev/null +++ b/xorg-server/glamor/glamor_gradient.c @@ -0,0 +1,1584 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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. + * + * Authors: + * Junyan He <junyan.he@linux.intel.com> + * + */ + +/** @file glamor_gradient.c + * + * Gradient acceleration implementation + */ + +#include "glamor_priv.h" + +#ifdef RENDER + +#define LINEAR_SMALL_STOPS (6 + 2) +#define LINEAR_LARGE_STOPS (16 + 2) + +#define RADIAL_SMALL_STOPS (6 + 2) +#define RADIAL_LARGE_STOPS (16 + 2) + +#ifdef GLAMOR_GRADIENT_SHADER + +static GLint +_glamor_create_getcolor_fs_program(ScreenPtr screen, int stops_count, int use_array) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + + char *gradient_fs = NULL; + GLint fs_getcolor_prog; + + #define gradient_fs_getcolor\ + GLAMOR_DEFAULT_PRECISION\ + "uniform int n_stop;\n"\ + "uniform float stops[%d];\n"\ + "uniform vec4 stop_colors[%d];\n"\ + "vec4 get_color(float stop_len)\n"\ + "{\n"\ + " int i = 0;\n"\ + " float new_alpha; \n"\ + " vec4 gradient_color;\n"\ + " float percentage; \n"\ + " for(i = 0; i < n_stop - 1; i++) {\n"\ + " if(stop_len < stops[i])\n"\ + " break; \n"\ + " }\n"\ + " \n"\ + " if(stops[i] - stops[i-1] > 2.0)\n"\ + " percentage = 0.0;\n" /*For comply with pixman, walker->stepper overflow.*/\ + " else if(stops[i] - stops[i-1] < 0.000001)\n"\ + " percentage = 0.0;\n"\ + " else \n"\ + " percentage = (stop_len - stops[i-1])/(stops[i] - stops[i-1]);\n"\ + " new_alpha = percentage * stop_colors[i].a + \n"\ + " (1.0-percentage) * stop_colors[i-1].a; \n"\ + " gradient_color = vec4((percentage * stop_colors[i].rgb \n"\ + " + (1.0-percentage) * stop_colors[i-1].rgb)*new_alpha, \n"\ + " new_alpha);\n"\ + " \n"\ + " return gradient_color;\n"\ + "}\n" + + /* Because the array access for shader is very slow, the performance is very low + if use array. So use global uniform to replace for it if the number of n_stops is small.*/ + const char *gradient_fs_getcolor_no_array = + GLAMOR_DEFAULT_PRECISION + "uniform int n_stop;\n" + "uniform float stop0;\n" + "uniform float stop1;\n" + "uniform float stop2;\n" + "uniform float stop3;\n" + "uniform float stop4;\n" + "uniform float stop5;\n" + "uniform float stop6;\n" + "uniform float stop7;\n" + "uniform vec4 stop_color0;\n" + "uniform vec4 stop_color1;\n" + "uniform vec4 stop_color2;\n" + "uniform vec4 stop_color3;\n" + "uniform vec4 stop_color4;\n" + "uniform vec4 stop_color5;\n" + "uniform vec4 stop_color6;\n" + "uniform vec4 stop_color7;\n" + "\n" + "vec4 get_color(float stop_len)\n" + "{\n" + " float stop_after;\n" + " float stop_before;\n" + " vec4 stop_color_before;\n" + " vec4 stop_color_after;\n" + " float new_alpha; \n" + " vec4 gradient_color;\n" + " float percentage; \n" + " \n" + " if((stop_len < stop0) && (n_stop >= 1)) {\n" + " stop_color_before = stop_color0;\n" + " stop_color_after = stop_color0;\n" + " stop_after = stop0;\n" + " stop_before = stop0;\n" + " } else if((stop_len < stop1) && (n_stop >= 2)) {\n" + " stop_color_before = stop_color0;\n" + " stop_color_after = stop_color1;\n" + " stop_after = stop1;\n" + " stop_before = stop0;\n" + " } else if((stop_len < stop2) && (n_stop >= 3)) {\n" + " stop_color_before = stop_color1;\n" + " stop_color_after = stop_color2;\n" + " stop_after = stop2;\n" + " stop_before = stop1;\n" + " } else if((stop_len < stop3) && (n_stop >= 4)){\n" + " stop_color_before = stop_color2;\n" + " stop_color_after = stop_color3;\n" + " stop_after = stop3;\n" + " stop_before = stop2;\n" + " } else if((stop_len < stop4) && (n_stop >= 5)){\n" + " stop_color_before = stop_color3;\n" + " stop_color_after = stop_color4;\n" + " stop_after = stop4;\n" + " stop_before = stop3;\n" + " } else if((stop_len < stop5) && (n_stop >= 6)){\n" + " stop_color_before = stop_color4;\n" + " stop_color_after = stop_color5;\n" + " stop_after = stop5;\n" + " stop_before = stop4;\n" + " } else if((stop_len < stop6) && (n_stop >= 7)){\n" + " stop_color_before = stop_color5;\n" + " stop_color_after = stop_color6;\n" + " stop_after = stop6;\n" + " stop_before = stop5;\n" + " } else if((stop_len < stop7) && (n_stop >= 8)){\n" + " stop_color_before = stop_color6;\n" + " stop_color_after = stop_color7;\n" + " stop_after = stop7;\n" + " stop_before = stop6;\n" + " } else {\n" + " stop_color_before = stop_color7;\n" + " stop_color_after = stop_color7;\n" + " stop_after = stop7;\n" + " stop_before = stop7;\n" + " }\n" + " if(stop_after - stop_before > 2.0)\n" + " percentage = 0.0;\n"//For comply with pixman, walker->stepper overflow. + " else if(stop_after - stop_before < 0.000001)\n" + " percentage = 0.0;\n" + " else \n" + " percentage = (stop_len - stop_before)/(stop_after - stop_before);\n" + " new_alpha = percentage * stop_color_after.a + \n" + " (1.0-percentage) * stop_color_before.a; \n" + " gradient_color = vec4((percentage * stop_color_after.rgb \n" + " + (1.0-percentage) * stop_color_before.rgb)*new_alpha, \n" + " new_alpha);\n" + " \n" + " return gradient_color;\n" + "}\n"; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + + if(use_array) { + XNFasprintf(&gradient_fs, + gradient_fs_getcolor, stops_count, stops_count); + fs_getcolor_prog = glamor_compile_glsl_prog(dispatch, GL_FRAGMENT_SHADER, + gradient_fs); + free(gradient_fs); + } else { + fs_getcolor_prog = glamor_compile_glsl_prog(dispatch, GL_FRAGMENT_SHADER, + gradient_fs_getcolor_no_array); + } + + return fs_getcolor_prog; +} + +static void +_glamor_create_radial_gradient_program(ScreenPtr screen, int stops_count, int dyn_gen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + int index; + + GLint gradient_prog = 0; + char *gradient_fs = NULL; + GLint fs_main_prog, fs_getcolor_prog, vs_prog; + + const char *gradient_vs = + GLAMOR_DEFAULT_PRECISION + "attribute vec4 v_position;\n" + "attribute vec4 v_texcoord;\n" + "varying vec2 source_texture;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = v_position;\n" + " source_texture = v_texcoord.xy;\n" + "}\n"; + + /* + * Refer to pixman radial gradient. + * + * The problem is given the two circles of c1 and c2 with the radius of r1 and + * r1, we need to caculate the t, which is used to do interpolate with stops, + * using the fomula: + * length((1-t)*c1 + t*c2 - p) = (1-t)*r1 + t*r2 + * expand the fomula with xy coond, get the following: + * sqrt(sqr((1-t)*c1.x + t*c2.x - p.x) + sqr((1-t)*c1.y + t*c2.y - p.y)) + * = (1-t)r1 + t*r2 + * <====> At*t- 2Bt + C = 0 + * where A = sqr(c2.x - c1.x) + sqr(c2.y - c1.y) - sqr(r2 -r1) + * B = (p.x - c1.x)*(c2.x - c1.x) + (p.y - c1.y)*(c2.y - c1.y) + r1*(r2 -r1) + * C = sqr(p.x - c1.x) + sqr(p.y - c1.y) - r1*r1 + * + * solve the fomula and we get the result of + * t = (B + sqrt(B*B - A*C)) / A or + * t = (B - sqrt(B*B - A*C)) / A (quadratic equation have two solutions) + * + * The solution we are going to prefer is the bigger one, unless the + * radius associated to it is negative (or it falls outside the valid t range) + */ + + #define gradient_radial_fs_template\ + GLAMOR_DEFAULT_PRECISION\ + "uniform mat3 transform_mat;\n"\ + "uniform int repeat_type;\n"\ + "uniform float A_value;\n"\ + "uniform vec2 c1;\n"\ + "uniform float r1;\n"\ + "uniform vec2 c2;\n"\ + "uniform float r2;\n"\ + "varying vec2 source_texture;\n"\ + "\n"\ + "vec4 get_color(float stop_len);\n"\ + "\n"\ + "int t_invalid;\n"\ + "\n"\ + "float get_stop_len()\n"\ + "{\n"\ + " float t = 0.0;\n"\ + " float sqrt_value;\n"\ + " int revserse = 0;\n"\ + " t_invalid = 0;\n"\ + " \n"\ + " vec3 tmp = vec3(source_texture.x, source_texture.y, 1.0);\n"\ + " vec3 source_texture_trans = transform_mat * tmp;\n"\ + " source_texture_trans.xy = source_texture_trans.xy/source_texture_trans.z;\n"\ + " float B_value = (source_texture_trans.x - c1.x) * (c2.x - c1.x)\n"\ + " + (source_texture_trans.y - c1.y) * (c2.y - c1.y)\n"\ + " + r1 * (r2 - r1);\n"\ + " float C_value = (source_texture_trans.x - c1.x) * (source_texture_trans.x - c1.x)\n"\ + " + (source_texture_trans.y - c1.y) * (source_texture_trans.y - c1.y)\n"\ + " - r1*r1;\n"\ + " if(abs(A_value) < 0.00001) {\n"\ + " if(B_value == 0.0) {\n"\ + " t_invalid = 1;\n"\ + " return t;\n"\ + " }\n"\ + " t = 0.5 * C_value / B_value;"\ + " } else {\n"\ + " sqrt_value = B_value * B_value - A_value * C_value;\n"\ + " if(sqrt_value < 0.0) {\n"\ + " t_invalid = 1;\n"\ + " return t;\n"\ + " }\n"\ + " sqrt_value = sqrt(sqrt_value);\n"\ + " t = (B_value + sqrt_value) / A_value;\n"\ + " }\n"\ + " if(repeat_type == %d) {\n" /* RepeatNone case. */\ + " if((t <= 0.0) || (t > 1.0))\n"\ + /* try another if first one invalid*/\ + " t = (B_value - sqrt_value) / A_value;\n"\ + " \n"\ + " if((t <= 0.0) || (t > 1.0)) {\n" /*still invalid, return.*/\ + " t_invalid = 1;\n"\ + " return t;\n"\ + " }\n"\ + " } else {\n"\ + " if(t * (r2 - r1) <= -1.0 * r1)\n"\ + /* try another if first one invalid*/\ + " t = (B_value - sqrt_value) / A_value;\n"\ + " \n"\ + " if(t * (r2 -r1) <= -1.0 * r1) {\n" /*still invalid, return.*/\ + " t_invalid = 1;\n"\ + " return t;\n"\ + " }\n"\ + " }\n"\ + " \n"\ + " if(repeat_type == %d){\n" /* repeat normal*/\ + " while(t > 1.0) \n"\ + " t = t - 1.0; \n"\ + " while(t < 0.0) \n"\ + " t = t + 1.0; \n"\ + " }\n"\ + " \n"\ + " if(repeat_type == %d) {\n" /* repeat reflect*/\ + " while(t > 1.0) {\n"\ + " t = t - 1.0; \n"\ + " if(revserse == 0)\n"\ + " revserse = 1;\n"\ + " else\n"\ + " revserse = 0;\n"\ + " }\n"\ + " while(t < 0.0) {\n"\ + " t = t + 1.0; \n"\ + " if(revserse == 0)\n"\ + " revserse = 1;\n"\ + " else\n"\ + " revserse = 0;\n"\ + " }\n"\ + " if(revserse == 1) {\n"\ + " t = 1.0 - t; \n"\ + " }\n"\ + " }\n"\ + " \n"\ + " return t;\n"\ + "}\n"\ + "\n"\ + "void main()\n"\ + "{\n"\ + " float stop_len = get_stop_len();\n"\ + " if(t_invalid == 1) {\n"\ + " gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n"\ + " } else {\n"\ + " gl_FragColor = get_color(stop_len);\n"\ + " }\n"\ + "}\n" + + glamor_priv = glamor_get_screen_private(screen); + + if ((glamor_priv->radial_max_nstops >= stops_count) && (dyn_gen)) { + /* Very Good, not to generate again. */ + return; + } + + dispatch = glamor_get_dispatch(glamor_priv); + + if (dyn_gen && glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][2]) { + dispatch->glDeleteShader( + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_VS_PROG][2]); + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_VS_PROG][2] = 0; + + dispatch->glDeleteShader( + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][2]); + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][2] = 0; + + dispatch->glDeleteShader( + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][2]); + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][2] = 0; + + dispatch->glDeleteProgram(glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][2]); + glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][2] = 0; + } + + gradient_prog = dispatch->glCreateProgram(); + + vs_prog = glamor_compile_glsl_prog(dispatch, + GL_VERTEX_SHADER, gradient_vs); + + XNFasprintf(&gradient_fs, + gradient_radial_fs_template, + PIXMAN_REPEAT_NONE, PIXMAN_REPEAT_NORMAL, PIXMAN_REPEAT_REFLECT); + + fs_main_prog = glamor_compile_glsl_prog(dispatch, + GL_FRAGMENT_SHADER, gradient_fs); + + free(gradient_fs); + + fs_getcolor_prog = + _glamor_create_getcolor_fs_program(screen, stops_count, (stops_count > 0)); + + dispatch->glAttachShader(gradient_prog, vs_prog); + dispatch->glAttachShader(gradient_prog, fs_getcolor_prog); + dispatch->glAttachShader(gradient_prog, fs_main_prog); + + dispatch->glBindAttribLocation(gradient_prog, GLAMOR_VERTEX_POS, "v_positionsition"); + dispatch->glBindAttribLocation(gradient_prog, GLAMOR_VERTEX_SOURCE, "v_texcoord"); + + glamor_link_glsl_prog(dispatch, gradient_prog); + + dispatch->glUseProgram(0); + + if (dyn_gen) { + index = 2; + glamor_priv->radial_max_nstops = stops_count; + } else if (stops_count) { + index = 1; + } else { + index = 0; + } + + glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][index] = gradient_prog; + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_VS_PROG][index] = vs_prog; + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][index] = fs_main_prog; + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][index] = fs_getcolor_prog; + + glamor_put_dispatch(glamor_priv); +} + +static void +_glamor_create_linear_gradient_program(ScreenPtr screen, int stops_count, int dyn_gen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + + int index = 0; + GLint gradient_prog = 0; + char *gradient_fs = NULL; + GLint fs_main_prog, fs_getcolor_prog, vs_prog; + + const char *gradient_vs = + GLAMOR_DEFAULT_PRECISION + "attribute vec4 v_position;\n" + "attribute vec4 v_texcoord;\n" + "varying vec2 source_texture;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = v_position;\n" + " source_texture = v_texcoord.xy;\n" + "}\n"; + + /* + * | + * |\ + * | \ + * | \ + * | \ + * |\ \ + * | \ \ + * cos_val = |\ p1d \ / + * sqrt(1/(slope*slope+1.0)) ------>\ \ \ / + * | \ \ \ + * | \ \ / \ + * | \ *Pt1\ + * *p1 | \ \ *P + * \ | / \ \ / + * \ | / \ \ / + * \ | pd \ + * \ | \ / \ + * p2* | \ / \ / + * slope = (p2.y - p1.y) / | / p2d / + * (p2.x - p1.x) | / \ / + * | / \ / + * | / / + * | / / + * | / *Pt2 + * | / + * | / + * | / + * | / + * | / + * -------+--------------------------------- + * O| + * | + * | + * + * step 1: compute the distance of p, pt1 and pt2 in the slope direction. + * Caculate the distance on Y axis first and multiply cos_val to + * get the value on slope direction(pd, p1d and p2d represent the + * distance of p, pt1, and pt2 respectively). + * + * step 2: caculate the percentage of (pd - p1d)/(p2d - p1d). + * If (pd - p1d) > (p2d - p1d) or < 0, then sub or add (p2d - p1d) + * to make it in the range of [0, (p2d - p1d)]. + * + * step 3: compare the percentage to every stop and find the stpos just + * before and after it. Use the interpolation fomula to compute RGBA. + */ + + #define gradient_fs_template \ + GLAMOR_DEFAULT_PRECISION\ + "uniform mat3 transform_mat;\n"\ + "uniform int repeat_type;\n"\ + "uniform int hor_ver;\n"\ + "uniform float pt_slope;\n"\ + "uniform float cos_val;\n"\ + "uniform float p1_distance;\n"\ + "uniform float pt_distance;\n"\ + "varying vec2 source_texture;\n"\ + "\n"\ + "vec4 get_color(float stop_len);\n"\ + "\n"\ + "float get_stop_len()\n"\ + "{\n"\ + " vec3 tmp = vec3(source_texture.x, source_texture.y, 1.0);\n"\ + " float len_percentage;\n"\ + " float distance;\n"\ + " float _p1_distance;\n"\ + " float _pt_distance;\n"\ + " float y_dist;\n"\ + " float stop_after;\n"\ + " float stop_before;\n"\ + " vec4 stop_color_before;\n"\ + " vec4 stop_color_after;\n"\ + " float new_alpha; \n"\ + " int revserse = 0;\n"\ + " vec4 gradient_color;\n"\ + " float percentage; \n"\ + " vec3 source_texture_trans = transform_mat * tmp;\n"\ + " \n"\ + " if(hor_ver == 0) { \n" /*Normal case.*/\ + " y_dist = source_texture_trans.y - source_texture_trans.x*pt_slope;\n"\ + " distance = y_dist * cos_val;\n"\ + " _p1_distance = p1_distance * source_texture_trans.z;\n"\ + " _pt_distance = pt_distance * source_texture_trans.z;\n"\ + " \n"\ + " } else if (hor_ver == 1) {\n"/*horizontal case.*/\ + " distance = source_texture_trans.x;\n"\ + " _p1_distance = p1_distance * source_texture_trans.z;\n"\ + " _pt_distance = pt_distance * source_texture_trans.z;\n"\ + " } \n"\ + " \n"\ + " distance = distance - _p1_distance; \n"\ + " \n"\ + " if(repeat_type == %d){\n" /* repeat normal*/\ + " while(distance > _pt_distance) \n"\ + " distance = distance - (_pt_distance); \n"\ + " while(distance < 0.0) \n"\ + " distance = distance + (_pt_distance); \n"\ + " }\n"\ + " \n"\ + " if(repeat_type == %d) {\n" /* repeat reflect*/\ + " while(distance > _pt_distance) {\n"\ + " distance = distance - (_pt_distance); \n"\ + " if(revserse == 0)\n"\ + " revserse = 1;\n"\ + " else\n"\ + " revserse = 0;\n"\ + " }\n"\ + " while(distance < 0.0) {\n"\ + " distance = distance + (_pt_distance); \n"\ + " if(revserse == 0)\n"\ + " revserse = 1;\n"\ + " else\n"\ + " revserse = 0;\n"\ + " }\n"\ + " if(revserse == 1) {\n"\ + " distance = (_pt_distance) - distance; \n"\ + " }\n"\ + " }\n"\ + " \n"\ + " len_percentage = distance/(_pt_distance);\n"\ + " \n"\ + " return len_percentage;\n"\ + "}\n"\ + "\n"\ + "void main()\n"\ + "{\n"\ + " float stop_len = get_stop_len();\n"\ + " gl_FragColor = get_color(stop_len);\n"\ + "}\n" + + glamor_priv = glamor_get_screen_private(screen); + + if ((glamor_priv->linear_max_nstops >= stops_count) && (dyn_gen)) { + /* Very Good, not to generate again. */ + return; + } + + dispatch = glamor_get_dispatch(glamor_priv); + if (dyn_gen && glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][2]) { + dispatch->glDeleteShader( + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_VS_PROG][2]); + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_VS_PROG][2] = 0; + + dispatch->glDeleteShader( + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][2]); + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][2] = 0; + + dispatch->glDeleteShader( + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][2]); + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][2] = 0; + + dispatch->glDeleteProgram(glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][2]); + glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][2] = 0; + } + + gradient_prog = dispatch->glCreateProgram(); + + vs_prog = glamor_compile_glsl_prog(dispatch, + GL_VERTEX_SHADER, gradient_vs); + + XNFasprintf(&gradient_fs, + gradient_fs_template, + PIXMAN_REPEAT_NORMAL, PIXMAN_REPEAT_REFLECT); + + fs_main_prog = glamor_compile_glsl_prog(dispatch, + GL_FRAGMENT_SHADER, gradient_fs); + free(gradient_fs); + + fs_getcolor_prog = + _glamor_create_getcolor_fs_program(screen, stops_count, (stops_count > 0)); + + dispatch->glAttachShader(gradient_prog, vs_prog); + dispatch->glAttachShader(gradient_prog, fs_getcolor_prog); + dispatch->glAttachShader(gradient_prog, fs_main_prog); + + dispatch->glBindAttribLocation(gradient_prog, GLAMOR_VERTEX_POS, "v_position"); + dispatch->glBindAttribLocation(gradient_prog, GLAMOR_VERTEX_SOURCE, "v_texcoord"); + + glamor_link_glsl_prog(dispatch, gradient_prog); + + dispatch->glUseProgram(0); + + if (dyn_gen) { + index = 2; + glamor_priv->linear_max_nstops = stops_count; + } else if (stops_count) { + index = 1; + } else { + index = 0; + } + + glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][index] = gradient_prog; + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_VS_PROG][index] = vs_prog; + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][index] = fs_main_prog; + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][index] = fs_getcolor_prog; + + glamor_put_dispatch(glamor_priv); +} + +void +glamor_init_gradient_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + int i; + + glamor_priv = glamor_get_screen_private(screen); + + for (i = 0; i < 3; i++) { + glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][i] = 0; + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_VS_PROG][i] = 0; + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][i] = 0; + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][i] = 0; + + glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][i] = 0; + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_VS_PROG][i] = 0; + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][i] = 0; + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][i] = 0; + } + glamor_priv->linear_max_nstops = 0; + glamor_priv->radial_max_nstops = 0; + + _glamor_create_linear_gradient_program(screen, 0, 0); + _glamor_create_linear_gradient_program(screen, LINEAR_LARGE_STOPS, 0); + + _glamor_create_radial_gradient_program(screen, 0, 0); + _glamor_create_radial_gradient_program(screen, RADIAL_LARGE_STOPS, 0); +} + +void +glamor_fini_gradient_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + int i = 0; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + + for (i = 0; i < 3; i++) { + /* Linear Gradient */ + if (glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_VS_PROG][i]) + dispatch->glDeleteShader( + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_VS_PROG][i]); + + if (glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][i]) + dispatch->glDeleteShader( + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][i]); + + if (glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][i]) + dispatch->glDeleteShader( + glamor_priv->linear_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][i]); + + if (glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][i]) + dispatch->glDeleteProgram(glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][i]); + + /* Radial Gradient */ + if (glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_VS_PROG][i]) + dispatch->glDeleteShader( + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_VS_PROG][i]); + + if (glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][i]) + dispatch->glDeleteShader( + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_MAIN_PROG][i]); + + if (glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][i]) + dispatch->glDeleteShader( + glamor_priv->radial_gradient_shaders[SHADER_GRADIENT_FS_GETCOLOR_PROG][i]); + + if (glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][i]) + dispatch->glDeleteProgram(glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][i]); + } + + glamor_put_dispatch(glamor_priv); +} + +static void +_glamor_gradient_convert_trans_matrix(PictTransform *from, float to[3][3], + int width, int height, int normalize) +{ + /* + * Because in the shader program, we normalize all the pixel cood to [0, 1], + * so with the transform matrix, the correct logic should be: + * v_s = A*T*v + * v_s: point vector in shader after normalized. + * A: The transition matrix from width X height --> 1.0 X 1.0 + * T: The transform matrix. + * v: point vector in width X height space. + * + * result is OK if we use this fomula. But for every point in width X height space, + * we can just use their normalized point vector in shader, namely we can just + * use the result of A*v in shader. So we have no chance to insert T in A*v. + * We can just convert v_s = A*T*v to v_s = A*T*inv(A)*A*v, where inv(A) is the + * inverse matrix of A. Now, v_s = (A*T*inv(A)) * (A*v) + * So, to get the correct v_s, we need to cacula1 the matrix: (A*T*inv(A)), and + * we name this matrix T_s. + * + * Firstly, because A is for the scale convertion, we find + * -- -- + * |1/w 0 0 | + * A = | 0 1/h 0 | + * | 0 0 1.0| + * -- -- + * so T_s = A*T*inv(a) and result + * + * -- -- + * | t11 h*t12/w t13/w| + * T_s = | w*t21/h t22 t23/h| + * | w*t31 h*t32 t33 | + * -- -- + */ + + to[0][0] = (float)pixman_fixed_to_double(from->matrix[0][0]); + to[0][1] = (float)pixman_fixed_to_double(from->matrix[0][1]) + * (normalize ? (((float)height) / ((float)width)) : 1.0); + to[0][2] = (float)pixman_fixed_to_double(from->matrix[0][2]) + / (normalize ? ((float)width) : 1.0); + + to[1][0] = (float)pixman_fixed_to_double(from->matrix[1][0]) + * (normalize ? (((float)width) / ((float)height)) : 1.0); + to[1][1] = (float)pixman_fixed_to_double(from->matrix[1][1]); + to[1][2] = (float)pixman_fixed_to_double(from->matrix[1][2]) + / (normalize ? ((float)height) : 1.0); + + to[2][0] = (float)pixman_fixed_to_double(from->matrix[2][0]) + * (normalize ? ((float)width) : 1.0); + to[2][1] = (float)pixman_fixed_to_double(from->matrix[2][1]) + * (normalize ? ((float)height) : 1.0); + to[2][2] = (float)pixman_fixed_to_double(from->matrix[2][2]); + + DEBUGF("the transform matrix is:\n%f\t%f\t%f\n%f\t%f\t%f\n%f\t%f\t%f\n", + to[0][0], to[0][1], to[0][2], + to[1][0], to[1][1], to[1][2], + to[2][0], to[2][1], to[2][2]); +} + +static int +_glamor_gradient_set_pixmap_destination(ScreenPtr screen, + glamor_screen_private *glamor_priv, + PicturePtr dst_picture, + GLfloat *xscale, GLfloat *yscale, + int x_source, int y_source, + float vertices[8], + float tex_vertices[8], + int tex_normalize) +{ + glamor_pixmap_private *pixmap_priv; + PixmapPtr pixmap = NULL; + glamor_gl_dispatch *dispatch = NULL; + + pixmap = glamor_get_drawable_pixmap(dst_picture->pDrawable); + pixmap_priv = glamor_get_pixmap_private(pixmap); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) { /* should always have here. */ + return 0; + } + + glamor_set_destination_pixmap_priv_nc(pixmap_priv); + + pixmap_priv_get_dest_scale(pixmap_priv, xscale, yscale); + + DEBUGF("xscale = %f, yscale = %f," + " x_source = %d, y_source = %d, width = %d, height = %d\n", + *xscale, *yscale, x_source, y_source, + dst_picture->pDrawable->width, dst_picture->pDrawable->height); + + glamor_set_normalize_vcoords_tri_strip(*xscale, *yscale, + 0, 0, + (INT16)(dst_picture->pDrawable->width), + (INT16)(dst_picture->pDrawable->height), + glamor_priv->yInverted, vertices); + + if (tex_normalize) { + glamor_set_normalize_tcoords_tri_stripe(*xscale, *yscale, + x_source, y_source, + (INT16)(dst_picture->pDrawable->width + x_source), + (INT16)(dst_picture->pDrawable->height + y_source), + glamor_priv->yInverted, tex_vertices); + } else { + glamor_set_tcoords_tri_strip((INT16)(dst_picture->pDrawable->width), + (INT16)(dst_picture->pDrawable->height), + x_source, y_source, + (INT16)(dst_picture->pDrawable->width) + x_source, + (INT16)(dst_picture->pDrawable->height) + y_source, + glamor_priv->yInverted, tex_vertices); + } + + DEBUGF("vertices --> leftup : %f X %f, rightup: %f X %f," + "rightbottom: %f X %f, leftbottom : %f X %f\n", + vertices[0], vertices[1], vertices[2], vertices[3], + vertices[4], vertices[5], vertices[6], vertices[7]); + DEBUGF("tex_vertices --> leftup : %f X %f, rightup: %f X %f," + "rightbottom: %f X %f, leftbottom : %f X %f\n", + tex_vertices[0], tex_vertices[1], tex_vertices[2], tex_vertices[3], + tex_vertices[4], tex_vertices[5], tex_vertices[6], tex_vertices[7]); + + dispatch = glamor_get_dispatch(glamor_priv); + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, + GL_FALSE, 0, vertices); + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT, + GL_FALSE, 0, tex_vertices); + + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + + glamor_put_dispatch(glamor_priv); + + return 1; +} + +static int +_glamor_gradient_set_stops(PicturePtr src_picture, PictGradient * pgradient, + GLfloat *stop_colors, GLfloat *n_stops) +{ + int i; + int count = 1; + + for (i = 0; i < pgradient->nstops; i++) { + stop_colors[count*4] = pixman_fixed_to_double( + pgradient->stops[i].color.red); + stop_colors[count*4+1] = pixman_fixed_to_double( + pgradient->stops[i].color.green); + stop_colors[count*4+2] = pixman_fixed_to_double( + pgradient->stops[i].color.blue); + stop_colors[count*4+3] = pixman_fixed_to_double( + pgradient->stops[i].color.alpha); + + n_stops[count] = (GLfloat)pixman_fixed_to_double( + pgradient->stops[i].x); + count++; + } + + /* for the end stop. */ + count++; + + switch (src_picture->repeatType) { +#define REPEAT_FILL_STOPS(m, n) \ + stop_colors[(m)*4 + 0] = stop_colors[(n)*4 + 0]; \ + stop_colors[(m)*4 + 1] = stop_colors[(n)*4 + 1]; \ + stop_colors[(m)*4 + 2] = stop_colors[(n)*4 + 2]; \ + stop_colors[(m)*4 + 3] = stop_colors[(n)*4 + 3]; + + default: + case PIXMAN_REPEAT_NONE: + stop_colors[0] = 0.0; //R + stop_colors[1] = 0.0; //G + stop_colors[2] = 0.0; //B + stop_colors[3] = 0.0; //Alpha + n_stops[0] = -(float)INT_MAX; //should be small enough. + + stop_colors[0 + (count-1)*4] = 0.0; //R + stop_colors[1 + (count-1)*4] = 0.0; //G + stop_colors[2 + (count-1)*4] = 0.0; //B + stop_colors[3 + (count-1)*4] = 0.0; //Alpha + n_stops[count-1] = (float)INT_MAX; //should be large enough. + break; + case PIXMAN_REPEAT_NORMAL: + REPEAT_FILL_STOPS(0, count - 2); + n_stops[0] = n_stops[count-2] - 1.0; + + REPEAT_FILL_STOPS(count - 1, 1); + n_stops[count-1] = n_stops[1] + 1.0; + break; + case PIXMAN_REPEAT_REFLECT: + REPEAT_FILL_STOPS(0, 1); + n_stops[0] = -n_stops[1]; + + REPEAT_FILL_STOPS(count - 1, count - 2); + n_stops[count-1] = 1.0 + 1.0 - n_stops[count-2]; + break; + case PIXMAN_REPEAT_PAD: + REPEAT_FILL_STOPS(0, 1); + n_stops[0] = -(float)INT_MAX; + + REPEAT_FILL_STOPS(count - 1, count - 2); + n_stops[count-1] = (float)INT_MAX; + break; +#undef REPEAT_FILL_STOPS + } + + for (i = 0; i < count; i++) { + DEBUGF("n_stops[%d] = %f, color = r:%f g:%f b:%f a:%f\n", + i, n_stops[i], + stop_colors[i*4], stop_colors[i*4+1], + stop_colors[i*4+2], stop_colors[i*4+3]); + } + + return count; +} + +PicturePtr +glamor_generate_radial_gradient_picture(ScreenPtr screen, + PicturePtr src_picture, + int x_source, int y_source, + int width, int height, + PictFormatShort format) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + PicturePtr dst_picture = NULL; + PixmapPtr pixmap = NULL; + GLint gradient_prog = 0; + int error; + float tex_vertices[8]; + int stops_count = 0; + int count = 0; + GLfloat *stop_colors = NULL; + GLfloat *n_stops = NULL; + GLfloat xscale, yscale; + float vertices[8]; + float transform_mat[3][3]; + static const float identity_mat[3][3] = {{1.0, 0.0, 0.0}, + {0.0, 1.0, 0.0}, + {0.0, 0.0, 1.0}}; + GLfloat stop_colors_st[RADIAL_SMALL_STOPS*4]; + GLfloat n_stops_st[RADIAL_SMALL_STOPS]; + GLfloat A_value; + GLfloat cxy[4]; + float c1x, c1y, c2x, c2y, r1, r2; + + GLint transform_mat_uniform_location = 0; + GLint repeat_type_uniform_location = 0; + GLint n_stop_uniform_location = 0; + GLint stops_uniform_location = 0; + GLint stop_colors_uniform_location = 0; + GLint stop0_uniform_location = 0; + GLint stop1_uniform_location = 0; + GLint stop2_uniform_location = 0; + GLint stop3_uniform_location = 0; + GLint stop4_uniform_location = 0; + GLint stop5_uniform_location = 0; + GLint stop6_uniform_location = 0; + GLint stop7_uniform_location = 0; + GLint stop_color0_uniform_location = 0; + GLint stop_color1_uniform_location = 0; + GLint stop_color2_uniform_location = 0; + GLint stop_color3_uniform_location = 0; + GLint stop_color4_uniform_location = 0; + GLint stop_color5_uniform_location = 0; + GLint stop_color6_uniform_location = 0; + GLint stop_color7_uniform_location = 0; + GLint A_value_uniform_location = 0; + GLint c1_uniform_location = 0; + GLint r1_uniform_location = 0; + GLint c2_uniform_location = 0; + GLint r2_uniform_location = 0; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + + /* Create a pixmap with VBO. */ + pixmap = glamor_create_pixmap(screen, + width, height, + PIXMAN_FORMAT_DEPTH(format), + 0); + if (!pixmap) + goto GRADIENT_FAIL; + + dst_picture = CreatePicture(0, &pixmap->drawable, + PictureMatchFormat(screen, + PIXMAN_FORMAT_DEPTH(format), format), + 0, 0, serverClient, &error); + + /* Release the reference, picture will hold the last one. */ + glamor_destroy_pixmap(pixmap); + + if (!dst_picture) + goto GRADIENT_FAIL; + + ValidatePicture(dst_picture); + + stops_count = src_picture->pSourcePict->radial.nstops + 2; + + /* Because the max value of nstops is unkown, so create a program + when nstops > LINEAR_LARGE_STOPS.*/ + if (stops_count <= RADIAL_SMALL_STOPS) { + gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][0]; + } else if (stops_count <= RADIAL_LARGE_STOPS) { + gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][1]; + } else { + _glamor_create_radial_gradient_program(screen, + src_picture->pSourcePict->linear.nstops + 2, + 1); + gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_RADIAL][2]; + } + + /* Bind all the uniform vars .*/ + transform_mat_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "transform_mat"); + repeat_type_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "repeat_type"); + n_stop_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "n_stop"); + A_value_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "A_value"); + repeat_type_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "repeat_type"); + c1_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "c1"); + r1_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "r1"); + c2_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "c2"); + r2_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "r2"); + + if (src_picture->pSourcePict->radial.nstops + 2 <= RADIAL_SMALL_STOPS) { + stop0_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop0"); + stop1_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop1"); + stop2_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop2"); + stop3_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop3"); + stop4_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop4"); + stop5_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop5"); + stop6_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop6"); + stop7_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop7"); + + stop_color0_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color0"); + stop_color1_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color1"); + stop_color2_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color2"); + stop_color3_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color3"); + stop_color4_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color4"); + stop_color5_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color5"); + stop_color6_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color6"); + stop_color7_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color7"); + } else { + stops_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stops"); + stop_colors_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_colors"); + } + + dispatch->glUseProgram(gradient_prog); + + dispatch->glUniform1i(repeat_type_uniform_location, src_picture->repeatType); + + + if (src_picture->transform) { + _glamor_gradient_convert_trans_matrix(src_picture->transform, + transform_mat, + width, height, 0); + dispatch->glUniformMatrix3fv(transform_mat_uniform_location, + 1, 1, &transform_mat[0][0]); + } else { + dispatch->glUniformMatrix3fv(transform_mat_uniform_location, + 1, 1, &identity_mat[0][0]); + } + + if (!_glamor_gradient_set_pixmap_destination(screen, glamor_priv, dst_picture, + &xscale, &yscale, x_source, y_source, + vertices, tex_vertices, 0)) + goto GRADIENT_FAIL; + + /* Set all the stops and colors to shader. */ + if (stops_count > RADIAL_SMALL_STOPS) { + stop_colors = malloc(4 * stops_count * sizeof(float)); + if (stop_colors == NULL) { + ErrorF("Failed to allocate stop_colors memory.\n"); + goto GRADIENT_FAIL; + } + + n_stops = malloc(stops_count * sizeof(float)); + if (n_stops == NULL) { + ErrorF("Failed to allocate n_stops memory.\n"); + goto GRADIENT_FAIL; + } + } else { + stop_colors = stop_colors_st; + n_stops = n_stops_st; + } + + count = _glamor_gradient_set_stops(src_picture, &src_picture->pSourcePict->gradient, + stop_colors, n_stops); + + if (src_picture->pSourcePict->linear.nstops + 2 <= RADIAL_SMALL_STOPS) { + int j = 0; + dispatch->glUniform4f(stop_color0_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color1_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color2_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color3_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color4_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color5_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color6_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color7_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + + j = 0; + dispatch->glUniform1f(stop0_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop1_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop2_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop3_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop4_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop5_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop6_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop7_uniform_location, n_stops[j++]); + dispatch->glUniform1i(n_stop_uniform_location, count); + } else { + dispatch->glUniform4fv(stop_colors_uniform_location, count, stop_colors); + dispatch->glUniform1fv(stops_uniform_location, count, n_stops); + dispatch->glUniform1i(n_stop_uniform_location, count); + } + + c1x = (float)pixman_fixed_to_double(src_picture->pSourcePict->radial.c1.x); + c1y = (float)pixman_fixed_to_double(src_picture->pSourcePict->radial.c1.y); + c2x = (float)pixman_fixed_to_double(src_picture->pSourcePict->radial.c2.x); + c2y = (float)pixman_fixed_to_double(src_picture->pSourcePict->radial.c2.y); + + r1 = (float)pixman_fixed_to_double(src_picture->pSourcePict->radial.c1.radius); + r2 = (float)pixman_fixed_to_double(src_picture->pSourcePict->radial.c2.radius); + + glamor_set_circle_centre(width, height, c1x, c1y, glamor_priv->yInverted, cxy); + dispatch->glUniform2fv(c1_uniform_location, 1, cxy); + dispatch->glUniform1f(r1_uniform_location, r1); + + glamor_set_circle_centre(width, height, c2x, c2y, glamor_priv->yInverted, cxy); + dispatch->glUniform2fv(c2_uniform_location, 1, cxy); + dispatch->glUniform1f(r2_uniform_location, r2); + + A_value = (c2x - c1x) * (c2x - c1x) + (c2y - c1y) * (c2y - c1y) - (r2 - r1) * (r2 - r1); + dispatch->glUniform1f(A_value_uniform_location, A_value); + + DEBUGF("C1:(%f, %f) R1:%f\nC2:(%f, %f) R2:%f\nA = %f\n", + c1x, c1y, r1, c2x, c2y, r2, A_value); + + /* Now rendering. */ + dispatch->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + /* Do the clear logic.*/ + if (stops_count > RADIAL_SMALL_STOPS) { + free(n_stops); + free(stop_colors); + } + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glUseProgram(0); + + glamor_put_dispatch(glamor_priv); + return dst_picture; + +GRADIENT_FAIL: + if (dst_picture) { + FreePicture(dst_picture, 0); + } + + if (stops_count > RADIAL_SMALL_STOPS) { + if (n_stops) + free(n_stops); + if (stop_colors) + free(stop_colors); + } + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glUseProgram(0); + glamor_put_dispatch(glamor_priv); + return NULL; +} + +PicturePtr +glamor_generate_linear_gradient_picture(ScreenPtr screen, + PicturePtr src_picture, + int x_source, int y_source, + int width, int height, + PictFormatShort format) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + PicturePtr dst_picture = NULL; + PixmapPtr pixmap = NULL; + GLint gradient_prog = 0; + int error; + float pt_distance; + float p1_distance; + GLfloat cos_val; + float tex_vertices[8]; + int stops_count = 0; + GLfloat *stop_colors = NULL; + GLfloat *n_stops = NULL; + int count = 0; + float slope; + GLfloat xscale, yscale; + GLfloat pt1[2], pt2[2]; + float vertices[8]; + float transform_mat[3][3]; + static const float identity_mat[3][3] = {{1.0, 0.0, 0.0}, + {0.0, 1.0, 0.0}, + {0.0, 0.0, 1.0}}; + GLfloat stop_colors_st[LINEAR_SMALL_STOPS*4]; + GLfloat n_stops_st[LINEAR_SMALL_STOPS]; + + GLint transform_mat_uniform_location = 0; + GLint n_stop_uniform_location = 0; + GLint stops_uniform_location = 0; + GLint stop0_uniform_location = 0; + GLint stop1_uniform_location = 0; + GLint stop2_uniform_location = 0; + GLint stop3_uniform_location = 0; + GLint stop4_uniform_location = 0; + GLint stop5_uniform_location = 0; + GLint stop6_uniform_location = 0; + GLint stop7_uniform_location = 0; + GLint stop_colors_uniform_location = 0; + GLint stop_color0_uniform_location = 0; + GLint stop_color1_uniform_location = 0; + GLint stop_color2_uniform_location = 0; + GLint stop_color3_uniform_location = 0; + GLint stop_color4_uniform_location = 0; + GLint stop_color5_uniform_location = 0; + GLint stop_color6_uniform_location = 0; + GLint stop_color7_uniform_location = 0; + GLint pt_slope_uniform_location = 0; + GLint repeat_type_uniform_location = 0; + GLint hor_ver_uniform_location = 0; + GLint cos_val_uniform_location = 0; + GLint p1_distance_uniform_location = 0; + GLint pt_distance_uniform_location = 0; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + + /* Create a pixmap with VBO. */ + pixmap = glamor_create_pixmap(screen, + width, height, + PIXMAN_FORMAT_DEPTH(format), + 0); + + if (!pixmap) + goto GRADIENT_FAIL; + + dst_picture = CreatePicture(0, &pixmap->drawable, + PictureMatchFormat(screen, + PIXMAN_FORMAT_DEPTH(format), format), + 0, 0, serverClient, &error); + + /* Release the reference, picture will hold the last one. */ + glamor_destroy_pixmap(pixmap); + + if (!dst_picture) + goto GRADIENT_FAIL; + + ValidatePicture(dst_picture); + + stops_count = src_picture->pSourcePict->linear.nstops + 2; + + /* Because the max value of nstops is unkown, so create a program + when nstops > LINEAR_LARGE_STOPS.*/ + if (stops_count <= LINEAR_SMALL_STOPS) { + gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][0]; + } else if (stops_count <= LINEAR_LARGE_STOPS) { + gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][1]; + } else { + _glamor_create_linear_gradient_program(screen, + src_picture->pSourcePict->linear.nstops + 2, 1); + gradient_prog = glamor_priv->gradient_prog[SHADER_GRADIENT_LINEAR][2]; + } + + /* Bind all the uniform vars .*/ + n_stop_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "n_stop"); + pt_slope_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "pt_slope"); + repeat_type_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "repeat_type"); + hor_ver_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "hor_ver"); + transform_mat_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "transform_mat"); + cos_val_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "cos_val"); + p1_distance_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "p1_distance"); + pt_distance_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "pt_distance"); + + if (src_picture->pSourcePict->linear.nstops + 2 <= LINEAR_SMALL_STOPS) { + stop0_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop0"); + stop1_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop1"); + stop2_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop2"); + stop3_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop3"); + stop4_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop4"); + stop5_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop5"); + stop6_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop6"); + stop7_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop7"); + + stop_color0_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color0"); + stop_color1_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color1"); + stop_color2_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color2"); + stop_color3_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color3"); + stop_color4_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color4"); + stop_color5_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color5"); + stop_color6_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color6"); + stop_color7_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_color7"); + } else { + stops_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stops"); + stop_colors_uniform_location = + dispatch->glGetUniformLocation(gradient_prog, "stop_colors"); + } + + dispatch->glUseProgram(gradient_prog); + + dispatch->glUniform1i(repeat_type_uniform_location, src_picture->repeatType); + + /* set the transform matrix. */ + if (src_picture->transform) { + _glamor_gradient_convert_trans_matrix(src_picture->transform, + transform_mat, + width, height, 1); + dispatch->glUniformMatrix3fv(transform_mat_uniform_location, + 1, 1, &transform_mat[0][0]); + } else { + dispatch->glUniformMatrix3fv(transform_mat_uniform_location, + 1, 1, &identity_mat[0][0]); + } + + if (!_glamor_gradient_set_pixmap_destination(screen, glamor_priv, dst_picture, + &xscale, &yscale, x_source, y_source, + vertices, tex_vertices, 1)) + goto GRADIENT_FAIL; + + /* Normalize the PTs. */ + glamor_set_normalize_pt(xscale, yscale, + pixman_fixed_to_double(src_picture->pSourcePict->linear.p1.x), + pixman_fixed_to_double(src_picture->pSourcePict->linear.p1.y), + glamor_priv->yInverted, + pt1); + DEBUGF("pt1:(%f, %f) ---> (%f %f)\n", pixman_fixed_to_double(src_picture->pSourcePict->linear.p1.x), + pixman_fixed_to_double(src_picture->pSourcePict->linear.p1.y), pt1[0], pt1[1]); + + glamor_set_normalize_pt(xscale, yscale, + pixman_fixed_to_double(src_picture->pSourcePict->linear.p2.x), + pixman_fixed_to_double(src_picture->pSourcePict->linear.p2.y), + glamor_priv->yInverted, + pt2); + DEBUGF("pt2:(%f, %f) ---> (%f %f)\n", pixman_fixed_to_double(src_picture->pSourcePict->linear.p2.x), + pixman_fixed_to_double(src_picture->pSourcePict->linear.p2.y), pt2[0], pt2[1]); + + /* Set all the stops and colors to shader. */ + if (stops_count > LINEAR_SMALL_STOPS) { + stop_colors = malloc(4 * stops_count * sizeof(float)); + if (stop_colors == NULL) { + ErrorF("Failed to allocate stop_colors memory.\n"); + goto GRADIENT_FAIL; + } + + n_stops = malloc(stops_count * sizeof(float)); + if (n_stops == NULL) { + ErrorF("Failed to allocate n_stops memory.\n"); + goto GRADIENT_FAIL; + } + } else { + stop_colors = stop_colors_st; + n_stops = n_stops_st; + } + + count = _glamor_gradient_set_stops(src_picture, &src_picture->pSourcePict->gradient, + stop_colors, n_stops); + + if (src_picture->pSourcePict->linear.nstops + 2 <= LINEAR_SMALL_STOPS) { + int j = 0; + dispatch->glUniform4f(stop_color0_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color1_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color2_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color3_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color4_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color5_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color6_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + j++; + dispatch->glUniform4f(stop_color7_uniform_location, + stop_colors[4*j+0], stop_colors[4*j+1], + stop_colors[4*j+2], stop_colors[4*j+3]); + + j = 0; + dispatch->glUniform1f(stop0_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop1_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop2_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop3_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop4_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop5_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop6_uniform_location, n_stops[j++]); + dispatch->glUniform1f(stop7_uniform_location, n_stops[j++]); + + dispatch->glUniform1i(n_stop_uniform_location, count); + } else { + dispatch->glUniform4fv(stop_colors_uniform_location, count, stop_colors); + dispatch->glUniform1fv(stops_uniform_location, count, n_stops); + dispatch->glUniform1i(n_stop_uniform_location, count); + } + + if (src_picture->pSourcePict->linear.p2.y == + src_picture->pSourcePict->linear.p1.y) { // The horizontal case. + dispatch->glUniform1i(hor_ver_uniform_location, 1); + DEBUGF("p1.y: %f, p2.y: %f, enter the horizontal case\n", + pt1[1], pt2[1]); + + p1_distance = pt1[0]; + pt_distance = (pt2[0] - p1_distance); + dispatch->glUniform1f(p1_distance_uniform_location, p1_distance); + dispatch->glUniform1f(pt_distance_uniform_location, pt_distance); + } else { + /* The slope need to compute here. In shader, the viewport set will change + the orginal slope and the slope which is vertical to it will not be correct.*/ + slope = - (float)(src_picture->pSourcePict->linear.p2.x + - src_picture->pSourcePict->linear.p1.x) / + (float)(src_picture->pSourcePict->linear.p2.y + - src_picture->pSourcePict->linear.p1.y); + slope = slope * yscale / xscale; + dispatch->glUniform1f(pt_slope_uniform_location, slope); + dispatch->glUniform1i(hor_ver_uniform_location, 0); + + cos_val = sqrt(1.0 / (slope * slope + 1.0)); + dispatch->glUniform1f(cos_val_uniform_location, cos_val); + + p1_distance = (pt1[1] - pt1[0] * slope) * cos_val; + pt_distance = (pt2[1] - pt2[0] * slope) * cos_val - p1_distance; + dispatch->glUniform1f(p1_distance_uniform_location, p1_distance); + dispatch->glUniform1f(pt_distance_uniform_location, pt_distance); + } + + /* Now rendering. */ + dispatch->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + /* Do the clear logic.*/ + if (stops_count > LINEAR_SMALL_STOPS) { + free(n_stops); + free(stop_colors); + } + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glUseProgram(0); + + glamor_put_dispatch(glamor_priv); + return dst_picture; + +GRADIENT_FAIL: + if (dst_picture) { + FreePicture(dst_picture, 0); + } + + if (stops_count > LINEAR_SMALL_STOPS) { + if (n_stops) + free(n_stops); + if (stop_colors) + free(stop_colors); + } + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glUseProgram(0); + glamor_put_dispatch(glamor_priv); + return NULL; +} + +#endif /* End of GLAMOR_GRADIENT_SHADER */ + +#endif /* End of RENDER */ diff --git a/xorg-server/glamor/glamor_largepixmap.c b/xorg-server/glamor/glamor_largepixmap.c new file mode 100644 index 000000000..91ee8f2df --- /dev/null +++ b/xorg-server/glamor/glamor_largepixmap.c @@ -0,0 +1,1324 @@ +#include <stdlib.h> + +#include "glamor_priv.h" + +/** + * Clip the boxes regards to each pixmap's block array. + * + * Should translate the region to relative coords to the pixmap, + * start at (0,0). + */ +#if 0 +//#define DEBUGF(str, ...) do {} while(0) +#define DEBUGF(str, ...) ErrorF(str, ##__VA_ARGS__) +//#define DEBUGRegionPrint(x) do {} while (0) +#define DEBUGRegionPrint RegionPrint +#endif + +static glamor_pixmap_clipped_regions * +__glamor_compute_clipped_regions(int block_w, + int block_h, + int block_stride, + int x, int y, + int w, int h, + RegionPtr region, + int *n_region, + int reverse, + int upsidedown) +{ + glamor_pixmap_clipped_regions * clipped_regions; + BoxPtr extent; + int start_x, start_y, end_x, end_y; + int start_block_x, start_block_y; + int end_block_x, end_block_y; + int loop_start_block_x, loop_start_block_y; + int loop_end_block_x, loop_end_block_y; + int loop_block_stride; + int i, j, delta_i, delta_j; + RegionRec temp_region; + RegionPtr current_region; + int block_idx; + int k = 0; + int temp_block_idx; + + extent = RegionExtents(region); + start_x = MAX(x, extent->x1); + start_y = MAX(y, extent->y1); + end_x = MIN(x + w, extent->x2); + end_y = MIN(y + h, extent->y2); + + DEBUGF("start compute clipped regions:\n"); + DEBUGF("block w %d h %d x %d y %d w %d h %d, block_stride %d \n", + block_w, block_h, x, y, w, h, block_stride); + DEBUGRegionPrint(region); + + DEBUGF("start_x %d start_y %d end_x %d end_y %d \n", start_x, start_y, end_x, end_y); + + if (start_x >= end_x || start_y >= end_y) { + *n_region = 0; + return NULL; + } + + start_block_x = (start_x - x)/ block_w; + start_block_y = (start_y - y)/ block_h; + end_block_x = (end_x - x)/ block_w; + end_block_y = (end_y - y)/ block_h; + + clipped_regions = calloc((end_block_x - start_block_x + 1) + * (end_block_y - start_block_y + 1), + sizeof(*clipped_regions)); + + + DEBUGF("startx %d starty %d endx %d endy %d \n", + start_x, start_y, end_x, end_y); + DEBUGF("start_block_x %d end_block_x %d \n", start_block_x, end_block_x); + DEBUGF("start_block_y %d end_block_y %d \n", start_block_y, end_block_y); + + if (!reverse) { + loop_start_block_x = start_block_x; + loop_end_block_x = end_block_x + 1; + delta_i = 1; + } else { + loop_start_block_x = end_block_x; + loop_end_block_x = start_block_x - 1; + delta_i = -1; + } + + if (!upsidedown) { + loop_start_block_y = start_block_y; + loop_end_block_y = end_block_y + 1; + delta_j = 1; + } else { + loop_start_block_y = end_block_y; + loop_end_block_y = start_block_y - 1; + delta_j = -1; + } + + loop_block_stride = delta_j * block_stride; + block_idx = (loop_start_block_y - delta_j) * block_stride; + + for(j = loop_start_block_y; j != loop_end_block_y; j += delta_j) + { + block_idx += loop_block_stride; + temp_block_idx = block_idx + loop_start_block_x; + for(i = loop_start_block_x; + i != loop_end_block_x; i += delta_i, temp_block_idx += delta_i) + { + BoxRec temp_box; + temp_box.x1 = x + i * block_w; + temp_box.y1 = y + j * block_h; + temp_box.x2 = MIN(temp_box.x1 + block_w, end_x); + temp_box.y2 = MIN(temp_box.y1 + block_h, end_y); + RegionInitBoxes(&temp_region, &temp_box, 1); + DEBUGF("block idx %d \n",temp_block_idx); + DEBUGRegionPrint(&temp_region); + current_region = RegionCreate(NULL, 4); + RegionIntersect(current_region, &temp_region, region); + DEBUGF("i %d j %d region: \n",i ,j); + DEBUGRegionPrint(current_region); + if (RegionNumRects(current_region)) { + clipped_regions[k].region = current_region; + clipped_regions[k].block_idx = temp_block_idx; + k++; + } else + RegionDestroy(current_region); + RegionUninit(&temp_region); + } + } + + *n_region = k; + return clipped_regions; +} + +/** + * Do a two round clipping, + * first is to clip the region regard to current pixmap's + * block array. Then for each clipped region, do a inner + * block clipping. This is to make sure the final result + * will be shapped by inner_block_w and inner_block_h, and + * the final region also will not cross the pixmap's block + * boundary. + * + * This is mainly used by transformation support when do + * compositing. + */ + +glamor_pixmap_clipped_regions * +glamor_compute_clipped_regions_ext(glamor_pixmap_private *pixmap_priv, + RegionPtr region, + int *n_region, + int inner_block_w, int inner_block_h, + int reverse, int upsidedown) +{ + glamor_pixmap_clipped_regions * clipped_regions, *inner_regions, *result_regions; + int i, j, x, y, k, inner_n_regions; + int width, height; + glamor_pixmap_private_large_t *priv; + priv = &pixmap_priv->large; + + DEBUGF("ext called \n"); + + if (pixmap_priv->type != GLAMOR_TEXTURE_LARGE) { + clipped_regions = calloc(1, sizeof(*clipped_regions)); + if (clipped_regions == NULL) { + *n_region = 0; + return NULL; + } + clipped_regions[0].region = RegionCreate(NULL, 1); + clipped_regions[0].block_idx = 0; + RegionCopy(clipped_regions[0].region, region); + *n_region = 1; + priv->block_w = priv->base.pixmap->drawable.width; + priv->block_h = priv->base.pixmap->drawable.height; + priv->box_array = &priv->box; + priv->box.x1 = priv->box.y1 = 0; + priv->box.x2 = priv->block_w; + priv->box.y2 = priv->block_h; + } else { + clipped_regions = __glamor_compute_clipped_regions(priv->block_w, + priv->block_h, + priv->block_wcnt, + 0, 0, + priv->base.pixmap->drawable.width, + priv->base.pixmap->drawable.height, + region, n_region, reverse, upsidedown + ); + + if (clipped_regions == NULL) { + *n_region = 0; + return NULL; + } + } + if (inner_block_w >= priv->block_w + && inner_block_h >= priv->block_h) + return clipped_regions; + result_regions = calloc(*n_region + * ((priv->block_w + inner_block_w - 1)/inner_block_w) + * ((priv->block_h + inner_block_h - 1)/ inner_block_h), + sizeof(*result_regions)); + k = 0; + for(i = 0; i < *n_region; i++) + { + x = priv->box_array[clipped_regions[i].block_idx].x1; + y = priv->box_array[clipped_regions[i].block_idx].y1; + width = priv->box_array[clipped_regions[i].block_idx].x2 - x; + height = priv->box_array[clipped_regions[i].block_idx].y2 - y; + inner_regions = __glamor_compute_clipped_regions(inner_block_w, + inner_block_h, + 0, x, y, + width, + height, + clipped_regions[i].region, + &inner_n_regions, reverse, upsidedown); + for(j = 0; j < inner_n_regions; j++) + { + result_regions[k].region = inner_regions[j].region; + result_regions[k].block_idx = clipped_regions[i].block_idx; + k++; + } + free(inner_regions); + } + *n_region = k; + free(clipped_regions); + return result_regions; +} + +/* + * + * For the repeat pad mode, we can simply convert the region and + * let the out-of-box region can cover the needed edge of the source/mask + * Then apply a normal clip we can get what we want. + */ +static RegionPtr +_glamor_convert_pad_region(RegionPtr region, int w, int h) +{ + RegionPtr pad_region; + int nrect; + BoxPtr box; + int overlap; + + nrect = RegionNumRects(region); + box = RegionRects(region); + pad_region = RegionCreate(NULL, 4); + if (pad_region == NULL) + return NULL; + while(nrect--) { + BoxRec pad_box; + RegionRec temp_region; + pad_box = *box; + if (pad_box.x1 < 0 && pad_box.x2 <= 0) + pad_box.x2 = 1; + else if (pad_box.x1 >= w && pad_box.x2 > w) + pad_box.x1 = w - 1; + if (pad_box.y1 < 0 && pad_box.y2 <=0) + pad_box.y2 = 1; + else if (pad_box.y1 >= h && pad_box.y2 > h) + pad_box.y1 = h - 1; + RegionInitBoxes(&temp_region, &pad_box, 1); + RegionAppend(pad_region, &temp_region); + RegionUninit(&temp_region); + box++; + } + RegionValidate(pad_region, &overlap); + return pad_region; +} + +/* + * For one type of large pixmap, its one direction is not exceed the + * size limitation, and in another word, on one direction it has only + * one block. + * + * This case of reflect repeating, we can optimize it and avoid repeat + * clip on that direction. We can just enlarge the repeat box and can + * cover all the dest region on that direction. But latter, we need to + * fixup the clipped result to get a correct coords for the subsequent + * processing. This function is to do the coords correction. + * + * */ +static void +_glamor_largepixmap_reflect_fixup(short *xy1, short *xy2, int wh) +{ + int odd1, odd2; + int c1, c2; + + if (*xy2 - *xy1 > wh) { + *xy1 = 0; + *xy2 = wh; + return; + } + modulus(*xy1, wh, c1); + odd1 = ((*xy1 - c1) / wh) & 0x1; + modulus(*xy2, wh, c2); + odd2 = ((*xy2 - c2) / wh) & 0x1; + + if (odd1 && odd2) { + *xy1 = wh - c2; + *xy2 = wh - c1; + } else if (odd1 && !odd2) { + *xy1 = 0; + *xy2 = MAX(c2, wh - c1); + } else if (!odd1 && odd2) { + *xy2 = wh; + *xy1 = MIN(c1, wh - c2); + } else { + *xy1 = c1; + *xy2 = c2; + } +} + +/** + * Clip the boxes regards to each pixmap's block array. + * + * Should translate the region to relative coords to the pixmap, + * start at (0,0). + * + * @is_transform: if it is set, it has a transform matrix. + * + */ +static glamor_pixmap_clipped_regions * +_glamor_compute_clipped_regions(glamor_pixmap_private *pixmap_priv, + RegionPtr region, int *n_region, + int repeat_type, int is_transform, + int reverse, int upsidedown) +{ + glamor_pixmap_clipped_regions * clipped_regions; + BoxPtr extent; + int i, j; + RegionPtr current_region; + int pixmap_width, pixmap_height; + int m; + BoxRec repeat_box; + RegionRec repeat_region; + int right_shift = 0; + int down_shift = 0; + int x_center_shift = 0, y_center_shift = 0; + glamor_pixmap_private_large_t *priv; + priv = &pixmap_priv->large; + + DEBUGRegionPrint(region); + if (pixmap_priv->type != GLAMOR_TEXTURE_LARGE) { + clipped_regions = calloc(1, sizeof(*clipped_regions)); + clipped_regions[0].region = RegionCreate(NULL, 1); + clipped_regions[0].block_idx = 0; + RegionCopy(clipped_regions[0].region, region); + *n_region = 1; + return clipped_regions; + } + + pixmap_width = priv->base.pixmap->drawable.width; + pixmap_height = priv->base.pixmap->drawable.height; + if (repeat_type == 0 || repeat_type == RepeatPad) { + RegionPtr saved_region = NULL; + if (repeat_type == RepeatPad) { + saved_region = region; + region = _glamor_convert_pad_region(saved_region, pixmap_width, pixmap_height); + if (region == NULL) { + *n_region = 0; + return NULL; + } + } + clipped_regions = __glamor_compute_clipped_regions(priv->block_w, + priv->block_h, + priv->block_wcnt, + 0, 0, + priv->base.pixmap->drawable.width, + priv->base.pixmap->drawable.height, + region, n_region, reverse, upsidedown + ); + if (saved_region) + RegionDestroy(region); + return clipped_regions; + } + extent = RegionExtents(region); + + x_center_shift = extent->x1 / pixmap_width; + if (x_center_shift < 0) + x_center_shift--; + if (abs(x_center_shift) & 1) + x_center_shift++; + y_center_shift = extent->y1 / pixmap_height; + if (y_center_shift < 0) + y_center_shift--; + if (abs(y_center_shift) & 1) + y_center_shift++; + + if (extent->x1 < 0) + right_shift = ((-extent->x1 + pixmap_width - 1) / pixmap_width ); + if (extent->y1 < 0) + down_shift = ((-extent->y1 + pixmap_height - 1) / pixmap_height ); + + if (right_shift != 0 || down_shift != 0) { + if (repeat_type == RepeatReflect) { + right_shift = (right_shift + 1)&~1; + down_shift = (down_shift + 1)&~1; + } + RegionTranslate(region, right_shift * pixmap_width, down_shift * pixmap_height); + } + + extent = RegionExtents(region); + /* Tile a large pixmap to another large pixmap. + * We can't use the target large pixmap as the + * loop variable, instead we need to loop for all + * the blocks in the tile pixmap. + * + * simulate repeat each single block to cover the + * target's blocks. Two special case: + * a block_wcnt == 1 or block_hcnt ==1, then we + * only need to loop one direction as the other + * direction is fully included in the first block. + * + * For the other cases, just need to start + * from a proper shiftx/shifty, and then increase + * y by tile_height each time to walk trhough the + * target block and then walk trhough the target + * at x direction by increate tile_width each time. + * + * This way, we can consolidate all the sub blocks + * of the target boxes into one tile source's block. + * + * */ + m = 0; + clipped_regions = calloc(priv->block_wcnt * priv->block_hcnt, + sizeof(*clipped_regions)); + if (clipped_regions == NULL) { + *n_region = 0; + return NULL; + } + if (right_shift != 0 || down_shift != 0) { + DEBUGF("region to be repeated shifted \n"); + DEBUGRegionPrint(region); + } + DEBUGF("repeat pixmap width %d height %d \n", pixmap_width, pixmap_height); + DEBUGF("extent x1 %d y1 %d x2 %d y2 %d \n", extent->x1, extent->y1, extent->x2, extent->y2); + for(j = 0; j < priv->block_hcnt; j++) + { + for(i = 0; i < priv->block_wcnt; i++) + { + int dx = pixmap_width; + int dy = pixmap_height; + int idx; + int shift_x; + int shift_y; + int saved_y1, saved_y2; + int x_idx = 0, y_idx = 0, saved_y_idx = 0; + RegionRec temp_region; + BoxRec reflect_repeat_box; + BoxPtr valid_repeat_box; + + shift_x = (extent->x1 / pixmap_width) * pixmap_width; + shift_y = (extent->y1 / pixmap_height) * pixmap_height; + idx = j * priv->block_wcnt + i; + if (repeat_type == RepeatReflect) { + x_idx = (extent->x1 / pixmap_width); + y_idx = (extent->y1 / pixmap_height); + } + + /* Construct a rect to clip the target region. */ + repeat_box.x1 = shift_x + priv->box_array[idx].x1; + repeat_box.y1 = shift_y + priv->box_array[idx].y1; + if (priv->block_wcnt == 1) { + repeat_box.x2 = extent->x2; + dx = extent->x2 - repeat_box.x1; + } else + repeat_box.x2 = shift_x + priv->box_array[idx].x2; + if (priv->block_hcnt == 1) { + repeat_box.y2 = extent->y2; + dy = extent->y2 - repeat_box.y1; + } else + repeat_box.y2 = shift_y + priv->box_array[idx].y2; + + current_region = RegionCreate(NULL, 4); + RegionInit(&temp_region, NULL, 4); + DEBUGF("init repeat box %d %d %d %d \n", + repeat_box.x1, repeat_box.y1, repeat_box.x2, repeat_box.y2); + + if (repeat_type == RepeatNormal) { + saved_y1 = repeat_box.y1; + saved_y2 = repeat_box.y2; + for(; repeat_box.x1 < extent->x2; + repeat_box.x1 += dx, repeat_box.x2 += dx) + { + repeat_box.y1 = saved_y1; + repeat_box.y2 = saved_y2; + for( repeat_box.y1 = saved_y1, repeat_box.y2 = saved_y2; + repeat_box.y1 < extent->y2; + repeat_box.y1 += dy, repeat_box.y2 += dy) + { + + RegionInitBoxes(&repeat_region, &repeat_box, 1); + DEBUGF("Start to clip repeat region: \n"); + DEBUGRegionPrint(&repeat_region); + RegionIntersect(&temp_region, &repeat_region, region); + DEBUGF("clip result:\n"); + DEBUGRegionPrint(&temp_region); + RegionAppend(current_region, &temp_region); + RegionUninit(&repeat_region); + } + } + } else if (repeat_type == RepeatReflect) { + saved_y1 = repeat_box.y1; + saved_y2 = repeat_box.y2; + saved_y_idx = y_idx; + for(; ; repeat_box.x1 += dx, repeat_box.x2 += dx) + { + repeat_box.y1 = saved_y1; + repeat_box.y2 = saved_y2; + y_idx = saved_y_idx; + reflect_repeat_box.x1 = (x_idx & 1) ? + ((2 * x_idx + 1) * dx - repeat_box.x2) : repeat_box.x1; + reflect_repeat_box.x2 = (x_idx & 1) ? + ((2 * x_idx + 1) * dx - repeat_box.x1) : repeat_box.x2; + valid_repeat_box = &reflect_repeat_box; + + if (valid_repeat_box->x1 >= extent->x2) + break; + for( repeat_box.y1 = saved_y1, repeat_box.y2 = saved_y2; + ; + repeat_box.y1 += dy, repeat_box.y2 += dy) + { + + DEBUGF("x_idx %d y_idx %d dx %d dy %d\n", x_idx, y_idx, dx, dy); + DEBUGF("repeat box %d %d %d %d \n", + repeat_box.x1, repeat_box.y1, repeat_box.x2, repeat_box.y2); + + if (priv->block_hcnt > 1) { + reflect_repeat_box.y1 = (y_idx & 1) ? + ((2 * y_idx + 1) * dy - repeat_box.y2) : repeat_box.y1; + reflect_repeat_box.y2 = (y_idx & 1) ? + ((2 * y_idx + 1) * dy - repeat_box.y1) : repeat_box.y2; + } else { + reflect_repeat_box.y1 = repeat_box.y1; + reflect_repeat_box.y2 = repeat_box.y2; + } + + DEBUGF("valid_repeat_box x1 %d y1 %d \n", + valid_repeat_box->x1, valid_repeat_box->y1); + if (valid_repeat_box->y1 >= extent->y2) + break; + RegionInitBoxes(&repeat_region, valid_repeat_box, 1); + DEBUGF("start to clip repeat[reflect] region: \n"); + DEBUGRegionPrint(&repeat_region); + RegionIntersect(&temp_region, &repeat_region, region); + DEBUGF("result:\n"); + DEBUGRegionPrint(&temp_region); + if (is_transform && RegionNumRects(&temp_region)) { + BoxRec temp_box; + BoxPtr temp_extent; + temp_extent = RegionExtents(&temp_region); + if (priv->block_wcnt > 1) { + if (x_idx & 1) { + temp_box.x1 = ((2 * x_idx + 1)*dx - temp_extent->x2); + temp_box.x2 = ((2 * x_idx + 1)*dx - temp_extent->x1); + } else { + temp_box.x1 = temp_extent->x1; + temp_box.x2 = temp_extent->x2; + } + modulus(temp_box.x1, pixmap_width, temp_box.x1); + modulus(temp_box.x2, pixmap_width, temp_box.x2); + if (temp_box.x2 == 0) temp_box.x2 = pixmap_width; + } else { + temp_box.x1 = temp_extent->x1; + temp_box.x2 = temp_extent->x2; + _glamor_largepixmap_reflect_fixup(&temp_box.x1, &temp_box.x2, pixmap_width); + } + + if (priv->block_hcnt > 1) { + if (y_idx & 1) { + temp_box.y1 = ((2 * y_idx + 1)*dy - temp_extent->y2); + temp_box.y2 = ((2 * y_idx + 1)*dy - temp_extent->y1); + } else { + temp_box.y1 = temp_extent->y1; + temp_box.y2 = temp_extent->y2; + } + + modulus(temp_box.y1, pixmap_height, temp_box.y1); + modulus(temp_box.y2, pixmap_height, temp_box.y2); + if (temp_box.y2 == 0) temp_box.y2 = pixmap_height; + } else { + temp_box.y1 = temp_extent->y1; + temp_box.y2 = temp_extent->y2; + _glamor_largepixmap_reflect_fixup(&temp_box.y1, &temp_box.y2, pixmap_height); + } + + RegionInitBoxes(&temp_region, &temp_box, 1); + RegionTranslate(&temp_region, x_center_shift * pixmap_width, y_center_shift * pixmap_height); + DEBUGF("for transform result:\n"); + DEBUGRegionPrint(&temp_region); + } + RegionAppend(current_region, &temp_region); + RegionUninit(&repeat_region); + y_idx++; + } + x_idx++; + } + } + DEBUGF("dx %d dy %d \n", dx, dy); + + if (RegionNumRects(current_region)) { + + if ((right_shift != 0 || down_shift != 0) && !(is_transform && repeat_type == RepeatReflect)) + RegionTranslate(current_region, + -right_shift * pixmap_width, + -down_shift * pixmap_height); + clipped_regions[m].region = current_region; + clipped_regions[m].block_idx = idx; + m++; + } else + RegionDestroy(current_region); + RegionUninit(&temp_region); + } + } + + if (right_shift != 0 || down_shift != 0) + RegionTranslate(region, -right_shift * pixmap_width, -down_shift * pixmap_height); + *n_region = m; + + return clipped_regions; +} + +glamor_pixmap_clipped_regions * +glamor_compute_clipped_regions(glamor_pixmap_private *priv, RegionPtr region, + int *n_region, int repeat_type, + int reverse, int upsidedown) +{ + return _glamor_compute_clipped_regions(priv, region, n_region, repeat_type, 0, reverse, upsidedown); +} + +/* XXX overflow still exist. maybe we need to change to use region32. + * by default. Or just use region32 for repeat cases? + **/ +glamor_pixmap_clipped_regions * +glamor_compute_transform_clipped_regions(glamor_pixmap_private *priv, struct pixman_transform *transform, + RegionPtr region, int *n_region, int dx, int dy, int repeat_type, + int reverse, int upsidedown) +{ + BoxPtr temp_extent; + struct pixman_box32 temp_box; + struct pixman_box16 short_box; + RegionPtr temp_region; + glamor_pixmap_clipped_regions *ret; + + temp_region = RegionCreate(NULL, 4); + temp_extent = RegionExtents(region); + DEBUGF("dest region \n"); + DEBUGRegionPrint(region); + /* dx/dy may exceed MAX SHORT. we have to use + * a box32 to represent it.*/ + temp_box.x1 = temp_extent->x1 + dx; + temp_box.x2 = temp_extent->x2 + dx; + temp_box.y1 = temp_extent->y1 + dy; + temp_box.y2 = temp_extent->y2 + dy; + + DEBUGF("source box %d %d %d %d \n", temp_box.x1, temp_box.y1, temp_box.x2, temp_box.y2); + if (transform) + glamor_get_transform_extent_from_box(&temp_box, transform); + if (repeat_type == RepeatNone) { + if (temp_box.x1 < 0) temp_box.x1 = 0; + if (temp_box.y1 < 0) temp_box.y1 = 0; + temp_box.x2 = MIN(temp_box.x2, priv->base.pixmap->drawable.width); + temp_box.y2 = MIN(temp_box.y2, priv->base.pixmap->drawable.height); + } + /* Now copy back the box32 to a box16 box. */ + short_box.x1 = temp_box.x1; + short_box.y1 = temp_box.y1; + short_box.x2 = temp_box.x2; + short_box.y2 = temp_box.y2; + RegionInitBoxes(temp_region, &short_box, 1); + DEBUGF("copy to temp source region \n"); + DEBUGRegionPrint(temp_region); + ret = _glamor_compute_clipped_regions(priv, + temp_region, + n_region, + repeat_type, + 1, reverse, + upsidedown); + DEBUGF("n_regions = %d \n", *n_region); + RegionDestroy(temp_region); + + return ret; +} +/* + * As transform and repeatpad mode. + * We may get a clipped result which in multipe regions. + * It's not easy to do a 2nd round clipping just as we do + * without transform/repeatPad. As it's not easy to reverse + * the 2nd round clipping result with a transform/repeatPad mode, + * or even impossible for some transformation. + * + * So we have to merge the fragmental region into one region + * if the clipped result cross the region boundary. + */ +static void +glamor_merge_clipped_regions(glamor_pixmap_private *pixmap_priv, int repeat_type, + glamor_pixmap_clipped_regions *clipped_regions, + int *n_regions, int *need_clean_fbo) +{ + BoxPtr temp_extent; + BoxRec temp_box, copy_box; + RegionPtr temp_region; + glamor_pixmap_private *temp_priv; + PixmapPtr temp_pixmap; + int overlap; + int i; + int pixmap_width, pixmap_height; + glamor_pixmap_private_large_t *priv; + + priv = &pixmap_priv->large; + pixmap_width = priv->base.pixmap->drawable.width; + pixmap_height = priv->base.pixmap->drawable.height; + + temp_region = RegionCreate(NULL, 4); + for(i = 0; i < *n_regions; i++) + { + DEBUGF("Region %d:\n", i); + DEBUGRegionPrint(clipped_regions[i].region); + RegionAppend(temp_region, clipped_regions[i].region); + } + + RegionValidate(temp_region, &overlap); + DEBUGF("temp region: \n"); + DEBUGRegionPrint(temp_region); + temp_extent = RegionExtents(temp_region); + + temp_box = *temp_extent; + + DEBUGF("need copy region: \n"); + DEBUGF("%d %d %d %d \n", temp_box.x1, temp_box.y1, temp_box.x2, temp_box.y2); + temp_pixmap = glamor_create_pixmap(priv->base.pixmap->drawable.pScreen, + temp_box.x2 - temp_box.x1, + temp_box.y2 - temp_box.y1, + priv->base.pixmap->drawable.depth, + GLAMOR_CREATE_PIXMAP_FIXUP); + if (temp_pixmap == NULL) { + assert(0); + return; + } + + temp_priv = glamor_get_pixmap_private(temp_pixmap); + assert(temp_priv->type != GLAMOR_TEXTURE_LARGE); + + priv->box = temp_box; + if (temp_extent->x1 >= 0 && temp_extent->x2 <= pixmap_width + && temp_extent->y1 >= 0 && temp_extent->y2 <= pixmap_height) { + int dx, dy; + copy_box.x1 = 0; + copy_box.y1 = 0; + copy_box.x2 = temp_extent->x2 - temp_extent->x1; + copy_box.y2 = temp_extent->y2 - temp_extent->y1; + dx = temp_extent->x1; + dy = temp_extent->y1; + glamor_copy_n_to_n(&priv->base.pixmap->drawable, + &temp_pixmap->drawable, + NULL, ©_box, 1, dx, + dy, 0, 0, 0, NULL); +// glamor_solid(temp_pixmap, 0, 0, temp_pixmap->drawable.width, +// temp_pixmap->drawable.height, GXcopy, 0xffffffff, 0xff00); + } else { + for (i = 0; i < *n_regions; i++) + { + BoxPtr box; + int nbox; + box = REGION_RECTS(clipped_regions[i].region); + nbox = REGION_NUM_RECTS(clipped_regions[i].region); + while(nbox--) { + int dx, dy, c, d; + DEBUGF("box x1 %d y1 %d x2 %d y2 %d \n", + box->x1, box->y1, box->x2, box->y2); + modulus(box->x1, pixmap_width, c); + dx = c - (box->x1 - temp_box.x1); + copy_box.x1 = box->x1 - temp_box.x1; + copy_box.x2 = box->x2 - temp_box.x1; + + modulus(box->y1, pixmap_height, d); + dy = d - (box->y1 - temp_box.y1); + copy_box.y1 = box->y1 - temp_box.y1; + copy_box.y2 = box->y2 - temp_box.y1; + + DEBUGF("copying box %d %d %d %d, dx %d dy %d\n", + copy_box.x1, copy_box.y1, copy_box.x2, + copy_box.y2, dx, dy); + + glamor_copy_n_to_n(&priv->base.pixmap->drawable, + &temp_pixmap->drawable, + NULL, ©_box, 1, dx, + dy, 0, 0, 0, NULL); + box++; + } + } + //glamor_solid(temp_pixmap, 0, 0, temp_pixmap->drawable.width, + // temp_pixmap->drawable.height, GXcopy, 0xffffffff, 0xff); + } + /* The first region will be released at caller side. */ + for(i = 1; i < *n_regions; i++) + RegionDestroy(clipped_regions[i].region); + RegionDestroy(temp_region); + priv->box = temp_box; + priv->base.fbo = glamor_pixmap_detach_fbo(temp_priv); + DEBUGF("priv box x1 %d y1 %d x2 %d y2 %d \n", + priv->box.x1, priv->box.y1, priv->box.x2, priv->box.y2); + glamor_destroy_pixmap(temp_pixmap); + *need_clean_fbo = 1; + *n_regions = 1; +} + + + +/** + * Given an expected transformed block width and block height, + * + * This function calculate a new block width and height which + * guarantee the transform result will not exceed the given + * block width and height. + * + * For large block width and height (> 2048), we choose a + * smaller new width and height and to reduce the cross region + * boundary and can avoid some overhead. + * + **/ +Bool +glamor_get_transform_block_size(struct pixman_transform *transform, + int block_w, int block_h, + int *transformed_block_w, + int *transformed_block_h) +{ + double a,b,c,d,e,f,g,h; + double scale; + int width, height; + a = pixman_fixed_to_double(transform->matrix[0][0]); + b = pixman_fixed_to_double(transform->matrix[0][1]); + c = pixman_fixed_to_double(transform->matrix[1][0]); + d = pixman_fixed_to_double(transform->matrix[1][1]); + scale = pixman_fixed_to_double(transform->matrix[2][2]); + if (block_w > 2048) { + /* For large block size, we shrink it to smaller box, + * thus latter we may get less cross boundary regions and + * thus can avoid some extra copy. + * + **/ + width = block_w / 4; + height = block_h / 4; + } else { + width = block_w - 2; + height = block_h - 2; + } + e = a + b; + f = c + d; + + g = a - b; + h = c - d; + + e = MIN(block_w, floor(width * scale) / MAX(fabs(e), fabs(g))); + f = MIN(block_h, floor(height * scale) / MAX(fabs(f), fabs(h))); + *transformed_block_w = MIN(e, f) - 1; + *transformed_block_h = *transformed_block_w; + if (*transformed_block_w <= 0 || *transformed_block_h <= 0) + return FALSE; + DEBUGF("original block_w/h %d %d, fixed %d %d \n", block_w, block_h, + *transformed_block_w, *transformed_block_h); + return TRUE; +} + +#define VECTOR_FROM_POINT(p, x, y) \ + p.v[0] = x; \ + p.v[1] = y; \ + p.v[2] = 1.0; +void +glamor_get_transform_extent_from_box(struct pixman_box32 *box, + struct pixman_transform *transform) +{ + struct pixman_f_vector p0, p1, p2, p3; + float min_x, min_y, max_x, max_y; + + struct pixman_f_transform ftransform; + + VECTOR_FROM_POINT(p0, box->x1, box->y1) + VECTOR_FROM_POINT(p1, box->x2, box->y1) + VECTOR_FROM_POINT(p2, box->x2, box->y2) + VECTOR_FROM_POINT(p3, box->x1, box->y2) + + pixman_f_transform_from_pixman_transform(&ftransform, transform); + pixman_f_transform_point(&ftransform, &p0); + pixman_f_transform_point(&ftransform, &p1); + pixman_f_transform_point(&ftransform, &p2); + pixman_f_transform_point(&ftransform, &p3); + + min_x = MIN(p0.v[0], p1.v[0]); + min_x = MIN(min_x, p2.v[0]); + min_x = MIN(min_x, p3.v[0]); + + min_y = MIN(p0.v[1], p1.v[1]); + min_y = MIN(min_y, p2.v[1]); + min_y = MIN(min_y, p3.v[1]); + + max_x = MAX(p0.v[0], p1.v[0]); + max_x = MAX(max_x, p2.v[0]); + max_x = MAX(max_x, p3.v[0]); + + max_y = MAX(p0.v[1], p1.v[1]); + max_y = MAX(max_y, p2.v[1]); + max_y = MAX(max_y, p3.v[1]); + box->x1 = floor(min_x) - 1; + box->y1 = floor(min_y) - 1; + box->x2 = ceil(max_x) + 1; + box->y2 = ceil(max_y) + 1; +} + +static void +_glamor_process_transformed_clipped_region(glamor_pixmap_private *priv, + int repeat_type, + glamor_pixmap_clipped_regions *clipped_regions, + int *n_regions, int *need_clean_fbo) +{ + int shift_x, shift_y; + if (*n_regions != 1) { + /* Merge all source regions into one region. */ + glamor_merge_clipped_regions(priv, repeat_type, + clipped_regions, n_regions, + need_clean_fbo); + } else { + SET_PIXMAP_FBO_CURRENT(priv, + clipped_regions[0].block_idx); + if (repeat_type == RepeatReflect || repeat_type == RepeatNormal) { + /* The required source areas are in one region, + * we need to shift the corresponding box's coords to proper position, + * thus we can calculate the relative coords correctly.*/ + BoxPtr temp_box; + int rem; + temp_box = RegionExtents(clipped_regions[0].region); + modulus(temp_box->x1, priv->base.pixmap->drawable.width, rem); + shift_x = (temp_box->x1 - rem) / priv->base.pixmap->drawable.width; + modulus(temp_box->y1, priv->base.pixmap->drawable.height, rem); + shift_y = (temp_box->y1 - rem) / priv->base.pixmap->drawable.height; + + if (shift_x != 0) { + priv->large.box.x1 += shift_x * priv->base.pixmap->drawable.width; + priv->large.box.x2 += shift_x * priv->base.pixmap->drawable.width; + } + if (shift_y != 0) { + priv->large.box.y1 += shift_y * priv->base.pixmap->drawable.height; + priv->large.box.y2 += shift_y * priv->base.pixmap->drawable.height; + } + } + } +} + + +Bool +glamor_composite_largepixmap_region(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + glamor_pixmap_private * source_pixmap_priv, + glamor_pixmap_private * mask_pixmap_priv, + glamor_pixmap_private * dest_pixmap_priv, + RegionPtr region, Bool force_clip, + INT16 x_source, + INT16 y_source, + INT16 x_mask, + INT16 y_mask, + INT16 x_dest, INT16 y_dest, + CARD16 width, CARD16 height) +{ + glamor_screen_private *glamor_priv; + glamor_pixmap_clipped_regions *clipped_dest_regions; + glamor_pixmap_clipped_regions *clipped_source_regions; + glamor_pixmap_clipped_regions *clipped_mask_regions; + int n_dest_regions; + int n_mask_regions; + int n_source_regions; + int i,j,k; + int need_clean_source_fbo = 0; + int need_clean_mask_fbo = 0; + int is_normal_source_fbo = 0; + int is_normal_mask_fbo = 0; + int fixed_block_width, fixed_block_height; + int null_source, null_mask; + glamor_pixmap_private * need_free_source_pixmap_priv = NULL; + glamor_pixmap_private * need_free_mask_pixmap_priv = NULL; + int source_repeat_type = 0, mask_repeat_type = 0; + int ok = TRUE; + + if (source->repeat) + source_repeat_type = source->repeatType; + else + source_repeat_type = RepeatNone; + + if (mask && mask->repeat) + mask_repeat_type = mask->repeatType; + else + mask_repeat_type = RepeatNone; + + glamor_priv = dest_pixmap_priv->base.glamor_priv; + fixed_block_width = glamor_priv->max_fbo_size; + fixed_block_height = glamor_priv->max_fbo_size; + /* If we got an totally out-of-box region for a source or mask + * region without repeat, we need to set it as null_source and + * give it a solid color (0,0,0,0). */ + null_source = 0; + null_mask = 0; + RegionTranslate(region, -dest->pDrawable->x, + -dest->pDrawable->y); + + /* need to transform the dest region to the correct sourcei/mask region. + * it's a little complex, as one single edge of the + * target region may be transformed to cross a block boundary of the + * source or mask. Then it's impossible to handle it as usual way. + * We may have to split the original dest region to smaller region, and + * make sure each region's transformed region can fit into one texture, + * and then continue this loop again, and each time when a transformed region + * cross the bound, we need to copy it to a single pixmap and do the composition + * with the new pixmap. If the transformed region doesn't cross a source/mask's + * boundary then we don't need to copy. + * + */ + if (source_pixmap_priv + && source->transform + && source_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + int source_transformed_block_width, source_transformed_block_height; + if (!glamor_get_transform_block_size(source->transform, + source_pixmap_priv->large.block_w, + source_pixmap_priv->large.block_h, + &source_transformed_block_width, + &source_transformed_block_height)) { + DEBUGF("source block size less than 1, fallback.\n"); + RegionTranslate(region, dest->pDrawable->x, + dest->pDrawable->y); + return FALSE; + } + fixed_block_width = min(fixed_block_width , source_transformed_block_width); + fixed_block_height = min(fixed_block_height , source_transformed_block_height); + DEBUGF("new source block size %d x %d \n", fixed_block_width, fixed_block_height); + } + + if (mask_pixmap_priv + && mask->transform + && mask_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + int mask_transformed_block_width, mask_transformed_block_height; + if (!glamor_get_transform_block_size(mask->transform, + mask_pixmap_priv->large.block_w, + mask_pixmap_priv->large.block_h, + &mask_transformed_block_width, + &mask_transformed_block_height)) { + DEBUGF("mask block size less than 1, fallback.\n"); + RegionTranslate(region, dest->pDrawable->x, + dest->pDrawable->y); + return FALSE; + } + fixed_block_width = min(fixed_block_width , mask_transformed_block_width); + fixed_block_height = min(fixed_block_height , mask_transformed_block_height); + DEBUGF("new mask block size %d x %d \n", fixed_block_width, fixed_block_height); + } + + /*compute the correct block width and height whose transformed source/mask + *region can fit into one texture.*/ + if (force_clip || fixed_block_width < glamor_priv->max_fbo_size + || fixed_block_height < glamor_priv->max_fbo_size) + clipped_dest_regions = glamor_compute_clipped_regions_ext(dest_pixmap_priv, + region, + &n_dest_regions, + fixed_block_width, + fixed_block_height, + 0, 0); + else + clipped_dest_regions = glamor_compute_clipped_regions(dest_pixmap_priv, + region, + &n_dest_regions, + 0, 0, 0); + DEBUGF("dest clipped result %d region: \n", n_dest_regions); + if (source_pixmap_priv + && (source_pixmap_priv == dest_pixmap_priv || source_pixmap_priv == mask_pixmap_priv) + && source_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + /* XXX self-copy...*/ + need_free_source_pixmap_priv = source_pixmap_priv; + source_pixmap_priv = malloc(sizeof(*source_pixmap_priv)); + *source_pixmap_priv = *need_free_source_pixmap_priv; + need_free_source_pixmap_priv = source_pixmap_priv; + } + assert(mask_pixmap_priv != dest_pixmap_priv); + + for(i = 0; i < n_dest_regions; i++) + { + DEBUGF("dest region %d idx %d\n", i, clipped_dest_regions[i].block_idx); + DEBUGRegionPrint(clipped_dest_regions[i].region); + SET_PIXMAP_FBO_CURRENT(dest_pixmap_priv, clipped_dest_regions[i].block_idx); + if ( source_pixmap_priv && source_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + if (!source->transform && source_repeat_type != RepeatPad) { + RegionTranslate(clipped_dest_regions[i].region, + x_source - x_dest, + y_source - y_dest); + clipped_source_regions = glamor_compute_clipped_regions(source_pixmap_priv, + clipped_dest_regions[i].region, + &n_source_regions, source_repeat_type, + 0, 0); + is_normal_source_fbo = 1; + } + else { + clipped_source_regions = glamor_compute_transform_clipped_regions(source_pixmap_priv, + source->transform, + clipped_dest_regions[i].region, + &n_source_regions, + x_source - x_dest, y_source - y_dest, + source_repeat_type, 0, 0); + is_normal_source_fbo = 0; + if (n_source_regions == 0) { + /* Pad the out-of-box region to (0,0,0,0). */ + null_source = 1; + n_source_regions = 1; + } else + _glamor_process_transformed_clipped_region(source_pixmap_priv, + source_repeat_type, clipped_source_regions, &n_source_regions, + &need_clean_source_fbo); + } + DEBUGF("source clipped result %d region: \n", n_source_regions); + for(j = 0; j < n_source_regions; j++) + { + if (is_normal_source_fbo) + SET_PIXMAP_FBO_CURRENT(source_pixmap_priv, + clipped_source_regions[j].block_idx); + + if (mask_pixmap_priv && mask_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + if (is_normal_mask_fbo && is_normal_source_fbo) { + /* both mask and source are normal fbo box without transform or repeatpad. + * The region is clipped against source and then we clip it against mask here.*/ + DEBUGF("source region %d idx %d\n", j, clipped_source_regions[j].block_idx); + DEBUGRegionPrint(clipped_source_regions[j].region); + RegionTranslate(clipped_source_regions[j].region, + - x_source + x_mask, + - y_source + y_mask); + clipped_mask_regions = glamor_compute_clipped_regions(mask_pixmap_priv, + clipped_source_regions[j].region, + &n_mask_regions, mask_repeat_type, + 0, 0); + is_normal_mask_fbo = 1; + } else if (is_normal_mask_fbo && !is_normal_source_fbo) { + assert(n_source_regions == 1); + /* The source fbo is not a normal fbo box, it has transform or repeatpad. + * the valid clip region should be the clip dest region rather than the + * clip source region.*/ + RegionTranslate(clipped_dest_regions[i].region, + - x_dest + x_mask, + - y_dest + y_mask); + clipped_mask_regions = glamor_compute_clipped_regions(mask_pixmap_priv, + clipped_dest_regions[i].region, + &n_mask_regions, mask_repeat_type, + 0, 0); + is_normal_mask_fbo = 1; + } else { + /* This mask region has transform or repeatpad, we need clip it agains the previous + * valid region rather than the mask region. */ + if (!is_normal_source_fbo) + clipped_mask_regions = glamor_compute_transform_clipped_regions(mask_pixmap_priv, + mask->transform, + clipped_dest_regions[i].region, + &n_mask_regions, + x_mask - x_dest, + y_mask - y_dest, + mask_repeat_type, 0, 0); + else + clipped_mask_regions = glamor_compute_transform_clipped_regions(mask_pixmap_priv, + mask->transform, + clipped_source_regions[j].region, + &n_mask_regions, + x_mask - x_source, y_mask - y_source, + mask_repeat_type, 0, 0); + is_normal_mask_fbo = 0; + if (n_mask_regions == 0) { + /* Pad the out-of-box region to (0,0,0,0). */ + null_mask = 1; + n_mask_regions = 1; + } else + _glamor_process_transformed_clipped_region(mask_pixmap_priv, + mask_repeat_type, clipped_mask_regions, &n_mask_regions, + &need_clean_mask_fbo); + } + DEBUGF("mask clipped result %d region: \n", n_mask_regions); + +#define COMPOSITE_REGION(region) do { \ + if (!glamor_composite_clipped_region(op, \ + null_source ? NULL : source, \ + null_mask ? NULL : mask, dest, \ + null_source ? NULL : source_pixmap_priv, \ + null_mask ? NULL : mask_pixmap_priv, \ + dest_pixmap_priv, region, \ + x_source, y_source, x_mask, y_mask, \ + x_dest, y_dest)) { \ + assert(0); \ + } \ + } while(0) + + for(k = 0; k < n_mask_regions; k++) + { + DEBUGF("mask region %d idx %d\n", k, clipped_mask_regions[k].block_idx); + DEBUGRegionPrint(clipped_mask_regions[k].region); + if (is_normal_mask_fbo) { + SET_PIXMAP_FBO_CURRENT(mask_pixmap_priv, + clipped_mask_regions[k].block_idx); + DEBUGF("mask fbo off %d %d \n", + mask_pixmap_priv->large.box.x1, + mask_pixmap_priv->large.box.y1); + DEBUGF("start composite mask hasn't transform.\n"); + RegionTranslate(clipped_mask_regions[k].region, + x_dest - x_mask + dest->pDrawable->x, + y_dest - y_mask + dest->pDrawable->y); + COMPOSITE_REGION(clipped_mask_regions[k].region); + } else if (!is_normal_mask_fbo && !is_normal_source_fbo) { + DEBUGF("start composite both mask and source have transform.\n"); + RegionTranslate(clipped_dest_regions[i].region, + dest->pDrawable->x, + dest->pDrawable->y); + COMPOSITE_REGION(clipped_dest_regions[i].region); + } else { + DEBUGF("start composite only mask has transform.\n"); + RegionTranslate(clipped_source_regions[j].region, + x_dest - x_source + dest->pDrawable->x, + y_dest - y_source + dest->pDrawable->y); + COMPOSITE_REGION(clipped_source_regions[j].region); + } + RegionDestroy(clipped_mask_regions[k].region); + } + free(clipped_mask_regions); + if (null_mask) null_mask = 0; + if (need_clean_mask_fbo) { + assert(is_normal_mask_fbo == 0); + glamor_destroy_fbo(mask_pixmap_priv->base.fbo); + mask_pixmap_priv->base.fbo = NULL; + need_clean_mask_fbo = 0; + } + } else { + if (is_normal_source_fbo) { + RegionTranslate(clipped_source_regions[j].region, + -x_source + x_dest + dest->pDrawable->x, + -y_source + y_dest + dest->pDrawable->y); + COMPOSITE_REGION(clipped_source_regions[j].region); + } else { + /* Source has transform or repeatPad. dest regions is the right + * region to do the composite. */ + RegionTranslate(clipped_dest_regions[i].region, + dest->pDrawable->x, + dest->pDrawable->y); + COMPOSITE_REGION(clipped_dest_regions[i].region); + } + } + if (clipped_source_regions && clipped_source_regions[j].region) + RegionDestroy(clipped_source_regions[j].region); + } + free(clipped_source_regions); + if (null_source) null_source = 0; + if (need_clean_source_fbo) { + assert(is_normal_source_fbo == 0); + glamor_destroy_fbo(source_pixmap_priv->base.fbo); + source_pixmap_priv->base.fbo = NULL; + need_clean_source_fbo = 0; + } + } + else { + if (mask_pixmap_priv && mask_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + if (!mask->transform && mask_repeat_type != RepeatPad) { + RegionTranslate(clipped_dest_regions[i].region, + x_mask - x_dest, + y_mask - y_dest); + clipped_mask_regions = glamor_compute_clipped_regions(mask_pixmap_priv, + clipped_dest_regions[i].region, + &n_mask_regions, mask_repeat_type, 0, 0); + is_normal_mask_fbo = 1; + } + else { + clipped_mask_regions = glamor_compute_transform_clipped_regions(mask_pixmap_priv, + mask->transform, + clipped_dest_regions[i].region, + &n_mask_regions, + x_mask - x_dest, y_mask - y_dest, + mask_repeat_type, 0, 0); + is_normal_mask_fbo = 0; + if (n_mask_regions == 0) { + /* Pad the out-of-box region to (0,0,0,0). */ + null_mask = 1; + n_mask_regions = 1; + } else + _glamor_process_transformed_clipped_region(mask_pixmap_priv, + mask_repeat_type, clipped_mask_regions, &n_mask_regions, + &need_clean_mask_fbo); + } + + for(k = 0; k < n_mask_regions; k++) + { + DEBUGF("mask region %d idx %d\n", k, clipped_mask_regions[k].block_idx); + DEBUGRegionPrint(clipped_mask_regions[k].region); + if (is_normal_mask_fbo) { + SET_PIXMAP_FBO_CURRENT(mask_pixmap_priv, + clipped_mask_regions[k].block_idx); + RegionTranslate(clipped_mask_regions[k].region, + x_dest - x_mask + dest->pDrawable->x, + y_dest - y_mask + dest->pDrawable->y); + COMPOSITE_REGION(clipped_mask_regions[k].region); + } else { + RegionTranslate(clipped_dest_regions[i].region, + dest->pDrawable->x, + dest->pDrawable->y); + COMPOSITE_REGION(clipped_dest_regions[i].region); + } + RegionDestroy(clipped_mask_regions[k].region); + } + free(clipped_mask_regions); + if (null_mask) null_mask = 0; + if (need_clean_mask_fbo) { + glamor_destroy_fbo(mask_pixmap_priv->base.fbo); + mask_pixmap_priv->base.fbo = NULL; + need_clean_mask_fbo = 0; + } + } + else { + RegionTranslate(clipped_dest_regions[i].region, + dest->pDrawable->x, + dest->pDrawable->y); + COMPOSITE_REGION(clipped_dest_regions[i].region); + } + } + RegionDestroy(clipped_dest_regions[i].region); + } + free(clipped_dest_regions); + free(need_free_source_pixmap_priv); + free(need_free_mask_pixmap_priv); + ok = TRUE; + return ok; +} diff --git a/xorg-server/glamor/glamor_picture.c b/xorg-server/glamor/glamor_picture.c new file mode 100644 index 000000000..7d5ffbb76 --- /dev/null +++ b/xorg-server/glamor/glamor_picture.c @@ -0,0 +1,131 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + * + */ + +#include <stdlib.h> + +#include "glamor_priv.h" +#include "mipict.h" + +/* Upload picture to texture. We may need to flip the y axis or + * wire alpha to 1. So we may conditional create fbo for the picture. + * */ +enum glamor_pixmap_status +glamor_upload_picture_to_texture(PicturePtr picture) +{ + PixmapPtr pixmap; + assert(picture->pDrawable); + pixmap = glamor_get_drawable_pixmap(picture->pDrawable); + + return glamor_upload_pixmap_to_texture(pixmap); +} + + +Bool +glamor_prepare_access_picture(PicturePtr picture, glamor_access_t access) +{ + if (!picture || !picture->pDrawable) + return TRUE; + + return glamor_prepare_access(picture->pDrawable, access); +} + +void +glamor_finish_access_picture(PicturePtr picture, glamor_access_t access) +{ + if (!picture || !picture->pDrawable) + return; + + glamor_finish_access(picture->pDrawable, access); +} + +/* + * We should already have drawable attached to it, if it has one. + * Then set the attached pixmap to is_picture format, and set + * the pict format. + * */ +int +glamor_create_picture(PicturePtr picture) +{ + PixmapPtr pixmap; + glamor_pixmap_private *pixmap_priv; + + if (!picture || !picture->pDrawable) + return 0; + + pixmap = glamor_get_drawable_pixmap(picture->pDrawable); + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (!pixmap_priv) { + /* We must create a pixmap priv to track the picture format even + * if the pixmap is a pure in memory pixmap. The reason is that + * we may need to upload this pixmap to a texture on the fly. During + * the uploading, we need to know the picture format. */ + glamor_set_pixmap_type(pixmap, GLAMOR_MEMORY); + pixmap_priv = glamor_get_pixmap_private(pixmap); + } else { + if (GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) { + /* If the picture format is not compatible with glamor fbo format, + * we have to mark this pixmap as a separated texture, and don't + * fallback to DDX layer. */ + if (pixmap_priv->type == GLAMOR_TEXTURE_DRM + && !glamor_pict_format_is_compatible(picture->format, + pixmap->drawable.depth)) + glamor_set_pixmap_type(pixmap, GLAMOR_SEPARATE_TEXTURE); + } + } + + pixmap_priv->base.is_picture = 1; + pixmap_priv->base.picture = picture; + + return miCreatePicture(picture); +} + +void +glamor_destroy_picture(PicturePtr picture) +{ + PixmapPtr pixmap; + glamor_pixmap_private *pixmap_priv; + + if (!picture || !picture->pDrawable) + return; + + pixmap = glamor_get_drawable_pixmap(picture->pDrawable); + pixmap_priv = glamor_get_pixmap_private(pixmap); + + if (pixmap_priv) { + pixmap_priv->base.is_picture = 0; + pixmap_priv->base.picture = NULL; + } + miDestroyPicture(picture); +} + +void +glamor_picture_format_fixup(PicturePtr picture, + glamor_pixmap_private * pixmap_priv) +{ + pixmap_priv->base.picture = picture; +} diff --git a/xorg-server/glamor/glamor_pixmap.c b/xorg-server/glamor/glamor_pixmap.c new file mode 100644 index 000000000..84694ec3c --- /dev/null +++ b/xorg-server/glamor/glamor_pixmap.c @@ -0,0 +1,1433 @@ +/* + * Copyright © 2001 Keith Packard + * Copyright © 2008 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + +#include <stdlib.h> + +#include "glamor_priv.h" +/** + * Sets the offsets to add to coordinates to make them address the same bits in + * the backing drawable. These coordinates are nonzero only for redirected + * windows. + */ +void +glamor_get_drawable_deltas(DrawablePtr drawable, PixmapPtr pixmap, + int *x, int *y) +{ +#ifdef COMPOSITE + if (drawable->type == DRAWABLE_WINDOW) { + *x = -pixmap->screen_x; + *y = -pixmap->screen_y; + return; + } +#endif + + *x = 0; + *y = 0; +} + + +void +glamor_pixmap_init(ScreenPtr screen) +{ + +} + +void +glamor_pixmap_fini(ScreenPtr screen) +{ +} + +void +glamor_set_destination_pixmap_fbo(glamor_pixmap_fbo * fbo, int x0, int y0, int width, int height) +{ + glamor_gl_dispatch *dispatch = glamor_get_dispatch(fbo->glamor_priv); + dispatch->glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb); +#ifndef GLAMOR_GLES2 + dispatch->glMatrixMode(GL_PROJECTION); + dispatch->glLoadIdentity(); + dispatch->glMatrixMode(GL_MODELVIEW); + dispatch->glLoadIdentity(); +#endif + dispatch->glViewport(x0, y0, + width, height); + + glamor_put_dispatch(fbo->glamor_priv); +} + +void +glamor_set_destination_pixmap_priv_nc(glamor_pixmap_private * pixmap_priv) +{ + int w,h; + + PIXMAP_PRIV_GET_ACTUAL_SIZE(pixmap_priv, w, h); + glamor_set_destination_pixmap_fbo(pixmap_priv->base.fbo, 0, 0, + w, h); +} + +int +glamor_set_destination_pixmap_priv(glamor_pixmap_private * pixmap_priv) +{ + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + return -1; + + glamor_set_destination_pixmap_priv_nc(pixmap_priv); + return 0; +} + +int +glamor_set_destination_pixmap(PixmapPtr pixmap) +{ + int err; + glamor_pixmap_private *pixmap_priv = + glamor_get_pixmap_private(pixmap); + + err = glamor_set_destination_pixmap_priv(pixmap_priv); + return err; +} + +Bool +glamor_set_planemask(PixmapPtr pixmap, unsigned long planemask) +{ + if (glamor_pm_is_solid(&pixmap->drawable, planemask)) { + return GL_TRUE; + } + + glamor_fallback("unsupported planemask %lx\n", planemask); + return GL_FALSE; +} + +Bool +glamor_set_alu(struct glamor_gl_dispatch *dispatch, unsigned char alu) +{ +#ifndef GLAMOR_GLES2 + if (alu == GXcopy) { + dispatch->glDisable(GL_COLOR_LOGIC_OP); + return TRUE; + } + dispatch->glEnable(GL_COLOR_LOGIC_OP); + switch (alu) { + case GXclear: + dispatch->glLogicOp(GL_CLEAR); + break; + case GXand: + dispatch->glLogicOp(GL_AND); + break; + case GXandReverse: + dispatch->glLogicOp(GL_AND_REVERSE); + break; + case GXandInverted: + dispatch->glLogicOp(GL_AND_INVERTED); + break; + case GXnoop: + dispatch->glLogicOp(GL_NOOP); + break; + case GXxor: + dispatch->glLogicOp(GL_XOR); + break; + case GXor: + dispatch->glLogicOp(GL_OR); + break; + case GXnor: + dispatch->glLogicOp(GL_NOR); + break; + case GXequiv: + dispatch->glLogicOp(GL_EQUIV); + break; + case GXinvert: + dispatch->glLogicOp(GL_INVERT); + break; + case GXorReverse: + dispatch->glLogicOp(GL_OR_REVERSE); + break; + case GXcopyInverted: + dispatch->glLogicOp(GL_COPY_INVERTED); + break; + case GXorInverted: + dispatch->glLogicOp(GL_OR_INVERTED); + break; + case GXnand: + dispatch->glLogicOp(GL_NAND); + break; + case GXset: + dispatch->glLogicOp(GL_SET); + break; + default: + glamor_fallback("unsupported alu %x\n", alu); + return FALSE; + } +#else + if (alu != GXcopy) + return FALSE; +#endif + return TRUE; +} + +static void * +_glamor_color_convert_a1_a8(void *src_bits, void *dst_bits, int w, int h, int stride, int revert) +{ + PictFormatShort dst_format, src_format; + pixman_image_t *dst_image; + pixman_image_t *src_image; + int src_stride; + + if (revert == REVERT_UPLOADING_A1) { + src_format = PICT_a1; + dst_format = PICT_a8; + src_stride = PixmapBytePad(w, 1); + } else { + dst_format = PICT_a1; + src_format = PICT_a8; + src_stride = (((w * 8 + 7) / 8) + 3) & ~3; + } + + dst_image = pixman_image_create_bits(dst_format, + w, h, + dst_bits, + stride); + if (dst_image == NULL) { + return NULL; + } + + src_image = pixman_image_create_bits(src_format, + w, h, + src_bits, + src_stride); + + if (src_image == NULL) { + pixman_image_unref(dst_image); + return NULL; + } + + pixman_image_composite(PictOpSrc, src_image, NULL, dst_image, + 0, 0, 0, 0, 0, 0, + w,h); + + pixman_image_unref(src_image); + pixman_image_unref(dst_image); + return dst_bits; +} + +#define ADJUST_BITS(d, src_bits, dst_bits) (((dst_bits) == (src_bits)) ? (d) : \ + (((dst_bits) > (src_bits)) ? \ + (((d) << ((dst_bits) - (src_bits))) \ + + (( 1 << ((dst_bits) - (src_bits))) >> 1)) \ + : ((d) >> ((src_bits) - (dst_bits))))) + +#define GLAMOR_DO_CONVERT(src, dst, no_alpha, swap, \ + a_shift_src, a_bits_src, \ + b_shift_src, b_bits_src, \ + g_shift_src, g_bits_src, \ + r_shift_src, r_bits_src, \ + a_shift, a_bits, \ + b_shift, b_bits, \ + g_shift, g_bits, \ + r_shift, r_bits) \ + { \ + typeof(src) a,b,g,r; \ + typeof(src) a_mask_src, b_mask_src, g_mask_src, r_mask_src;\ + a_mask_src = (((1 << (a_bits_src)) - 1) << a_shift_src);\ + b_mask_src = (((1 << (b_bits_src)) - 1) << b_shift_src);\ + g_mask_src = (((1 << (g_bits_src)) - 1) << g_shift_src);\ + r_mask_src = (((1 << (r_bits_src)) - 1) << r_shift_src);\ + if (no_alpha) \ + a = (a_mask_src) >> (a_shift_src); \ + else \ + a = ((src) & (a_mask_src)) >> (a_shift_src); \ + b = ((src) & (b_mask_src)) >> (b_shift_src); \ + g = ((src) & (g_mask_src)) >> (g_shift_src); \ + r = ((src) & (r_mask_src)) >> (r_shift_src); \ + a = ADJUST_BITS(a, a_bits_src, a_bits); \ + b = ADJUST_BITS(b, b_bits_src, b_bits); \ + g = ADJUST_BITS(g, g_bits_src, g_bits); \ + r = ADJUST_BITS(r, r_bits_src, r_bits); \ + if (swap == 0) \ + (*dst) = ((a) << (a_shift)) | ((b) << (b_shift)) | ((g) << (g_shift)) | ((r) << (r_shift)); \ + else \ + (*dst) = ((a) << (a_shift)) | ((r) << (b_shift)) | ((g) << (g_shift)) | ((b) << (r_shift)); \ + } + +static void * +_glamor_color_revert_x2b10g10r10(void *src_bits, void *dst_bits, int w, int h, int stride, int no_alpha, int revert, int swap_rb) +{ + int x,y; + unsigned int *words, *saved_words, *source_words; + int swap = !(swap_rb == SWAP_NONE_DOWNLOADING || swap_rb == SWAP_NONE_UPLOADING); + + source_words = src_bits; + words = dst_bits; + saved_words = words; + + for (y = 0; y < h; y++) + { + DEBUGF("Line %d : ", y); + for (x = 0; x < w; x++) + { + unsigned int pixel = source_words[x]; + + if (revert == REVERT_DOWNLOADING_2_10_10_10) + GLAMOR_DO_CONVERT(pixel, &words[x], no_alpha, swap, + 24, 8, 16, 8, 8, 8, 0, 8, + 30, 2, 20, 10, 10, 10, 0, 10) + else + GLAMOR_DO_CONVERT(pixel, &words[x], no_alpha, swap, + 30, 2, 20, 10, 10, 10, 0, 10, + 24, 8, 16, 8, 8, 8, 0, 8); + DEBUGF("%x:%x ", pixel, words[x]); + } + DEBUGF("\n"); + words += stride / sizeof(*words); + source_words += stride / sizeof(*words); + } + DEBUGF("\n"); + return saved_words; + +} + +static void * +_glamor_color_revert_x1b5g5r5(void *src_bits, void *dst_bits, int w, int h, int stride, int no_alpha, int revert, int swap_rb) +{ + int x,y; + unsigned short *words, *saved_words, *source_words; + int swap = !(swap_rb == SWAP_NONE_DOWNLOADING || swap_rb == SWAP_NONE_UPLOADING); + + words = dst_bits; + source_words = src_bits; + saved_words = words; + + for (y = 0; y < h; y++) + { + DEBUGF("Line %d : ", y); + for (x = 0; x < w; x++) + { + unsigned short pixel = source_words[x]; + + if (revert == REVERT_DOWNLOADING_1_5_5_5) + GLAMOR_DO_CONVERT(pixel, &words[x], no_alpha, swap, + 0, 1, 1, 5, 6, 5, 11, 5, + 15, 1, 10, 5, 5, 5, 0, 5) + else + GLAMOR_DO_CONVERT(pixel, &words[x], no_alpha, swap, + 15, 1, 10, 5, 5, 5, 0, 5, + 0, 1, 1, 5, 6, 5, 11, 5); + DEBUGF("%04x:%04x ", pixel, words[x]); + } + DEBUGF("\n"); + words += stride / sizeof(*words); + source_words += stride / sizeof(*words); + } + DEBUGF("\n"); + return saved_words; +} + +/* + * This function is to convert an unsupported color format to/from a + * supported GL format. + * Here are the current scenarios: + * + * @no_alpha: + * If it is set, then we need to wire the alpha value to 1. + * @revert: + REVERT_DOWNLOADING_A1 : convert an Alpha8 buffer to a A1 buffer. + REVERT_UPLOADING_A1 : convert an A1 buffer to an Alpha8 buffer + REVERT_DOWNLOADING_2_10_10_10 : convert r10G10b10X2 to X2B10G10R10 + REVERT_UPLOADING_2_10_10_10 : convert X2B10G10R10 to R10G10B10X2 + REVERT_DOWNLOADING_1_5_5_5 : convert B5G5R5X1 to X1R5G5B5 + REVERT_UPLOADING_1_5_5_5 : convert X1R5G5B5 to B5G5R5X1 + @swap_rb: if we have the swap_rb set, then we need to swap the R and B's position. + * + */ + +static void * +glamor_color_convert_to_bits(void *src_bits, void *dst_bits, int w, int h, int stride, int no_alpha, int revert, int swap_rb) +{ + if (revert == REVERT_DOWNLOADING_A1 || revert == REVERT_UPLOADING_A1) { + return _glamor_color_convert_a1_a8(src_bits, dst_bits, w, h, stride, revert); + } else if (revert == REVERT_DOWNLOADING_2_10_10_10 || revert == REVERT_UPLOADING_2_10_10_10) { + return _glamor_color_revert_x2b10g10r10(src_bits, dst_bits, w, h, stride, no_alpha, revert, swap_rb); + } else if (revert == REVERT_DOWNLOADING_1_5_5_5 || revert == REVERT_UPLOADING_1_5_5_5) { + return _glamor_color_revert_x1b5g5r5(src_bits, dst_bits, w, h, stride, no_alpha, revert, swap_rb); + } else + ErrorF("convert a non-supported mode %x.\n", revert); + + return NULL; +} + +/** + * Upload pixmap to a specified texture. + * This texture may not be the one attached to it. + **/ +int in_restore = 0; +static void +__glamor_upload_pixmap_to_texture(PixmapPtr pixmap, unsigned int *tex, + GLenum format, + GLenum type, + int x, int y, int w, int h, + void *bits, int pbo) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(pixmap->drawable.pScreen); + glamor_gl_dispatch *dispatch; + int non_sub = 0; + unsigned int iformat = 0; + + dispatch = glamor_get_dispatch(glamor_priv); + if (*tex == 0) { + dispatch->glGenTextures(1, tex); + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) + gl_iformat_for_depth(pixmap->drawable.depth, &iformat); + else + iformat = format; + non_sub = 1; + assert(x == 0 && y == 0); + } + + dispatch->glBindTexture(GL_TEXTURE_2D, *tex); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + dispatch->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + if (bits == NULL) + dispatch->glBindBuffer(GL_PIXEL_UNPACK_BUFFER, + pbo); + if (non_sub) + dispatch->glTexImage2D(GL_TEXTURE_2D, + 0, iformat, w, h, 0, + format, type, + bits); + else + dispatch->glTexSubImage2D(GL_TEXTURE_2D, + 0, x, y, w, h, + format, type, + bits); + + if (bits == NULL) + dispatch->glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glamor_put_dispatch(glamor_priv); +} + +static Bool +_glamor_upload_bits_to_pixmap_texture(PixmapPtr pixmap, GLenum format, GLenum type, + int no_alpha, int revert, + int swap_rb, int x, int y, int w, int h, + int stride, void* bits, int pbo) +{ + glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); + glamor_screen_private *glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); + glamor_gl_dispatch *dispatch; + static float vertices[8]; + static float texcoords[8] = { 0, 1, + 1, 1, + 1, 0, + 0, 0 + }; + static float texcoords_inv[8] = { 0, 0, + 1, 0, + 1, 1, + 0, 1 + }; + float *ptexcoords; + float dst_xscale, dst_yscale; + GLuint tex = 0; + int need_flip; + int need_free_bits = 0; + + need_flip = !glamor_priv->yInverted; + + if (bits == NULL) + goto ready_to_upload; + + if (revert > REVERT_NORMAL) { + /* XXX if we are restoring the pixmap, then we may not need to allocate + * new buffer */ + void *converted_bits; + + if (pixmap->drawable.depth == 1) + stride = (((w * 8 + 7) / 8) + 3) & ~3; + + converted_bits = malloc(h * stride); + + if (converted_bits == NULL) + return FALSE; + bits = glamor_color_convert_to_bits(bits, converted_bits, w, h, + stride, + no_alpha, revert, swap_rb); + if (bits == NULL) { + ErrorF("Failed to convert pixmap no_alpha %d," + "revert mode %d, swap mode %d\n", no_alpha, revert, swap_rb); + return FALSE; + } + no_alpha = 0; + revert = REVERT_NONE; + swap_rb = SWAP_NONE_UPLOADING; + need_free_bits = TRUE; + } + +ready_to_upload: + + /* Try fast path firstly, upload the pixmap to the texture attached + * to the fbo directly. */ + if (no_alpha == 0 + && revert == REVERT_NONE + && swap_rb == SWAP_NONE_UPLOADING + && !need_flip +#ifdef WALKAROUND_LARGE_TEXTURE_MAP + && pixmap_priv->type != GLAMOR_TEXTURE_LARGE +#endif + ) { + int fbo_x_off, fbo_y_off; + assert(pixmap_priv->base.fbo->tex); + pixmap_priv_get_fbo_off(pixmap_priv, &fbo_x_off, &fbo_y_off); + + assert(x + fbo_x_off >= 0 && y + fbo_y_off >= 0); + assert(x + fbo_x_off + w <= pixmap_priv->base.fbo->width); + assert(y + fbo_y_off + h <= pixmap_priv->base.fbo->height); + __glamor_upload_pixmap_to_texture(pixmap, &pixmap_priv->base.fbo->tex, + format, type, + x + fbo_x_off, y + fbo_y_off, w, h, + bits, pbo); + return TRUE; + } + + if (need_flip) + ptexcoords = texcoords; + else + ptexcoords = texcoords_inv; + + pixmap_priv_get_dest_scale(pixmap_priv, &dst_xscale, &dst_yscale); + glamor_set_normalize_vcoords(pixmap_priv, dst_xscale, + dst_yscale, + x, y, + x + w, y + h, + glamor_priv->yInverted, + vertices); + /* Slow path, we need to flip y or wire alpha to 1. */ + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), + vertices); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), + ptexcoords); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + + glamor_set_destination_pixmap_priv_nc(pixmap_priv); + __glamor_upload_pixmap_to_texture(pixmap, &tex, + format, type, + 0, 0, w, h, + bits, pbo); + dispatch->glActiveTexture(GL_TEXTURE0); + dispatch->glBindTexture(GL_TEXTURE_2D, tex); + + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); +#ifndef GLAMOR_GLES2 + dispatch->glEnable(GL_TEXTURE_2D); +#endif + dispatch->glUseProgram(glamor_priv->finish_access_prog[no_alpha]); + dispatch->glUniform1i(glamor_priv-> + finish_access_revert[no_alpha], + revert); + dispatch->glUniform1i(glamor_priv->finish_access_swap_rb[no_alpha], + swap_rb); + + dispatch->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + +#ifndef GLAMOR_GLES2 + dispatch->glDisable(GL_TEXTURE_2D); +#endif + dispatch->glUseProgram(0); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glDeleteTextures(1, &tex); + dispatch->glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glamor_put_dispatch(glamor_priv); + + if (need_free_bits) + free(bits); + return TRUE; +} + +/* + * Prepare to upload a pixmap to texture memory. + * no_alpha equals 1 means the format needs to wire alpha to 1. + * Two condtion need to setup a fbo for a pixmap + * 1. !yInverted, we need to do flip if we are not yInverted. + * 2. no_alpha != 0, we need to wire the alpha. + * */ +static int +glamor_pixmap_upload_prepare(PixmapPtr pixmap, GLenum format, int no_alpha, int revert, int swap_rb) +{ + int flag = 0; + glamor_pixmap_private *pixmap_priv; + glamor_screen_private *glamor_priv; + glamor_pixmap_fbo *fbo; + GLenum iformat; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); + + if (pixmap_priv->base.gl_fbo) + return 0; + + if (pixmap_priv->base.fbo + && (pixmap_priv->base.fbo->width < pixmap->drawable.width + || pixmap_priv->base.fbo->height < pixmap->drawable.height)) { + fbo = glamor_pixmap_detach_fbo(pixmap_priv); + glamor_destroy_fbo(fbo); + } + + if (pixmap_priv->base.fbo && pixmap_priv->base.fbo->fb) + return 0; + + if (!(no_alpha + || (revert == REVERT_NORMAL) + || (swap_rb != SWAP_NONE_UPLOADING) + || !glamor_priv->yInverted)) { + /* We don't need a fbo, a simple texture uploading should work. */ + + flag = GLAMOR_CREATE_FBO_NO_FBO; + } + + if ((flag == GLAMOR_CREATE_FBO_NO_FBO + && pixmap_priv->base.fbo && pixmap_priv->base.fbo->tex) + || (flag == 0 + && pixmap_priv->base.fbo && pixmap_priv->base.fbo->fb)) + return 0; + + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) + gl_iformat_for_depth(pixmap->drawable.depth, &iformat); + else + iformat = format; + + if (!glamor_pixmap_ensure_fbo(pixmap, iformat, flag)) + return -1; + + return 0; +} + +/* + * upload sub region to a large region. + * */ +static void +glamor_put_bits(char *dst_bits, int dst_stride, char *src_bits, + int src_stride, int bpp, + int x, int y, int w, int h) +{ + int j; + int byte_per_pixel; + + byte_per_pixel = bpp / 8; + src_bits += y * src_stride + (x * byte_per_pixel); + + for(j = y; j < y + h; j++) + { + memcpy(dst_bits, src_bits, w * byte_per_pixel); + src_bits += src_stride; + dst_bits += dst_stride; + } +} +/* + * download sub region from a large region. + */ +static void +glamor_get_bits(char *dst_bits, int dst_stride, char *src_bits, + int src_stride, int bpp, + int x, int y, int w, int h) +{ + int j; + int byte_per_pixel; + + byte_per_pixel = bpp / 8; + dst_bits += y * dst_stride + x * byte_per_pixel; + + for(j = y; j < y + h; j++) + { + memcpy(dst_bits, src_bits, w * byte_per_pixel); + src_bits += src_stride; + dst_bits += dst_stride; + } +} + + +Bool +glamor_upload_sub_pixmap_to_texture(PixmapPtr pixmap, int x, int y, int w, int h, + int stride, void *bits, int pbo) +{ + GLenum format, type; + int no_alpha, revert, swap_rb; + glamor_pixmap_private *pixmap_priv; + Bool force_clip; + + if (glamor_get_tex_format_type_from_pixmap(pixmap, + &format, + &type, + &no_alpha, + &revert, + &swap_rb, 1)) { + glamor_fallback("Unknown pixmap depth %d.\n", + pixmap->drawable.depth); + return TRUE; + } + if (glamor_pixmap_upload_prepare(pixmap, format, no_alpha, revert, swap_rb)) + return FALSE; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + force_clip = pixmap_priv->base.glamor_priv->gl_flavor != GLAMOR_GL_DESKTOP + && !glamor_check_fbo_size(pixmap_priv->base.glamor_priv, w, h); + + if (pixmap_priv->type == GLAMOR_TEXTURE_LARGE || force_clip) { + RegionRec region; + BoxRec box; + int n_region; + glamor_pixmap_clipped_regions *clipped_regions; + void *sub_bits; + int i,j; + + sub_bits = malloc(h * stride); + if (sub_bits == NULL) + return FALSE; + box.x1 = x; + box.y1 = y; + box.x2 = x + w; + box.y2 = y + h; + RegionInitBoxes(®ion, &box, 1); + if (!force_clip) + clipped_regions = glamor_compute_clipped_regions(pixmap_priv, ®ion, &n_region, 0, 0, 0); + else + clipped_regions = glamor_compute_clipped_regions_ext(pixmap_priv, ®ion, &n_region, + pixmap_priv->base.glamor_priv->max_fbo_size, + pixmap_priv->base.glamor_priv->max_fbo_size, 0, 0); + DEBUGF("prepare upload %dx%d to a large pixmap %p\n", w, h, pixmap); + for(i = 0; i < n_region; i++) + { + BoxPtr boxes; + int nbox; + int temp_stride; + void *temp_bits; + + assert(pbo == 0); + + SET_PIXMAP_FBO_CURRENT(pixmap_priv, clipped_regions[i].block_idx); + + boxes = RegionRects(clipped_regions[i].region); + nbox = RegionNumRects(clipped_regions[i].region); + DEBUGF("split to %d boxes\n", nbox); + for(j = 0; j < nbox; j++) + { + temp_stride = PixmapBytePad(boxes[j].x2 - boxes[j].x1, + pixmap->drawable.depth); + + if (boxes[j].x1 == x && temp_stride == stride) { + temp_bits = (char*)bits + (boxes[j].y1 - y) * stride; + } else { + temp_bits = sub_bits; + glamor_put_bits(temp_bits, temp_stride, bits, stride, + pixmap->drawable.bitsPerPixel, + boxes[j].x1 - x, boxes[j].y1 - y, + boxes[j].x2 - boxes[j].x1, + boxes[j].y2 - boxes[j].y1); + } + DEBUGF("upload x %d y %d w %d h %d temp stride %d \n", + boxes[j].x1 - x, boxes[j].y1 - y, + boxes[j].x2 - boxes[j].x1, + boxes[j].y2 - boxes[j].y1, temp_stride); + if (_glamor_upload_bits_to_pixmap_texture(pixmap, format, type, no_alpha, + revert, swap_rb, boxes[j].x1, boxes[j].y1, + boxes[j].x2 - boxes[j].x1, + boxes[j].y2 - boxes[j].y1, + temp_stride, temp_bits, pbo) == FALSE) { + RegionUninit(®ion); + free(sub_bits); + assert(0); + return FALSE; + } + } + RegionDestroy(clipped_regions[i].region); + } + free(sub_bits); + free(clipped_regions); + RegionUninit(®ion); + return TRUE; + } else + return _glamor_upload_bits_to_pixmap_texture(pixmap, format, type, no_alpha, revert, swap_rb, + x, y, w, h, stride, bits, pbo); +} + +enum glamor_pixmap_status +glamor_upload_pixmap_to_texture(PixmapPtr pixmap) +{ + glamor_pixmap_private *pixmap_priv; + void *data; + int pbo; + int ret; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + + if ((pixmap_priv->base.fbo) + && (pixmap_priv->base.fbo->pbo_valid)) { + data = NULL; + pbo = pixmap_priv->base.fbo->pbo; + } else { + data = pixmap->devPrivate.ptr; + pbo = 0; + } + + if (glamor_upload_sub_pixmap_to_texture(pixmap, 0, 0, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->devKind, + data, pbo)) + ret = GLAMOR_UPLOAD_DONE; + else + ret = GLAMOR_UPLOAD_FAILED; + + return ret; +} + +void +glamor_restore_pixmap_to_texture(PixmapPtr pixmap) +{ + if (glamor_upload_pixmap_to_texture(pixmap) != GLAMOR_UPLOAD_DONE) + LogMessage(X_WARNING, "Failed to restore pixmap to texture.\n"); +} + +/* + * as gles2 only support a very small set of color format and + * type when do glReadPixel, + * Before we use glReadPixels to get back a textured pixmap, + * Use shader to convert it to a supported format and thus + * get a new temporary pixmap returned. + * */ + +glamor_pixmap_fbo * +glamor_es2_pixmap_read_prepare(PixmapPtr source, int x, int y, int w, int h, GLenum format, + GLenum type, int no_alpha, int revert, int swap_rb) + +{ + glamor_pixmap_private *source_priv; + glamor_screen_private *glamor_priv; + ScreenPtr screen; + glamor_pixmap_fbo *temp_fbo; + glamor_gl_dispatch *dispatch; + float temp_xscale, temp_yscale, source_xscale, source_yscale; + static float vertices[8]; + static float texcoords[8]; + + screen = source->drawable.pScreen; + + glamor_priv = glamor_get_screen_private(screen); + source_priv = glamor_get_pixmap_private(source); + temp_fbo = glamor_create_fbo(glamor_priv, + w, h, + format, + 0); + if (temp_fbo == NULL) + return NULL; + + dispatch = glamor_get_dispatch(glamor_priv); + temp_xscale = 1.0 / w; + temp_yscale = 1.0 / h; + + glamor_set_normalize_vcoords((struct glamor_pixmap_private*)NULL,temp_xscale, + temp_yscale, + 0, 0, + w, h, + glamor_priv->yInverted, + vertices); + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), + vertices); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); + + pixmap_priv_get_scale(source_priv, &source_xscale, &source_yscale); + glamor_set_normalize_tcoords(source_priv, source_xscale, + source_yscale, + x, y, + x + w, y + h, + glamor_priv->yInverted, + texcoords); + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), + texcoords); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + + dispatch->glActiveTexture(GL_TEXTURE0); + dispatch->glBindTexture(GL_TEXTURE_2D, source_priv->base.fbo->tex); + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + + glamor_set_destination_pixmap_fbo(temp_fbo, 0, 0, w, h); + dispatch->glUseProgram(glamor_priv->finish_access_prog[no_alpha]); + dispatch->glUniform1i(glamor_priv-> + finish_access_revert[no_alpha], + revert); + dispatch->glUniform1i(glamor_priv->finish_access_swap_rb[no_alpha], + swap_rb); + + dispatch->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glUseProgram(0); + glamor_put_dispatch(glamor_priv); + return temp_fbo; +} + +/* + * Download a sub region of pixmap to a specified memory region. + * The pixmap must have a valid FBO, otherwise return a NULL. + * */ + +static void * +_glamor_download_sub_pixmap_to_cpu(PixmapPtr pixmap, GLenum format, + GLenum type, int no_alpha, + int revert, int swap_rb, + int x, int y, int w, int h, + int stride, void *bits, int pbo, glamor_access_t access) +{ + glamor_pixmap_private *pixmap_priv; + GLenum gl_access = 0, gl_usage = 0; + void *data, *read; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(pixmap->drawable.pScreen); + glamor_gl_dispatch *dispatch; + glamor_pixmap_fbo *temp_fbo = NULL; + int need_post_conversion = 0; + int need_free_data = 0; + int fbo_x_off, fbo_y_off; + + data = bits; + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + return NULL; + + switch (access) { + case GLAMOR_ACCESS_RO: + gl_access = GL_READ_ONLY; + gl_usage = GL_STREAM_READ; + break; + case GLAMOR_ACCESS_WO: + return bits; + case GLAMOR_ACCESS_RW: + gl_access = GL_READ_WRITE; + gl_usage = GL_DYNAMIC_DRAW; + break; + default: + ErrorF("Glamor: Invalid access code. %d\n", access); + assert(0); + } + + glamor_set_destination_pixmap_priv_nc(pixmap_priv); + + need_post_conversion = (revert > REVERT_NORMAL); + if (need_post_conversion) { + if (pixmap->drawable.depth == 1) { + int temp_stride; + temp_stride = (((w * 8 + 7) / 8) + 3) & ~3; + data = malloc(temp_stride * h); + if (data == NULL) + return NULL; + need_free_data = 1; + } + } + + pixmap_priv_get_fbo_off(pixmap_priv, &fbo_x_off, &fbo_y_off); + + if (glamor_priv->gl_flavor == GLAMOR_GL_ES2 + && !need_post_conversion + && (swap_rb != SWAP_NONE_DOWNLOADING || revert != REVERT_NONE)) { + if (!(temp_fbo = glamor_es2_pixmap_read_prepare(pixmap, x, y, w, h, + format, type, no_alpha, + revert, swap_rb))) { + free(data); + return NULL; + } + x = 0; + y = 0; + fbo_x_off = 0; + fbo_y_off = 0; + } + + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glPixelStorei(GL_PACK_ALIGNMENT, 4); + + if (glamor_priv->has_pack_invert || glamor_priv->yInverted) { + + if (!glamor_priv->yInverted) { + assert(glamor_priv->gl_flavor == + GLAMOR_GL_DESKTOP); + dispatch->glPixelStorei(GL_PACK_INVERT_MESA, 1); + } + + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP && data == NULL) { + assert(pbo > 0); + dispatch->glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); + dispatch->glBufferData(GL_PIXEL_PACK_BUFFER, + stride * + h, + NULL, gl_usage); + } + + dispatch->glReadPixels(x + fbo_x_off, y + fbo_y_off, w, h, format, type, data); + + if (!glamor_priv->yInverted) { + assert(glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP); + dispatch->glPixelStorei(GL_PACK_INVERT_MESA, 0); + } + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP && bits == NULL) { + bits = dispatch->glMapBuffer(GL_PIXEL_PACK_BUFFER, + gl_access); + dispatch->glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } + } else { + unsigned int temp_pbo; + int yy; + + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glGenBuffers(1, &temp_pbo); + dispatch->glBindBuffer(GL_PIXEL_PACK_BUFFER, + temp_pbo); + dispatch->glBufferData(GL_PIXEL_PACK_BUFFER, + stride * + h, + NULL, GL_STREAM_READ); + dispatch->glReadPixels(x + fbo_x_off, y + fbo_y_off, w, h, + format, type, 0); + read = dispatch->glMapBuffer(GL_PIXEL_PACK_BUFFER, + GL_READ_ONLY); + for (yy = 0; yy < pixmap->drawable.height; yy++) + memcpy((char*)data + yy * stride, + (char*)read + (h - yy - 1) * stride, stride); + dispatch->glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + dispatch->glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + dispatch->glDeleteBuffers(1, &temp_pbo); + } + + dispatch->glBindFramebuffer(GL_FRAMEBUFFER, 0); + glamor_put_dispatch(glamor_priv); + + if (need_post_conversion) { + /* As OpenGL desktop version never enters here. + * Don't need to consider if the pbo is valid.*/ + bits = glamor_color_convert_to_bits(data, bits, + w, h, + stride, + no_alpha, + revert, swap_rb); + } + + if (temp_fbo != NULL) + glamor_destroy_fbo(temp_fbo); + if (need_free_data) + free(data); + + return bits; +} + +void * +glamor_download_sub_pixmap_to_cpu(PixmapPtr pixmap, int x, int y, int w, int h, + int stride, void *bits, int pbo, glamor_access_t access) +{ + GLenum format, type; + int no_alpha, revert, swap_rb; + glamor_pixmap_private *pixmap_priv; + Bool force_clip; + + if (glamor_get_tex_format_type_from_pixmap(pixmap, + &format, + &type, + &no_alpha, + &revert, + &swap_rb, 0)) { + glamor_fallback("Unknown pixmap depth %d.\n", + pixmap->drawable.depth); + return NULL; + } + + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + return NULL; + + force_clip = pixmap_priv->base.glamor_priv->gl_flavor != GLAMOR_GL_DESKTOP + && !glamor_check_fbo_size(pixmap_priv->base.glamor_priv, w, h); + + if (pixmap_priv->type == GLAMOR_TEXTURE_LARGE || force_clip) { + + RegionRec region; + BoxRec box; + int n_region; + glamor_pixmap_clipped_regions *clipped_regions; + void *sub_bits; + int i,j; + + sub_bits = malloc(h * stride); + if (sub_bits == NULL) + return FALSE; + box.x1 = x; + box.y1 = y; + box.x2 = x + w; + box.y2 = y + h; + RegionInitBoxes(®ion, &box, 1); + + if (!force_clip) + clipped_regions = glamor_compute_clipped_regions(pixmap_priv, ®ion, &n_region, 0, 0, 0); + else + clipped_regions = glamor_compute_clipped_regions_ext(pixmap_priv, ®ion, &n_region, + pixmap_priv->base.glamor_priv->max_fbo_size, + pixmap_priv->base.glamor_priv->max_fbo_size, 0, 0); + + DEBUGF("start download large pixmap %p %dx%d \n", pixmap, w, h); + for(i = 0; i < n_region; i++) + { + BoxPtr boxes; + int nbox; + int temp_stride; + void *temp_bits; + + assert(pbo == 0); + SET_PIXMAP_FBO_CURRENT(pixmap_priv, clipped_regions[i].block_idx); + + boxes = RegionRects(clipped_regions[i].region); + nbox = RegionNumRects(clipped_regions[i].region); + for(j = 0; j < nbox; j++) + { + temp_stride = PixmapBytePad(boxes[j].x2 - boxes[j].x1, + pixmap->drawable.depth); + + if (boxes[j].x1 == x && temp_stride == stride) { + temp_bits = (char*)bits + (boxes[j].y1 - y) * stride; + } else { + temp_bits = sub_bits; + } + DEBUGF("download x %d y %d w %d h %d temp stride %d \n", + boxes[j].x1, boxes[j].y1, + boxes[j].x2 - boxes[j].x1, + boxes[j].y2 - boxes[j].y1, temp_stride); + + /* For large pixmap, we don't support pbo currently.*/ + assert(pbo == 0); + if (_glamor_download_sub_pixmap_to_cpu(pixmap, format, type, no_alpha, + revert, swap_rb, boxes[j].x1, boxes[j].y1, + boxes[j].x2 - boxes[j].x1, + boxes[j].y2 - boxes[j].y1, + temp_stride, temp_bits, pbo, access) == FALSE) { + RegionUninit(®ion); + free(sub_bits); + assert(0); + return NULL; + } + if (boxes[j].x1 != x || temp_stride != stride) + glamor_get_bits(bits, stride, temp_bits, temp_stride, + pixmap->drawable.bitsPerPixel, + boxes[j].x1 - x , boxes[j].y1 - y, + boxes[j].x2 - boxes[j].x1, + boxes[j].y2 - boxes[j].y1); + } + + RegionDestroy(clipped_regions[i].region); + } + free(sub_bits); + free(clipped_regions); + RegionUninit(®ion); + return bits; + } else + return _glamor_download_sub_pixmap_to_cpu(pixmap, format, type, no_alpha, revert, swap_rb, + x, y, w, h, stride, + bits, pbo, access); +} + + +/** + * Move a pixmap to CPU memory. + * The input data is the pixmap's fbo. + * The output data is at pixmap->devPrivate.ptr. We always use pbo + * to read the fbo and then map it to va. If possible, we will use + * it directly as devPrivate.ptr. + * If successfully download a fbo to cpu then return TRUE. + * Otherwise return FALSE. + **/ +Bool +glamor_download_pixmap_to_cpu(PixmapPtr pixmap, glamor_access_t access) +{ + glamor_pixmap_private *pixmap_priv = + glamor_get_pixmap_private(pixmap); + unsigned int stride; + void *data = NULL, *dst; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(pixmap->drawable.pScreen); + glamor_gl_dispatch *dispatch; + int pbo = 0; + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + return TRUE; + + glamor_debug_output(GLAMOR_DEBUG_TEXTURE_DOWNLOAD, + "Downloading pixmap %p %dx%d depth%d\n", + pixmap, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->drawable.depth); + + stride = pixmap->devKind; + + if (access == GLAMOR_ACCESS_WO + || glamor_priv->gl_flavor == GLAMOR_GL_ES2 + || (!glamor_priv->has_pack_invert && !glamor_priv->yInverted) + || pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + data = malloc(stride * pixmap->drawable.height); + } else { + dispatch = glamor_get_dispatch(glamor_priv); + if (pixmap_priv->base.fbo->pbo == 0) + dispatch->glGenBuffers(1, + &pixmap_priv->base.fbo->pbo); + glamor_put_dispatch(glamor_priv); + pbo = pixmap_priv->base.fbo->pbo; + } + + if (pixmap_priv->type == GLAMOR_TEXTURE_DRM) { + stride = PixmapBytePad(pixmap->drawable.width, pixmap->drawable.depth); + pixmap_priv->base.drm_stride = pixmap->devKind; + pixmap->devKind = stride; + } + + dst = glamor_download_sub_pixmap_to_cpu(pixmap, 0, 0, + pixmap->drawable.width, + pixmap->drawable.height, + pixmap->devKind, + data, pbo, access); + + if (!dst) { + if (data) + free(data); + return FALSE; + } + + if (pbo != 0) + pixmap_priv->base.fbo->pbo_valid = 1; + + pixmap_priv->base.gl_fbo = GLAMOR_FBO_DOWNLOADED; + + pixmap->devPrivate.ptr = dst; + + return TRUE; +} + +/* fixup a fbo to the exact size as the pixmap. */ +/* XXX LARGE pixmap? */ +Bool +glamor_fixup_pixmap_priv(ScreenPtr screen, glamor_pixmap_private *pixmap_priv) +{ + glamor_pixmap_fbo *old_fbo; + glamor_pixmap_fbo *new_fbo = NULL; + PixmapPtr scratch = NULL; + glamor_pixmap_private *scratch_priv; + DrawablePtr drawable; + GCPtr gc = NULL; + int ret = FALSE; + + drawable = &pixmap_priv->base.pixmap->drawable; + + if (!GLAMOR_PIXMAP_FBO_NOT_EAXCT_SIZE(pixmap_priv)) + return TRUE; + + old_fbo = pixmap_priv->base.fbo; + + if (!old_fbo) + return FALSE; + + gc = GetScratchGC(drawable->depth, screen); + if (!gc) + goto fail; + + scratch = glamor_create_pixmap(screen, drawable->width, drawable->height, + drawable->depth, + GLAMOR_CREATE_PIXMAP_FIXUP); + + scratch_priv = glamor_get_pixmap_private(scratch); + + if (!scratch_priv->base.fbo) + goto fail; + + ValidateGC(&scratch->drawable, gc); + glamor_copy_area(drawable, + &scratch->drawable, + gc, 0, 0, + drawable->width, drawable->height, + 0, 0); + old_fbo = glamor_pixmap_detach_fbo(pixmap_priv); + new_fbo = glamor_pixmap_detach_fbo(scratch_priv); + glamor_pixmap_attach_fbo(pixmap_priv->base.pixmap, new_fbo); + glamor_pixmap_attach_fbo(scratch, old_fbo); + + DEBUGF("old %dx%d type %d\n", + drawable->width, drawable->height, pixmap_priv->type); + DEBUGF("copy tex %d %dx%d to tex %d %dx%d \n", + old_fbo->tex, old_fbo->width, old_fbo->height, new_fbo->tex, new_fbo->width, new_fbo->height); + ret = TRUE; +fail: + if (gc) + FreeScratchGC(gc); + if (scratch) + glamor_destroy_pixmap(scratch); + + return ret; +} + +/* + * We may use this function to reduce a large pixmap to a small sub + * pixmap. Two scenarios currently: + * 1. When fallback a large textured pixmap to CPU but we do need to + * do rendering within a small sub region, then we can just get a + * sub region. + * + * 2. When uploading a large pixmap to texture but we only need to + * use part of the source/mask picture. As glTexImage2D will be more + * efficient to upload a contingent region rather than a sub block + * in a large buffer. We use this function to gather the sub region + * to a contingent sub pixmap. + * + * The sub-pixmap must have the same format as the source pixmap. + * + * */ +PixmapPtr +glamor_get_sub_pixmap(PixmapPtr pixmap, int x, int y, int w, int h, glamor_access_t access) +{ + glamor_screen_private *glamor_priv; + PixmapPtr sub_pixmap; + glamor_pixmap_private *sub_pixmap_priv, *pixmap_priv; + void *data; + int pbo; + int flag; + if (x < 0 || y < 0) + return NULL; + w = (x + w) > pixmap->drawable.width ? (pixmap->drawable.width - x) : w; + h = (y + h) > pixmap->drawable.height ? (pixmap->drawable.height - y) : h; + if (access == GLAMOR_ACCESS_WO) { + sub_pixmap = glamor_create_pixmap(pixmap->drawable.pScreen, w, h, + pixmap->drawable.depth, GLAMOR_CREATE_PIXMAP_CPU); + return sub_pixmap; + } + + glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); + pixmap_priv = glamor_get_pixmap_private(pixmap); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) + return NULL; + if (glamor_priv->gl_flavor == GLAMOR_GL_ES2 || pixmap_priv->type == GLAMOR_TEXTURE_LARGE) + flag = GLAMOR_CREATE_PIXMAP_CPU; + else + flag = GLAMOR_CREATE_PIXMAP_MAP; + + sub_pixmap = glamor_create_pixmap(pixmap->drawable.pScreen, w, h, + pixmap->drawable.depth, flag); + + if (sub_pixmap == NULL) + return NULL; + + sub_pixmap_priv = glamor_get_pixmap_private(sub_pixmap); + pbo = sub_pixmap_priv ? (sub_pixmap_priv->base.fbo ? sub_pixmap_priv->base.fbo->pbo : 0): 0; + + if (pixmap_priv->base.is_picture) { + sub_pixmap_priv->base.picture = pixmap_priv->base.picture; + sub_pixmap_priv->base.is_picture = pixmap_priv->base.is_picture; + } + + if (pbo) + data = NULL; + else + data = sub_pixmap->devPrivate.ptr; + + data = glamor_download_sub_pixmap_to_cpu(pixmap, x, y, w, h, sub_pixmap->devKind, + data, pbo, access); + if(data == NULL) { + fbDestroyPixmap(sub_pixmap); + return NULL; + } + if (pbo) { + assert(sub_pixmap->devPrivate.ptr == NULL); + sub_pixmap->devPrivate.ptr = data; + sub_pixmap_priv->base.fbo->pbo_valid = 1; + } +#if 0 + struct pixman_box16 box; + PixmapPtr new_sub_pixmap; + int dx, dy; + box.x1 = 0; + box.y1 = 0; + box.x2 = w; + box.y2 = h; + + dx = x; + dy = y; + + new_sub_pixmap = glamor_create_pixmap(pixmap->drawable.pScreen, w, h, + pixmap->drawable.depth, GLAMOR_CREATE_PIXMAP_CPU); + glamor_copy_n_to_n(&pixmap->drawable, &new_sub_pixmap->drawable, NULL, &box, 1, dx, dy, 0, 0, 0, NULL); + glamor_compare_pixmaps(new_sub_pixmap, sub_pixmap, 0, 0, w, h, 1, 1); +#endif + + return sub_pixmap; +} + +void +glamor_put_sub_pixmap(PixmapPtr sub_pixmap, PixmapPtr pixmap, int x, int y, int w, int h, glamor_access_t access) +{ + void *bits; + int pbo; + glamor_pixmap_private *sub_pixmap_priv; + if (access != GLAMOR_ACCESS_RO) { + sub_pixmap_priv = glamor_get_pixmap_private(sub_pixmap); + if (sub_pixmap_priv->base.fbo + && sub_pixmap_priv->base.fbo->pbo_valid) { + bits = NULL; + pbo = sub_pixmap_priv->base.fbo->pbo; + } else { + bits = sub_pixmap->devPrivate.ptr; + pbo = 0; + } + + assert(x >= 0 && y >= 0); + w = (w > sub_pixmap->drawable.width) ? sub_pixmap->drawable.width : w; + h = (h > sub_pixmap->drawable.height) ? sub_pixmap->drawable.height : h; + glamor_upload_sub_pixmap_to_texture(pixmap, x, y, w, h, sub_pixmap->devKind, bits, pbo); + } + glamor_destroy_pixmap(sub_pixmap); +} diff --git a/xorg-server/glamor/glamor_polyfillrect.c b/xorg-server/glamor/glamor_polyfillrect.c new file mode 100644 index 000000000..4e1f7b3a9 --- /dev/null +++ b/xorg-server/glamor/glamor_polyfillrect.c @@ -0,0 +1,127 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +#include "glamor_priv.h" + +/** @file glamor_fillspans.c + * + * GC PolyFillRect implementation, taken straight from fb_fill.c + */ + +static Bool +_glamor_poly_fill_rect(DrawablePtr drawable, + GCPtr gc, int nrect, xRectangle * prect, Bool fallback) +{ + int fullX1, fullX2, fullY1, fullY2; + int xorg, yorg; + int n; + register BoxPtr pbox; + RegionPtr pClip = fbGetCompositeClip(gc); + Bool ret = FALSE; + + xorg = drawable->x; + yorg = drawable->y; + + while (nrect--) { + fullX1 = prect->x + xorg; + fullY1 = prect->y + yorg; + fullX2 = fullX1 + (int) prect->width; + fullY2 = fullY1 + (int) prect->height; + + n = REGION_NUM_RECTS(pClip); + pbox = REGION_RECTS(pClip); + /* + * clip the rectangle to each box in the clip region + * this is logically equivalent to calling Intersect(), + * but rectangles may overlap each other here. + */ + while (n--) { + int x1 = fullX1; + int x2 = fullX2; + int y1 = fullY1; + int y2 = fullY2; + + if (pbox->x1 > x1) + x1 = pbox->x1; + if (pbox->x2 < x2) + x2 = pbox->x2; + if (pbox->y1 > y1) + y1 = pbox->y1; + if (pbox->y2 < y2) + y2 = pbox->y2; + + pbox++; + if (x1 >= x2 || y1 >= y2) + continue; + if (!glamor_fill(drawable, gc, x1, y1, x2 - x1, + y2 - y1, fallback)) { + nrect++; + goto fail; + } + } + prect++; + } + ret = TRUE; + goto done; + +fail: + + if (!fallback + && glamor_ddx_fallback_check_pixmap(drawable) + && glamor_ddx_fallback_check_gc(gc)) + goto done; + + glamor_fallback(" to %p (%c)\n", + drawable, glamor_get_drawable_location(drawable)); + if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RW)) { + if (glamor_prepare_access_gc(gc)) { + fbPolyFillRect(drawable, gc, nrect, prect); + glamor_finish_access_gc(gc); + } + glamor_finish_access(drawable, GLAMOR_ACCESS_RW); + } + ret = TRUE; + +done: + return ret; +} + + +void +glamor_poly_fill_rect(DrawablePtr drawable, + GCPtr gc, int nrect, xRectangle * prect) +{ + _glamor_poly_fill_rect(drawable, gc, nrect, prect, TRUE); +} + +Bool +glamor_poly_fill_rect_nf(DrawablePtr drawable, + GCPtr gc, int nrect, xRectangle * prect) +{ + return _glamor_poly_fill_rect(drawable, gc, nrect, prect, FALSE); +} diff --git a/xorg-server/glamor/glamor_polylines.c b/xorg-server/glamor/glamor_polylines.c new file mode 100644 index 000000000..e723e9500 --- /dev/null +++ b/xorg-server/glamor/glamor_polylines.c @@ -0,0 +1,135 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +#include "glamor_priv.h" + +/** @file glamor_polylines.c + * + * GC PolyFillRect implementation, taken straight from fb_fill.c + */ + +/** + * glamor_poly_lines() checks if it can accelerate the lines as a group of + * horizontal or vertical lines (rectangles), and uses existing rectangle fill + * acceleration if so. + */ +static Bool +_glamor_poly_lines(DrawablePtr drawable, GCPtr gc, int mode, int n, + DDXPointPtr points, Bool fallback) +{ + xRectangle *rects; + int x1, x2, y1, y2; + int i; + + /* Don't try to do wide lines or non-solid fill style. */ + if (gc->lineWidth != 0) { + /* This ends up in miSetSpans, which is accelerated as well as we + * can hope X wide lines will be. + */ + goto wide_line; + } + if (gc->lineStyle != LineSolid) { + glamor_fallback + ("non-solid fill line style %d\n", + gc->lineStyle); + goto fail; + } + rects = malloc(sizeof(xRectangle) * (n - 1)); + x1 = points[0].x; + y1 = points[0].y; + /* If we have any non-horizontal/vertical, fall back. */ + for (i = 0; i < n - 1; i++) { + if (mode == CoordModePrevious) { + x2 = x1 + points[i + 1].x; + y2 = y1 + points[i + 1].y; + } else { + x2 = points[i + 1].x; + y2 = points[i + 1].y; + } + if (x1 != x2 && y1 != y2) { + free(rects); + glamor_fallback("stub diagonal poly_line\n"); + goto fail; + } + if (x1 < x2) { + rects[i].x = x1; + rects[i].width = x2 - x1 + 1; + } else { + rects[i].x = x2; + rects[i].width = x1 - x2 + 1; + } + if (y1 < y2) { + rects[i].y = y1; + rects[i].height = y2 - y1 + 1; + } else { + rects[i].y = y2; + rects[i].height = y1 - y2 + 1; + } + + x1 = x2; + y1 = y2; + } + gc->ops->PolyFillRect(drawable, gc, n - 1, rects); + free(rects); + return TRUE; + + fail: + if (!fallback + && glamor_ddx_fallback_check_pixmap(drawable) + && glamor_ddx_fallback_check_gc(gc)) + return FALSE; + + if (gc->lineWidth == 0) { + if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RW)) { + if (glamor_prepare_access_gc(gc)) { + fbPolyLine(drawable, gc, mode, n, points); + glamor_finish_access_gc(gc); + } + glamor_finish_access(drawable, GLAMOR_ACCESS_RW); + } + } else { +wide_line: + /* fb calls mi functions in the lineWidth != 0 case. */ + fbPolyLine(drawable, gc, mode, n, points); + } + return TRUE; +} + +void +glamor_poly_lines(DrawablePtr drawable, GCPtr gc, int mode, int n, + DDXPointPtr points) +{ + _glamor_poly_lines(drawable, gc, mode, n, points, TRUE); +} + +Bool +glamor_poly_lines_nf(DrawablePtr drawable, GCPtr gc, int mode, int n, + DDXPointPtr points) +{ + return _glamor_poly_lines(drawable, gc, mode, n, points, FALSE); +} diff --git a/xorg-server/glamor/glamor_polyops.c b/xorg-server/glamor/glamor_polyops.c new file mode 100644 index 000000000..59301784d --- /dev/null +++ b/xorg-server/glamor/glamor_polyops.c @@ -0,0 +1,85 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + +#include "glamor_priv.h" + +static Bool +_glamor_poly_point(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr ppt, Bool fallback) +{ + if (!fallback + && glamor_ddx_fallback_check_gc(pGC) + && glamor_ddx_fallback_check_pixmap(pDrawable)) + return FALSE; + + miPolyPoint(pDrawable, pGC, mode, npt, ppt); + + return TRUE; +} + +void +glamor_poly_point(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr ppt) +{ + _glamor_poly_point(pDrawable, pGC, mode, npt, ppt, TRUE); +} + +Bool +glamor_poly_point_nf(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr ppt) +{ + return _glamor_poly_point(pDrawable, pGC, mode, npt, ppt, FALSE); +} + +static Bool +_glamor_poly_segment(DrawablePtr pDrawable, GCPtr pGC, int nseg, + xSegment *pSeg, Bool fallback) +{ + if (!fallback + && glamor_ddx_fallback_check_gc(pGC) + && glamor_ddx_fallback_check_pixmap(pDrawable)) + return FALSE; + + miPolySegment(pDrawable, pGC, nseg, pSeg); + + return TRUE; +} + +void +glamor_poly_segment(DrawablePtr pDrawable, GCPtr pGC, int nseg, + xSegment *pSeg) +{ + _glamor_poly_segment(pDrawable, pGC, nseg, pSeg, TRUE); +} + +Bool +glamor_poly_segment_nf(DrawablePtr pDrawable, GCPtr pGC, int nseg, + xSegment *pSeg) +{ + return _glamor_poly_segment(pDrawable, pGC, nseg, pSeg, FALSE); +} diff --git a/xorg-server/glamor/glamor_priv.h b/xorg-server/glamor/glamor_priv.h new file mode 100644 index 000000000..7b8f762c9 --- /dev/null +++ b/xorg-server/glamor/glamor_priv.h @@ -0,0 +1,1047 @@ +/* + * Copyright © 2008 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ +#ifndef GLAMOR_PRIV_H +#define GLAMOR_PRIV_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "compiler.h" + +#include <xorg-server.h> +#ifndef DEBUG +#define NDEBUG +#endif +#include "glamor.h" +#include "compat-api.h" + +#define GL_GLEXT_PROTOTYPES + +#ifdef GLAMOR_GLES2 +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#define GLAMOR_DEFAULT_PRECISION "precision mediump float;\n" +#include "glamor_glext.h" +#else +#include <GL/gl.h> +#include <GL/glext.h> +#define GLAMOR_DEFAULT_PRECISION +#endif + +#ifdef RENDER +#include "glyphstr.h" +#endif + +#include "glamor_debug.h" + +#include <list.h> +/* The list.h rename all the function to add xorg_ prefix. + We add hack here to avoid the compile error when using + old version xserver header file. + These will be removed in future. */ +#ifndef xorg_list_entry +#define xorg_list list +#define xorg_list_for_each_entry list_for_each_entry +#define xorg_list_for_each_entry_safe list_for_each_entry_safe +#define xorg_list_del list_del +#define xorg_list_add list_add +#define xorg_list_append list_append +#define xorg_list_init list_init +#endif + +struct glamor_pixmap_private; + +typedef struct glamor_composite_shader { + GLuint prog; + GLint dest_to_dest_uniform_location; + GLint dest_to_source_uniform_location; + GLint dest_to_mask_uniform_location; + GLint source_uniform_location; + GLint mask_uniform_location; + GLint source_wh; + GLint mask_wh; + GLint source_repeat_mode; + GLint mask_repeat_mode; + union { + float source_solid_color[4]; + struct { + struct glamor_pixmap_private *source_priv; + PicturePtr source; + }; + }; + + union { + float mask_solid_color[4]; + struct { + struct glamor_pixmap_private *mask_priv; + PicturePtr mask; + }; + }; +} glamor_composite_shader; + +enum shader_source { + SHADER_SOURCE_SOLID, + SHADER_SOURCE_TEXTURE, + SHADER_SOURCE_TEXTURE_ALPHA, + SHADER_SOURCE_COUNT, +}; + +enum shader_mask { + SHADER_MASK_NONE, + SHADER_MASK_SOLID, + SHADER_MASK_TEXTURE, + SHADER_MASK_TEXTURE_ALPHA, + SHADER_MASK_COUNT, +}; + +enum shader_in { + SHADER_IN_SOURCE_ONLY, + SHADER_IN_NORMAL, + SHADER_IN_CA_SOURCE, + SHADER_IN_CA_ALPHA, + SHADER_IN_COUNT, +}; + +struct shader_key { + enum shader_source source; + enum shader_mask mask; + enum shader_in in; +}; + +struct blendinfo { + Bool dest_alpha; + Bool source_alpha; + GLenum source_blend; + GLenum dest_blend; +}; + +typedef struct { + INT16 x_src; + INT16 y_src; + INT16 x_mask; + INT16 y_mask; + INT16 x_dst; + INT16 y_dst; + INT16 width; + INT16 height; +} glamor_composite_rect_t; + + +enum glamor_vertex_type { + GLAMOR_VERTEX_POS, + GLAMOR_VERTEX_SOURCE, + GLAMOR_VERTEX_MASK +}; + + +enum gradient_shader { + SHADER_GRADIENT_LINEAR, + SHADER_GRADIENT_RADIAL, + SHADER_GRADIENT_CONICAL, + SHADER_GRADIENT_COUNT, +}; + +enum gradient_shader_prog { + SHADER_GRADIENT_VS_PROG, + SHADER_GRADIENT_FS_MAIN_PROG, + SHADER_GRADIENT_FS_GETCOLOR_PROG, + SHADER_GRADIENT_PROG_COUNT, +}; + +struct glamor_screen_private; +struct glamor_pixmap_private; + +enum glamor_gl_flavor { + GLAMOR_GL_DESKTOP, // OPENGL API + GLAMOR_GL_ES2 // OPENGL ES2.0 API +}; + +#define GLAMOR_CREATE_PIXMAP_CPU 0x100 +#define GLAMOR_CREATE_PIXMAP_FIXUP 0x101 +#define GLAMOR_CREATE_FBO_NO_FBO 0x103 +#define GLAMOR_CREATE_PIXMAP_MAP 0x104 + +#define GLAMOR_CREATE_TEXTURE_EXACT_SIZE 0x104 + +#define GLAMOR_NUM_GLYPH_CACHE_FORMATS 2 + +#define GLAMOR_COMPOSITE_VBO_VERT_CNT (64*1024) + +typedef struct { + PicturePtr picture; /* Where the glyphs of the cache are stored */ + GlyphPtr *glyphs; + uint16_t count; + uint16_t evict; +} glamor_glyph_cache_t; + +#include "glamor_gl_dispatch.h" + +struct glamor_saved_procs { + CloseScreenProcPtr close_screen; + CreateGCProcPtr create_gc; + CreatePixmapProcPtr create_pixmap; + DestroyPixmapProcPtr destroy_pixmap; + GetSpansProcPtr get_spans; + GetImageProcPtr get_image; + CompositeProcPtr composite; + CompositeRectsProcPtr composite_rects; + TrapezoidsProcPtr trapezoids; + GlyphsProcPtr glyphs; + ChangeWindowAttributesProcPtr change_window_attributes; + CopyWindowProcPtr copy_window; + BitmapToRegionProcPtr bitmap_to_region; + TrianglesProcPtr triangles; + AddTrapsProcPtr addtraps; + CreatePictureProcPtr create_picture; + DestroyPictureProcPtr destroy_picture; + UnrealizeGlyphProcPtr unrealize_glyph; + SetWindowPixmapProcPtr set_window_pixmap; +}; + +#ifdef GLAMOR_GLES2 +#define CACHE_FORMAT_COUNT 3 +#else +#define CACHE_FORMAT_COUNT 2 +#endif + +#define CACHE_BUCKET_WCOUNT 4 +#define CACHE_BUCKET_HCOUNT 4 + +#define GLAMOR_TICK_AFTER(t0, t1) \ + (((int)(t1) - (int)(t0)) < 0) + +#define IDLE_STATE 0 +#define RENDER_STATE 1 +#define BLIT_STATE 2 +#define RENDER_IDEL_MAX 32 + +typedef struct glamor_screen_private { + struct glamor_gl_dispatch _dispatch; + int yInverted; + unsigned int tick; + enum glamor_gl_flavor gl_flavor; + int has_pack_invert; + int has_fbo_blit; + int max_fbo_size; + + struct xorg_list fbo_cache[CACHE_FORMAT_COUNT][CACHE_BUCKET_WCOUNT][CACHE_BUCKET_HCOUNT]; + unsigned long fbo_cache_watermark; + + /* glamor_solid */ + GLint solid_prog; + GLint solid_color_uniform_location; + + /* vertext/elment_index buffer object for render */ + GLuint vbo, ebo; + int vbo_offset; + int vbo_size; + char *vb; + int vb_stride; + Bool has_source_coords, has_mask_coords; + int render_nr_verts; + glamor_composite_shader composite_shader[SHADER_SOURCE_COUNT] + [SHADER_MASK_COUNT] + [SHADER_IN_COUNT]; + glamor_glyph_cache_t glyphCaches[GLAMOR_NUM_GLYPH_CACHE_FORMATS]; + Bool glyph_cache_initialized; + + /* shaders to restore a texture to another texture.*/ + GLint finish_access_prog[2]; + GLint finish_access_revert[2]; + GLint finish_access_swap_rb[2]; + + /* glamor_tile */ + GLint tile_prog; + GLint tile_wh; + + /* glamor gradient, 0 for small nstops, 1 for + large nstops and 2 for dynamic generate. */ + GLint gradient_prog[SHADER_GRADIENT_COUNT][3]; + GLint linear_gradient_shaders[SHADER_GRADIENT_PROG_COUNT][3]; + int linear_max_nstops; + GLint radial_gradient_shaders[SHADER_GRADIENT_PROG_COUNT][3]; + int radial_max_nstops; + + /* glamor trapezoid shader. */ + GLint trapezoid_prog; + + /* glamor_putimage */ + GLint put_image_xybitmap_prog; + GLint put_image_xybitmap_fg_uniform_location; + GLint put_image_xybitmap_bg_uniform_location; + + PixmapPtr *back_pixmap; + int screen_fbo; + struct glamor_saved_procs saved_procs; + char delayed_fallback_string[GLAMOR_DELAYED_STRING_MAX + 1]; + int delayed_fallback_pending; + int flags; + int state; + unsigned int render_idle_cnt; + ScreenPtr screen; + int dri3_enabled; + + /* xv */ + GLint xv_prog; +} glamor_screen_private; + +typedef enum glamor_access { + GLAMOR_ACCESS_RO, + GLAMOR_ACCESS_RW, + GLAMOR_ACCESS_WO, +} glamor_access_t; + +#define GLAMOR_FBO_NORMAL 1 +#define GLAMOR_FBO_DOWNLOADED 2 +/* glamor_pixmap_fbo: + * @list: to be used to link to the cache pool list. + * @expire: when push to cache pool list, set a expire count. + * will be freed when glamor_priv->tick is equal or + * larger than this expire count in block handler. + * @pbo_valid: The pbo has a valid copy of the pixmap's data. + * @tex: attached texture. + * @fb: attached fbo. + * @pbo: attached pbo. + * @width: width of this fbo. + * @height: height of this fbo. + * @format: internal format of this fbo's texture. + * @type: internal type of this fbo's texture. + * @glamor_priv: point to glamor private data. + */ +typedef struct glamor_pixmap_fbo { + struct xorg_list list; + unsigned int expire; + unsigned char pbo_valid; + GLuint tex; + GLuint fb; + GLuint pbo; + int width; + int height; + GLenum format; + GLenum type; + glamor_screen_private *glamor_priv; +} glamor_pixmap_fbo; + +/* + * glamor_pixmap_private - glamor pixmap's private structure. + * @gl_fbo: + * 0 - The pixmap doesn't has a fbo attached to it. + * GLAMOR_FBO_NORMAL - The pixmap has a fbo and can be accessed normally. + * GLAMOR_FBO_DOWNLOADED - The pixmap has a fbo and already downloaded to + * CPU, so it can only be treated as a in-memory pixmap + * if this bit is set. + * @gl_tex: The pixmap is in a gl texture originally. + * @is_picture: The drawable is attached to a picture. + * @pict_format: the corresponding picture's format. + * @pixmap: The corresponding pixmap's pointer. + * + * For GLAMOR_TEXTURE_LARGE, nbox should larger than 1. + * And the box and fbo will both have nbox elements. + * and box[i] store the relatively coords in this pixmap + * of the fbo[i]. The reason why use boxes not region to + * represent this structure is we may need to use overlapped + * boxes for one pixmap for some special reason. + * + * pixmap + * ****************** + * * fbo0 * fbo1 * + * * * * + * ****************** + * * fbo2 * fbo3 * + * * * * + * ****************** + * + * Let's assume the texture has size of 1024x1024 + * box[0] = {0,0,1024,1024} + * box[1] = {1024,0,2048,2048} + * ... + * + * For GLAMOR_TEXTURE_ATLAS nbox should be 1. And box + * and fbo both has one elements, and the box store + * the relatively coords in the fbo of this pixmap: + * + * fbo + * ****************** + * * pixmap * + * * ********* * + * * * * * + * * ********* * + * * * + * ****************** + * + * Assume the pixmap is at the (100,100) relatively to + * the fbo's origin. + * box[0]={100, 100, 1124, 1124}; + * + * Considering large pixmap is not a normal case, to keep + * it simple, I designe it as the following way. + * When deal with a large pixmap, it split the working + * rectangle into serval boxes, and each box fit into a + * corresponding fbo. And then the rendering function will + * loop from the left-top box to the right-bottom box, + * each time, we will set current box and current fbo + * to the box and fbo elements. Thus the inner routines + * can handle it as normal, only the coords calculation need + * to aware of it's large pixmap. + * + * Currently, we haven't implemented the atlas pixmap. + * + **/ + +typedef struct glamor_pixmap_clipped_regions{ + int block_idx; + RegionPtr region; +} glamor_pixmap_clipped_regions; + +#define SET_PIXMAP_FBO_CURRENT(priv, idx) \ + do { \ + if (priv->type == GLAMOR_TEXTURE_LARGE) { \ + (priv)->large.base.fbo = priv->large.fbo_array[idx]; \ + (priv)->large.box = priv->large.box_array[idx]; \ + } \ + } while(0) + +typedef struct glamor_pixmap_private_base { + glamor_pixmap_type_t type; + unsigned char gl_fbo:2; + unsigned char is_picture:1; + unsigned char gl_tex:1; + glamor_pixmap_fbo *fbo; + PixmapPtr pixmap; + int drm_stride; + glamor_screen_private *glamor_priv; + PicturePtr picture; +}glamor_pixmap_private_base_t; + +/* + * @base.fbo: current fbo. + * @box: current fbo's coords in the whole pixmap. + * @block_w: block width of this large pixmap. + * @block_h: block height of this large pixmap. + * @block_wcnt: block count in one block row. + * @block_hcnt: block count in one block column. + * @nbox: total block count. + * @box_array: contains each block's corresponding box. + * @fbo_array: contains each block's fbo pointer. + * + **/ +typedef struct glamor_pixmap_private_large { + union { + glamor_pixmap_type_t type; + glamor_pixmap_private_base_t base; + }; + BoxRec box; + int block_w; + int block_h; + int block_wcnt; + int block_hcnt; + int nbox; + BoxPtr box_array; + glamor_pixmap_fbo **fbo_array; +}glamor_pixmap_private_large_t; + +/* + * @box: the relative coords in the corresponding fbo. + */ +typedef struct glamor_pixmap_private_atlas { + union { + glamor_pixmap_type_t type; + glamor_pixmap_private_base_t base; + }; + BoxRec box; +}glamor_pixmap_private_atlas_t; + +typedef struct glamor_pixmap_private { + union { + glamor_pixmap_type_t type; + glamor_pixmap_private_base_t base; + glamor_pixmap_private_large_t large; + glamor_pixmap_private_atlas_t atlas; + }; +}glamor_pixmap_private; + +/* + * Pixmap dynamic status, used by dynamic upload feature. + * + * GLAMOR_NONE: initial status, don't need to do anything. + * GLAMOR_UPLOAD_PENDING: marked as need to be uploaded to gl texture. + * GLAMOR_UPLOAD_DONE: the pixmap has been uploaded successfully. + * GLAMOR_UPLOAD_FAILED: fail to upload the pixmap. + * + * */ +typedef enum glamor_pixmap_status { + GLAMOR_NONE, + GLAMOR_UPLOAD_PENDING, + GLAMOR_UPLOAD_DONE, + GLAMOR_UPLOAD_FAILED +} glamor_pixmap_status_t; + +extern DevPrivateKey glamor_screen_private_key; +extern DevPrivateKey glamor_pixmap_private_key; +static inline glamor_screen_private * +glamor_get_screen_private(ScreenPtr screen) +{ + return (glamor_screen_private *) + dixLookupPrivate(&screen->devPrivates, + glamor_screen_private_key); +} + +static inline void +glamor_set_screen_private(ScreenPtr screen, glamor_screen_private *priv) +{ + dixSetPrivate(&screen->devPrivates, + glamor_screen_private_key, + priv); +} + + + +static inline glamor_pixmap_private * +glamor_get_pixmap_private(PixmapPtr pixmap) +{ + glamor_pixmap_private *priv; + priv = dixLookupPrivate(&pixmap->devPrivates, + glamor_pixmap_private_key); + if (!priv) { + glamor_set_pixmap_type(pixmap, GLAMOR_MEMORY); + priv = dixLookupPrivate(&pixmap->devPrivates, + glamor_pixmap_private_key); + } + return priv; +} + +void glamor_set_pixmap_private(PixmapPtr pixmap, glamor_pixmap_private *priv); + +/** + * Returns TRUE if the given planemask covers all the significant bits in the + * pixel values for pDrawable. + */ +static inline Bool +glamor_pm_is_solid(DrawablePtr drawable, unsigned long planemask) +{ + return (planemask & FbFullMask(drawable->depth)) == + FbFullMask(drawable->depth); +} + +extern int glamor_debug_level; + +/* glamor.c */ +PixmapPtr glamor_get_drawable_pixmap(DrawablePtr drawable); + +Bool glamor_destroy_pixmap(PixmapPtr pixmap); + +glamor_pixmap_fbo* glamor_pixmap_detach_fbo(glamor_pixmap_private *pixmap_priv); +void glamor_pixmap_attach_fbo(PixmapPtr pixmap, glamor_pixmap_fbo *fbo); +glamor_pixmap_fbo * glamor_create_fbo_from_tex(glamor_screen_private *glamor_priv, + int w, int h, GLenum format, GLint tex, int flag); +glamor_pixmap_fbo * glamor_create_fbo(glamor_screen_private *glamor_priv, + int w, int h, GLenum format, int flag); +void glamor_destroy_fbo(glamor_pixmap_fbo *fbo); +void glamor_pixmap_destroy_fbo(glamor_pixmap_private *priv); +void glamor_purge_fbo(glamor_pixmap_fbo *fbo); + +void glamor_init_pixmap_fbo(ScreenPtr screen); +void glamor_fini_pixmap_fbo(ScreenPtr screen); +Bool glamor_pixmap_fbo_fixup(ScreenPtr screen, PixmapPtr pixmap); +void glamor_fbo_expire(glamor_screen_private *glamor_priv); + +glamor_pixmap_fbo * +glamor_create_fbo_array(glamor_screen_private *glamor_priv, + int w, int h, GLenum format, int flag, + int block_w, int block_h, glamor_pixmap_private *); + +/* glamor_copyarea.c */ +RegionPtr +glamor_copy_area(DrawablePtr src, DrawablePtr dst, GCPtr gc, + int srcx, int srcy, int width, int height, int dstx, + int dsty); +void glamor_copy_n_to_n(DrawablePtr src, DrawablePtr dst, GCPtr gc, + BoxPtr box, int nbox, int dx, int dy, Bool reverse, + Bool upsidedown, Pixel bitplane, void *closure); + +/* glamor_copywindow.c */ +void glamor_copy_window(WindowPtr win, DDXPointRec old_origin, + RegionPtr src_region); + +/* glamor_core.c */ +Bool glamor_prepare_access(DrawablePtr drawable, glamor_access_t access); +void glamor_finish_access(DrawablePtr drawable, glamor_access_t access); +Bool glamor_prepare_access_window(WindowPtr window); +void glamor_finish_access_window(WindowPtr window); +Bool glamor_prepare_access_gc(GCPtr gc); +void glamor_finish_access_gc(GCPtr gc); +void glamor_init_finish_access_shaders(ScreenPtr screen); +void glamor_fini_finish_access_shaders(ScreenPtr screen); +const Bool glamor_get_drawable_location(const DrawablePtr drawable); +void glamor_get_drawable_deltas(DrawablePtr drawable, PixmapPtr pixmap, + int *x, int *y); +Bool glamor_stipple(PixmapPtr pixmap, PixmapPtr stipple, + int x, int y, int width, int height, + unsigned char alu, unsigned long planemask, + unsigned long fg_pixel, unsigned long bg_pixel, + int stipple_x, int stipple_y); +GLint glamor_compile_glsl_prog(glamor_gl_dispatch * dispatch, GLenum type, + const char *source); +void glamor_link_glsl_prog(glamor_gl_dispatch * dispatch, GLint prog); +void glamor_get_color_4f_from_pixel(PixmapPtr pixmap, + unsigned long fg_pixel, + GLfloat * color); + +int glamor_set_destination_pixmap(PixmapPtr pixmap); +int glamor_set_destination_pixmap_priv(glamor_pixmap_private * + pixmap_priv); +void glamor_set_destination_pixmap_fbo(glamor_pixmap_fbo *, int, int, int, int); + +/* nc means no check. caller must ensure this pixmap has valid fbo. + * usually use the GLAMOR_PIXMAP_PRIV_HAS_FBO firstly. + * */ +void glamor_set_destination_pixmap_priv_nc(glamor_pixmap_private * + pixmap_priv); + +glamor_pixmap_fbo * +glamor_es2_pixmap_read_prepare(PixmapPtr source, int x, int y, int w, int h, GLenum format, + GLenum type, int no_alpha, int revert, int swap_rb); + +Bool glamor_set_alu(struct glamor_gl_dispatch *dispatch, + unsigned char alu); +Bool glamor_set_planemask(PixmapPtr pixmap, unsigned long planemask); +Bool glamor_change_window_attributes(WindowPtr pWin, unsigned long mask); +RegionPtr glamor_bitmap_to_region(PixmapPtr pixmap); +Bool glamor_gl_has_extension(const char *extension); +int glamor_gl_get_version(void); + +#define GLAMOR_GL_VERSION_ENCODE(major, minor) ( \ + ((major) * 256) \ + + ((minor) * 1)) + + + + +/* glamor_fill.c */ +Bool glamor_fill(DrawablePtr drawable, + GCPtr gc, int x, int y, int width, int height, Bool fallback); +Bool glamor_solid(PixmapPtr pixmap, int x, int y, int width, int height, + unsigned char alu, unsigned long planemask, + unsigned long fg_pixel); +Bool +glamor_solid_boxes(PixmapPtr pixmap, + BoxPtr box, int nbox, + unsigned long fg_pixel); + +/* glamor_fillspans.c */ +void glamor_fill_spans(DrawablePtr drawable, + GCPtr gc, + int n, DDXPointPtr points, int *widths, int sorted); + +void glamor_init_solid_shader(ScreenPtr screen); +void glamor_fini_solid_shader(ScreenPtr screen); + +/* glamor_getspans.c */ +void + +glamor_get_spans(DrawablePtr drawable, + int wmax, + DDXPointPtr points, + int *widths, int nspans, char *dst_start); + +/* glamor_glyphs.c */ +void glamor_glyphs_fini(ScreenPtr screen); +void glamor_glyphs(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, + INT16 ySrc, int nlist, GlyphListPtr list, + GlyphPtr * glyphs); + +/* glamor_setspans.c */ +void glamor_set_spans(DrawablePtr drawable, GCPtr gc, char *src, + DDXPointPtr points, int *widths, int n, int sorted); + +/* glamor_polyfillrect.c */ +void +glamor_poly_fill_rect(DrawablePtr drawable, + GCPtr gc, int nrect, xRectangle * prect); + +/* glamor_polylines.c */ +void + +glamor_poly_lines(DrawablePtr drawable, GCPtr gc, int mode, int n, + DDXPointPtr points); + +/* glamor_putimage.c */ +void + +glamor_put_image(DrawablePtr drawable, GCPtr gc, int depth, int x, int y, + int w, int h, int leftPad, int format, char *bits); +void glamor_init_putimage_shaders(ScreenPtr screen); +void glamor_fini_putimage_shaders(ScreenPtr screen); + +/* glamor_render.c */ +Bool +glamor_composite_clipped_region(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + glamor_pixmap_private *soruce_pixmap_priv, + glamor_pixmap_private *mask_pixmap_priv, + glamor_pixmap_private *dest_pixmap_priv, + RegionPtr region, + int x_source, + int y_source, + int x_mask, + int y_mask, + int x_dest, + int y_dest); + +void glamor_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); + +void glamor_init_composite_shaders(ScreenPtr screen); +void glamor_fini_composite_shaders(ScreenPtr screen); +void glamor_composite_glyph_rects(CARD8 op, + PicturePtr src, PicturePtr mask, + PicturePtr dst, int nrect, + glamor_composite_rect_t * rects); +void glamor_composite_rects (CARD8 op, + PicturePtr pDst, + xRenderColor *color, + int nRect, + xRectangle *rects); +void glamor_init_trapezoid_shader(ScreenPtr screen); +void glamor_fini_trapezoid_shader(ScreenPtr screen); +PicturePtr glamor_convert_gradient_picture(ScreenPtr screen, + PicturePtr source, + int x_source, + int y_source, int width, int height); + +Bool glamor_composite_choose_shader(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + glamor_pixmap_private *source_pixmap_priv, + glamor_pixmap_private *mask_pixmap_priv, + glamor_pixmap_private *dest_pixmap_priv, + struct shader_key *s_key, + glamor_composite_shader **shader, + struct blendinfo *op_info, + PictFormatShort *psaved_source_format); + +void +glamor_composite_set_shader_blend(glamor_pixmap_private *dest_priv, + struct shader_key *key, + glamor_composite_shader *shader, + struct blendinfo *op_info); + +void glamor_setup_composite_vbo(ScreenPtr screen, int n_verts); +void glamor_emit_composite_vert(ScreenPtr screen, + const float *src_coords, + const float *mask_coords, + const float *dst_coords, int i); + +/* glamor_trapezoid.c */ +void glamor_trapezoids(CARD8 op, + PicturePtr src, PicturePtr dst, + PictFormatPtr mask_format, INT16 x_src, INT16 y_src, + int ntrap, xTrapezoid * traps); + +/* glamor_tile.c */ +Bool glamor_tile(PixmapPtr pixmap, PixmapPtr tile, + int x, int y, int width, int height, + unsigned char alu, unsigned long planemask, + int tile_x, int tile_y); +void glamor_init_tile_shader(ScreenPtr screen); +void glamor_fini_tile_shader(ScreenPtr screen); + +/* glamor_gradient.c */ +void glamor_init_gradient_shader(ScreenPtr screen); +void glamor_fini_gradient_shader(ScreenPtr screen); +PicturePtr glamor_generate_linear_gradient_picture(ScreenPtr screen, + PicturePtr src_picture, + int x_source, int y_source, + int width, int height, + PictFormatShort format); +PicturePtr glamor_generate_radial_gradient_picture(ScreenPtr screen, + PicturePtr src_picture, + int x_source, int y_source, + int width, int height, + PictFormatShort format); + +/* glamor_triangles.c */ +void + +glamor_triangles(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, INT16 ySrc, int ntris, xTriangle * tris); + +/* glamor_pixmap.c */ + +void glamor_pixmap_init(ScreenPtr screen); +void glamor_pixmap_fini(ScreenPtr screen); +/** + * Download a pixmap's texture to cpu memory. If success, + * One copy of current pixmap's texture will be put into + * the pixmap->devPrivate.ptr. Will use pbo to map to + * the pointer if possible. + * The pixmap must be a gl texture pixmap. gl_fbo and + * gl_tex must be 1. Used by glamor_prepare_access. + * + */ +Bool glamor_download_pixmap_to_cpu(PixmapPtr pixmap, + glamor_access_t access); + +void * +glamor_download_sub_pixmap_to_cpu(PixmapPtr pixmap, int x, int y, int w, int h, + int stride, void *bits, int pbo, glamor_access_t access); + + +/** + * Restore a pixmap's data which is downloaded by + * glamor_download_pixmap_to_cpu to its original + * gl texture. Used by glamor_finish_access. + * + * The pixmap must be + * in texture originally. In other word, the gl_fbo + * must be 1. + **/ +void glamor_restore_pixmap_to_texture(PixmapPtr pixmap); + +/** + * According to the flag, + * if the flag is GLAMOR_CREATE_FBO_NO_FBO then just ensure + * the fbo has a valid texture. Otherwise, it will ensure + * the fbo has valid texture and attach to a valid fb. + * If the fbo already has a valid glfbo then do nothing. + */ +Bool +glamor_pixmap_ensure_fbo(PixmapPtr pixmap, GLenum format, int flag); + +/** + * Upload a pixmap to gl texture. Used by dynamic pixmap + * uploading feature. The pixmap must be a software pixmap. + * This function will change current FBO and current shaders. + */ +enum glamor_pixmap_status glamor_upload_pixmap_to_texture(PixmapPtr + pixmap); + +Bool +glamor_upload_sub_pixmap_to_texture(PixmapPtr pixmap, int x, int y, int w, int h, + int stride, void *bits, int pbo); + +PixmapPtr +glamor_get_sub_pixmap(PixmapPtr pixmap, int x, int y, + int w, int h, glamor_access_t access); +void +glamor_put_sub_pixmap(PixmapPtr sub_pixmap, PixmapPtr pixmap, int x, int y, + int w, int h, glamor_access_t access); + +glamor_pixmap_clipped_regions * +glamor_compute_clipped_regions(glamor_pixmap_private *priv, RegionPtr region, + int *clipped_nbox, int repeat_type, + int reverse, int upsidedown); + +glamor_pixmap_clipped_regions * +glamor_compute_clipped_regions_ext(glamor_pixmap_private *pixmap_priv, + RegionPtr region, + int *n_region, + int inner_block_w, int inner_block_h, + int reverse, int upsidedown); + +glamor_pixmap_clipped_regions * +glamor_compute_transform_clipped_regions(glamor_pixmap_private *priv, struct pixman_transform *transform, + RegionPtr region, int *n_region, int dx, int dy, int repeat_type, + int reverse, int upsidedown); + +Bool +glamor_composite_largepixmap_region(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + glamor_pixmap_private * source_pixmap_priv, + glamor_pixmap_private * mask_pixmap_priv, + glamor_pixmap_private * dest_pixmap_priv, + RegionPtr region, Bool force_clip, + INT16 x_source, + INT16 y_source, + INT16 x_mask, + INT16 y_mask, + INT16 x_dest, INT16 y_dest, + CARD16 width, CARD16 height); + +Bool +glamor_get_transform_block_size(struct pixman_transform *transform, + int block_w, int block_h, + int *transformed_block_w, + int *transformed_block_h); + +void +glamor_get_transform_extent_from_box(struct pixman_box32 *temp_box, + struct pixman_transform *transform); + +/** + * Upload a picture to gl texture. Similar to the + * glamor_upload_pixmap_to_texture. Used in rendering. + **/ +enum glamor_pixmap_status + glamor_upload_picture_to_texture(PicturePtr picture); + +/** + * Upload bits to a pixmap's texture. This function will + * convert the bits to the specified format/type format + * if the conversion is unavoidable. + **/ +Bool glamor_upload_bits_to_pixmap_texture(PixmapPtr pixmap, GLenum format, GLenum type, + int no_alpha, int revert, int swap_rb, void *bits); + +/** + * Destroy all the resources allocated on the uploading + * phase, includs the tex and fbo. + **/ +void glamor_destroy_upload_pixmap(PixmapPtr pixmap); + +int glamor_create_picture(PicturePtr picture); + +void glamor_set_window_pixmap(WindowPtr pWindow, PixmapPtr pPixmap); + +Bool +glamor_prepare_access_picture(PicturePtr picture, glamor_access_t access); + +void glamor_finish_access_picture(PicturePtr picture, glamor_access_t access); + +void glamor_destroy_picture(PicturePtr picture); + +/* fixup a fbo to the exact size as the pixmap. */ +Bool +glamor_fixup_pixmap_priv(ScreenPtr screen, glamor_pixmap_private *pixmap_priv); + +void +glamor_picture_format_fixup(PicturePtr picture, + glamor_pixmap_private * pixmap_priv); + +void +glamor_get_image(DrawablePtr pDrawable, int x, int y, int w, int h, + unsigned int format, unsigned long planeMask, char *d); + +void +glamor_add_traps(PicturePtr pPicture, + INT16 x_off, + INT16 y_off, int ntrap, xTrap * traps); + +RegionPtr +glamor_copy_plane(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, + int srcx, int srcy, int w, int h, int dstx, int dsty, + unsigned long bitPlane); + +void +glamor_image_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase); + +void +glamor_poly_glyph_blt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr * ppci, pointer pglyphBase); + +void +glamor_push_pixels(GCPtr pGC, PixmapPtr pBitmap, + DrawablePtr pDrawable, int w, int h, int x, int y); + +void +glamor_poly_point(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr ppt); + +void +glamor_poly_segment(DrawablePtr pDrawable, GCPtr pGC, int nseg, + xSegment *pSeg); + +void +glamor_poly_line(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, + DDXPointPtr ppt); + +void +glamor_composite_rectangles(CARD8 op, + PicturePtr dst, + xRenderColor *color, + int num_rects, + xRectangle *rects); + +/* glamor_xv */ +typedef struct { + uint32_t transform_index; + uint32_t gamma; /* gamma value x 1000 */ + int brightness; + int saturation; + int hue; + int contrast; + + DrawablePtr pDraw; + PixmapPtr pPixmap; + uint32_t src_pitch; + uint8_t *src_addr; + int src_w, src_h, dst_w, dst_h; + int src_x, src_y, drw_x, drw_y; + int w, h; + RegionRec clip; + PixmapPtr src_pix[3]; /* y, u, v for planar */ + int src_pix_w, src_pix_h; +} glamor_port_private; + +void glamor_init_xv_shader(ScreenPtr screen); +void glamor_fini_xv_shader(ScreenPtr screen); + +#include"glamor_utils.h" + +/* Dynamic pixmap upload to texture if needed. + * Sometimes, the target is a gl texture pixmap/picture, + * but the source or mask is in cpu memory. In that case, + * upload the source/mask to gl texture and then avoid + * fallback the whole process to cpu. Most of the time, + * this will increase performance obviously. */ + +#define GLAMOR_PIXMAP_DYNAMIC_UPLOAD +#define GLAMOR_GRADIENT_SHADER +#define GLAMOR_TRAPEZOID_SHADER +#define GLAMOR_TEXTURED_LARGE_PIXMAP 1 +#define WALKAROUND_LARGE_TEXTURE_MAP +#if 0 +#define MAX_FBO_SIZE 32 /* For test purpose only. */ +#endif +//#define GLYPHS_NO_EDEGEMAP_OVERLAP_CHECK +#define GLYPHS_EDEGE_OVERLAP_LOOSE_CHECK + +#endif /* GLAMOR_PRIV_H */ diff --git a/xorg-server/glamor/glamor_putimage.c b/xorg-server/glamor/glamor_putimage.c new file mode 100644 index 000000000..99f7ac6f5 --- /dev/null +++ b/xorg-server/glamor/glamor_putimage.c @@ -0,0 +1,363 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + + +/** @file glamor_putaimge.c + * + * XPutImage implementation + */ +#include "glamor_priv.h" + +void +glamor_init_putimage_shaders(ScreenPtr screen) +{ +#if 0 + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + const char *xybitmap_vs = + "uniform float x_bias;\n" "uniform float x_scale;\n" + "uniform float y_bias;\n" "uniform float y_scale;\n" + "varying vec2 bitmap_coords;\n" "void main()\n" "{\n" + " gl_Position = vec4((gl_Vertex.x + x_bias) * x_scale,\n" + " (gl_Vertex.y + y_bias) * y_scale,\n" + " 0,\n" + " 1);\n" + " bitmap_coords = gl_MultiTexCoord0.xy;\n" "}\n"; + const char *xybitmap_fs = + "uniform vec4 fg, bg;\n" "varying vec2 bitmap_coords;\n" + "uniform sampler2D bitmap_sampler;\n" "void main()\n" "{\n" + " float bitmap_value = texture2D(bitmap_sampler,\n" + " bitmap_coords).x;\n" + " gl_FragColor = mix(bg, fg, bitmap_value);\n" "}\n"; + GLint fs_prog, vs_prog, prog; + GLint sampler_uniform_location; + + if (!GLEW_ARB_fragment_shader) + return; + + prog = dispatch->glCreateProgram(); + vs_prog = glamor_compile_glsl_prog(GL_VERTEX_SHADER, xybitmap_vs); + fs_prog = + glamor_compile_glsl_prog(GL_FRAGMENT_SHADER, xybitmap_fs); + dispatch->glAttachShader(prog, vs_prog); + dispatch->glAttachShader(prog, fs_prog); + glamor_link_glsl_prog(prog); + + dispatch->glUseProgram(prog); + sampler_uniform_location = + dispatch->glGetUniformLocation(prog, "bitmap_sampler"); + dispatch->glUniform1i(sampler_uniform_location, 0); + + glamor_priv->put_image_xybitmap_fg_uniform_location = + dispatch->glGetUniformLocation(prog, "fg"); + glamor_priv->put_image_xybitmap_bg_uniform_location = + dispatch->glGetUniformLocation(prog, "bg"); + glamor_get_transform_uniform_locations(prog, + &glamor_priv->put_image_xybitmap_transform); + glamor_priv->put_image_xybitmap_prog = prog; + dispatch->glUseProgram(0); +#endif +} + + +/* Do an XYBitmap putimage. The bits are byte-aligned rows of bitmap + * data (where each row starts at a bit index of left_pad), and the + * destination gets filled with the gc's fg color where the bitmap is set + * and the bg color where the bitmap is unset. + * + * Implement this by passing the bitmap right through to GL, and sampling + * it to choose between fg and bg in the fragment shader. The driver may + * be exploding the bitmap up to be an 8-bit alpha texture, in which + * case we might be better off just doing the fg/bg choosing in the CPU + * and just draw the resulting texture to the destination. + */ +#if 0 + +static int +y_flip(PixmapPtr pixmap, int y) +{ + ScreenPtr screen = pixmap->drawable.pScreen; + PixmapPtr screen_pixmap = screen->GetScreenPixmap(screen); + + if (pixmap == screen_pixmap) + return (pixmap->drawable.height - 1) - y; + else + return y; +} + + +static void +glamor_put_image_xybitmap(DrawablePtr drawable, GCPtr gc, + int x, int y, int w, int h, int left_pad, + int image_format, char *bits) +{ + ScreenPtr screen = drawable->pScreen; + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + float fg[4], bg[4]; + GLuint tex; + unsigned int stride = PixmapBytePad(1, w + left_pad); + RegionPtr clip; + BoxPtr box; + int nbox; + float dest_coords[8]; + const float bitmap_coords[8] = { + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + 0.0, 1.0, + }; + GLfloat xscale, yscale; + glamor_pixmap_private *pixmap_priv; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + + pixmap_priv_get_scale(pixmap_priv, &xscale, &yscale); + + glamor_set_normalize_vcoords(xscale, yscale, + x, y, + x + w, y + h, + glamor_priv->yInverted, dest_coords); + + glamor_fallback("glamor_put_image_xybitmap: disabled\n"); + goto fail; + + if (glamor_priv->put_image_xybitmap_prog == 0) { + ErrorF("no program for xybitmap putimage\n"); + goto fail; + } + + glamor_set_alu(gc->alu); + if (!glamor_set_planemask(pixmap, gc->planemask)) + goto fail; + + dispatch->glUseProgram(glamor_priv->put_image_xybitmap_prog); + + glamor_get_color_4f_from_pixel(pixmap, gc->fgPixel, fg); + dispatch->glUniform4fv + (glamor_priv->put_image_xybitmap_fg_uniform_location, 1, fg); + glamor_get_color_4f_from_pixel(pixmap, gc->bgPixel, bg); + dispatch->glUniform4fv + (glamor_priv->put_image_xybitmap_bg_uniform_location, 1, bg); + + dispatch->glGenTextures(1, &tex); + dispatch->glActiveTexture(GL_TEXTURE0); + dispatch->glEnable(GL_TEXTURE_2D); + dispatch->glBindTexture(GL_TEXTURE_2D, tex); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + dispatch->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + dispatch->glPixelStorei(GL_UNPACK_ROW_LENGTH, stride * 8); + dispatch->glPixelStorei(GL_UNPACK_SKIP_PIXELS, left_pad); + dispatch->glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, + w, h, 0, GL_COLOR_INDEX, GL_BITMAP, bits); + dispatch->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + dispatch->glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + + /* Now that we've set up our bitmap texture and the shader, shove + * the destination rectangle through the cliprects and run the + * shader on the resulting fragments. + */ + dispatch->glVertexPointer(2, GL_FLOAT, 0, dest_coords); + dispatch->glEnableClientState(GL_VERTEX_ARRAY); + dispatch->glClientActiveTexture(GL_TEXTURE0); + dispatch->glTexCoordPointer(2, GL_FLOAT, 0, bitmap_coords); + dispatch->glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + dispatch->glEnable(GL_SCISSOR_TEST); + clip = fbGetCompositeClip(gc); + for (nbox = REGION_NUM_RECTS(clip), + box = REGION_RECTS(clip); nbox--; box++) { + int x1 = x; + int y1 = y; + int x2 = x + w; + int y2 = y + h; + + if (x1 < box->x1) + x1 = box->x1; + if (y1 < box->y1) + y1 = box->y1; + if (x2 > box->x2) + x2 = box->x2; + if (y2 > box->y2) + y2 = box->y2; + if (x1 >= x2 || y1 >= y2) + continue; + + dispatch->glScissor(box->x1, + y_flip(pixmap, box->y1), + box->x2 - box->x1, box->y2 - box->y1); + dispatch->glDrawArrays(GL_QUADS, 0, 4); + } + + dispatch->glDisable(GL_SCISSOR_TEST); + glamor_set_alu(GXcopy); + glamor_set_planemask(pixmap, ~0); + dispatch->glDeleteTextures(1, &tex); + dispatch->glDisable(GL_TEXTURE_2D); + dispatch->glDisableClientState(GL_VERTEX_ARRAY); + dispatch->glDisableClientState(GL_TEXTURE_COORD_ARRAY); + return; + glamor_set_alu(GXcopy); + glamor_set_planemask(pixmap, ~0); + glamor_fallback(": to %p (%c)\n", + drawable, glamor_get_drawable_location(drawable)); +fail: + if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RW)) { + fbPutImage(drawable, gc, 1, x, y, w, h, left_pad, XYBitmap, + bits); + glamor_finish_access(drawable, GLAMOR_ACCESS_RW); + } +} +#endif + +void +glamor_fini_putimage_shaders(ScreenPtr screen) +{ +} + + +static Bool +_glamor_put_image(DrawablePtr drawable, GCPtr gc, int depth, int x, int y, + int w, int h, int left_pad, int image_format, char *bits, Bool fallback) +{ + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + glamor_pixmap_private *pixmap_priv = + glamor_get_pixmap_private(pixmap); + RegionPtr clip; + int x_off, y_off; + Bool ret = FALSE; + PixmapPtr temp_pixmap, sub_pixmap; + glamor_pixmap_private *temp_pixmap_priv; + BoxRec box; + + glamor_get_drawable_deltas(drawable, pixmap, &x_off, &y_off); + clip = fbGetCompositeClip(gc); + if (image_format == XYBitmap) { + assert(depth == 1); + goto fail; + } + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) { + glamor_fallback("has no fbo.\n"); + goto fail; + } + + if (image_format != ZPixmap) { + glamor_fallback("non-ZPixmap\n"); + goto fail; + } + + if (!glamor_set_planemask(pixmap, gc->planemask)) { + goto fail; + } + /* create a temporary pixmap and upload the bits to that + * pixmap, then apply clip copy it to the destination pixmap.*/ + box.x1 = x + drawable->x; + box.y1 = y + drawable->y; + box.x2 = x + w + drawable->x; + box.y2 = y + h + drawable->y; + + if ((clip != NULL && RegionContainsRect(clip, &box) != rgnIN) + || gc->alu != GXcopy) { + temp_pixmap = glamor_create_pixmap(drawable->pScreen, w, h, depth, 0); + if (temp_pixmap == NULL) + goto fail; + + temp_pixmap_priv = glamor_get_pixmap_private(temp_pixmap); + + if (GLAMOR_PIXMAP_PRIV_IS_PICTURE(pixmap_priv)) { + temp_pixmap_priv->base.picture = pixmap_priv->base.picture; + temp_pixmap_priv->base.is_picture = pixmap_priv->base.is_picture; + } + + glamor_upload_sub_pixmap_to_texture(temp_pixmap, 0, 0, w, h, + pixmap->devKind, bits, 0); + + glamor_copy_area(&temp_pixmap->drawable, drawable, gc, 0, 0, w, h, x, y); + glamor_destroy_pixmap(temp_pixmap); + } else + glamor_upload_sub_pixmap_to_texture(pixmap, x + drawable->x + x_off, y + drawable->y + y_off, + w, h, PixmapBytePad(w, depth), bits, 0); + ret = TRUE; + goto done; + +fail: + glamor_set_planemask(pixmap, ~0); + + if (!fallback + && glamor_ddx_fallback_check_pixmap(&pixmap->drawable)) + goto done; + + glamor_fallback("to %p (%c)\n", + drawable, glamor_get_drawable_location(drawable)); + + sub_pixmap = glamor_get_sub_pixmap(pixmap, x + x_off + drawable->x, + y + y_off + drawable->y, w, h, + GLAMOR_ACCESS_RW); + if (sub_pixmap) { + if (clip != NULL) + pixman_region_translate (clip, -x - drawable->x, -y - drawable->y); + + fbPutImage(&sub_pixmap->drawable, gc, depth, 0, 0, w, h, + left_pad, image_format, bits); + + glamor_put_sub_pixmap(sub_pixmap, pixmap, + x + x_off + drawable->x, + y + y_off + drawable->y, + w, h, GLAMOR_ACCESS_RW); + if (clip != NULL) + pixman_region_translate (clip, x + drawable->x, y + drawable->y); + } else + fbPutImage(drawable, gc, depth, x, y, w, h, + left_pad, image_format, bits); + ret = TRUE; + +done: + return ret; +} + +void +glamor_put_image(DrawablePtr drawable, GCPtr gc, int depth, int x, int y, + int w, int h, int left_pad, int image_format, char *bits) +{ + _glamor_put_image(drawable, gc, depth, x, y, w, h, + left_pad, image_format, bits, TRUE); +} + +Bool +glamor_put_image_nf(DrawablePtr drawable, GCPtr gc, int depth, int x, int y, + int w, int h, int left_pad, int image_format, char *bits) +{ + return _glamor_put_image(drawable, gc, depth, x, y, w, h, + left_pad, image_format, bits, FALSE); +} + diff --git a/xorg-server/glamor/glamor_render.c b/xorg-server/glamor/glamor_render.c new file mode 100644 index 000000000..76a571f8b --- /dev/null +++ b/xorg-server/glamor/glamor_render.c @@ -0,0 +1,2135 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + * Junyan He <junyan.he@linux.intel.com> + * + */ + +/** @file glamor_render.c + * + * Render acceleration implementation + */ + +#include "glamor_priv.h" + +#ifdef RENDER +#include "mipict.h" +#include "fbpict.h" +#if 0 +//#define DEBUGF(str, ...) do {} while(0) +#define DEBUGF(str, ...) ErrorF(str, ##__VA_ARGS__) +//#define DEBUGRegionPrint(x) do {} while (0) +#define DEBUGRegionPrint RegionPrint +#endif + +static struct blendinfo composite_op_info[] = { + [PictOpClear] = {0, 0, GL_ZERO, GL_ZERO}, + [PictOpSrc] = {0, 0, GL_ONE, GL_ZERO}, + [PictOpDst] = {0, 0, GL_ZERO, GL_ONE}, + [PictOpOver] = {0, 1, GL_ONE, GL_ONE_MINUS_SRC_ALPHA}, + [PictOpOverReverse] = {1, 0, GL_ONE_MINUS_DST_ALPHA, GL_ONE}, + [PictOpIn] = {1, 0, GL_DST_ALPHA, GL_ZERO}, + [PictOpInReverse] = {0, 1, GL_ZERO, GL_SRC_ALPHA}, + [PictOpOut] = {1, 0, GL_ONE_MINUS_DST_ALPHA, GL_ZERO}, + [PictOpOutReverse] = {0, 1, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA}, + [PictOpAtop] = {1, 1, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, + [PictOpAtopReverse] = {1, 1, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA}, + [PictOpXor] = + {1, 1, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA}, + [PictOpAdd] = {0, 0, GL_ONE, GL_ONE}, +}; +#define RepeatFix 10 +static GLuint +glamor_create_composite_fs(glamor_gl_dispatch * dispatch, + struct shader_key *key) +{ + const char *repeat_define = + "#define RepeatNone 0\n" + "#define RepeatNormal 1\n" + "#define RepeatPad 2\n" + "#define RepeatReflect 3\n" + "#define RepeatFix 10\n" + "uniform int source_repeat_mode;\n" + "uniform int mask_repeat_mode;\n"; + const char *relocate_texture = + GLAMOR_DEFAULT_PRECISION + "vec2 rel_tex_coord(vec2 texture, vec4 wh, int repeat) \n" + "{\n" + " vec2 rel_tex; \n" + " rel_tex = texture * wh.xy; \n" + " if (repeat == RepeatNone)\n" + " return rel_tex; \n" + " else if (repeat == RepeatNormal) \n" + " rel_tex = floor(rel_tex) + (fract(rel_tex) / wh.xy); \n" + " else if(repeat == RepeatPad) { \n" + " if (rel_tex.x >= 1.0) rel_tex.x = 1.0 - wh.z * wh.x / 2.; \n" + " else if(rel_tex.x < 0.0) rel_tex.x = 0.0; \n" + " if (rel_tex.y >= 1.0) rel_tex.y = 1.0 - wh.w * wh.y / 2.; \n" + " else if(rel_tex.y < 0.0) rel_tex.y = 0.0; \n" + " rel_tex = rel_tex / wh.xy; \n" + " } \n" + " else if(repeat == RepeatReflect) {\n" + " if ((1.0 - mod(abs(floor(rel_tex.x)), 2.0)) < 0.001)\n" + " rel_tex.x = 2.0 - (1.0 - fract(rel_tex.x))/wh.x;\n" + " else \n" + " rel_tex.x = fract(rel_tex.x)/wh.x;\n" + " if ((1.0 - mod(abs(floor(rel_tex.y)), 2.0)) < 0.001)\n" + " rel_tex.y = 2.0 - (1.0 - fract(rel_tex.y))/wh.y;\n" + " else \n" + " rel_tex.y = fract(rel_tex.y)/wh.y;\n" + " } \n" + " return rel_tex; \n" + "}\n"; + /* The texture and the pixmap size is not match eaxctly, so can't sample it directly. + * rel_sampler will recalculate the texture coords.*/ + const char *rel_sampler = + " vec4 rel_sampler(sampler2D tex_image, vec2 tex, vec4 wh, int repeat, int set_alpha)\n" + "{\n" + " tex = rel_tex_coord(tex, wh, repeat - RepeatFix);\n" + " if (repeat == RepeatFix) {\n" + " if (!(tex.x >= 0.0 && tex.x < 1.0 \n" + " && tex.y >= 0.0 && tex.y < 1.0))\n" + " return vec4(0.0, 0.0, 0.0, set_alpha);\n" + " tex = (fract(tex) / wh.xy);\n" + " }\n" + " if (set_alpha != 1)\n" + " return texture2D(tex_image, tex);\n" + " else\n" + " return vec4(texture2D(tex_image, tex).rgb, 1.0);\n" + "}\n"; + + const char *source_solid_fetch = + GLAMOR_DEFAULT_PRECISION + "uniform vec4 source;\n" + "vec4 get_source()\n" "{\n" " return source;\n" "}\n"; + const char *source_alpha_pixmap_fetch = + GLAMOR_DEFAULT_PRECISION + "varying vec2 source_texture;\n" + "uniform sampler2D source_sampler;\n" + "uniform vec4 source_wh;" + "vec4 get_source()\n" + "{\n" + " if (source_repeat_mode < RepeatFix)\n" + " return texture2D(source_sampler, source_texture);\n" + " else \n" + " return rel_sampler(source_sampler, source_texture,\n" + " source_wh, source_repeat_mode, 0);\n" + "}\n"; + const char *source_pixmap_fetch = + GLAMOR_DEFAULT_PRECISION "varying vec2 source_texture;\n" + "uniform sampler2D source_sampler;\n" + "uniform vec4 source_wh;\n" + "vec4 get_source()\n" + "{\n" + " if (source_repeat_mode < RepeatFix) \n" + " return vec4(texture2D(source_sampler, source_texture).rgb, 1);\n" + " else \n" + " return rel_sampler(source_sampler, source_texture,\n" + " source_wh, source_repeat_mode, 1);\n" + "}\n"; + const char *mask_solid_fetch = + GLAMOR_DEFAULT_PRECISION "uniform vec4 mask;\n" + "vec4 get_mask()\n" "{\n" " return mask;\n" "}\n"; + const char *mask_alpha_pixmap_fetch = + GLAMOR_DEFAULT_PRECISION "varying vec2 mask_texture;\n" + "uniform sampler2D mask_sampler;\n" + "uniform vec4 mask_wh;\n" + "vec4 get_mask()\n" + "{\n" + " if (mask_repeat_mode < RepeatFix) \n" + " return texture2D(mask_sampler, mask_texture);\n" + " else \n" + " return rel_sampler(mask_sampler, mask_texture,\n" + " mask_wh, mask_repeat_mode, 0);\n" + "}\n"; + const char *mask_pixmap_fetch = + GLAMOR_DEFAULT_PRECISION "varying vec2 mask_texture;\n" + "uniform sampler2D mask_sampler;\n" + "uniform vec4 mask_wh;\n" + "vec4 get_mask()\n" + "{\n" + " if (mask_repeat_mode < RepeatFix) \n" + " return vec4(texture2D(mask_sampler, mask_texture).rgb, 1);\n" + " else \n" + " return rel_sampler(mask_sampler, mask_texture,\n" + " mask_wh, mask_repeat_mode, 1);\n" + "}\n"; + const char *in_source_only = + GLAMOR_DEFAULT_PRECISION "void main()\n" "{\n" + " gl_FragColor = get_source();\n" "}\n"; + const char *in_normal = + GLAMOR_DEFAULT_PRECISION "void main()\n" "{\n" + " gl_FragColor = get_source() * get_mask().a;\n" "}\n"; + const char *in_ca_source = + GLAMOR_DEFAULT_PRECISION "void main()\n" "{\n" + " gl_FragColor = get_source() * get_mask();\n" "}\n"; + const char *in_ca_alpha = + GLAMOR_DEFAULT_PRECISION "void main()\n" "{\n" + " gl_FragColor = get_source().a * get_mask();\n" "}\n"; + char *source; + const char *source_fetch; + const char *mask_fetch = ""; + const char *in; + GLuint prog; + + switch (key->source) { + case SHADER_SOURCE_SOLID: + source_fetch = source_solid_fetch; + break; + case SHADER_SOURCE_TEXTURE_ALPHA: + source_fetch = source_alpha_pixmap_fetch; + break; + case SHADER_SOURCE_TEXTURE: + source_fetch = source_pixmap_fetch; + break; + default: + FatalError("Bad composite shader source"); + } + + switch (key->mask) { + case SHADER_MASK_NONE: + break; + case SHADER_MASK_SOLID: + mask_fetch = mask_solid_fetch; + break; + case SHADER_MASK_TEXTURE_ALPHA: + mask_fetch = mask_alpha_pixmap_fetch; + break; + case SHADER_MASK_TEXTURE: + mask_fetch = mask_pixmap_fetch; + break; + default: + FatalError("Bad composite shader mask"); + } + + switch (key->in) { + case SHADER_IN_SOURCE_ONLY: + in = in_source_only; + break; + case SHADER_IN_NORMAL: + in = in_normal; + break; + case SHADER_IN_CA_SOURCE: + in = in_ca_source; + break; + case SHADER_IN_CA_ALPHA: + in = in_ca_alpha; + break; + default: + FatalError("Bad composite IN type"); + } + + XNFasprintf(&source, "%s%s%s%s%s%s", repeat_define, relocate_texture, rel_sampler,source_fetch, mask_fetch, in); + + + prog = glamor_compile_glsl_prog(dispatch, GL_FRAGMENT_SHADER, + source); + free(source); + + return prog; +} + +static GLuint +glamor_create_composite_vs(glamor_gl_dispatch * dispatch, + struct shader_key *key) +{ + const char *main_opening = + "attribute vec4 v_position;\n" + "attribute vec4 v_texcoord0;\n" + "attribute vec4 v_texcoord1;\n" + "varying vec2 source_texture;\n" + "varying vec2 mask_texture;\n" + "void main()\n" "{\n" " gl_Position = v_position;\n"; + const char *source_coords = + " source_texture = v_texcoord0.xy;\n"; + const char *mask_coords = " mask_texture = v_texcoord1.xy;\n"; + const char *main_closing = "}\n"; + const char *source_coords_setup = ""; + const char *mask_coords_setup = ""; + char *source; + GLuint prog; + + if (key->source != SHADER_SOURCE_SOLID) + source_coords_setup = source_coords; + + if (key->mask != SHADER_MASK_NONE + && key->mask != SHADER_MASK_SOLID) + mask_coords_setup = mask_coords; + + XNFasprintf(&source, + "%s%s%s%s", + main_opening, + source_coords_setup, mask_coords_setup, main_closing); + + prog = + glamor_compile_glsl_prog(dispatch, GL_VERTEX_SHADER, source); + free(source); + + return prog; +} + +static void +glamor_create_composite_shader(ScreenPtr screen, struct shader_key *key, + glamor_composite_shader * shader) +{ + GLuint vs, fs, prog; + GLint source_sampler_uniform_location, + mask_sampler_uniform_location; + glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); + glamor_gl_dispatch *dispatch; + + dispatch = glamor_get_dispatch(glamor_priv); + vs = glamor_create_composite_vs(dispatch, key); + if (vs == 0) + goto out; + fs = glamor_create_composite_fs(dispatch, key); + if (fs == 0) + goto out; + + prog = dispatch->glCreateProgram(); + dispatch->glAttachShader(prog, vs); + dispatch->glAttachShader(prog, fs); + + dispatch->glBindAttribLocation(prog, GLAMOR_VERTEX_POS, + "v_position"); + dispatch->glBindAttribLocation(prog, GLAMOR_VERTEX_SOURCE, + "v_texcoord0"); + dispatch->glBindAttribLocation(prog, GLAMOR_VERTEX_MASK, + "v_texcoord1"); + + glamor_link_glsl_prog(dispatch, prog); + + shader->prog = prog; + + dispatch->glUseProgram(prog); + + if (key->source == SHADER_SOURCE_SOLID) { + shader->source_uniform_location = + dispatch->glGetUniformLocation(prog, "source"); + } else { + source_sampler_uniform_location = + dispatch->glGetUniformLocation(prog, "source_sampler"); + dispatch->glUniform1i(source_sampler_uniform_location, 0); + shader->source_wh = dispatch->glGetUniformLocation(prog, "source_wh"); + shader->source_repeat_mode = dispatch->glGetUniformLocation(prog, "source_repeat_mode"); + } + + if (key->mask != SHADER_MASK_NONE) { + if (key->mask == SHADER_MASK_SOLID) { + shader->mask_uniform_location = + dispatch->glGetUniformLocation(prog, "mask"); + } else { + mask_sampler_uniform_location = + dispatch->glGetUniformLocation(prog, + "mask_sampler"); + dispatch->glUniform1i + (mask_sampler_uniform_location, 1); + shader->mask_wh = dispatch->glGetUniformLocation(prog, "mask_wh"); + shader->mask_repeat_mode = dispatch->glGetUniformLocation(prog, "mask_repeat_mode"); + } + } + +out: + glamor_put_dispatch(glamor_priv); +} + +static glamor_composite_shader * +glamor_lookup_composite_shader(ScreenPtr screen, struct + shader_key + *key) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_composite_shader *shader; + + shader = + &glamor_priv->composite_shader[key->source][key-> + mask][key->in]; + if (shader->prog == 0) + glamor_create_composite_shader(screen, key, shader); + + return shader; +} + +static void +glamor_init_eb(unsigned short *eb, int vert_cnt) +{ + int i, j; + for(i = 0, j = 0; j < vert_cnt; i += 6, j += 4) + { + eb[i] = j; + eb[i + 1] = j + 1; + eb[i + 2] = j + 2; + eb[i + 3] = j; + eb[i + 4] = j + 2; + eb[i + 5] = j + 3; + } +} + +void +glamor_init_composite_shaders(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + unsigned short *eb; + float *vb = NULL; + int eb_size; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glGenBuffers(1, &glamor_priv->vbo); + dispatch->glGenBuffers(1, &glamor_priv->ebo); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glamor_priv->ebo); + + eb_size = GLAMOR_COMPOSITE_VBO_VERT_CNT * sizeof(short) * 2; + + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) { + dispatch->glBufferData(GL_ELEMENT_ARRAY_BUFFER, + eb_size, + NULL, GL_STATIC_DRAW); + eb = dispatch->glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + } + else { + vb = malloc(GLAMOR_COMPOSITE_VBO_VERT_CNT * sizeof(float) * 2); + if (vb == NULL) + FatalError("Failed to allocate vb memory.\n"); + eb = malloc(eb_size); + } + + if (eb == NULL) + FatalError("fatal error, fail to get element buffer. GL context may be not created correctly.\n"); + glamor_init_eb(eb, GLAMOR_COMPOSITE_VBO_VERT_CNT); + + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) { + dispatch->glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } else { + dispatch->glBufferData(GL_ELEMENT_ARRAY_BUFFER, + eb_size, + eb, GL_STATIC_DRAW); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, glamor_priv->vbo); + dispatch->glBufferData(GL_ARRAY_BUFFER, + GLAMOR_COMPOSITE_VBO_VERT_CNT * sizeof(float) * 2, + NULL, GL_DYNAMIC_DRAW); + dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0); + + free(eb); + glamor_priv->vb = (char*)vb; + } + + glamor_put_dispatch(glamor_priv); +} + +void +glamor_fini_composite_shaders(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + glamor_composite_shader *shader; + int i,j,k; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glDeleteBuffers(1, &glamor_priv->vbo); + dispatch->glDeleteBuffers(1, &glamor_priv->ebo); + + for(i = 0; i < SHADER_SOURCE_COUNT; i++) + for(j = 0; j < SHADER_MASK_COUNT; j++) + for(k = 0; k < SHADER_IN_COUNT; k++) + { + shader = &glamor_priv->composite_shader[i][j][k]; + if (shader->prog) + dispatch->glDeleteProgram(shader->prog); + } + if (glamor_priv->gl_flavor != GLAMOR_GL_DESKTOP + && glamor_priv->vb) + free(glamor_priv->vb); + + glamor_put_dispatch(glamor_priv); +} + +static Bool +glamor_set_composite_op(ScreenPtr screen, + CARD8 op, struct blendinfo *op_info_result, + PicturePtr dest, PicturePtr mask) +{ + GLenum source_blend, dest_blend; + struct blendinfo *op_info; + + if (op >= ARRAY_SIZE(composite_op_info)) { + glamor_fallback("unsupported render op %d \n", op); + return GL_FALSE; + } + op_info = &composite_op_info[op]; + + source_blend = op_info->source_blend; + dest_blend = op_info->dest_blend; + + /* If there's no dst alpha channel, adjust the blend op so that we'll treat + * it as always 1. + */ + if (PICT_FORMAT_A(dest->format) == 0 && op_info->dest_alpha) { + if (source_blend == GL_DST_ALPHA) + source_blend = GL_ONE; + else if (source_blend == GL_ONE_MINUS_DST_ALPHA) + source_blend = GL_ZERO; + } + + /* Set up the source alpha value for blending in component alpha mode. */ + if (mask && mask->componentAlpha + && PICT_FORMAT_RGB(mask->format) != 0 && op_info->source_alpha) + { + if (dest_blend == GL_SRC_ALPHA) + dest_blend = GL_SRC_COLOR; + else if (dest_blend == GL_ONE_MINUS_SRC_ALPHA) + dest_blend = GL_ONE_MINUS_SRC_COLOR; + } + + op_info_result->source_blend = source_blend; + op_info_result->dest_blend = dest_blend; + op_info_result->source_alpha = op_info->source_alpha; + op_info_result->dest_alpha = op_info->dest_alpha; + + return TRUE; +} + +static void +glamor_set_composite_texture(glamor_screen_private *glamor_priv, int unit, + PicturePtr picture, + glamor_pixmap_private * pixmap_priv, + GLuint wh_location, GLuint repeat_location) +{ + glamor_gl_dispatch *dispatch; + float wh[4]; + int repeat_type; + + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glActiveTexture(GL_TEXTURE0 + unit); + dispatch->glBindTexture(GL_TEXTURE_2D, pixmap_priv->base.fbo->tex); + repeat_type = picture->repeatType; + switch (picture->repeatType) { + case RepeatNone: +#ifndef GLAMOR_GLES2 + /* XXX GLES2 doesn't support GL_CLAMP_TO_BORDER. */ + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_BORDER); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_BORDER); +#else + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); +#endif + break; + case RepeatNormal: + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_REPEAT); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GL_REPEAT); + break; + case RepeatPad: + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + break; + case RepeatReflect: + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_MIRRORED_REPEAT); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GL_MIRRORED_REPEAT); + break; + } + + switch (picture->filter) { + default: + case PictFilterFast: + case PictFilterNearest: + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + break; + case PictFilterGood: + case PictFilterBest: + case PictFilterBilinear: + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_LINEAR); + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_LINEAR); + break; + } +#ifndef GLAMOR_GLES2 + dispatch->glEnable(GL_TEXTURE_2D); +#endif + + /* + * GLES2 doesn't support RepeatNone. We need to fix it anyway. + * + **/ + if (repeat_type != RepeatNone) + repeat_type += RepeatFix; + else if (glamor_priv->gl_flavor == GLAMOR_GL_ES2 + || pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + if (picture->transform + || (GLAMOR_PIXMAP_FBO_NOT_EAXCT_SIZE(pixmap_priv))) + repeat_type += RepeatFix; + } + if (repeat_type >= RepeatFix) { + glamor_pixmap_fbo_fix_wh_ratio(wh, pixmap_priv); + if ((wh[0] != 1.0 || wh[1] != 1.0 ) + || (glamor_priv->gl_flavor == GLAMOR_GL_ES2 + && repeat_type == RepeatFix)) + dispatch->glUniform4fv(wh_location, 1, wh); + else + repeat_type -= RepeatFix; + } + dispatch->glUniform1i(repeat_location, repeat_type); + glamor_put_dispatch(glamor_priv); +} + +static void +glamor_set_composite_solid(glamor_gl_dispatch * dispatch, float *color, + GLint uniform_location) +{ + dispatch->glUniform4fv(uniform_location, 1, color); +} + +static int +compatible_formats(CARD8 op, PicturePtr dst, PicturePtr src) +{ + if (op == PictOpSrc) { + if (src->format == dst->format) + return 1; + + if (src->format == PICT_a8r8g8b8 + && dst->format == PICT_x8r8g8b8) + return 1; + + if (src->format == PICT_a8b8g8r8 + && dst->format == PICT_x8b8g8r8) + return 1; + } else if (op == PictOpOver) { + if (src->alphaMap || dst->alphaMap) + return 0; + + if (src->format != dst->format) + return 0; + + if (src->format == PICT_x8r8g8b8 + || src->format == PICT_x8b8g8r8) + return 1; + } + + return 0; +} + +static char +glamor_get_picture_location(PicturePtr picture) +{ + if (picture == NULL) + return ' '; + + if (picture->pDrawable == NULL) { + switch (picture->pSourcePict->type) { + case SourcePictTypeSolidFill: + return 'c'; + case SourcePictTypeLinear: + return 'l'; + case SourcePictTypeRadial: + return 'r'; + default: + return '?'; + } + } + return glamor_get_drawable_location(picture->pDrawable); +} + +static Bool +glamor_composite_with_copy(CARD8 op, + PicturePtr source, + PicturePtr dest, + INT16 x_source, + INT16 y_source, + INT16 x_dest, + INT16 y_dest, + RegionPtr region) +{ + int ret = FALSE; + if (!source->pDrawable) + return FALSE; + + if (!compatible_formats(op, dest, source)) + return FALSE; + + if (source->repeat || source->transform) { + return FALSE; + } + + x_dest += dest->pDrawable->x; + y_dest += dest->pDrawable->y; + x_source += source->pDrawable->x; + y_source += source->pDrawable->y; + if (PICT_FORMAT_A(source->format) == 0) { + /* Fallback if we sample outside the source so that we + * swizzle the correct clear color for out-of-bounds texels. + */ + if (region->extents.x1 + x_source - x_dest < 0) + goto cleanup_region; + if (region->extents.x2 + x_source - x_dest > source->pDrawable->width) + goto cleanup_region; + + if (region->extents.y1 + y_source - y_dest < 0) + goto cleanup_region; + if (region->extents.y2 + y_source - y_dest > source->pDrawable->height) + goto cleanup_region; + } + ret = glamor_copy_n_to_n_nf(source->pDrawable, + dest->pDrawable, NULL, + RegionRects(region), RegionNumRects(region), + x_source - x_dest, y_source - y_dest, + FALSE, FALSE, 0, NULL); +cleanup_region: + return ret; +} + +void +glamor_setup_composite_vbo(ScreenPtr screen, int n_verts) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_gl_dispatch *dispatch; + int vert_size; + + glamor_priv->render_nr_verts = 0; + glamor_priv->vb_stride = 2 * sizeof(float); + if (glamor_priv->has_source_coords) + glamor_priv->vb_stride += 2 * sizeof(float); + if (glamor_priv->has_mask_coords) + glamor_priv->vb_stride += 2 * sizeof(float); + + vert_size = n_verts * glamor_priv->vb_stride; + + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glBindBuffer(GL_ARRAY_BUFFER, glamor_priv->vbo); + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) { + if (glamor_priv->vbo_size < (glamor_priv->vbo_offset + vert_size)) { + glamor_priv->vbo_size = GLAMOR_COMPOSITE_VBO_VERT_CNT * + glamor_priv->vb_stride; + glamor_priv->vbo_offset = 0; + dispatch->glBufferData(GL_ARRAY_BUFFER, + glamor_priv->vbo_size, + NULL, GL_STREAM_DRAW); + } + + glamor_priv->vb = dispatch->glMapBufferRange(GL_ARRAY_BUFFER, + glamor_priv->vbo_offset, + vert_size, + GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + assert(glamor_priv->vb != NULL); + glamor_priv->vb -= glamor_priv->vbo_offset; + } else + glamor_priv->vbo_offset = 0; + + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glamor_priv->ebo); + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, + GL_FALSE, glamor_priv->vb_stride, + (void *) ((long) + glamor_priv->vbo_offset)); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); + + if (glamor_priv->has_source_coords) { + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, + GL_FLOAT, GL_FALSE, + glamor_priv->vb_stride, + (void *) ((long) + glamor_priv->vbo_offset + + + 2 * + sizeof(float))); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + } + + if (glamor_priv->has_mask_coords) { + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_MASK, 2, + GL_FLOAT, GL_FALSE, + glamor_priv->vb_stride, + (void *) ((long) + glamor_priv->vbo_offset + + + (glamor_priv->has_source_coords + ? 4 : 2) * + sizeof(float))); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_MASK); + } + glamor_put_dispatch(glamor_priv); +} + +void +glamor_emit_composite_vert(ScreenPtr screen, + const float *src_coords, + const float *mask_coords, + const float *dst_coords, int i) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + float *vb = (float *) (glamor_priv->vb + glamor_priv->vbo_offset); + int j = 0; + + vb[j++] = dst_coords[i * 2 + 0]; + vb[j++] = dst_coords[i * 2 + 1]; + if (glamor_priv->has_source_coords) { + vb[j++] = src_coords[i * 2 + 0]; + vb[j++] = src_coords[i * 2 + 1]; + } + if (glamor_priv->has_mask_coords) { + vb[j++] = mask_coords[i * 2 + 0]; + vb[j++] = mask_coords[i * 2 + 1]; + } + + glamor_priv->render_nr_verts++; + glamor_priv->vbo_offset += glamor_priv->vb_stride; +} + + + +static void +glamor_flush_composite_rects(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_gl_dispatch *dispatch; + + dispatch = glamor_get_dispatch(glamor_priv); + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) + dispatch->glUnmapBuffer(GL_ARRAY_BUFFER); + else { + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, glamor_priv->vbo); + dispatch->glBufferData(GL_ARRAY_BUFFER, + glamor_priv->vbo_offset, + glamor_priv->vb, GL_DYNAMIC_DRAW); + } + + if (!glamor_priv->render_nr_verts) + return; + +#ifndef GLAMOR_GLES2 + dispatch->glDrawRangeElements(GL_TRIANGLES, 0, glamor_priv->render_nr_verts, + (glamor_priv->render_nr_verts * 3) / 2, + GL_UNSIGNED_SHORT, NULL); +#else + dispatch->glDrawElements(GL_TRIANGLES, (glamor_priv->render_nr_verts * 3) / 2, + GL_UNSIGNED_SHORT, NULL); +#endif + glamor_put_dispatch(glamor_priv); +} + +int pict_format_combine_tab[][3] = { + {PICT_TYPE_ARGB, PICT_TYPE_A, PICT_TYPE_ARGB}, + {PICT_TYPE_ABGR, PICT_TYPE_A, PICT_TYPE_ABGR}, +}; + +static Bool +combine_pict_format(PictFormatShort * des, const PictFormatShort src, + const PictFormatShort mask, enum shader_in in_ca) +{ + PictFormatShort new_vis; + int src_type, mask_type, src_bpp, mask_bpp; + int i; + if (src == mask) { + *des = src; + return TRUE; + } + src_bpp = PICT_FORMAT_BPP(src); + mask_bpp = PICT_FORMAT_BPP(mask); + + assert(src_bpp == mask_bpp); + + new_vis = PICT_FORMAT_VIS(src) | PICT_FORMAT_VIS(mask); + + switch (in_ca) { + case SHADER_IN_SOURCE_ONLY: + return TRUE; + case SHADER_IN_NORMAL: + src_type = PICT_FORMAT_TYPE(src); + mask_type = PICT_TYPE_A; + break; + case SHADER_IN_CA_SOURCE: + src_type = PICT_FORMAT_TYPE(src); + mask_type = PICT_FORMAT_TYPE(mask); + break; + case SHADER_IN_CA_ALPHA: + src_type = PICT_TYPE_A; + mask_type = PICT_FORMAT_TYPE(mask); + break; + default: + return FALSE; + } + + if (src_type == mask_type) { + *des = PICT_VISFORMAT(src_bpp, src_type, new_vis); + return TRUE; + } + + for (i = 0; + i < + sizeof(pict_format_combine_tab) / + sizeof(pict_format_combine_tab[0]); i++) { + if ((src_type == pict_format_combine_tab[i][0] + && mask_type == pict_format_combine_tab[i][1]) + || (src_type == pict_format_combine_tab[i][1] + && mask_type == pict_format_combine_tab[i][0])) { + *des = PICT_VISFORMAT(src_bpp, + pict_format_combine_tab[i] + [2], new_vis); + return TRUE; + } + } + return FALSE; +} + +static void +glamor_set_normalize_tcoords_generic(glamor_pixmap_private *priv, + int repeat_type, + float *matrix, + float xscale, float yscale, + int x1, int y1, int x2, int y2, + int yInverted, float *texcoords, + int stride) +{ + if (!matrix && repeat_type == RepeatNone) + glamor_set_normalize_tcoords_ext(priv, xscale, yscale, + x1, y1, + x2, y2, + yInverted, + texcoords, stride); + else if (matrix && repeat_type == RepeatNone) + glamor_set_transformed_normalize_tcoords_ext(priv, matrix, xscale, + yscale, x1, y1, + x2, y2, + yInverted, + texcoords, stride); + else if (!matrix && repeat_type != RepeatNone) + glamor_set_repeat_normalize_tcoords_ext(priv, repeat_type, + xscale, yscale, + x1, y1, + x2, y2, + yInverted, + texcoords, stride); + else if (matrix && repeat_type != RepeatNone) + glamor_set_repeat_transformed_normalize_tcoords_ext(priv, repeat_type, + matrix, xscale, yscale, + x1, y1, + x2, y2, + yInverted, + texcoords, stride); +} + +Bool glamor_composite_choose_shader(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + glamor_pixmap_private *source_pixmap_priv, + glamor_pixmap_private *mask_pixmap_priv, + glamor_pixmap_private *dest_pixmap_priv, + struct shader_key *s_key, + glamor_composite_shader **shader, + struct blendinfo *op_info, + PictFormatShort *psaved_source_format) +{ + ScreenPtr screen = dest->pDrawable->pScreen; + PixmapPtr dest_pixmap = dest_pixmap_priv->base.pixmap; + PixmapPtr source_pixmap = NULL; + PixmapPtr mask_pixmap = NULL; + enum glamor_pixmap_status source_status = GLAMOR_NONE; + enum glamor_pixmap_status mask_status = GLAMOR_NONE; + PictFormatShort saved_source_format = 0; + struct shader_key key; + GLfloat source_solid_color[4]; + GLfloat mask_solid_color[4]; + Bool ret = FALSE; + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dest_pixmap_priv)) { + glamor_fallback("dest has no fbo.\n"); + goto fail; + } + + memset(&key, 0, sizeof(key)); + if (!source) { + key.source = SHADER_SOURCE_SOLID; + source_solid_color[0] = 0.0; + source_solid_color[1] = 0.0; + source_solid_color[2] = 0.0; + source_solid_color[3] = 0.0; + } else if (!source->pDrawable) { + if (source->pSourcePict->type == SourcePictTypeSolidFill) { + key.source = SHADER_SOURCE_SOLID; + glamor_get_rgba_from_pixel(source-> + pSourcePict->solidFill. + color, + &source_solid_color[0], + &source_solid_color[1], + &source_solid_color[2], + &source_solid_color[3], + PICT_a8r8g8b8); + } else + goto fail; + } else { + key.source = SHADER_SOURCE_TEXTURE_ALPHA; + } + + if (mask) { + if (!mask->pDrawable) { + if (mask->pSourcePict->type == + SourcePictTypeSolidFill) { + key.mask = SHADER_MASK_SOLID; + glamor_get_rgba_from_pixel + (mask->pSourcePict->solidFill.color, + &mask_solid_color[0], + &mask_solid_color[1], + &mask_solid_color[2], + &mask_solid_color[3], PICT_a8r8g8b8); + } else + goto fail; + } else { + key.mask = SHADER_MASK_TEXTURE_ALPHA; + } + + if (!mask->componentAlpha) { + key.in = SHADER_IN_NORMAL; + } else { + if (op == PictOpClear) + key.mask = SHADER_MASK_NONE; + else if (op == PictOpSrc || op == PictOpAdd + || op == PictOpIn || op == PictOpOut + || op == PictOpOverReverse) + key.in = SHADER_IN_CA_SOURCE; + else if (op == PictOpOutReverse || op == PictOpInReverse) { + key.in = SHADER_IN_CA_ALPHA; + } else { + glamor_fallback("Unsupported component alpha op: %d\n", op); + goto fail; + } + } + } else { + key.mask = SHADER_MASK_NONE; + key.in = SHADER_IN_SOURCE_ONLY; + } + + if (source && source->alphaMap) { + glamor_fallback("source alphaMap\n"); + goto fail; + } + if (mask && mask->alphaMap) { + glamor_fallback("mask alphaMap\n"); + goto fail; + } + + if (key.source == SHADER_SOURCE_TEXTURE || + key.source == SHADER_SOURCE_TEXTURE_ALPHA) { + source_pixmap = source_pixmap_priv->base.pixmap; + if (source_pixmap == dest_pixmap) { + /* XXX source and the dest share the same texture. + * Does it need special handle? */ + glamor_fallback("source == dest\n"); + } + if (source_pixmap_priv->base.gl_fbo == 0) { + /* XXX in Xephyr, we may have gl_fbo equal to 1 but gl_tex + * equal to zero when the pixmap is screen pixmap. Then we may + * refer the tex zero directly latter in the composition. + * It seems that it works fine, but it may have potential problem*/ +#ifdef GLAMOR_PIXMAP_DYNAMIC_UPLOAD + source_status = GLAMOR_UPLOAD_PENDING; +#else + glamor_fallback("no texture in source\n"); + goto fail; +#endif + } + } + + if (key.mask == SHADER_MASK_TEXTURE || + key.mask == SHADER_MASK_TEXTURE_ALPHA) { + mask_pixmap = mask_pixmap_priv->base.pixmap; + if (mask_pixmap == dest_pixmap) { + glamor_fallback("mask == dest\n"); + goto fail; + } + if (mask_pixmap_priv->base.gl_fbo == 0) { +#ifdef GLAMOR_PIXMAP_DYNAMIC_UPLOAD + mask_status = GLAMOR_UPLOAD_PENDING; +#else + glamor_fallback("no texture in mask\n"); + goto fail; +#endif + } + } + +#ifdef GLAMOR_PIXMAP_DYNAMIC_UPLOAD + if (source_status == GLAMOR_UPLOAD_PENDING + && mask_status == GLAMOR_UPLOAD_PENDING + && source_pixmap == mask_pixmap) { + + if (source->format != mask->format) { + saved_source_format = source->format; + + if (!combine_pict_format(&source->format, source->format, + mask->format, key.in)) { + glamor_fallback("combine source %x mask %x failed.\n", + source->format, mask->format); + goto fail; + } + + if (source->format != saved_source_format) { + glamor_picture_format_fixup(source, + source_pixmap_priv); + } + /* XXX + * By default, glamor_upload_picture_to_texture will wire alpha to 1 + * if one picture doesn't have alpha. So we don't do that again in + * rendering function. But here is a special case, as source and + * mask share the same texture but may have different formats. For + * example, source doesn't have alpha, but mask has alpha. Then the + * texture will have the alpha value for the mask. And will not wire + * to 1 for the source. In this case, we have to use different shader + * to wire the source's alpha to 1. + * + * But this may cause a potential problem if the source's repeat mode + * is REPEAT_NONE, and if the source is smaller than the dest, then + * for the region not covered by the source may be painted incorrectly. + * because we wire the alpha to 1. + * + **/ + if (!PICT_FORMAT_A(saved_source_format) + && PICT_FORMAT_A(mask->format)) + key.source = SHADER_SOURCE_TEXTURE; + + if (!PICT_FORMAT_A(mask->format) + && PICT_FORMAT_A(saved_source_format)) + key.mask = SHADER_MASK_TEXTURE; + + mask_status = GLAMOR_NONE; + } + + source_status = glamor_upload_picture_to_texture(source); + if (source_status != GLAMOR_UPLOAD_DONE) { + glamor_fallback("Failed to upload source texture.\n"); + goto fail; + } + } else { + if (source_status == GLAMOR_UPLOAD_PENDING) { + source_status = glamor_upload_picture_to_texture(source); + if (source_status != GLAMOR_UPLOAD_DONE) { + glamor_fallback("Failed to upload source texture.\n"); + goto fail; + } + } + + if (mask_status == GLAMOR_UPLOAD_PENDING) { + mask_status = glamor_upload_picture_to_texture(mask); + if (mask_status != GLAMOR_UPLOAD_DONE) { + glamor_fallback("Failed to upload mask texture.\n"); + goto fail; + } + } + } +#endif + + /*Before enter the rendering stage, we need to fixup + * transformed source and mask, if the transform is not int translate. */ + if (key.source != SHADER_SOURCE_SOLID + && source->transform + && !pixman_transform_is_int_translate(source->transform) + && source_pixmap_priv->type != GLAMOR_TEXTURE_LARGE) { + if (!glamor_fixup_pixmap_priv(screen, source_pixmap_priv)) + goto fail; + } + if (key.mask != SHADER_MASK_NONE && key.mask != SHADER_MASK_SOLID + && mask->transform + && !pixman_transform_is_int_translate(mask->transform) + && mask_pixmap_priv->type != GLAMOR_TEXTURE_LARGE) { + if (!glamor_fixup_pixmap_priv(screen, mask_pixmap_priv)) + goto fail; + } + + + if (!glamor_set_composite_op(screen, op, op_info, dest, mask)) + goto fail; + + *shader = glamor_lookup_composite_shader(screen, &key); + if ((*shader)->prog == 0) { + glamor_fallback("no shader program for this" + "render acccel mode\n"); + goto fail; + } + + if (key.source == SHADER_SOURCE_SOLID) + memcpy(&(*shader)->source_solid_color[0], + source_solid_color, 4*sizeof(float)); + else { + (*shader)->source_priv = source_pixmap_priv; + (*shader)->source = source; + } + + if (key.mask == SHADER_MASK_SOLID) + memcpy(&(*shader)->mask_solid_color[0], + mask_solid_color, 4*sizeof(float)); + else { + (*shader)->mask_priv = mask_pixmap_priv; + (*shader)->mask = mask; + } + + ret = TRUE; + memcpy(s_key, &key, sizeof(key)); + *psaved_source_format = saved_source_format; + goto done; + +fail: + if (saved_source_format) + source->format = saved_source_format; +done: + return ret; +} + +void +glamor_composite_set_shader_blend(glamor_pixmap_private *dest_priv, + struct shader_key *key, + glamor_composite_shader *shader, + struct blendinfo *op_info) +{ + glamor_gl_dispatch *dispatch; + glamor_screen_private *glamor_priv; + + glamor_priv = dest_priv->base.glamor_priv; + + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glUseProgram(shader->prog); + + if (key->source == SHADER_SOURCE_SOLID) { + glamor_set_composite_solid(dispatch, + shader->source_solid_color, + shader->source_uniform_location); + } else { + glamor_set_composite_texture(glamor_priv, 0, + shader->source, + shader->source_priv, shader->source_wh, + shader->source_repeat_mode); + } + + if (key->mask != SHADER_MASK_NONE) { + if (key->mask == SHADER_MASK_SOLID) { + glamor_set_composite_solid(dispatch, + shader->mask_solid_color, + shader->mask_uniform_location); + } else { + glamor_set_composite_texture(glamor_priv, 1, + shader->mask, + shader->mask_priv, shader->mask_wh, + shader->mask_repeat_mode); + } + } + + if (op_info->source_blend == GL_ONE + && op_info->dest_blend == GL_ZERO) { + dispatch->glDisable(GL_BLEND); + } else { + dispatch->glEnable(GL_BLEND); + dispatch->glBlendFunc(op_info->source_blend, + op_info->dest_blend); + } + + glamor_put_dispatch(glamor_priv); +} + +static Bool +glamor_composite_with_shader(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + glamor_pixmap_private *source_pixmap_priv, + glamor_pixmap_private *mask_pixmap_priv, + glamor_pixmap_private *dest_pixmap_priv, + int nrect, glamor_composite_rect_t * rects, + Bool two_pass_ca) +{ + ScreenPtr screen = dest->pDrawable->pScreen; + glamor_screen_private *glamor_priv = dest_pixmap_priv->base.glamor_priv; + PixmapPtr dest_pixmap = dest_pixmap_priv->base.pixmap; + PixmapPtr source_pixmap = NULL; + PixmapPtr mask_pixmap = NULL; + glamor_gl_dispatch *dispatch = NULL; + GLfloat dst_xscale, dst_yscale; + GLfloat mask_xscale = 1, mask_yscale = 1, + src_xscale = 1, src_yscale = 1; + struct shader_key key, key_ca; + float *vertices; + int dest_x_off, dest_y_off; + int source_x_off, source_y_off; + int mask_x_off, mask_y_off; + PictFormatShort saved_source_format = 0; + float src_matrix[9], mask_matrix[9]; + float *psrc_matrix = NULL, *pmask_matrix = NULL; + int vert_stride = 4; + int nrect_max; + Bool ret = FALSE; + glamor_composite_shader *shader = NULL, *shader_ca = NULL; + struct blendinfo op_info, op_info_ca; + + if(!glamor_composite_choose_shader(op, source, mask, dest, + source_pixmap_priv, mask_pixmap_priv, + dest_pixmap_priv, + &key, &shader, &op_info, + &saved_source_format)) { + glamor_fallback("glamor_composite_choose_shader failed\n"); + return ret; + } + if (two_pass_ca) { + if(!glamor_composite_choose_shader(PictOpAdd, source, mask, dest, + source_pixmap_priv, mask_pixmap_priv, + dest_pixmap_priv, + &key_ca, &shader_ca, &op_info_ca, + &saved_source_format)) { + glamor_fallback("glamor_composite_choose_shader failed\n"); + return ret; + } + } + + glamor_set_destination_pixmap_priv_nc(dest_pixmap_priv); + glamor_composite_set_shader_blend(dest_pixmap_priv, &key, shader, &op_info); + + dispatch = glamor_get_dispatch(glamor_priv); + + glamor_priv->has_source_coords = key.source != SHADER_SOURCE_SOLID; + glamor_priv->has_mask_coords = (key.mask != SHADER_MASK_NONE && + key.mask != SHADER_MASK_SOLID); + + dest_pixmap = glamor_get_drawable_pixmap(dest->pDrawable); + dest_pixmap_priv = glamor_get_pixmap_private(dest_pixmap); + glamor_get_drawable_deltas(dest->pDrawable, dest_pixmap, + &dest_x_off, &dest_y_off); + pixmap_priv_get_dest_scale(dest_pixmap_priv, &dst_xscale, &dst_yscale); + + if (glamor_priv->has_source_coords) { + source_pixmap = source_pixmap_priv->base.pixmap; + glamor_get_drawable_deltas(source->pDrawable, + source_pixmap, &source_x_off, + &source_y_off); + pixmap_priv_get_scale(source_pixmap_priv, &src_xscale, + &src_yscale); + if (source->transform) { + psrc_matrix = src_matrix; + glamor_picture_get_matrixf(source, psrc_matrix); + } + vert_stride += 4; + } + + if (glamor_priv->has_mask_coords) { + mask_pixmap = mask_pixmap_priv->base.pixmap; + glamor_get_drawable_deltas(mask->pDrawable, mask_pixmap, + &mask_x_off, &mask_y_off); + pixmap_priv_get_scale(mask_pixmap_priv, &mask_xscale, + &mask_yscale); + if (mask->transform) { + pmask_matrix = mask_matrix; + glamor_picture_get_matrixf(mask, pmask_matrix); + } + vert_stride += 4; + } + + nrect_max = (vert_stride * nrect) > GLAMOR_COMPOSITE_VBO_VERT_CNT ? + (GLAMOR_COMPOSITE_VBO_VERT_CNT / vert_stride) : nrect; + + while(nrect) { + int mrect, rect_processed; + int vb_stride; + + mrect = nrect > nrect_max ? nrect_max : nrect ; + glamor_setup_composite_vbo(screen, mrect * vert_stride); + rect_processed = mrect; + vb_stride = glamor_priv->vb_stride/sizeof(float); + while (mrect--) { + INT16 x_source; + INT16 y_source; + INT16 x_mask; + INT16 y_mask; + INT16 x_dest; + INT16 y_dest; + CARD16 width; + CARD16 height; + + x_dest = rects->x_dst + dest_x_off; + y_dest = rects->y_dst + dest_y_off; + x_source = rects->x_src + source_x_off; + y_source = rects->y_src + source_y_off; + x_mask = rects->x_mask + mask_x_off; + y_mask = rects->y_mask + mask_y_off; + width = rects->width; + height = rects->height; + + DEBUGF("dest(%d,%d) source(%d %d) mask (%d %d), width %d height %d \n", + x_dest, y_dest, x_source, y_source,x_mask,y_mask,width,height); + vertices = (float*)(glamor_priv->vb + glamor_priv->vbo_offset); + assert(glamor_priv->vbo_offset < glamor_priv->vbo_size - glamor_priv->vb_stride); + glamor_set_normalize_vcoords_ext(dest_pixmap_priv, dst_xscale, + dst_yscale, + x_dest, y_dest, + x_dest + width, y_dest + height, + glamor_priv->yInverted, + vertices, vb_stride); + vertices += 2; + if (key.source != SHADER_SOURCE_SOLID) { + glamor_set_normalize_tcoords_generic( + source_pixmap_priv, source->repeatType, psrc_matrix, + src_xscale, src_yscale, x_source, y_source, + x_source + width, y_source + height, + glamor_priv->yInverted, vertices, vb_stride); + vertices += 2; + } + + if (key.mask != SHADER_MASK_NONE + && key.mask != SHADER_MASK_SOLID) { + glamor_set_normalize_tcoords_generic( + mask_pixmap_priv, mask->repeatType, pmask_matrix, + mask_xscale, mask_yscale, x_mask, y_mask, + x_mask + width, y_mask + height, + glamor_priv->yInverted, vertices, vb_stride); + } + glamor_priv->render_nr_verts += 4; + glamor_priv->vbo_offset += glamor_priv->vb_stride * 4; + rects++; + } + glamor_flush_composite_rects(screen); + nrect -= rect_processed; + if (two_pass_ca) { + glamor_composite_set_shader_blend(dest_pixmap_priv, + &key_ca, shader_ca, + &op_info_ca); + glamor_flush_composite_rects(screen); + if (nrect) + glamor_composite_set_shader_blend(dest_pixmap_priv, + &key, shader, + &op_info); + } + } + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_MASK); + dispatch->glDisable(GL_BLEND); +#ifndef GLAMOR_GLES2 + dispatch->glActiveTexture(GL_TEXTURE0); + dispatch->glDisable(GL_TEXTURE_2D); + dispatch->glActiveTexture(GL_TEXTURE1); + dispatch->glDisable(GL_TEXTURE_2D); +#endif + DEBUGF("finish rendering.\n"); + dispatch->glUseProgram(0); + glamor_priv->state = RENDER_STATE; + glamor_priv->render_idle_cnt = 0; + if (saved_source_format) + source->format = saved_source_format; + glamor_put_dispatch(glamor_priv); + + ret = TRUE; + return ret; +} + +PicturePtr +glamor_convert_gradient_picture(ScreenPtr screen, + PicturePtr source, + int x_source, + int y_source, int width, int height) +{ + PixmapPtr pixmap; + PicturePtr dst = NULL; + int error; + PictFormatShort format; + if (!source->pDrawable) + format = PICT_a8r8g8b8; + else + format = source->format; +#ifdef GLAMOR_GRADIENT_SHADER + if (!source->pDrawable) { + if (source->pSourcePict->type == SourcePictTypeLinear) { + dst = glamor_generate_linear_gradient_picture(screen, + source, x_source, y_source, width, height, format); + } else if (source->pSourcePict->type == SourcePictTypeRadial) { + dst = glamor_generate_radial_gradient_picture(screen, + source, x_source, y_source, width, height, format); + } + + if (dst) { +#if 0 /* Debug to compare it to pixman, Enable it if needed. */ + glamor_compare_pictures(screen, source, + dst, x_source, y_source, width, height, + 0, 3); +#endif + return dst; + } + } +#endif + pixmap = glamor_create_pixmap(screen, + width, + height, + PIXMAN_FORMAT_DEPTH(format), + GLAMOR_CREATE_PIXMAP_CPU); + + if (!pixmap) + return NULL; + + dst = CreatePicture(0, + &pixmap->drawable, + PictureMatchFormat(screen, + PIXMAN_FORMAT_DEPTH(format), + format), + 0, 0, serverClient, &error); + glamor_destroy_pixmap(pixmap); + if (!dst) + return NULL; + + ValidatePicture(dst); + + fbComposite(PictOpSrc, source, NULL, dst, x_source, y_source, + 0, 0, 0, 0, width, height); + return dst; +} + +Bool +glamor_composite_clipped_region(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + glamor_pixmap_private *source_pixmap_priv, + glamor_pixmap_private *mask_pixmap_priv, + glamor_pixmap_private *dest_pixmap_priv, + RegionPtr region, + int x_source, + int y_source, + int x_mask, + int y_mask, + int x_dest, + int y_dest) +{ + ScreenPtr screen = dest->pDrawable->pScreen; + PixmapPtr source_pixmap = NULL, mask_pixmap = NULL; + PicturePtr temp_src = source, temp_mask = mask; + glamor_pixmap_private *temp_src_priv = source_pixmap_priv; + glamor_pixmap_private *temp_mask_priv = mask_pixmap_priv; + int x_temp_src, y_temp_src, x_temp_mask, y_temp_mask; + BoxPtr extent; + glamor_composite_rect_t rect[10]; + glamor_composite_rect_t *prect = rect; + int prect_size = ARRAY_SIZE(rect); + int ok = FALSE; + int i; + int width; + int height; + BoxPtr box; + int nbox; + Bool two_pass_ca = FALSE; + + extent = RegionExtents(region); + box = RegionRects(region); + nbox = RegionNumRects(region); + width = extent->x2 - extent->x1; + height = extent->y2 - extent->y1; + + x_temp_src = x_source; + y_temp_src = y_source; + x_temp_mask = x_mask; + y_temp_mask = y_mask; + + DEBUGF("clipped (%d %d) (%d %d) (%d %d) width %d height %d \n", + x_source, y_source, x_mask, y_mask, x_dest, y_dest, width, height); + + if (source_pixmap_priv) + source_pixmap = source_pixmap_priv->base.pixmap; + + if (mask_pixmap_priv) + mask_pixmap = mask_pixmap_priv->base.pixmap; + + /* XXX is it possible source mask have non-zero drawable.x/y? */ + if (source + && ((!source->pDrawable + && (source->pSourcePict->type != SourcePictTypeSolidFill)) + || (source->pDrawable + && !GLAMOR_PIXMAP_PRIV_HAS_FBO(source_pixmap_priv) + && (source_pixmap->drawable.width != width + || source_pixmap->drawable.height != height)))) { + temp_src = + glamor_convert_gradient_picture(screen, source, + extent->x1 + x_source - x_dest, + extent->y1 + y_source - y_dest, + width, height); + if (!temp_src) { + temp_src = source; + goto out; + } + temp_src_priv = glamor_get_pixmap_private((PixmapPtr)(temp_src->pDrawable)); + x_temp_src = - extent->x1 + x_dest; + y_temp_src = - extent->y1 + y_dest; + } + + if (mask + && + ((!mask->pDrawable + && (mask->pSourcePict->type != SourcePictTypeSolidFill)) + || (mask->pDrawable + && !GLAMOR_PIXMAP_PRIV_HAS_FBO(mask_pixmap_priv) + && (mask_pixmap->drawable.width != width + || mask_pixmap->drawable.height != height)))) { + /* XXX if mask->pDrawable is the same as source->pDrawable, we have an opportunity + * to do reduce one convertion. */ + temp_mask = + glamor_convert_gradient_picture(screen, mask, + extent->x1 + x_mask - x_dest, + extent->y1 + y_mask - y_dest, + width, height); + if (!temp_mask) { + temp_mask = mask; + goto out; + } + temp_mask_priv = glamor_get_pixmap_private((PixmapPtr)(temp_mask->pDrawable)); + x_temp_mask = - extent->x1 + x_dest; + y_temp_mask = - extent->y1 + y_dest; + } + /* Do two-pass PictOpOver componentAlpha, until we enable + * dual source color blending. + */ + + if (mask && mask->componentAlpha) { + if (op == PictOpOver) { + two_pass_ca = TRUE; + op = PictOpOutReverse; + } + } + + if (!mask && temp_src) { + if (glamor_composite_with_copy(op, temp_src, dest, + x_temp_src, y_temp_src, + x_dest, y_dest, region)) { + ok = TRUE; + goto out; + } + } + + /*XXXXX, self copy?*/ + + x_dest += dest->pDrawable->x; + y_dest += dest->pDrawable->y; + if (temp_src && temp_src->pDrawable) { + x_temp_src += temp_src->pDrawable->x; + y_temp_src += temp_src->pDrawable->y; + } + if (temp_mask && temp_mask->pDrawable) { + x_temp_mask += temp_mask->pDrawable->x; + y_temp_mask += temp_mask->pDrawable->y; + } + + if (nbox > ARRAY_SIZE(rect)) { + prect = calloc(nbox, sizeof(*prect)); + if (prect) + prect_size = nbox; + else { + prect = rect; + prect_size = ARRAY_SIZE(rect); + } + } + while(nbox) { + int box_cnt; + box_cnt = nbox > prect_size ? prect_size : nbox; + for (i = 0; i < box_cnt; i++) { + prect[i].x_src = box[i].x1 + x_temp_src - x_dest; + prect[i].y_src = box[i].y1 + y_temp_src - y_dest; + prect[i].x_mask = box[i].x1 + x_temp_mask - x_dest; + prect[i].y_mask = box[i].y1 + y_temp_mask - y_dest; + prect[i].x_dst = box[i].x1; + prect[i].y_dst = box[i].y1; + prect[i].width = box[i].x2 - box[i].x1; + prect[i].height = box[i].y2 - box[i].y1; + DEBUGF("dest %d %d \n", prect[i].x_dst, prect[i].y_dst); + } + ok = glamor_composite_with_shader(op, temp_src, temp_mask, dest, + temp_src_priv, temp_mask_priv, + dest_pixmap_priv, + box_cnt, prect, two_pass_ca); + if (!ok) + break; + nbox -= box_cnt; + box += box_cnt; + } + + if (prect != rect) + free(prect); +out: + if (temp_src != source) + FreePicture(temp_src, 0); + if (temp_mask != mask) + FreePicture(temp_mask, 0); + + return ok; +} + +static Bool +_glamor_composite(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + INT16 x_source, + INT16 y_source, + INT16 x_mask, + INT16 y_mask, + INT16 x_dest, INT16 y_dest, + CARD16 width, CARD16 height, Bool fallback) +{ + ScreenPtr screen = dest->pDrawable->pScreen; + glamor_pixmap_private *dest_pixmap_priv; + glamor_pixmap_private *source_pixmap_priv = + NULL, *mask_pixmap_priv = NULL; + PixmapPtr dest_pixmap = + glamor_get_drawable_pixmap(dest->pDrawable); + PixmapPtr source_pixmap = NULL, mask_pixmap = NULL; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + Bool ret = TRUE; + RegionRec region; + BoxPtr extent; + int nbox, ok = FALSE; + PixmapPtr sub_dest_pixmap = NULL; + PixmapPtr sub_source_pixmap = NULL; + PixmapPtr sub_mask_pixmap = NULL; + int dest_x_off, dest_y_off, saved_dest_x, saved_dest_y; + int source_x_off, source_y_off, saved_source_x, saved_source_y; + int mask_x_off, mask_y_off, saved_mask_x, saved_mask_y; + DrawablePtr saved_dest_drawable; + DrawablePtr saved_source_drawable; + DrawablePtr saved_mask_drawable; + int force_clip = 0; + dest_pixmap_priv = glamor_get_pixmap_private(dest_pixmap); + + if (source->pDrawable) { + source_pixmap = glamor_get_drawable_pixmap(source->pDrawable); + source_pixmap_priv = glamor_get_pixmap_private(source_pixmap); + if (source_pixmap_priv && source_pixmap_priv->type == GLAMOR_DRM_ONLY) + goto fail; + } + + if (mask && mask->pDrawable) { + mask_pixmap = glamor_get_drawable_pixmap(mask->pDrawable); + mask_pixmap_priv = glamor_get_pixmap_private(mask_pixmap); + if (mask_pixmap_priv && mask_pixmap_priv->type == GLAMOR_DRM_ONLY) + goto fail; + } + + DEBUGF("source pixmap %p (%d %d) mask(%d %d) dest(%d %d) width %d height %d \n", + source_pixmap, x_source, y_source, x_mask, y_mask, x_dest, y_dest, width, height); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dest_pixmap_priv)) { + goto fail; + } + + if (op >= ARRAY_SIZE(composite_op_info)) + goto fail; + + if (mask && mask->componentAlpha) { + if (op == PictOpAtop + || op == PictOpAtopReverse + || op == PictOpXor + || op >= PictOpSaturate) { + glamor_fallback + ("glamor_composite(): component alpha op %x\n", op); + goto fail; + } + } + + if ((source && source->filter >= PictFilterConvolution) + || (mask && mask->filter >= PictFilterConvolution)) { + glamor_fallback("glamor_composite(): unsupported filter\n"); + goto fail; + } + + if (!miComputeCompositeRegion(®ion, + source, mask, dest, + x_source + (source_pixmap ? source->pDrawable->x : 0), + y_source + (source_pixmap ? source->pDrawable->y : 0), + x_mask + (mask_pixmap ? mask->pDrawable->x : 0), + y_mask + (mask_pixmap ? mask->pDrawable->y : 0), + x_dest + dest->pDrawable->x, + y_dest + dest->pDrawable->y, + width, + height)) { + ret = TRUE; + goto done; + } + + nbox = REGION_NUM_RECTS(®ion); + DEBUGF("first clipped when compositing.\n"); + DEBUGRegionPrint(®ion); + extent = RegionExtents(®ion); + if (nbox == 0) { + ret = TRUE; + goto done; + } + /* If destination is not a large pixmap, but the region is larger + * than texture size limitation, and source or mask is memory pixmap, + * then there may be need to load a large memory pixmap to a + * texture, and this is not permitted. Then we force to clip the + * destination and make sure latter will not upload a large memory + * pixmap. */ + if (!glamor_check_fbo_size(glamor_priv, + extent->x2 - extent->x1, extent->y2 - extent->y1) + && (dest_pixmap_priv->type != GLAMOR_TEXTURE_LARGE) + && ((source_pixmap_priv + && (source_pixmap_priv->type == GLAMOR_MEMORY || source->repeatType == RepeatPad)) + || (mask_pixmap_priv + && (mask_pixmap_priv->type == GLAMOR_MEMORY || mask->repeatType == RepeatPad)) + || (!source_pixmap_priv + && (source->pSourcePict->type != SourcePictTypeSolidFill)) + || (!mask_pixmap_priv && mask + && mask->pSourcePict->type != SourcePictTypeSolidFill))) + force_clip = 1; + + if (force_clip || dest_pixmap_priv->type == GLAMOR_TEXTURE_LARGE + || (source_pixmap_priv + && source_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) + || (mask_pixmap_priv + && mask_pixmap_priv->type == GLAMOR_TEXTURE_LARGE)) + ok = glamor_composite_largepixmap_region(op, + source, mask, dest, + source_pixmap_priv, + mask_pixmap_priv, + dest_pixmap_priv, + ®ion, force_clip, + x_source, y_source, + x_mask, y_mask, + x_dest, y_dest, + width, height); + else + ok = glamor_composite_clipped_region(op, source, + mask, dest, + source_pixmap_priv, + mask_pixmap_priv, + dest_pixmap_priv, + ®ion, + x_source, y_source, + x_mask, y_mask, + x_dest, y_dest); + + REGION_UNINIT(dest->pDrawable->pScreen, ®ion); + + if (ok) + goto done; +fail: + + if (!fallback + && glamor_ddx_fallback_check_pixmap(&dest_pixmap->drawable) + && (!source_pixmap + || glamor_ddx_fallback_check_pixmap(&source_pixmap->drawable)) + && (!mask_pixmap + || glamor_ddx_fallback_check_pixmap(&mask_pixmap->drawable))) { + ret = FALSE; + goto done; + } + + glamor_fallback + ("from picts %p:%p %dx%d / %p:%p %d x %d (%c,%c) to pict %p:%p %dx%d (%c)\n", + source, source->pDrawable, + source->pDrawable ? source->pDrawable->width : 0, + source->pDrawable ? source->pDrawable->height : 0, mask, + (!mask) ? NULL : mask->pDrawable, (!mask + || !mask->pDrawable) ? 0 : + mask->pDrawable->width, (!mask + || !mask-> + pDrawable) ? 0 : mask->pDrawable-> + height, glamor_get_picture_location(source), + glamor_get_picture_location(mask), dest, dest->pDrawable, + dest->pDrawable->width, dest->pDrawable->height, + glamor_get_picture_location(dest)); + +#define GET_SUB_PICTURE(p, access) do { \ + glamor_get_drawable_deltas(p->pDrawable, p ##_pixmap, \ + & p ##_x_off, & p ##_y_off); \ + sub_ ##p ##_pixmap = glamor_get_sub_pixmap(p ##_pixmap, \ + x_ ##p + p ##_x_off + p->pDrawable->x, \ + y_ ##p + p ##_y_off + p->pDrawable->y, \ + width, height, access); \ + if (sub_ ##p ##_pixmap != NULL) { \ + saved_ ##p ##_drawable = p->pDrawable; \ + saved_ ##p ##_x = x_ ##p; \ + saved_ ##p ##_y = y_ ##p; \ + if (p->pCompositeClip) \ + pixman_region_translate (p->pCompositeClip, \ + -p->pDrawable->x - x_ ##p, \ + -p->pDrawable->y - y_ ##p); \ + p->pDrawable = &sub_ ##p ##_pixmap->drawable; \ + x_ ##p = 0; \ + y_ ##p = 0; \ + } } while(0) + GET_SUB_PICTURE(dest, GLAMOR_ACCESS_RW); + if (source->pDrawable && !source->transform) + GET_SUB_PICTURE(source, GLAMOR_ACCESS_RO); + if (mask && mask->pDrawable && !mask->transform) + GET_SUB_PICTURE(mask, GLAMOR_ACCESS_RO); + + if (glamor_prepare_access_picture(dest, GLAMOR_ACCESS_RW)) { + if (source_pixmap == dest_pixmap || glamor_prepare_access_picture + (source, GLAMOR_ACCESS_RO)) { + if (!mask + || glamor_prepare_access_picture(mask, + GLAMOR_ACCESS_RO)) + { + fbComposite(op, + source, mask, dest, + x_source, y_source, + x_mask, y_mask, x_dest, + y_dest, width, height); + if (mask) + glamor_finish_access_picture(mask, GLAMOR_ACCESS_RO); + } + if (source_pixmap != dest_pixmap) + glamor_finish_access_picture(source, GLAMOR_ACCESS_RO); + } + glamor_finish_access_picture(dest, GLAMOR_ACCESS_RW); + } + +#define PUT_SUB_PICTURE(p, access) do { \ + if (sub_ ##p ##_pixmap != NULL) { \ + x_ ##p = saved_ ##p ##_x; \ + y_ ##p = saved_ ##p ##_y; \ + p->pDrawable = saved_ ##p ##_drawable; \ + if (p->pCompositeClip) \ + pixman_region_translate (p->pCompositeClip, \ + p->pDrawable->x + x_ ##p, \ + p->pDrawable->y + y_ ##p); \ + glamor_put_sub_pixmap(sub_ ##p ##_pixmap, p ##_pixmap, \ + x_ ##p + p ##_x_off + p->pDrawable->x, \ + y_ ##p + p ##_y_off + p->pDrawable->y, \ + width, height, access); \ + }} while(0) + if (mask && mask->pDrawable) + PUT_SUB_PICTURE(mask, GLAMOR_ACCESS_RO); + if (source->pDrawable) + PUT_SUB_PICTURE(source, GLAMOR_ACCESS_RO); + PUT_SUB_PICTURE(dest, GLAMOR_ACCESS_RW); + done: + return ret; +} + +void +glamor_composite(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + INT16 x_source, + INT16 y_source, + INT16 x_mask, + INT16 y_mask, + INT16 x_dest, INT16 y_dest, + CARD16 width, CARD16 height) +{ + _glamor_composite(op, source, mask, dest, x_source, y_source, + x_mask, y_mask, x_dest, y_dest, width, height, + TRUE); +} + +Bool +glamor_composite_nf(CARD8 op, + PicturePtr source, + PicturePtr mask, + PicturePtr dest, + INT16 x_source, + INT16 y_source, + INT16 x_mask, + INT16 y_mask, + INT16 x_dest, INT16 y_dest, + CARD16 width, CARD16 height) +{ + return _glamor_composite(op, source, mask, dest, x_source, y_source, + x_mask, y_mask, x_dest, y_dest, width, height, + FALSE); +} + +static void +glamor_get_src_rect_extent(int nrect, + glamor_composite_rect_t *rects, + BoxPtr extent) +{ + extent->x1 = MAXSHORT; + extent->y1 = MAXSHORT; + extent->x2 = MINSHORT; + extent->y2 = MINSHORT; + + while(nrect--) { + if (extent->x1 > rects->x_src) + extent->x1 = rects->x_src; + if (extent->y1 > rects->y_src) + extent->y1 = rects->y_src; + if (extent->x2 < rects->x_src + rects->width) + extent->x2 = rects->x_src + rects->width; + if (extent->y2 < rects->y_src + rects->height) + extent->y2 = rects->y_src + rects->height; + rects++; + } +} + +static void +glamor_composite_src_rect_translate(int nrect, + glamor_composite_rect_t *rects, + int x, int y) +{ + while(nrect--) { + rects->x_src += x; + rects->y_src += y; + rects++; + } +} + +void +glamor_composite_glyph_rects(CARD8 op, + PicturePtr src, PicturePtr mask, PicturePtr dst, + int nrect, glamor_composite_rect_t * rects) +{ + int n; + PicturePtr temp_src = NULL; + glamor_composite_rect_t *r; + + ValidatePicture(src); + ValidatePicture(dst); + if (!(glamor_is_large_picture(src) + || (mask && glamor_is_large_picture(mask)) + || glamor_is_large_picture(dst))) { + glamor_pixmap_private *src_pixmap_priv = NULL; + glamor_pixmap_private *mask_pixmap_priv = NULL; + glamor_pixmap_private *dst_pixmap_priv; + glamor_pixmap_private *temp_src_priv = NULL; + BoxRec src_extent; + + dst_pixmap_priv = glamor_get_pixmap_private + (glamor_get_drawable_pixmap(dst->pDrawable)); + + if (mask && mask->pDrawable) + mask_pixmap_priv = glamor_get_pixmap_private + (glamor_get_drawable_pixmap(mask->pDrawable)); + if (src->pDrawable) + src_pixmap_priv = glamor_get_pixmap_private + (glamor_get_drawable_pixmap(src->pDrawable)); + + if (!src->pDrawable + && (src->pSourcePict->type != SourcePictTypeSolidFill)) { + glamor_get_src_rect_extent(nrect, rects, &src_extent); + temp_src = glamor_convert_gradient_picture(dst->pDrawable->pScreen, + src, + src_extent.x1, src_extent.y1, + src_extent.x2 - src_extent.x1, + src_extent.y2 - src_extent.y1); + if (!temp_src) + goto fallback; + + temp_src_priv = glamor_get_pixmap_private + ((PixmapPtr)(temp_src->pDrawable)); + glamor_composite_src_rect_translate(nrect, rects, + -src_extent.x1, -src_extent.y1); + } else { + temp_src = src; + temp_src_priv = src_pixmap_priv; + } + + if (mask && mask->componentAlpha) { + if (op == PictOpOver) { + if (glamor_composite_with_shader(PictOpOutReverse, + temp_src, mask, dst, temp_src_priv, + mask_pixmap_priv, dst_pixmap_priv, nrect, rects, + TRUE)) + goto done; + } + } else { + if (glamor_composite_with_shader(op, temp_src, mask, dst, temp_src_priv, + mask_pixmap_priv, dst_pixmap_priv, nrect, rects, FALSE)) + goto done; + } + } +fallback: + n = nrect; + r = rects; + + while (n--) { + CompositePicture(op, + temp_src ? temp_src : src, + mask, + dst, + r->x_src, r->y_src, + r->x_mask, r->y_mask, + r->x_dst, r->y_dst, r->width, r->height); + r++; + } + +done: + if (temp_src && temp_src != src) + FreePicture(temp_src, 0); +} + +static Bool +_glamor_composite_rects (CARD8 op, + PicturePtr pDst, + xRenderColor *color, + int nRect, + xRectangle *rects, + Bool fallback) +{ + miCompositeRects(op, pDst, color, nRect, rects); + return TRUE; +} + +void +glamor_composite_rects (CARD8 op, + PicturePtr pDst, + xRenderColor *color, + int nRect, + xRectangle *rects) +{ + _glamor_composite_rects(op, pDst, color, nRect, rects, TRUE); +} + +Bool +glamor_composite_rects_nf (CARD8 op, + PicturePtr pDst, + xRenderColor *color, + int nRect, + xRectangle *rects) +{ + return _glamor_composite_rects(op, pDst, color, nRect, rects, FALSE); +} + +#endif /* RENDER */ diff --git a/xorg-server/glamor/glamor_setspans.c b/xorg-server/glamor/glamor_setspans.c new file mode 100644 index 000000000..3d447b606 --- /dev/null +++ b/xorg-server/glamor/glamor_setspans.c @@ -0,0 +1,111 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + +#include "glamor_priv.h" + +static Bool +_glamor_set_spans(DrawablePtr drawable, GCPtr gc, char *src, + DDXPointPtr points, int *widths, int numPoints, int sorted, + Bool fallback) +{ + PixmapPtr dest_pixmap = glamor_get_drawable_pixmap(drawable); + glamor_pixmap_private *dest_pixmap_priv; + int i; + uint8_t *drawpixels_src = (uint8_t *) src; + RegionPtr clip = fbGetCompositeClip(gc); + BoxRec *pbox; + int x_off, y_off; + Bool ret = FALSE; + + dest_pixmap_priv = glamor_get_pixmap_private(dest_pixmap); + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dest_pixmap_priv)) { + glamor_fallback("pixmap has no fbo.\n"); + goto fail; + } + + /* XXX Shall we set alu here? */ + if (!glamor_set_planemask(dest_pixmap, gc->planemask)) + goto fail; + + glamor_get_drawable_deltas(drawable, dest_pixmap, &x_off, &y_off); + for (i = 0; i < numPoints; i++) { + + int n = REGION_NUM_RECTS(clip); + pbox = REGION_RECTS(clip); + while (n--) { + int x1 = points[i].x; + int x2 = x1 + widths[i]; + int y1 = points[i].y; + + if (pbox->y1 > points[i].y || pbox->y2 < points[i].y) + break; + x1 = x1 > pbox->x1 ? x1 : pbox->x1; + x2 = x2 < pbox->x2 ? x2 : pbox->x2; + if (x1 >= x2) + continue; + glamor_upload_sub_pixmap_to_texture(dest_pixmap, x1 + x_off, y1 + y_off, x2 - x1, 1, + PixmapBytePad(widths[i], drawable->depth), + drawpixels_src, 0); + } + drawpixels_src += PixmapBytePad(widths[i], drawable->depth); + } + ret = TRUE; + goto done; + +fail: + if (!fallback + && glamor_ddx_fallback_check_pixmap(drawable)) + goto done; + + glamor_fallback("to %p (%c)\n", + drawable, glamor_get_drawable_location(drawable)); + if (glamor_prepare_access(drawable, GLAMOR_ACCESS_RW)) { + fbSetSpans(drawable, gc, src, points, widths, numPoints, sorted); + glamor_finish_access(drawable, GLAMOR_ACCESS_RW); + } + ret = TRUE; + +done: + return ret; +} + +void +glamor_set_spans(DrawablePtr drawable, GCPtr gc, char *src, + DDXPointPtr points, int *widths, int n, int sorted) +{ + _glamor_set_spans(drawable, gc, src, points, + widths, n, sorted, TRUE); +} + +Bool +glamor_set_spans_nf(DrawablePtr drawable, GCPtr gc, char *src, + DDXPointPtr points, int *widths, int n, int sorted) +{ + return _glamor_set_spans(drawable, gc, src, points, + widths, n, sorted, FALSE); +} diff --git a/xorg-server/glamor/glamor_tile.c b/xorg-server/glamor/glamor_tile.c new file mode 100644 index 000000000..60486cfc0 --- /dev/null +++ b/xorg-server/glamor/glamor_tile.c @@ -0,0 +1,325 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + +#include "glamor_priv.h" + +/** @file glamor_tile.c + * + * Implements the basic fill-with-a-tile support used by multiple GC ops. + */ + +void +glamor_init_tile_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + const char *tile_vs = + "attribute vec4 v_position;\n" + "attribute vec4 v_texcoord0;\n" + "varying vec2 tile_texture;\n" + "void main()\n" + "{\n" + " gl_Position = v_position;\n" + " tile_texture = v_texcoord0.xy;\n" "}\n"; + const char *tile_fs = + GLAMOR_DEFAULT_PRECISION + "varying vec2 tile_texture;\n" + "uniform sampler2D sampler;\n" + "uniform vec2 wh;" + "void main()\n" + "{\n" + " vec2 rel_tex;" + " rel_tex = tile_texture * wh; \n" + " rel_tex = floor(rel_tex) + (fract(rel_tex) / wh); \n" + " gl_FragColor = texture2D(sampler, rel_tex);\n" + "}\n"; + GLint fs_prog, vs_prog; + GLint sampler_uniform_location; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + glamor_priv->tile_prog = dispatch->glCreateProgram(); + vs_prog = glamor_compile_glsl_prog(dispatch, GL_VERTEX_SHADER, tile_vs); + fs_prog = glamor_compile_glsl_prog(dispatch, GL_FRAGMENT_SHADER, + tile_fs); + dispatch->glAttachShader(glamor_priv->tile_prog, vs_prog); + dispatch->glAttachShader(glamor_priv->tile_prog, fs_prog); + + dispatch->glBindAttribLocation(glamor_priv->tile_prog, + GLAMOR_VERTEX_POS, "v_position"); + dispatch->glBindAttribLocation(glamor_priv->tile_prog, + GLAMOR_VERTEX_SOURCE, + "v_texcoord0"); + glamor_link_glsl_prog(dispatch, glamor_priv->tile_prog); + + sampler_uniform_location = + dispatch->glGetUniformLocation(glamor_priv->tile_prog, + "sampler"); + dispatch->glUseProgram(glamor_priv->tile_prog); + dispatch->glUniform1i(sampler_uniform_location, 0); + + glamor_priv->tile_wh = + dispatch->glGetUniformLocation(glamor_priv->tile_prog, + "wh"); + dispatch->glUseProgram(0); + glamor_put_dispatch(glamor_priv); +} + +void +glamor_fini_tile_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glDeleteProgram(glamor_priv->tile_prog); + glamor_put_dispatch(glamor_priv); +} + +static void +_glamor_tile(PixmapPtr pixmap, PixmapPtr tile, + int x, int y, int width, int height, + int tile_x, int tile_y) +{ + ScreenPtr screen = pixmap->drawable.pScreen; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_gl_dispatch *dispatch; + int x1 = x; + int x2 = x + width; + int y1 = y; + int y2 = y + height; + int tile_x1 = tile_x; + int tile_x2 = tile_x + width; + int tile_y1 = tile_y; + int tile_y2 = tile_y + height; + float vertices[8]; + float source_texcoords[8]; + GLfloat dst_xscale, dst_yscale, src_xscale, src_yscale; + glamor_pixmap_private *src_pixmap_priv; + glamor_pixmap_private *dst_pixmap_priv; + float wh[4]; + src_pixmap_priv = glamor_get_pixmap_private(tile); + dst_pixmap_priv = glamor_get_pixmap_private(pixmap); + + glamor_set_destination_pixmap_priv_nc(dst_pixmap_priv); + pixmap_priv_get_dest_scale(dst_pixmap_priv, &dst_xscale, &dst_yscale); + pixmap_priv_get_scale(src_pixmap_priv, &src_xscale, + &src_yscale); + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glUseProgram(glamor_priv->tile_prog); + + glamor_pixmap_fbo_fix_wh_ratio(wh, src_pixmap_priv); + dispatch->glUniform2fv(glamor_priv->tile_wh, 1, wh); + dispatch->glActiveTexture(GL_TEXTURE0); + dispatch->glBindTexture(GL_TEXTURE_2D, + src_pixmap_priv->base.fbo->tex); + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GL_REPEAT); + dispatch->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_REPEAT); +#ifndef GLAMOR_GLES2 + dispatch->glEnable(GL_TEXTURE_2D); +#endif + glamor_set_repeat_normalize_tcoords + (src_pixmap_priv, RepeatNormal, + src_xscale, src_yscale, + tile_x1, tile_y1, + tile_x2, tile_y2, + glamor_priv->yInverted, + source_texcoords); + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, + GL_FLOAT, GL_FALSE, + 2 * sizeof(float), + source_texcoords); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + + glamor_set_normalize_vcoords(dst_pixmap_priv, dst_xscale, dst_yscale, + x1, y1, + x2, y2, + glamor_priv->yInverted, vertices); + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), + vertices); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); +#ifndef GLAMOR_GLES2 + dispatch->glDisable(GL_TEXTURE_2D); +#endif + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glUseProgram(0); + glamor_put_dispatch(glamor_priv); + + glamor_priv->state = RENDER_STATE; + glamor_priv->render_idle_cnt = 0; +} + +Bool +glamor_tile(PixmapPtr pixmap, PixmapPtr tile, + int x, int y, int width, int height, + unsigned char alu, unsigned long planemask, + int tile_x, int tile_y) +{ + ScreenPtr screen = pixmap->drawable.pScreen; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_pixmap_private *dst_pixmap_priv; + glamor_pixmap_private *src_pixmap_priv; + glamor_gl_dispatch *dispatch; + + dst_pixmap_priv = glamor_get_pixmap_private(pixmap); + src_pixmap_priv = glamor_get_pixmap_private(tile); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dst_pixmap_priv)) + return FALSE; + + if (glamor_priv->tile_prog == 0) { + glamor_fallback("Tiling unsupported\n"); + goto fail; + } + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(src_pixmap_priv)) { + /* XXX dynamic uploading candidate. */ + glamor_fallback("Non-texture tile pixmap\n"); + goto fail; + } + + if (!glamor_set_planemask(pixmap, planemask)) { + glamor_fallback("unsupported planemask %lx\n", planemask); + goto fail; + } + + dispatch = glamor_get_dispatch(glamor_priv); + if (!glamor_set_alu(dispatch, alu)) { + glamor_fallback("unsupported alu %x\n", alu); + glamor_put_dispatch(glamor_priv); + goto fail; + } + + if (dst_pixmap_priv->type == GLAMOR_TEXTURE_LARGE + || src_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + glamor_pixmap_clipped_regions *clipped_dst_regions; + int n_dst_region, i, j, k; + BoxRec box; + RegionRec region; + + box.x1 = x; + box.y1 = y; + box.x2 = x + width; + box.y2 = y + height; + RegionInitBoxes(®ion, &box, 1); + clipped_dst_regions = glamor_compute_clipped_regions(dst_pixmap_priv, + ®ion, &n_dst_region, 0, 0, 0); + for(i = 0; i < n_dst_region; i++) + { + int n_src_region; + glamor_pixmap_clipped_regions *clipped_src_regions; + BoxPtr current_boxes; + int n_current_boxes; + + SET_PIXMAP_FBO_CURRENT(dst_pixmap_priv, clipped_dst_regions[i].block_idx); + + if (src_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + RegionTranslate(clipped_dst_regions[i].region, + tile_x - x, tile_y - y); + DEBUGF("tiled a large src pixmap. %dx%d \n", tile->drawable.width, tile->drawable.height); + clipped_src_regions = glamor_compute_clipped_regions(src_pixmap_priv, + clipped_dst_regions[i].region, + &n_src_region, 1, 0, 0); + DEBUGF("got %d src regions %d \n", n_src_region); + for (j = 0; j < n_src_region; j++) + { + + SET_PIXMAP_FBO_CURRENT(src_pixmap_priv, clipped_src_regions[j].block_idx); + + RegionTranslate(clipped_src_regions[j].region, + x - tile_x, + y - tile_y); + current_boxes = RegionRects(clipped_src_regions[j].region); + n_current_boxes = RegionNumRects(clipped_src_regions[j].region); + for(k = 0; k < n_current_boxes; k++) + { + DEBUGF("Tile on %d %d %d %d dst block id %d tile block id %d tilex %d tiley %d\n", + current_boxes[k].x1, current_boxes[k].y1, + current_boxes[k].x2 - current_boxes[k].x1, + current_boxes[k].y2 - current_boxes[k].y1, + clipped_dst_regions[i].block_idx, + clipped_src_regions[j].block_idx, + (tile_x + (current_boxes[k].x1 - x)), + tile_y + (current_boxes[k].y1 - y)); + + _glamor_tile(pixmap, tile, + current_boxes[k].x1, current_boxes[k].y1, + current_boxes[k].x2 - current_boxes[k].x1, + current_boxes[k].y2 - current_boxes[k].y1, + (tile_x + (current_boxes[k].x1 - x)), + (tile_y + (current_boxes[k].y1 - y))); + } + + RegionDestroy(clipped_src_regions[j].region); + } + free(clipped_src_regions); + } else { + current_boxes = RegionRects(clipped_dst_regions[i].region); + n_current_boxes = RegionNumRects(clipped_dst_regions[i].region); + for(k = 0; k < n_current_boxes; k++) + { + _glamor_tile(pixmap, tile, + current_boxes[k].x1, current_boxes[k].y1, + current_boxes[k].x2 - current_boxes[k].x1, + current_boxes[k].y2 - current_boxes[k].y1, + (tile_x + (current_boxes[k].x1 - x)), + (tile_y + (current_boxes[k].y1 - y))); + } + } + RegionDestroy(clipped_dst_regions[i].region); + } + free(clipped_dst_regions); + RegionUninit(®ion); + } + else + _glamor_tile(pixmap, tile, x, y, width, height, tile_x, tile_y); + + glamor_set_alu(dispatch, GXcopy); + glamor_put_dispatch(glamor_priv); + return TRUE; +fail: + return FALSE; + +} diff --git a/xorg-server/glamor/glamor_trapezoid.c b/xorg-server/glamor/glamor_trapezoid.c new file mode 100644 index 000000000..76b3729cf --- /dev/null +++ b/xorg-server/glamor/glamor_trapezoid.c @@ -0,0 +1,1819 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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. + * + * Authors: + * Junyan He <junyan.he@linux.intel.com> + * + */ + +/** @file glamor_trapezoid.c + * + * Trapezoid acceleration implementation + */ + +#include "glamor_priv.h" + +#ifdef RENDER +#include "mipict.h" +#include "fbpict.h" + +static xFixed +_glamor_linefixedX (xLineFixed *l, xFixed y, Bool ceil) +{ + xFixed dx = l->p2.x - l->p1.x; + xFixed_32_32 ex = (xFixed_32_32) (y - l->p1.y) * dx; + xFixed dy = l->p2.y - l->p1.y; + if (ceil) + ex += (dy - 1); + return l->p1.x + (xFixed) (ex / dy); +} + +static xFixed +_glamor_linefixedY (xLineFixed *l, xFixed x, Bool ceil) +{ + xFixed dy = l->p2.y - l->p1.y; + xFixed_32_32 ey = (xFixed_32_32) (x - l->p1.x) * dy; + xFixed dx = l->p2.x - l->p1.x; + if (ceil) + ey += (dx - 1); + return l->p1.y + (xFixed) (ey / dx); +} + +#ifdef GLAMOR_TRAPEZOID_SHADER + +#define GLAMOR_VERTEX_TOP_BOTTOM (GLAMOR_VERTEX_SOURCE + 1) +#define GLAMOR_VERTEX_LEFT_PARAM (GLAMOR_VERTEX_SOURCE + 2) +#define GLAMOR_VERTEX_RIGHT_PARAM (GLAMOR_VERTEX_SOURCE + 3) + +#define DEBUG_CLIP_VTX 0 + +#define POINT_INSIDE_CLIP_RECT(point, rect) \ + (point[0] >= IntToxFixed(rect->x1) \ + && point[0] <= IntToxFixed(rect->x2) \ + && point[1] >= IntToxFixed(rect->y1) \ + && point[1] <= IntToxFixed(rect->y2)) + +static xFixed +_glamor_lines_crossfixedY (xLineFixed *l, xLineFixed *r) +{ + xFixed dx1 = l->p2.x - l->p1.x; + xFixed dx2 = r->p2.x - r->p1.x; + xFixed dy1 = l->p2.y - l->p1.y; + xFixed dy2 = r->p2.y - r->p1.y; + xFixed_32_32 tmp = (xFixed_32_32) dy2 * dy1; + xFixed_32_32 dividend1 = (tmp >> 32) * (l->p1.x - r->p1.x); + xFixed_32_32 dividend2; + xFixed_32_32 dividend3; + xFixed_32_32 divisor; + + tmp = (xFixed_32_32) dx1 * dy2; + dividend2 = (tmp >> 32) * l->p1.y; + tmp = (xFixed_32_32) dy1 * dx2; + dividend3 = (tmp >> 32) * r->p1.y; + divisor = ((xFixed_32_32) dx1 * (xFixed_32_32) dy2 + - (xFixed_32_32) dy1 * (xFixed_32_32) dx2) >> 32; + + if (divisor) + return (xFixed)((dividend2 - dividend1 - dividend3) / divisor); + + return 0xFFFFFFFF; +} + +static Bool +point_inside_trapezoid(int point[2], xTrapezoid * trap, xFixed cut_y) +{ + int ret = TRUE; + int tmp; + if (point[1] > trap->bottom) { + ret = FALSE; + if (DEBUG_CLIP_VTX) { + ErrorF("Out of Trap bottom, point[1] = %d(0x%x)), " + "bottom = %d(0x%x)\n", + (unsigned int)xFixedToInt(point[1]), point[1], + (unsigned int)xFixedToInt(trap->bottom), + (unsigned int)trap->bottom); + } + + return ret; + } + + if (point[1] < trap->top) { + ret = FALSE; + if (DEBUG_CLIP_VTX) { + ErrorF("Out of Trap top, point[1] = %d(0x%x)), " + "top = %d(0x%x)\n", + (unsigned int)xFixedToInt(point[1]), point[1], + (unsigned int)xFixedToInt(trap->top), + (unsigned int)trap->top); + } + + return ret; + } + + tmp = _glamor_linefixedX (&trap->left, point[1], FALSE); + if (point[0] < tmp) { + ret = FALSE; + + if (abs(cut_y - trap->top) < pixman_fixed_1_minus_e && + abs(point[1] - trap->top) < pixman_fixed_1_minus_e && + tmp - point[0] < pixman_fixed_1_minus_e) { + ret = TRUE; + } else if (abs(cut_y - trap->bottom) < pixman_fixed_1_minus_e && + point[1] - trap->bottom < pixman_fixed_1_minus_e && + tmp - point[0] < pixman_fixed_1_minus_e) { + ret = TRUE; + } + + if (DEBUG_CLIP_VTX && !ret) { + ErrorF("Out of Trap left, point[0] = %d(0x%x)), " + "left = %d(0x%x)\n", + (unsigned int)xFixedToInt(point[0]), point[0], + (unsigned int)xFixedToInt(tmp), (unsigned int)tmp); + } + + if (!ret) + return ret; + } + + tmp = _glamor_linefixedX (&trap->right, point[1], TRUE); + if (point[0] > tmp) { + ret = FALSE; + + if (abs(cut_y - trap->top) < pixman_fixed_1_minus_e && + abs(point[1] - trap->top) < pixman_fixed_1_minus_e && + point[0] - tmp < pixman_fixed_1_minus_e) { + ret = TRUE; + } else if (abs(cut_y - trap->bottom) < pixman_fixed_1_minus_e && + abs(point[1] - trap->bottom) < pixman_fixed_1_minus_e && + point[0] - tmp < pixman_fixed_1_minus_e) { + ret = TRUE; + } + + if (DEBUG_CLIP_VTX && !ret) { + ErrorF("Out of Trap right, point[0] = %d(0x%x)), " + "right = %d(0x%x)\n", + (unsigned int)xFixedToInt(point[0]), point[0], + (unsigned int)xFixedToInt(tmp), (unsigned int)tmp); + } + + if (!ret) + return ret; + } + + return ret; +} + +static void +glamor_emit_composite_triangle(ScreenPtr screen, + const float *src_coords, + const float *mask_coords, + const float *dst_coords) +{ + glamor_emit_composite_vert(screen, src_coords, mask_coords, + dst_coords, 0); + glamor_emit_composite_vert(screen, src_coords, mask_coords, + dst_coords, 1); + glamor_emit_composite_vert(screen, src_coords, mask_coords, + dst_coords, 2); +} + +static void +glamor_flush_composite_triangles(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_gl_dispatch *dispatch; + + dispatch = glamor_get_dispatch(glamor_priv); + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) + dispatch->glUnmapBuffer(GL_ARRAY_BUFFER); + else { + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, glamor_priv->vbo); + dispatch->glBufferData(GL_ARRAY_BUFFER, + glamor_priv->vbo_offset, + glamor_priv->vb, GL_DYNAMIC_DRAW); + } + + if (!glamor_priv->render_nr_verts) + return; + + dispatch->glDrawArrays(GL_TRIANGLES, 0, glamor_priv->render_nr_verts); + glamor_put_dispatch(glamor_priv); +} + +static Bool +_glamor_clip_trapezoid_vertex(xTrapezoid * trap, BoxPtr pbox, + int vertex[6], int *num) +{ + xFixed edge_cross_y = 0xFFFFFFFF; + int tl[2]; + int bl[2]; + int tr[2]; + int br[2]; + int left_cut_top[2]; + int left_cut_left[2]; + int left_cut_right[2]; + int left_cut_bottom[2]; + int right_cut_top[2]; + int right_cut_left[2]; + int right_cut_right[2]; + int right_cut_bottom[2]; + int tmp[2]; + int tmp_vtx[20*2]; + float tmp_vtx_slope[20]; + BoxRec trap_bound; + int i = 0; + int vertex_num = 0; + + if (DEBUG_CLIP_VTX) { + ErrorF("The parameter of xTrapezoid is:\ntop: %d 0x%x\tbottom: %d 0x%x\n" + "left: p1 (%d 0x%x, %d 0x%x)\tp2 (%d 0x%x, %d 0x%x)\n" + "right: p1 (%d 0x%x, %d 0x%x)\tp2 (%d 0x%x, %d 0x%x)\n", + xFixedToInt(trap->top), (unsigned int)trap->top, + xFixedToInt(trap->bottom), (unsigned int)trap->bottom, + xFixedToInt(trap->left.p1.x), (unsigned int)trap->left.p1.x, + xFixedToInt(trap->left.p1.y), (unsigned int)trap->left.p1.y, + xFixedToInt(trap->left.p2.x), (unsigned int)trap->left.p2.x, + xFixedToInt(trap->left.p2.y), (unsigned int)trap->left.p2.y, + xFixedToInt(trap->right.p1.x), (unsigned int)trap->right.p1.x, + xFixedToInt(trap->right.p1.y), (unsigned int)trap->right.p1.y, + xFixedToInt(trap->right.p2.x), (unsigned int)trap->right.p2.x, + xFixedToInt(trap->right.p2.y), (unsigned int)trap->right.p2.y); + } + + miTrapezoidBounds(1, trap, &trap_bound); + if (DEBUG_CLIP_VTX) + ErrorF("The bounds for this traps is: bounds.x1 = %d, bounds.x2 = %d, " + "bounds.y1 = %d, bounds.y2 = %d\n", trap_bound.x1, trap_bound.x2, + trap_bound.y1, trap_bound.y2); + + if (trap_bound.x1 > pbox->x2 || trap_bound.x2 < pbox->x1) + return FALSE; + if (trap_bound.y1 > pbox->y2 || trap_bound.y2 < pbox->y1) + return FALSE; + +#define IS_TRAP_EDGE_VERTICAL(edge) \ + (edge->p1.x == edge->p2.x) + +#define CACULATE_CUT_VERTEX(vtx, cal_x, ceil, vh_edge, edge) \ + do { \ + if(cal_x) { \ + vtx[1] = (vh_edge); \ + vtx[0] = (_glamor_linefixedX( \ + edge, vh_edge, ceil)); \ + if(DEBUG_CLIP_VTX) \ + ErrorF("The intersection point of line y=%d and " \ + "line of p1:(%d,%d) -- p2 (%d,%d) " \ + "is (%d, %d)\n", \ + xFixedToInt(vh_edge), \ + xFixedToInt(edge->p1.x), \ + xFixedToInt(edge->p1.y), \ + xFixedToInt(edge->p2.x), \ + xFixedToInt(edge->p2.y), \ + xFixedToInt(vtx[0]), \ + xFixedToInt(vtx[1])); \ + } else { \ + vtx[0] = (vh_edge); \ + vtx[1] = (_glamor_linefixedY( \ + edge, vh_edge, ceil)); \ + if(DEBUG_CLIP_VTX) \ + ErrorF("The intersection point of line x=%d and " \ + "line of p1:(%d,%d) -- p2 (%d,%d) " \ + "is (%d, %d)\n", \ + xFixedToInt(vh_edge), \ + xFixedToInt(edge->p1.x), \ + xFixedToInt(edge->p1.y), \ + xFixedToInt(edge->p2.x), \ + xFixedToInt(edge->p2.y), \ + xFixedToInt(vtx[0]), \ + xFixedToInt(vtx[1])); \ + } \ + } while(0) + +#define ADD_VERTEX_IF_INSIDE(vtx) \ + if(POINT_INSIDE_CLIP_RECT(vtx, pbox) \ + && point_inside_trapezoid(vtx, trap, edge_cross_y)){ \ + tmp_vtx[vertex_num] = xFixedToInt(vtx[0]); \ + tmp_vtx[vertex_num + 1] = xFixedToInt(vtx[1]); \ + vertex_num += 2; \ + if(DEBUG_CLIP_VTX) \ + ErrorF("@ Point: (%d, %d) is inside " \ + "the Rect and Trapezoid\n", \ + xFixedToInt(vtx[0]), \ + xFixedToInt(vtx[1])); \ + } else if(DEBUG_CLIP_VTX){ \ + ErrorF("X Point: (%d, %d) is outside " \ + "the Rect and Trapezoid\t", \ + xFixedToInt(vtx[0]), \ + xFixedToInt(vtx[1])); \ + if(POINT_INSIDE_CLIP_RECT(vtx, pbox)) \ + ErrorF("The Point is outside " \ + "the Trapezoid\n"); \ + else \ + ErrorF("The Point is outside " \ + "the Rect\n"); \ + } + + /*Trap's right edge cut right edge. */ + if((!IS_TRAP_EDGE_VERTICAL((&trap->left))) || + (!IS_TRAP_EDGE_VERTICAL((&trap->right)))) { + edge_cross_y = _glamor_lines_crossfixedY((&trap->left), (&trap->right)); + if (DEBUG_CLIP_VTX) { + ErrorF("Trap's left edge cut right edge at %d(0x%x), " + "trap_top = %x, trap_bottom = %x\n", + xFixedToInt(edge_cross_y), edge_cross_y, + (unsigned int)trap->top, (unsigned int)trap->bottom); + } + } + + /*Trap's TopLeft, BottomLeft, TopRight and BottomRight. */ + CACULATE_CUT_VERTEX(tl, 1, FALSE, trap->top, (&trap->left)); + CACULATE_CUT_VERTEX(bl, 1, FALSE, trap->bottom, (&trap->left)); + CACULATE_CUT_VERTEX(tr, 1, TRUE, trap->top, (&trap->right)); + CACULATE_CUT_VERTEX(br, 1, TRUE, trap->bottom, (&trap->right)); + + if (DEBUG_CLIP_VTX) + ErrorF("Trap's TopLeft, BottomLeft, TopRight and BottomRight\n"); + if (DEBUG_CLIP_VTX) + ErrorF("Caculate the vertex of trapezoid:\n" + " (%3d, %3d)-------------------------(%3d, %3d)\n" + " / \\ \n" + " / \\ \n" + " / \\ \n" + " (%3d, %3d)---------------------------------(%3d, %3d)\n" + "Clip with rect:\n" + " (%3d, %3d)------------------------(%3d, %3d) \n" + " | | \n" + " | | \n" + " | | \n" + " (%3d, %3d)------------------------(%3d, %3d) \n", + xFixedToInt(tl[0]), xFixedToInt(tl[1]), xFixedToInt(tr[0]), + xFixedToInt(tr[1]), xFixedToInt(bl[0]), xFixedToInt(bl[1]), + xFixedToInt(br[0]), xFixedToInt(br[1]), + pbox->x1, pbox->y1, pbox->x2, pbox->y1, pbox->x1, pbox->y2, + pbox->x2, pbox->y2); + + ADD_VERTEX_IF_INSIDE(tl); + ADD_VERTEX_IF_INSIDE(bl); + ADD_VERTEX_IF_INSIDE(tr); + ADD_VERTEX_IF_INSIDE(br); + + /*Trap's left edge cut Rect. */ + if (DEBUG_CLIP_VTX) + ErrorF("Trap's left edge cut Rect\n"); + CACULATE_CUT_VERTEX(left_cut_top, 1, FALSE, IntToxFixed(pbox->y1), (&trap->left)); + ADD_VERTEX_IF_INSIDE(left_cut_top); + if (!IS_TRAP_EDGE_VERTICAL((&trap->left))) { + CACULATE_CUT_VERTEX(left_cut_left, 0, FALSE, IntToxFixed(pbox->x1), (&trap->left)); + ADD_VERTEX_IF_INSIDE(left_cut_left); + } + CACULATE_CUT_VERTEX(left_cut_bottom, 1, FALSE, IntToxFixed(pbox->y2), (&trap->left)); + ADD_VERTEX_IF_INSIDE(left_cut_bottom); + if (!IS_TRAP_EDGE_VERTICAL((&trap->left))) { + CACULATE_CUT_VERTEX(left_cut_right, 0, FALSE, IntToxFixed(pbox->x2), (&trap->left)); + ADD_VERTEX_IF_INSIDE(left_cut_right); + } + + /*Trap's right edge cut Rect. */ + if (DEBUG_CLIP_VTX) + ErrorF("Trap's right edge cut Rect\n"); + CACULATE_CUT_VERTEX(right_cut_top, 1, TRUE, IntToxFixed(pbox->y1), (&trap->right)); + ADD_VERTEX_IF_INSIDE(right_cut_top); + if (!IS_TRAP_EDGE_VERTICAL((&trap->right))) { + CACULATE_CUT_VERTEX(right_cut_left, 0, TRUE, IntToxFixed(pbox->x1), (&trap->right)); + ADD_VERTEX_IF_INSIDE(right_cut_left); + } + CACULATE_CUT_VERTEX(right_cut_bottom, 1, TRUE, IntToxFixed(pbox->y2), (&trap->right)); + ADD_VERTEX_IF_INSIDE(right_cut_bottom); + if (!IS_TRAP_EDGE_VERTICAL((&trap->right))) { + CACULATE_CUT_VERTEX(right_cut_right, 0, TRUE, IntToxFixed(pbox->x2), (&trap->right)); + ADD_VERTEX_IF_INSIDE(right_cut_right); + } + + /* Trap's top cut Left and Right of rect. */ + if (DEBUG_CLIP_VTX) + ErrorF("Trap's top cut Left and Right of rect\n"); + tmp[0] = IntToxFixed(pbox->x1); + tmp[1] = trap->top; + ADD_VERTEX_IF_INSIDE(tmp); + tmp[0] = IntToxFixed(pbox->x2); + tmp[1] = trap->top; + ADD_VERTEX_IF_INSIDE(tmp); + + /* Trap's bottom cut Left and Right of rect. */ + if (DEBUG_CLIP_VTX) + ErrorF("Trap's bottom cut Left and Right of rect\n"); + tmp[0] = IntToxFixed(pbox->x1); + tmp[1] = trap->bottom; + ADD_VERTEX_IF_INSIDE(tmp); + tmp[0] = IntToxFixed(pbox->x2); + tmp[1] = trap->bottom; + ADD_VERTEX_IF_INSIDE(tmp); + + /* The orginal 4 vertex of rect. */ + if (DEBUG_CLIP_VTX) + ErrorF("The orginal 4 vertex of rect\n"); + tmp[0] = IntToxFixed(pbox->x1); + tmp[1] = IntToxFixed(pbox->y1); + ADD_VERTEX_IF_INSIDE(tmp); + tmp[0] = IntToxFixed(pbox->x1); + tmp[1] = IntToxFixed(pbox->y2); + ADD_VERTEX_IF_INSIDE(tmp); + tmp[0] = IntToxFixed(pbox->x2); + tmp[1] = IntToxFixed(pbox->y2); + ADD_VERTEX_IF_INSIDE(tmp); + tmp[0] = IntToxFixed(pbox->x2); + tmp[1] = IntToxFixed(pbox->y1); + ADD_VERTEX_IF_INSIDE(tmp); + + if (DEBUG_CLIP_VTX) { + ErrorF("\nThe candidate vertex number is %d\n", vertex_num / 2); + for (i = 0; i < vertex_num / 2; i++) { + ErrorF("(%d, %d) ", tmp_vtx[2*i], tmp_vtx[2*i + 1]); + } + ErrorF("\n"); + } + + /* Sort the vertex by X and then Y. */ + for (i = 0; i < vertex_num / 2; i++) { + int j; + for (j = 0; j < vertex_num / 2 - i - 1; j++) { + if (tmp_vtx[2*j] > tmp_vtx[2*(j+1)] + || (tmp_vtx[2*j] == tmp_vtx[2*(j+1)] + && tmp_vtx[2*j + 1] > tmp_vtx[2*(j+1) + 1])) { + tmp[0] = tmp_vtx[2*j]; + tmp[1] = tmp_vtx[2*j + 1]; + tmp_vtx[2*j] = tmp_vtx[2*(j+1)]; + tmp_vtx[2*j + 1] = tmp_vtx[2*(j+1) + 1]; + tmp_vtx[2*(j+1)] = tmp[0]; + tmp_vtx[2*(j+1) + 1] = tmp[1]; + } + } + + } + + if (DEBUG_CLIP_VTX) { + ErrorF("\nAfter sort vertex number is:\n"); + for (i = 0; i < vertex_num / 2; i++) { + ErrorF("(%d, %d) ", tmp_vtx[2*i], tmp_vtx[2*i + 1]); + } + ErrorF("\n"); + } + + memset(vertex, -1, 2*6); + *num = 0; + + for (i = 0; i < vertex_num / 2; i++) { + if (*num > 0 && vertex[2*(*num - 1)] == tmp_vtx[2*i] + && vertex[2*(*num - 1) + 1] == tmp_vtx[2*i + 1]) { + /*same vertex.*/ + if (DEBUG_CLIP_VTX) + ErrorF("X Point:(%d, %d) discard\n", + tmp_vtx[2*i], tmp_vtx[2*i + 1]); + continue; + } + + (*num)++; + if (*num > 6) { + if (DEBUG_CLIP_VTX) + FatalError("Trapezoid clip with Rect can never have vtx" + "number bigger than 6\n"); + else { + ErrorF("Trapezoid clip with Rect can never have vtx" + "number bigger than 6\n"); + *num = 6; + break; + } + } + + vertex[2*(*num - 1)] = tmp_vtx[2*i]; + vertex[2*(*num - 1) + 1] = tmp_vtx[2*i + 1]; + if (DEBUG_CLIP_VTX) + ErrorF("@ Point:(%d, %d) select, num now is %d\n", + tmp_vtx[2*i], tmp_vtx[2*i + 1], *num); + } + + /* Now we need to arrange the vtx in the polygon's counter-clockwise + order. We first select the left and top point as the start point and + sort every vtx by the slope from vtx to the start vtx. */ + for (i = 1; i < *num; i++) { + tmp_vtx_slope[i] = (vertex[2*i] != vertex[0] ? + (float)(vertex[2*i + 1] - vertex[1]) / (float)(vertex[2*i] - vertex[0]) + : (float)INT_MAX); + } + + if (DEBUG_CLIP_VTX) { + ErrorF("\nvtx number: %d, VTX and slope:\n", *num); + for (i = 0; i < *num; i++) { + ErrorF("(%d, %d):%f ", + vertex[2*i], vertex[2*i + 1], + tmp_vtx_slope[i]); + } + ErrorF("\n"); + } + + /* Sort the vertex by slope. */ + for (i = 0; i < *num - 1; i++) { + int j; + float tmp_slope; + for (j = 1; j < *num - i - 1; j++) { + if (tmp_vtx_slope[j] < tmp_vtx_slope[j + 1]) { + tmp_slope = tmp_vtx_slope[j]; + tmp_vtx_slope[j] = tmp_vtx_slope[j + 1]; + tmp_vtx_slope[j + 1] = tmp_slope; + tmp[0] = vertex[2*j]; + tmp[1] = vertex[2*j + 1]; + vertex[2*j] = vertex[2*(j+1)]; + vertex[2*j + 1] = vertex[2*(j+1) + 1]; + vertex[2*(j+1)] = tmp[0]; + vertex[2*(j+1) + 1] = tmp[1]; + } + } + } + + if (DEBUG_CLIP_VTX) { + ErrorF("\nBefore return, vtx number: %d, VTX and slope:\n", *num); + for (i = 0; i < *num; i++) { + ErrorF("(%d, %d):%f ", + vertex[2*i], vertex[2*i + 1], + tmp_vtx_slope[i]); + } + ErrorF("\n"); + } + + return TRUE; +} + +static void +glamor_setup_composite_vbo_for_trapezoid(ScreenPtr screen, int n_verts) +{ + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + glamor_gl_dispatch *dispatch; + int stride; + int vert_size; + + glamor_priv->render_nr_verts = 0; + + /* For GLAMOR_VERTEX_POS */ + glamor_priv->vb_stride = 2 * sizeof(float); + + /* For GLAMOR_GLAMOR_VERTEX_SOURCE */ + glamor_priv->vb_stride += 2 * sizeof(float); + + /* For GLAMOR_VERTEX_TOP_BOTTOM */ + glamor_priv->vb_stride += 2 * sizeof(float); + + /* For GLAMOR_VERTEX_LEFT_PARAM */ + glamor_priv->vb_stride += 4 * sizeof(float); + + /* For GLAMOR_VERTEX_RIGHT_PARAM */ + glamor_priv->vb_stride += 4 * sizeof(float); + + vert_size = n_verts * glamor_priv->vb_stride; + + dispatch = glamor_get_dispatch(glamor_priv); + + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_TOP_BOTTOM); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_LEFT_PARAM); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_RIGHT_PARAM); + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, glamor_priv->vbo); + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) { + if (glamor_priv->vbo_size < (glamor_priv->vbo_offset + vert_size)) { + glamor_priv->vbo_size = GLAMOR_COMPOSITE_VBO_VERT_CNT * + glamor_priv->vb_stride; + glamor_priv->vbo_offset = 0; + dispatch->glBufferData(GL_ARRAY_BUFFER, + glamor_priv->vbo_size, + NULL, GL_STREAM_DRAW); + } + + glamor_priv->vb = dispatch->glMapBufferRange(GL_ARRAY_BUFFER, + glamor_priv->vbo_offset, + vert_size, + GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + + assert(glamor_priv->vb != NULL); + glamor_priv->vb -= glamor_priv->vbo_offset; + } else { + glamor_priv->vbo_offset = 0; + } + + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glamor_priv->ebo); + + /* Set the vertex pointer. */ + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, + GL_FALSE, glamor_priv->vb_stride, + (void *) ((long)glamor_priv->vbo_offset)); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); + stride = 2; + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT, + GL_FALSE, glamor_priv->vb_stride, + (void *) ((long)glamor_priv->vbo_offset + stride * sizeof(float))); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + stride += 2; + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_TOP_BOTTOM, 2, GL_FLOAT, + GL_FALSE, glamor_priv->vb_stride, + (void *) ((long)glamor_priv->vbo_offset + stride * sizeof(float))); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_TOP_BOTTOM); + stride += 2; + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_LEFT_PARAM, 4, GL_FLOAT, + GL_FALSE, glamor_priv->vb_stride, + (void *) ((long)glamor_priv->vbo_offset + stride * sizeof(float))); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_LEFT_PARAM); + stride += 4; + + dispatch->glVertexAttribPointer(GLAMOR_VERTEX_RIGHT_PARAM, 4, GL_FLOAT, + GL_FALSE, glamor_priv->vb_stride, + (void *) ((long)glamor_priv->vbo_offset + stride * sizeof(float))); + dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_RIGHT_PARAM); + + glamor_put_dispatch(glamor_priv); +} + +static Bool +_glamor_trapezoids_with_shader(CARD8 op, + PicturePtr src, PicturePtr dst, + PictFormatPtr mask_format, INT16 x_src, INT16 y_src, + int ntrap, xTrapezoid * traps) +{ + ScreenPtr screen = dst->pDrawable->pScreen; + glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); + struct shader_key key; + glamor_composite_shader *shader = NULL; + struct blendinfo op_info; + PictFormatShort saved_source_format = 0; + PixmapPtr source_pixmap = NULL; + PixmapPtr dest_pixmap = NULL; + glamor_pixmap_private *source_pixmap_priv = NULL; + glamor_pixmap_private *dest_pixmap_priv = NULL; + glamor_pixmap_private *temp_src_priv = NULL; + int x_temp_src, y_temp_src; + int src_width, src_height; + int source_x_off, source_y_off; + GLfloat src_xscale = 1, src_yscale = 1; + int x_dst, y_dst; + int dest_x_off, dest_y_off; + GLfloat dst_xscale, dst_yscale; + BoxRec bounds; + PicturePtr temp_src = src; + glamor_gl_dispatch *dispatch = NULL; + int vert_stride = 3; + int ntriangle_per_loop; + int nclip_rect; + int mclip_rect; + int clip_processed; + int clipped_vtx[6*2]; + RegionRec region; + BoxPtr box = NULL; + BoxPtr pbox = NULL; + int traps_count = 0; + int traps_not_completed = 0; + xTrapezoid * ptrap = NULL; + int nbox; + float src_matrix[9]; + Bool ret = FALSE; + + /* If a mask format wasn't provided, we get to choose, but behavior should + * be as if there was no temporary mask the traps were accumulated into. + */ + if (!mask_format) { + if (dst->polyEdge == PolyEdgeSharp) + mask_format = PictureMatchFormat(screen, 1, PICT_a1); + else + mask_format = PictureMatchFormat(screen, 8, PICT_a8); + for (; ntrap; ntrap--, traps++) + glamor_trapezoids(op, src, dst, mask_format, x_src, + y_src, 1, traps); + return TRUE; + } + + miTrapezoidBounds(ntrap, traps, &bounds); + DEBUGF("The bounds for all traps is: bounds.x1 = %d, bounds.x2 = %d, " + "bounds.y1 = %d, bounds.y2 = %d\n", bounds.x1, bounds.x2, + bounds.y1, bounds.y2); + + /* No area need to render. */ + if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2) + return TRUE; + + dest_pixmap = glamor_get_drawable_pixmap(dst->pDrawable); + dest_pixmap_priv = glamor_get_pixmap_private(dest_pixmap); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(dest_pixmap_priv) + || dest_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { + /* Currently. Always fallback to cpu if destination is in CPU memory.*/ + ret = FALSE; + DEBUGF("dst pixmap has no FBO.\n"); + goto TRAPEZOID_OUT; + } + + if (src->pDrawable) { + source_pixmap = glamor_get_drawable_pixmap(src->pDrawable); + source_pixmap_priv = glamor_get_pixmap_private(source_pixmap); + temp_src_priv = source_pixmap_priv; + if (source_pixmap_priv + && (source_pixmap_priv->type == GLAMOR_DRM_ONLY + || source_pixmap_priv->type == GLAMOR_TEXTURE_LARGE)) { + ret = FALSE; + goto TRAPEZOID_OUT; + } + } + + x_dst = bounds.x1; + y_dst = bounds.y1; + + src_width = bounds.x2 - bounds.x1; + src_height = bounds.y2 - bounds.y1; + + x_temp_src = x_src + bounds.x1 - (traps[0].left.p1.x >> 16); + y_temp_src = y_src + bounds.y1 - (traps[0].left.p1.y >> 16); + + if ((!src->pDrawable && + (src->pSourcePict->type != SourcePictTypeSolidFill)) //1. The Gradient case. + /* 2. Has no fbo but can upload.*/ + || (src->pDrawable && !GLAMOR_PIXMAP_PRIV_HAS_FBO(source_pixmap_priv) + && ((src_width * src_height * 4 < + source_pixmap->drawable.width * source_pixmap->drawable.height) + || !glamor_check_fbo_size(glamor_priv, source_pixmap->drawable.width, + source_pixmap->drawable.height)))) { + + if (!glamor_check_fbo_size(glamor_priv, src_width, src_height)) { + ret = FALSE; + goto TRAPEZOID_OUT; + } + temp_src = glamor_convert_gradient_picture(screen, src, + x_src, y_src, + src_width, src_height); + if (!temp_src) { + temp_src = src; + ret = FALSE; + DEBUGF("Convert gradient picture failed\n"); + goto TRAPEZOID_OUT; + } + temp_src_priv = glamor_get_pixmap_private((PixmapPtr)temp_src->pDrawable); + x_temp_src = y_temp_src = 0; + } + + x_dst += dst->pDrawable->x; + y_dst += dst->pDrawable->y; + if (temp_src->pDrawable) { + x_temp_src += temp_src->pDrawable->x; + y_temp_src += temp_src->pDrawable->y; + } + + if (!miComputeCompositeRegion(®ion, + temp_src, NULL, dst, + x_temp_src, y_temp_src, + 0, 0, + x_dst, y_dst, + src_width, src_height)) { + DEBUGF("All the regions are clipped out, do nothing\n"); + goto TRAPEZOID_OUT; + } + + box = REGION_RECTS(®ion); + nbox = REGION_NUM_RECTS(®ion); + pbox = box; + + ret = glamor_composite_choose_shader(op, temp_src, NULL, dst, + temp_src_priv, NULL, dest_pixmap_priv, + &key, &shader, &op_info, &saved_source_format); + if (ret == FALSE) { + DEBUGF("can not set the shader program for composite\n"); + goto TRAPEZOID_RESET_GL; + } + glamor_set_destination_pixmap_priv_nc(dest_pixmap_priv); + glamor_composite_set_shader_blend(dest_pixmap_priv, &key, shader, &op_info); + glamor_priv->has_source_coords = key.source != SHADER_SOURCE_SOLID; + glamor_priv->has_mask_coords = (key.mask != SHADER_MASK_NONE && + key.mask != SHADER_MASK_SOLID); + + dispatch = glamor_get_dispatch(glamor_priv); + + glamor_get_drawable_deltas(dst->pDrawable, dest_pixmap, + &dest_x_off, &dest_y_off); + + pixmap_priv_get_dest_scale(dest_pixmap_priv, &dst_xscale, &dst_yscale); + + if (glamor_priv->has_source_coords) { + source_pixmap = glamor_get_drawable_pixmap(temp_src->pDrawable); + source_pixmap_priv = glamor_get_pixmap_private(source_pixmap); + glamor_get_drawable_deltas(temp_src->pDrawable, + source_pixmap, + &source_x_off, &source_y_off); + pixmap_priv_get_scale(source_pixmap_priv, + &src_xscale, &src_yscale); + glamor_picture_get_matrixf(temp_src, src_matrix); + vert_stride += 3; + } + + if (glamor_priv->has_mask_coords) { + DEBUGF("Should never have mask coords here!\n"); + ret = FALSE; + goto TRAPEZOID_RESET_GL; + } + + /* A trapezoid clip with a rectangle will at most generate a hexagon, + which can be devided into 4 triangles to render. */ + ntriangle_per_loop = (vert_stride * nbox * ntrap * 4) > GLAMOR_COMPOSITE_VBO_VERT_CNT ? + (GLAMOR_COMPOSITE_VBO_VERT_CNT / vert_stride) : nbox * ntrap * 4; + ntriangle_per_loop = (ntriangle_per_loop / 4) * 4; + + nclip_rect = nbox; + while (nclip_rect) { + mclip_rect = (nclip_rect * ntrap * 4) > ntriangle_per_loop ? + (ntriangle_per_loop / (4 * ntrap)) : nclip_rect; + + if (!mclip_rect) {/* Maybe too many traps. */ + mclip_rect = 1; + ptrap = traps; + traps_count = ntriangle_per_loop / 4; + traps_not_completed = ntrap - traps_count; + } else { + traps_count = ntrap; + ptrap = traps; + traps_not_completed = 0; + } + +NTRAPS_LOOP_AGAIN: + + glamor_setup_composite_vbo(screen, mclip_rect * traps_count * 4 * vert_stride); + clip_processed = mclip_rect; + + + while (mclip_rect--) { + while (traps_count--) { + int vtx_num; + int i; + float vertices[3*2], source_texcoords[3*2]; + + DEBUGF("In loop of render trapezoid, nclip_rect = %d, mclip_rect = %d, " + "clip_processed = %d, traps_count = %d, traps_not_completed = %d\n", + nclip_rect, mclip_rect, clip_processed, traps_count, traps_not_completed); + + if (_glamor_clip_trapezoid_vertex(ptrap, pbox, clipped_vtx, &vtx_num)) { + for (i = 0; i < vtx_num - 2; i++) { + int clipped_vtx_tmp[3*2]; + + clipped_vtx_tmp[0] = clipped_vtx[0]; + clipped_vtx_tmp[1] = clipped_vtx[1]; + clipped_vtx_tmp[2] = clipped_vtx[(i+1)*2]; + clipped_vtx_tmp[3] = clipped_vtx[(i+1)*2 + 1]; + clipped_vtx_tmp[4] = clipped_vtx[(i+2)*2]; + clipped_vtx_tmp[5] = clipped_vtx[(i+2)*2 + 1]; + glamor_set_normalize_tri_vcoords( + dst_xscale, dst_yscale, clipped_vtx_tmp, + glamor_priv->yInverted, vertices); + DEBUGF("vertices of triangle: (%f X %f), (%f X %f), " + "(%f X %f)\n", vertices[0], vertices[1], + vertices[2], vertices[3], vertices[4], vertices[5]); + + + if (key.source != SHADER_SOURCE_SOLID) { + if (src->transform) { + glamor_set_transformed_normalize_tri_tcoords( + source_pixmap_priv, + src_matrix, src_xscale, src_yscale, + clipped_vtx_tmp, + glamor_priv->yInverted, + source_texcoords); + } else { + glamor_set_normalize_tri_tcoords( + src_xscale, src_yscale, + clipped_vtx_tmp, + glamor_priv->yInverted, + source_texcoords); + } + + DEBUGF("source_texcoords of triangle: (%f X %f), " + "(%f X %f), (%f X %f)\n", + source_texcoords[0], source_texcoords[1], + source_texcoords[2], source_texcoords[3], + source_texcoords[4], source_texcoords[5]); + } + + glamor_emit_composite_triangle(screen, source_texcoords, + NULL, vertices); + } + } + + ptrap++; + } + + if (traps_not_completed) { /* one loop of ntraps not completed */ + mclip_rect = 1; + traps_count = traps_not_completed > (ntriangle_per_loop / 4) ? + (ntriangle_per_loop / 4) : traps_not_completed; + traps_not_completed -= traps_count; + glamor_flush_composite_triangles(screen); + goto NTRAPS_LOOP_AGAIN; + } else { + ptrap = traps; + traps_count = ntrap; + } + + pbox++; + } + + glamor_flush_composite_triangles(screen); + + nclip_rect -= clip_processed; + } + + ret = TRUE; + +TRAPEZOID_RESET_GL: + dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_MASK); + dispatch->glDisable(GL_BLEND); +#ifndef GLAMOR_GLES2 + dispatch->glActiveTexture(GL_TEXTURE0); + dispatch->glDisable(GL_TEXTURE_2D); + dispatch->glActiveTexture(GL_TEXTURE1); + dispatch->glDisable(GL_TEXTURE_2D); +#endif + dispatch->glUseProgram(0); + +TRAPEZOID_OUT: + if (box) { + REGION_UNINIT(dst->pDrawable->pScreen, ®ion); + } + + if (temp_src != src) { + FreePicture(temp_src, 0); + } else { + if (saved_source_format) { + src->format = saved_source_format; + } + } + + if (dispatch) { + glamor_put_dispatch(glamor_priv); + } + + return ret; +} + +void +glamor_init_trapezoid_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + GLint fs_prog, vs_prog; + + const char *trapezoid_vs = + GLAMOR_DEFAULT_PRECISION + "attribute vec4 v_position;\n" + "attribute vec2 v_texcoord;\n" + /* v_top_bottom, v_left_param and v_right_param contain the + constant value for all the vertex of one rect. Using uniform + is more suitable but we need to reset the uniform variables + for every rect rendering and can not use the vbo, which causes + performance loss. So we set these attributes to same value + for every vertex of one rect and so it is also a constant in FS */ + "attribute vec2 v_top_bottom;\n" + "attribute vec4 v_left_param;\n" + "attribute vec4 v_right_param;\n" + "\n" + "varying vec2 source_texture;\n" + "varying float trap_top;\n" + "varying float trap_bottom;\n" + "varying float trap_left_x;\n" + "varying float trap_left_y;\n" + "varying float trap_left_slope;\n" + "varying float trap_left_vertical_f;\n" + "varying float trap_right_x;\n" + "varying float trap_right_y;\n" + "varying float trap_right_slope;\n" + "varying float trap_right_vertical_f;\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = v_position;\n" + " source_texture = v_texcoord.xy;\n" + " trap_top = v_top_bottom.x;\n" + " trap_bottom = v_top_bottom.y;\n" + " \n" + " trap_left_x = v_left_param.x;\n" + " trap_left_y = v_left_param.y;\n" + " trap_left_slope = v_left_param.z;\n" + " trap_left_vertical_f = v_left_param.w;\n" + " \n" + " trap_right_x = v_right_param.x;\n" + " trap_right_y = v_right_param.y;\n" + " trap_right_slope = v_right_param.z;\n" + " trap_right_vertical_f = v_right_param.w;\n" + "}\n"; + + /* + * Because some GL fill function do not support the MultSample + * anti-alias, we need to do the MSAA here. This manner like + * pixman, will caculate the value of area in trapezoid dividing + * the totol area for each pixel, as follow: + | + ----+------------------------------------------------------> + | + | ------------- + | / \ + | / \ + | / \ + | / +----------------+ + | / |.....\ | + | / |......\ | + | / |.......\ | + | / |........\ | + | /-------------------+---------\ | + | | | + | | | + | +----------------+ + | + \|/ + + */ + const char *trapezoid_fs = + GLAMOR_DEFAULT_PRECISION + "varying vec2 source_texture; \n" + "varying float trap_top; \n" + "varying float trap_bottom; \n" + "varying float trap_left_x; \n" + "varying float trap_left_y; \n" + "varying float trap_left_slope; \n" + "varying float trap_left_vertical_f; \n" + "varying float trap_right_x; \n" + "varying float trap_right_y; \n" + "varying float trap_right_slope; \n" + "varying float trap_right_vertical_f; \n" + "float x_per_pix = 1.0;" + "float y_per_pix = 1.0;" + "\n" + "float get_alpha_val() \n" + "{ \n" + " float x_up_cut_left; \n" + " float x_bottom_cut_left; \n" + " float x_up_cut_right; \n" + " float x_bottom_cut_right; \n" + " bool trap_left_vertical;\n" + " bool trap_right_vertical;\n" + " if (abs(trap_left_vertical_f - 1.0) <= 0.0001)\n" + " trap_left_vertical = true;\n" + " else\n" + " trap_left_vertical = false;\n" + " if (abs(trap_right_vertical_f - 1.0) <= 0.0001)\n" + " trap_right_vertical = true;\n" + " else\n" + " trap_right_vertical = false;\n" + " \n" + " if(trap_left_vertical == true) { \n" + " x_up_cut_left = trap_left_x; \n" + " x_bottom_cut_left = trap_left_x; \n" + " } else { \n" + " x_up_cut_left = trap_left_x \n" + " + (source_texture.y - y_per_pix/2.0 - trap_left_y) \n" + " / trap_left_slope; \n" + " x_bottom_cut_left = trap_left_x \n" + " + (source_texture.y + y_per_pix/2.0 - trap_left_y) \n" + " / trap_left_slope; \n" + " } \n" + " \n" + " if(trap_right_vertical == true) { \n" + " x_up_cut_right = trap_right_x; \n" + " x_bottom_cut_right = trap_right_x; \n" + " } else { \n" + " x_up_cut_right = trap_right_x \n" + " + (source_texture.y - y_per_pix/2.0 - trap_right_y) \n" + " / trap_right_slope; \n" + " x_bottom_cut_right = trap_right_x \n" + " + (source_texture.y + y_per_pix/2.0 - trap_right_y) \n" + " / trap_right_slope; \n" + " } \n" + " \n" + " if((x_up_cut_left <= source_texture.x - x_per_pix/2.0) && \n" + " (x_bottom_cut_left <= source_texture.x - x_per_pix/2.0) && \n" + " (x_up_cut_right >= source_texture.x + x_per_pix/2.0) && \n" + " (x_bottom_cut_right >= source_texture.x + x_per_pix/2.0) && \n" + " (trap_top <= source_texture.y - y_per_pix/2.0) && \n" + " (trap_bottom >= source_texture.y + y_per_pix/2.0)) { \n" + // The complete inside case. + " return 1.0; \n" + " } else if((trap_top > source_texture.y + y_per_pix/2.0) || \n" + " (trap_bottom < source_texture.y - y_per_pix/2.0)) { \n" + // The complete outside. Above the top or Below the bottom. + " return 0.0; \n" + " } else { \n" + " if((x_up_cut_right < source_texture.x - x_per_pix/2.0 && \n" + " x_bottom_cut_right < source_texture.x - x_per_pix/2.0) \n" + " || (x_up_cut_left > source_texture.x + x_per_pix/2.0 && \n" + " x_bottom_cut_left > source_texture.x + x_per_pix/2.0)) { \n" + // The complete outside. At Left or Right of the trapezoide. + " return 0.0; \n" + " } \n" + " } \n" + // Get here, the pix is partly inside the trapezoid. + " { \n" + " float percent = 0.0; \n" + " float up = (source_texture.y - y_per_pix/2.0) >= trap_top ? \n" + " (source_texture.y - y_per_pix/2.0) : trap_top; \n" + " float bottom = (source_texture.y + y_per_pix/2.0) <= trap_bottom ? \n" + " (source_texture.y + y_per_pix/2.0) : trap_bottom; \n" + " float left = source_texture.x - x_per_pix/2.0; \n" + " float right = source_texture.x + x_per_pix/2.0; \n" + " \n" + " percent = (bottom - up) / y_per_pix; \n" + " \n" + " if(trap_left_vertical == true) { \n" + " if(trap_left_x > source_texture.x - x_per_pix/2.0 && \n" + " trap_left_x < source_texture.x + x_per_pix/2.0) \n" + " left = trap_left_x; \n" + " } \n" + " if(trap_right_vertical == true) { \n" + " if(trap_right_x > source_texture.x - x_per_pix/2.0 && \n" + " trap_right_x < source_texture.x + x_per_pix/2.0) \n" + " right = trap_right_x; \n" + " } \n" + " if((up >= bottom) || (left >= right)) \n" + " return 0.0; \n" + " \n" + " percent = percent * ((right - left)/x_per_pix); \n" + " if(trap_left_vertical == true && trap_right_vertical == true) \n" + " return percent; \n" + " \n" + " if(trap_left_vertical != true) { \n" + " float area; \n" + // the slope should never be 0.0 here + " float up_x = trap_left_x + (up - trap_left_y)/trap_left_slope; \n" + " float bottom_x = trap_left_x + (bottom - trap_left_y)/trap_left_slope; \n" + " if(trap_left_slope < 0.0 && up_x > left) { \n" + /* case 1 + | + ----+-------------------------------------> + | / + | / + | +---/--------+ + | | /.........| + | | /..........| + | |/...........| + | /............| + | /|............| + | +------------+ + | + \|/ + */ + " float left_y = trap_left_y + trap_left_slope*(left - trap_left_x); \n" + " if((up_x > left) && (left_y > up)) { \n" + " area = 0.5 * (up_x - left) * (left_y - up); \n" + " if(up_x > right) { \n" + " float right_y = trap_left_y \n" + " + trap_left_slope*(right - trap_left_x); \n" + " area = area - 0.5 * (up_x - right) * (right_y - up); \n" + " } \n" + " if(left_y > bottom) { \n" + " area = area - 0.5 * (bottom_x - left) * (left_y - bottom); \n" + " } \n" + " } else { \n" + " area = 0.0; \n" + " } \n" + " percent = percent * (1.0 - (area/((right-left)*(bottom-up)))); \n" + " } else if(trap_left_slope > 0.0 && bottom_x > left) { \n" + /* case 2 + | + ----+-------------------------------------> + | \ + | \ + | +\-----------+ + | | \..........| + | | \.........| + | | \........| + | | \.......| + | | \......| + | +------\-----+ + | \ + | \ + \|/ + */ + " float right_y = trap_left_y + trap_left_slope*(right - trap_left_x); \n" + " if((up_x < right) && (right_y > up)) { \n" + " area = 0.5 * (right - up_x) * (right_y - up); \n" + " if(up_x < left) { \n" + " float left_y = trap_left_y \n" + " + trap_left_slope*(left - trap_left_x); \n" + " area = area - 0.5 * (left - up_x) * (left_y - up); \n" + " } \n" + " if(right_y > bottom) { \n" + " area = area - 0.5 * (right - bottom_x) * (right_y - bottom); \n" + " } \n" + " } else { \n" + " area = 0.0; \n" + " } \n" + " percent = percent * (area/((right-left)*(bottom-up))); \n" + " } \n" + " } \n" + " \n" + " if(trap_right_vertical != true) { \n" + " float area; \n" + // the slope should never be 0.0 here + " float up_x = trap_right_x + (up - trap_right_y)/trap_right_slope; \n" + " float bottom_x = trap_right_x + (bottom - trap_right_y)/trap_right_slope; \n" + " if(trap_right_slope < 0.0 && bottom_x < right) { \n" + /* case 3 + | + ----+-------------------------------------> + | / + | +--------/---+ + | |......./ | + | |....../ | + | |...../ | + | |..../ | + | |.../ | + | +--/---------+ + | / + | + \|/ + */ + " float left_y = trap_right_y + trap_right_slope*(left - trap_right_x); \n" + " if((up_x > left) && (left_y > up)) { \n" + " area = 0.5 * (up_x - left) * (left_y - up); \n" + " if(up_x > right) { \n" + " float right_y = trap_right_y \n" + " + trap_right_slope*(right - trap_right_x); \n" + " area = area - 0.5 * (up_x - right) * (right_y - up); \n" + " } \n" + " if(left_y > bottom) { \n" + " area = area - 0.5 * (bottom_x - left) * (left_y - bottom); \n" + " } \n" + " } else { \n" + " area = 0.0; \n" + " } \n" + " percent = percent * (area/((right-left)*(bottom-up))); \n" + " } else if(trap_right_slope > 0.0 && up_x < right) { \n" + /* case 4 + | + ----+-------------------------------------> + | \ + | +--------\---+ + | |.........\ | + | |..........\ | + | |...........\| + | |............\ + | |............|\ + | +------------+ \ + | \ + | + \|/ + */ + " float right_y = trap_right_y + trap_right_slope*(right - trap_right_x); \n" + " if((up_x < right) && (right_y > up)) { \n" + " area = 0.5 * (right - up_x) * (right_y - up); \n" + " if(up_x < left) { \n" + " float left_y = trap_right_y \n" + " + trap_right_slope*(left - trap_right_x); \n" + " area = area - 0.5 * (left - up_x) * (left_y - up); \n" + " } \n" + " if(right_y > bottom) { \n" + " area = area - 0.5 * (right - bottom_x) * (right_y - bottom); \n" + " } \n" + " } else { \n" + " area = 0.0; \n" + " } \n" + " percent = percent * (1.0 - (area/((right-left)*(bottom-up)))); \n" + " } \n" + " } \n" + " \n" + " return percent; \n" + " } \n" + "} \n" + "\n" + "void main() \n" + "{ \n" + " float alpha_val = get_alpha_val(); \n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, alpha_val); \n" + "}\n"; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + + glamor_priv->trapezoid_prog = dispatch->glCreateProgram(); + + vs_prog = glamor_compile_glsl_prog(dispatch, + GL_VERTEX_SHADER, trapezoid_vs); + fs_prog = glamor_compile_glsl_prog(dispatch, + GL_FRAGMENT_SHADER, trapezoid_fs); + + dispatch->glAttachShader(glamor_priv->trapezoid_prog, vs_prog); + dispatch->glAttachShader(glamor_priv->trapezoid_prog, fs_prog); + + dispatch->glBindAttribLocation(glamor_priv->trapezoid_prog, + GLAMOR_VERTEX_POS, "v_positionsition"); + dispatch->glBindAttribLocation(glamor_priv->trapezoid_prog, + GLAMOR_VERTEX_SOURCE, "v_texcoord"); + dispatch->glBindAttribLocation(glamor_priv->trapezoid_prog, + GLAMOR_VERTEX_TOP_BOTTOM, "v_top_bottom"); + dispatch->glBindAttribLocation(glamor_priv->trapezoid_prog, + GLAMOR_VERTEX_LEFT_PARAM, "v_left_param"); + dispatch->glBindAttribLocation(glamor_priv->trapezoid_prog, + GLAMOR_VERTEX_RIGHT_PARAM, "v_right_param"); + + glamor_link_glsl_prog(dispatch, glamor_priv->trapezoid_prog); + + dispatch->glUseProgram(0); + + glamor_put_dispatch(glamor_priv); +} + +void +glamor_fini_trapezoid_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + dispatch->glDeleteProgram(glamor_priv->trapezoid_prog); + glamor_put_dispatch(glamor_priv); +} + +static Bool +_glamor_generate_trapezoid_with_shader(ScreenPtr screen, PicturePtr picture, + xTrapezoid * traps, int ntrap, BoxRec *bounds) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + glamor_pixmap_private *pixmap_priv; + PixmapPtr pixmap = NULL; + GLint trapezoid_prog; + GLfloat xscale, yscale; + float left_slope, right_slope; + xTrapezoid *ptrap; + BoxRec one_trap_bound; + int nrect_max; + int i, j; + float *vertices; + float params[4]; + + glamor_priv = glamor_get_screen_private(screen); + trapezoid_prog = glamor_priv->trapezoid_prog; + + pixmap = glamor_get_drawable_pixmap(picture->pDrawable); + pixmap_priv = glamor_get_pixmap_private(pixmap); + + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv) + || pixmap_priv->type == GLAMOR_TEXTURE_LARGE) { /* should always have here. */ + DEBUGF("GLAMOR_PIXMAP_PRIV_HAS_FBO check failed, fallback\n"); + return FALSE; + } + + /* First, clear all to zero */ + if (!glamor_solid(pixmap, 0, 0, pixmap_priv->base.pixmap->drawable.width, + pixmap_priv->base.pixmap->drawable.height, + GXclear, 0xFFFFFFFF, 0)) { + DEBUGF("glamor_solid failed, fallback\n"); + return FALSE; + } + + dispatch = glamor_get_dispatch(glamor_priv); + + glamor_set_destination_pixmap_priv_nc(pixmap_priv); + + pixmap_priv_get_dest_scale(pixmap_priv, (&xscale), (&yscale)); + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + /* Now draw the Trapezoid mask. */ + dispatch->glUseProgram(trapezoid_prog); + + dispatch->glEnable(GL_BLEND); + dispatch->glBlendFunc(GL_ONE, GL_ONE); + + nrect_max = GLAMOR_COMPOSITE_VBO_VERT_CNT / (4 * GLAMOR_VERTEX_RIGHT_PARAM); + + for (i = 0; i < ntrap;) { + int mrect; + int stride; + + mrect = (ntrap - i) > nrect_max ? nrect_max : (ntrap - i); + glamor_setup_composite_vbo_for_trapezoid(screen, 4 * mrect); + stride = glamor_priv->vb_stride / sizeof(float); + + for (j = 0; j < mrect; j++) { + ptrap = traps + i + j; + + DEBUGF("--- The parameter of xTrapezoid is:\ntop: %d 0x%x\tbottom: %d 0x%x\n" + "left: p1 (%d 0x%x, %d 0x%x)\tp2 (%d 0x%x, %d 0x%x)\n" + "right: p1 (%d 0x%x, %d 0x%x)\tp2 (%d 0x%x, %d 0x%x)\n", + xFixedToInt(ptrap->top), ptrap->top, + xFixedToInt(ptrap->bottom), ptrap->bottom, + xFixedToInt(ptrap->left.p1.x), ptrap->left.p1.x, + xFixedToInt(ptrap->left.p1.y), ptrap->left.p1.y, + xFixedToInt(ptrap->left.p2.x), ptrap->left.p2.x, + xFixedToInt(ptrap->left.p2.y), ptrap->left.p2.y, + xFixedToInt(ptrap->right.p1.x), ptrap->right.p1.x, + xFixedToInt(ptrap->right.p1.y), ptrap->right.p1.y, + xFixedToInt(ptrap->right.p2.x), ptrap->right.p2.x, + xFixedToInt(ptrap->right.p2.y), ptrap->right.p2.y); + + miTrapezoidBounds(1, ptrap, &one_trap_bound); + + vertices = (float*)(glamor_priv->vb + glamor_priv->vbo_offset) + 2; + glamor_set_tcoords_ext((pixmap_priv->base.pixmap->drawable.width), + (pixmap_priv->base.pixmap->drawable.height), + (one_trap_bound.x1), + (one_trap_bound.y1), + (one_trap_bound.x2), + (one_trap_bound.y2), + glamor_priv->yInverted, vertices, stride); + DEBUGF("tex_vertices --> leftup : %f X %f, rightup: %f X %f," + "rightbottom: %f X %f, leftbottom : %f X %f\n", + vertices[0], vertices[1], + vertices[1*stride], vertices[1*stride + 1], + vertices[2*stride], vertices[2*stride + 1], + vertices[3*stride], vertices[3*stride + 1]); + + /* Need to rebase. */ + one_trap_bound.x1 -= bounds->x1; + one_trap_bound.x2 -= bounds->x1; + one_trap_bound.y1 -= bounds->y1; + one_trap_bound.y2 -= bounds->y1; + + vertices -= 2; + + glamor_set_normalize_vcoords_ext(pixmap_priv, xscale, yscale, + one_trap_bound.x1, one_trap_bound.y1, + one_trap_bound.x2, one_trap_bound.y2, + glamor_priv->yInverted, vertices, stride); + DEBUGF("vertices --> leftup : %f X %f, rightup: %f X %f," + "rightbottom: %f X %f, leftbottom : %f X %f\n", + vertices[0], vertices[1], + vertices[1*stride], vertices[1*stride + 1], + vertices[2*stride], vertices[2*stride + 1], + vertices[3*stride], vertices[3*stride + 1]); + vertices += 4; + + /* Set the top and bottom. */ + params[0] = ((float)ptrap->top) / 65536; + params[1] = ((float)ptrap->bottom) / 65536; + glamor_set_const_ext(params, 2, vertices, 4, stride); + vertices += 2; + + /* Set the left params. */ + params[0] = ((float)ptrap->left.p1.x) / 65536; + params[1] = ((float)ptrap->left.p1.y) / 65536; + + if (ptrap->left.p1.x == ptrap->left.p2.x) { + left_slope = 0.0; + params[3] = 1.0; + } else { + left_slope = ((float)(ptrap->left.p1.y - ptrap->left.p2.y)) + / ((float)(ptrap->left.p1.x - ptrap->left.p2.x)); + params[3] = 0.0; + } + params[2] = left_slope; + glamor_set_const_ext(params, 4, vertices, 4, stride); + vertices += 4; + + /* Set the left params. */ + params[0] = ((float)ptrap->right.p1.x) / 65536; + params[1] = ((float)ptrap->right.p1.y) / 65536; + + if (ptrap->right.p1.x == ptrap->right.p2.x) { + right_slope = 0.0; + params[3] = 1.0; + } else { + right_slope = ((float)(ptrap->right.p1.y - ptrap->right.p2.y)) + / ((float)(ptrap->right.p1.x - ptrap->right.p2.x)); + params[3] = 0.0; + } + params[2] = right_slope; + glamor_set_const_ext(params, 4, vertices, 4, stride); + + DEBUGF("trap_top = %f, trap_bottom = %f, " + "trap_left_x = %f, trap_left_y = %f, left_slope = %f, " + "trap_right_x = %f, trap_right_y = %f, right_slope = %f\n", + ((float)ptrap->top) / 65536, ((float)ptrap->bottom) / 65536, + ((float)ptrap->left.p1.x) / 65536, ((float)ptrap->left.p1.y) / 65536, + left_slope, + ((float)ptrap->right.p1.x) / 65536, ((float)ptrap->right.p1.y) / 65536, + right_slope); + + glamor_priv->render_nr_verts += 4; + glamor_priv->vbo_offset += glamor_priv->vb_stride * 4; + } + + i += mrect; + + /* Now rendering. */ + if (!glamor_priv->render_nr_verts) + continue; + + if (glamor_priv->gl_flavor == GLAMOR_GL_DESKTOP) + dispatch->glUnmapBuffer(GL_ARRAY_BUFFER); + else { + dispatch->glBindBuffer(GL_ARRAY_BUFFER, glamor_priv->vbo); + dispatch->glBufferData(GL_ARRAY_BUFFER, + glamor_priv->vbo_offset, + glamor_priv->vb, GL_DYNAMIC_DRAW); + } + +#ifndef GLAMOR_GLES2 + dispatch->glDrawRangeElements(GL_TRIANGLES, 0, glamor_priv->render_nr_verts, + (glamor_priv->render_nr_verts * 3) / 2, + GL_UNSIGNED_SHORT, NULL); +#else + dispatch->glDrawElements(GL_TRIANGLES, (glamor_priv->render_nr_verts * 3) / 2, + GL_UNSIGNED_SHORT, NULL); +#endif + } + + dispatch->glBindBuffer(GL_ARRAY_BUFFER, 0); + dispatch->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + dispatch->glBlendFunc(GL_ONE, GL_ZERO); + dispatch->glDisable(GL_BLEND); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_TOP_BOTTOM); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_LEFT_PARAM); + dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_RIGHT_PARAM); + dispatch->glUseProgram(0); + glamor_put_dispatch(glamor_priv); + return TRUE; +} + +#endif /*GLAMOR_TRAPEZOID_SHADER */ + +/** + * Creates an appropriate picture for temp mask use. + */ +static PicturePtr +glamor_create_mask_picture(ScreenPtr screen, + PicturePtr dst, + PictFormatPtr pict_format, + CARD16 width, CARD16 height, int gpu) +{ + PixmapPtr pixmap; + PicturePtr picture; + int error; + + if (!pict_format) { + if (dst->polyEdge == PolyEdgeSharp) + pict_format = + PictureMatchFormat(screen, 1, PICT_a1); + else + pict_format = + PictureMatchFormat(screen, 8, PICT_a8); + if (!pict_format) + return 0; + } + + if (gpu) { + pixmap = glamor_create_pixmap(screen, width, height, + pict_format->depth, 0); + } else { + pixmap = glamor_create_pixmap(screen, 0, 0, + pict_format->depth, + GLAMOR_CREATE_PIXMAP_CPU); + } + + if (!pixmap) + return 0; + picture = CreatePicture(0, &pixmap->drawable, pict_format, + 0, 0, serverClient, &error); + glamor_destroy_pixmap(pixmap); + return picture; +} + +static int +_glamor_trapezoid_bounds (int ntrap, xTrapezoid *traps, BoxPtr box) +{ + int has_large_trapezoid = 0; + box->y1 = MAXSHORT; + box->y2 = MINSHORT; + box->x1 = MAXSHORT; + box->x2 = MINSHORT; + + for (; ntrap; ntrap--, traps++) { + INT16 x1, y1, x2, y2; + + if (!xTrapezoidValid(traps)) + continue; + y1 = xFixedToInt (traps->top); + if (y1 < box->y1) + box->y1 = y1; + + y2 = xFixedToInt (xFixedCeil (traps->bottom)); + if (y2 > box->y2) + box->y2 = y2; + + x1 = xFixedToInt (min (_glamor_linefixedX (&traps->left, traps->top, FALSE), + _glamor_linefixedX (&traps->left, traps->bottom, FALSE))); + if (x1 < box->x1) + box->x1 = x1; + + x2 = xFixedToInt (xFixedCeil (max (_glamor_linefixedX (&traps->right, traps->top, TRUE), + _glamor_linefixedX (&traps->right, traps->bottom, TRUE)))); + if (x2 > box->x2) + box->x2 = x2; + + if (!has_large_trapezoid && (x2 - x1) > 256 && (y2 - y1) > 32) + has_large_trapezoid = 1; + } + + return has_large_trapezoid; +} + +/** + * glamor_trapezoids will first try to create a trapezoid mask using shader, + * if failed, miTrapezoids will generate trapezoid mask accumulating in + * system memory. + */ +static Bool +_glamor_trapezoids(CARD8 op, + PicturePtr src, PicturePtr dst, + PictFormatPtr mask_format, INT16 x_src, INT16 y_src, + int ntrap, xTrapezoid * traps, Bool fallback) +{ + ScreenPtr screen = dst->pDrawable->pScreen; + BoxRec bounds; + PicturePtr picture; + INT16 x_dst, y_dst; + INT16 x_rel, y_rel; + int width, height, stride; + PixmapPtr pixmap; + pixman_image_t *image = NULL; + int ret = 0; + int has_large_trapezoid; + + /* If a mask format wasn't provided, we get to choose, but behavior should + * be as if there was no temporary mask the traps were accumulated into. + */ + if (!mask_format) { + if (dst->polyEdge == PolyEdgeSharp) + mask_format = + PictureMatchFormat(screen, 1, PICT_a1); + else + mask_format = + PictureMatchFormat(screen, 8, PICT_a8); + for (; ntrap; ntrap--, traps++) + glamor_trapezoids(op, src, dst, mask_format, x_src, + y_src, 1, traps); + return TRUE; + } + + has_large_trapezoid = _glamor_trapezoid_bounds(ntrap, traps, &bounds); + DEBUGF("The bounds for all traps is: bounds.x1 = %d, bounds.x2 = %d, " + "bounds.y1 = %d, bounds.y2 = %d, ---- ntrap = %d\n", bounds.x1, + bounds.x2, bounds.y1, bounds.y2, ntrap); + + if (bounds.y1 >= bounds.y2 || bounds.x1 >= bounds.x2) + return TRUE; + + x_dst = traps[0].left.p1.x >> 16; + y_dst = traps[0].left.p1.y >> 16; + + width = bounds.x2 - bounds.x1; + height = bounds.y2 - bounds.y1; + stride = PixmapBytePad(width, mask_format->depth); + +#ifdef GLAMOR_TRAPEZOID_SHADER + /* We seperate the render to two paths. + Some GL implemetation do not implement the Anti-Alias for triangles + and polygen's filling. So when the edge is not vertical or horizontal, + sawtooth will be obvious. The trapezoid is widely used to render wide + lines and circles. In these case, the line or circle will be divided + into a large number of small trapezoids to approximate it, so the sawtooth + at the edge will cause the result not be acceptable. + When the depth of the mask is 1, there is no Anti-Alias needed, so we + use the clip logic to generate the result directly(fast path). + When the depth is not 1, AA is needed and we use a shader to generate + a temp mask pixmap. + */ + if (mask_format->depth == 1) { + ret = _glamor_trapezoids_with_shader(op, src, dst, mask_format, + x_src, y_src, ntrap, traps); + if(ret) + return TRUE; + } else { + if (has_large_trapezoid || ntrap > 256) { + /* The shader speed is relative slower than pixman when generating big chunk + trapezoid mask. We fallback to pixman to improve the performance. */ + ; + } else if (dst->polyMode == PolyModeImprecise) { + /* The precise mode is that we sample the trapezoid on the centre points of + an (2*n+1)x(2*n-1) subpixel grid. It is computationally expensive in shader + and we use inside area ratio to replace it if the polymode == Imprecise. */ + picture = glamor_create_mask_picture(screen, dst, mask_format, + width, height, 1); + if (!picture) + return TRUE; + + ret = _glamor_generate_trapezoid_with_shader(screen, picture, traps, ntrap, &bounds); + + if (!ret) + FreePicture(picture, 0); + } + } +#endif + + if (!ret) { + DEBUGF("Fallback to sw rasterize of trapezoid\n"); + + picture = glamor_create_mask_picture(screen, dst, mask_format, + width, height, 0); + if (!picture) + return TRUE; + + image = pixman_image_create_bits(picture->format, + width, height, NULL, stride); + if (!image) { + FreePicture(picture, 0); + return TRUE; + } + + for (; ntrap; ntrap--, traps++) + pixman_rasterize_trapezoid(image, + (pixman_trapezoid_t *) traps, + -bounds.x1, -bounds.y1); + + pixmap = glamor_get_drawable_pixmap(picture->pDrawable); + + screen->ModifyPixmapHeader(pixmap, width, height, + mask_format->depth, + BitsPerPixel(mask_format->depth), + PixmapBytePad(width, + mask_format->depth), + pixman_image_get_data(image)); + } + + x_rel = bounds.x1 + x_src - x_dst; + y_rel = bounds.y1 + y_src - y_dst; + DEBUGF("x_src = %d, y_src = %d, x_dst = %d, y_dst = %d, " + "x_rel = %d, y_rel = %d\n", x_src, y_src, x_dst, + y_dst, x_rel, y_rel); + + CompositePicture(op, src, picture, dst, + x_rel, y_rel, + 0, 0, + bounds.x1, bounds.y1, + bounds.x2 - bounds.x1, bounds.y2 - bounds.y1); + + if (image) + pixman_image_unref(image); + + FreePicture(picture, 0); + return TRUE; +} + +void +glamor_trapezoids(CARD8 op, + PicturePtr src, PicturePtr dst, + PictFormatPtr mask_format, INT16 x_src, INT16 y_src, + int ntrap, xTrapezoid * traps) +{ + DEBUGF("x_src = %d, y_src = %d, ntrap = %d\n", x_src, y_src, ntrap); + + _glamor_trapezoids(op, src, dst, mask_format, x_src, + y_src, ntrap, traps, TRUE); +} + +Bool +glamor_trapezoids_nf(CARD8 op, + PicturePtr src, PicturePtr dst, + PictFormatPtr mask_format, INT16 x_src, INT16 y_src, + int ntrap, xTrapezoid * traps) +{ + DEBUGF("x_src = %d, y_src = %d, ntrap = %d\n", x_src, y_src, ntrap); + + return _glamor_trapezoids(op, src, dst, mask_format, x_src, + y_src, ntrap, traps, FALSE); +} + +#endif /* RENDER */ + diff --git a/xorg-server/glamor/glamor_triangles.c b/xorg-server/glamor/glamor_triangles.c new file mode 100644 index 000000000..e0f4a9708 --- /dev/null +++ b/xorg-server/glamor/glamor_triangles.c @@ -0,0 +1,80 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1998 Keith Packard + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@gmail.com> + * + */ + +#include "glamor_priv.h" + +static Bool +_glamor_triangles(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, INT16 ySrc, int ntris, xTriangle * tris, Bool fallback) +{ + if (!fallback + && glamor_ddx_fallback_check_pixmap(pDst->pDrawable) + && (!pSrc->pDrawable + || glamor_ddx_fallback_check_pixmap(pSrc->pDrawable))) + return FALSE; + + if (glamor_prepare_access_picture(pDst, GLAMOR_ACCESS_RW)) { + if (glamor_prepare_access_picture(pSrc, + GLAMOR_ACCESS_RO)) { + + fbTriangles(op, pSrc, pDst, maskFormat, xSrc, + ySrc, ntris, tris); + + glamor_finish_access_picture(pSrc, GLAMOR_ACCESS_RO); + } + + glamor_finish_access_picture(pDst, GLAMOR_ACCESS_RW); + } + return TRUE; +} + +void +glamor_triangles(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, INT16 ySrc, int ntris, xTriangle * tris) +{ + _glamor_triangles(op, pSrc, pDst, maskFormat, + xSrc, ySrc, ntris, tris, TRUE); +} + +Bool +glamor_triangles_nf(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, INT16 ySrc, int ntris, xTriangle * tris) +{ + return _glamor_triangles(op, pSrc, pDst, maskFormat, + xSrc, ySrc, ntris, tris, FALSE); +} + diff --git a/xorg-server/glamor/glamor_utils.h b/xorg-server/glamor/glamor_utils.h new file mode 100644 index 000000000..d30783826 --- /dev/null +++ b/xorg-server/glamor/glamor_utils.h @@ -0,0 +1,1835 @@ +/* + * Copyright © 2009 Intel Corporation + * + * 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. + * + * Authors: + * Zhigang Gong <zhigang.gong@linux.intel.com> + * + */ + +#ifndef GLAMOR_PRIV_H +#error This file can only be included by glamor_priv.h +#endif + +#ifndef __GLAMOR_UTILS_H__ +#define __GLAMOR_UTILS_H__ + +#define v_from_x_coord_x(_xscale_, _x_) ( 2 * (_x_) * (_xscale_) - 1.0) +#define v_from_x_coord_y(_yscale_, _y_) (-2 * (_y_) * (_yscale_) + 1.0) +#define v_from_x_coord_y_inverted(_yscale_, _y_) (2 * (_y_) * (_yscale_) - 1.0) +#define t_from_x_coord_x(_xscale_, _x_) ((_x_) * (_xscale_)) +#define t_from_x_coord_y(_yscale_, _y_) (1.0 - (_y_) * (_yscale_)) +#define t_from_x_coord_y_inverted(_yscale_, _y_) ((_y_) * (_yscale_)) + +#define pixmap_priv_get_dest_scale(_pixmap_priv_, _pxscale_, _pyscale_) \ + do { \ + int _w_,_h_; \ + PIXMAP_PRIV_GET_ACTUAL_SIZE(_pixmap_priv_, _w_, _h_); \ + *(_pxscale_) = 1.0 / _w_; \ + *(_pyscale_) = 1.0 / _h_; \ + } while(0) + +#define pixmap_priv_get_scale(_pixmap_priv_, _pxscale_, _pyscale_) \ + do { \ + *(_pxscale_) = 1.0 / (_pixmap_priv_)->base.fbo->width; \ + *(_pyscale_) = 1.0 / (_pixmap_priv_)->base.fbo->height; \ + } while(0) + +#define GLAMOR_PIXMAP_FBO_NOT_EAXCT_SIZE(priv) \ + (priv->base.fbo->width != priv->base.pixmap->drawable.width \ + || priv->base.fbo->height != priv->base.pixmap->drawable.height) \ + +#define PIXMAP_PRIV_GET_ACTUAL_SIZE(priv, w, h) \ + do { \ + if (unlikely(priv->type == GLAMOR_TEXTURE_LARGE)) { \ + w = priv->large.box.x2 - priv->large.box.x1; \ + h = priv->large.box.y2 - priv->large.box.y1; \ + } else { \ + w = priv->base.pixmap->drawable.width; \ + h = priv->base.pixmap->drawable.height; \ + } \ + } while(0) + +#define glamor_pixmap_fbo_fix_wh_ratio(wh, priv) \ + do { \ + int actual_w, actual_h; \ + PIXMAP_PRIV_GET_ACTUAL_SIZE(priv, actual_w, actual_h); \ + wh[0] = (float)priv->base.fbo->width / actual_w; \ + wh[1] = (float)priv->base.fbo->height / actual_h; \ + wh[2] = 1.0 / priv->base.fbo->width; \ + wh[3] = 1.0 / priv->base.fbo->height; \ + } while(0) + +#define pixmap_priv_get_fbo_off(_priv_, _xoff_, _yoff_) \ + do { \ + if (unlikely(_priv_ && (_priv_)->type == GLAMOR_TEXTURE_LARGE)) { \ + *(_xoff_) = - (_priv_)->large.box.x1; \ + *(_yoff_) = - (_priv_)->large.box.y1; \ + } else { \ + *(_xoff_) = 0; \ + *(_yoff_) = 0; \ + } \ + } while(0) + +#define xFixedToFloat(_val_) ((float)xFixedToInt(_val_) \ + + ((float)xFixedFrac(_val_) / 65536.0)) + +#define glamor_picture_get_matrixf(_picture_, _matrix_) \ + do { \ + int _i_; \ + if ((_picture_)->transform) \ + { \ + for(_i_ = 0; _i_ < 3; _i_++) \ + { \ + (_matrix_)[_i_ * 3 + 0] = \ + xFixedToFloat((_picture_)->transform->matrix[_i_][0]); \ + (_matrix_)[_i_ * 3 + 1] = \ + xFixedToFloat((_picture_)->transform->matrix[_i_][1]); \ + (_matrix_)[_i_ * 3 + 2] = \ + xFixedToFloat((_picture_)->transform->matrix[_i_][2]); \ + } \ + } \ + } while(0) + +#define fmod(x, w) (x - w * floor((float)x/w)) + +#define fmodulus(x, w, c) do {c = fmod(x, w); \ + c = c >= 0 ? c : c + w;} \ + while(0) +/* @x: is current coord + * @x2: is the right/bottom edge + * @w: is current width or height + * @odd: is output value, 0 means we are in an even region, 1 means we are in a + * odd region. + * @c: is output value, equal to x mod w. */ +#define fodd_repeat_mod(x, x2, w, odd, c) \ + do { \ + float shift; \ + fmodulus((x), w, c); \ + shift = fabs((x) - (c)); \ + shift = floor(fabs(round(shift)) / w); \ + odd = (int)shift & 1; \ + if (odd && (((x2 % w) == 0) && \ + round(fabs(x)) == x2)) \ + odd = 0; \ + } while(0) + +/* @txy: output value, is the corrected coords. + * @xy: input coords to be fixed up. + * @cd: xy mod wh, is a input value. + * @wh: current width or height. + * @bxy1,bxy2: current box edge's x1/x2 or y1/y2 + * + * case 1: + * ---------- + * | * | + * | | + * ---------- + * tx = (c - x1) mod w + * + * case 2: + * --------- + * * | | + * | | + * --------- + * tx = - (c - (x1 mod w)) + * + * case 3: + * + * ---------- + * | | * + * | | + * ---------- + * tx = ((x2 mod x) - c) + (x2 - x1) + **/ +#define __glamor_repeat_reflect_fixup(txy, xy, \ + cd, wh, bxy1, bxy2) \ + do { \ + cd = wh - cd; \ + if ( xy >= bxy1 && xy < bxy2) { \ + cd = cd - bxy1; \ + fmodulus(cd, wh, txy); \ + } else if (xy < bxy1) { \ + float bxy1_mod; \ + fmodulus(bxy1, wh, bxy1_mod); \ + txy = -(cd - bxy1_mod); \ + } \ + else if (xy >= bxy2) { \ + float bxy2_mod; \ + fmodulus(bxy2, wh, bxy2_mod); \ + if (bxy2_mod == 0) \ + bxy2_mod = wh; \ + txy = (bxy2_mod - cd) + bxy2 - bxy1; \ + } else {assert(0); txy = 0;} \ + } while(0) + +#define _glamor_repeat_reflect_fixup(txy, xy, cd, odd, \ + wh, bxy1, bxy2) \ + do { \ + if (odd) { \ + __glamor_repeat_reflect_fixup(txy, xy, \ + cd, wh, bxy1, bxy2); \ + } else \ + txy = xy - bxy1; \ + } while(0) + +#define _glamor_get_reflect_transform_coords(priv, repeat_type, \ + tx1, ty1, \ + _x1_, _y1_) \ + do { \ + int odd_x, odd_y; \ + float c, d; \ + fodd_repeat_mod(_x1_,priv->box.x2, \ + priv->base.pixmap->drawable.width, \ + odd_x, c); \ + fodd_repeat_mod(_y1_, priv->box.y2, \ + priv->base.pixmap->drawable.height, \ + odd_y, d); \ + DEBUGF("c %f d %f oddx %d oddy %d \n", \ + c, d, odd_x, odd_y); \ + DEBUGF("x2 %d x1 %d fbo->width %d \n", priv->box.x2, \ + priv->box.x1, priv->base.fbo->width); \ + DEBUGF("y2 %d y1 %d fbo->height %d \n", priv->box.y2, \ + priv->box.y1, priv->base.fbo->height); \ + _glamor_repeat_reflect_fixup(tx1, _x1_, c, odd_x, \ + priv->base.pixmap->drawable.width, \ + priv->box.x1, priv->box.x2); \ + _glamor_repeat_reflect_fixup(ty1, _y1_, d, odd_y, \ + priv->base.pixmap->drawable.height, \ + priv->box.y1, priv->box.y2); \ + } while(0) + +#define _glamor_get_repeat_coords(priv, repeat_type, tx1, \ + ty1, tx2, ty2, \ + _x1_, _y1_, _x2_, \ + _y2_, c, d, odd_x, odd_y) \ + do { \ + if (repeat_type == RepeatReflect) { \ + DEBUGF("x1 y1 %d %d\n", \ + _x1_, _y1_ ); \ + DEBUGF("width %d box.x1 %d \n", \ + (priv)->base.pixmap->drawable.width, \ + priv->box.x1); \ + if (odd_x) { \ + c = (priv)->base.pixmap->drawable.width \ + - c; \ + tx1 = c - priv->box.x1; \ + tx2 = tx1 - ((_x2_) - (_x1_)); \ + } else { \ + tx1 = c - priv->box.x1; \ + tx2 = tx1 + ((_x2_) - (_x1_)); \ + } \ + if (odd_y){ \ + d = (priv)->base.pixmap->drawable.height\ + - d; \ + ty1 = d - priv->box.y1; \ + ty2 = ty1 - ((_y2_) - (_y1_)); \ + } else { \ + ty1 = d - priv->box.y1; \ + ty2 = ty1 + ((_y2_) - (_y1_)); \ + } \ + } else { /* RepeatNormal*/ \ + tx1 = (c - priv->box.x1); \ + ty1 = (d - priv->box.y1); \ + tx2 = tx1 + ((_x2_) - (_x1_)); \ + ty2 = ty1 + ((_y2_) - (_y1_)); \ + } \ + } while(0) + + +/* _x1_ ... _y2_ may has fractional. */ +#define glamor_get_repeat_transform_coords(priv, repeat_type, tx1, \ + ty1, _x1_, _y1_) \ + do { \ + DEBUGF("width %d box.x1 %d x2 %d y1 %d y2 %d\n", \ + (priv)->base.pixmap->drawable.width, \ + priv->box.x1, priv->box.x2, priv->box.y1, \ + priv->box.y2); \ + DEBUGF("x1 %f y1 %f \n", _x1_, _y1_); \ + if (repeat_type != RepeatReflect) { \ + tx1 = _x1_ - priv->box.x1; \ + ty1 = _y1_ - priv->box.y1; \ + } else \ + _glamor_get_reflect_transform_coords(priv, repeat_type, \ + tx1, ty1, \ + _x1_, _y1_); \ + DEBUGF("tx1 %f ty1 %f \n", tx1, ty1); \ + } while(0) + +/* _x1_ ... _y2_ must be integer. */ +#define glamor_get_repeat_coords(priv, repeat_type, tx1, \ + ty1, tx2, ty2, _x1_, _y1_, _x2_, \ + _y2_) \ + do { \ + int c, d; \ + int odd_x = 0, odd_y = 0; \ + DEBUGF("width %d box.x1 %d x2 %d y1 %d y2 %d\n", \ + (priv)->base.pixmap->drawable.width, \ + priv->box.x1, priv->box.x2, \ + priv->box.y1, priv->box.y2); \ + modulus((_x1_), (priv)->base.pixmap->drawable.width, c); \ + modulus((_y1_), (priv)->base.pixmap->drawable.height, d); \ + DEBUGF("c %d d %d \n", c, d); \ + if (repeat_type == RepeatReflect) { \ + odd_x = abs((_x1_ - c) \ + / (priv->base.pixmap->drawable.width)) & 1; \ + odd_y = abs((_y1_ - d) \ + / (priv->base.pixmap->drawable.height)) & 1; \ + } \ + _glamor_get_repeat_coords(priv, repeat_type, tx1, ty1, tx2, ty2,\ + _x1_, _y1_, _x2_, _y2_, c, d, \ + odd_x, odd_y); \ + } while(0) + +#define glamor_transform_point(matrix, tx, ty, x, y) \ + do { \ + int _i_; \ + float _result_[4]; \ + for (_i_ = 0; _i_ < 3; _i_++) { \ + _result_[_i_] = (matrix)[_i_ * 3] * (x) + (matrix)[_i_ * 3 + 1] * (y) \ + + (matrix)[_i_ * 3 + 2]; \ + } \ + tx = _result_[0] / _result_[2]; \ + ty = _result_[1] / _result_[2]; \ + } while(0) + +#define _glamor_set_normalize_tpoint(xscale, yscale, _tx_, _ty_, \ + texcoord, yInverted) \ + do { \ + (texcoord)[0] = t_from_x_coord_x(xscale, _tx_); \ + if (likely(yInverted)) \ + (texcoord)[1] = t_from_x_coord_y_inverted(yscale, _ty_);\ + else \ + (texcoord)[1] = t_from_x_coord_y(yscale, _ty_); \ + DEBUGF("normalized point tx %f ty %f \n", (texcoord)[0], \ + (texcoord)[1]); \ + } while(0) + +#define glamor_set_transformed_point(priv, matrix, xscale, \ + yscale, texcoord, \ + x, y, \ + yInverted) \ + do { \ + float tx, ty; \ + int fbo_x_off, fbo_y_off; \ + pixmap_priv_get_fbo_off(priv, &fbo_x_off, &fbo_y_off); \ + glamor_transform_point(matrix, tx, ty, x, y); \ + DEBUGF("tx %f ty %f fbooff %d %d \n", \ + tx, ty, fbo_x_off, fbo_y_off); \ + \ + tx += fbo_x_off; \ + ty += fbo_y_off; \ + (texcoord)[0] = t_from_x_coord_x(xscale, tx); \ + if (likely(yInverted)) \ + (texcoord)[1] = t_from_x_coord_y_inverted(yscale, ty); \ + else \ + (texcoord)[1] = t_from_x_coord_y(yscale, ty); \ + DEBUGF("normalized tx %f ty %f \n", (texcoord)[0], (texcoord)[1]); \ + } while(0) + +#define glamor_set_transformed_normalize_tri_tcoords(priv, \ + matrix, \ + xscale, \ + yscale, \ + vtx, \ + yInverted, \ + texcoords) \ + do { \ + glamor_set_transformed_point(priv, matrix, xscale, yscale, \ + texcoords, (vtx)[0], (vtx)[1], \ + yInverted); \ + glamor_set_transformed_point(priv, matrix, xscale, yscale, \ + texcoords+2, (vtx)[2], (vtx)[3], \ + yInverted); \ + glamor_set_transformed_point(priv, matrix, xscale, yscale, \ + texcoords+4, (vtx)[4], (vtx)[5], \ + yInverted); \ + } while (0) + +#define glamor_set_transformed_normalize_tcoords_ext( priv, \ + matrix, \ + xscale, \ + yscale, \ + tx1, ty1, tx2, ty2, \ + yInverted, texcoords, \ + stride) \ + do { \ + glamor_set_transformed_point(priv, matrix, xscale, yscale, \ + texcoords, tx1, ty1, \ + yInverted); \ + glamor_set_transformed_point(priv, matrix, xscale, yscale, \ + texcoords + 1 * stride, tx2, ty1, \ + yInverted); \ + glamor_set_transformed_point(priv, matrix, xscale, yscale, \ + texcoords + 2 * stride, tx2, ty2, \ + yInverted); \ + glamor_set_transformed_point(priv, matrix, xscale, yscale, \ + texcoords + 3 * stride, tx1, ty2, \ + yInverted); \ + } while (0) + +#define glamor_set_transformed_normalize_tcoords( priv, \ + matrix, \ + xscale, \ + yscale, \ + tx1, ty1, tx2, ty2, \ + yInverted, texcoords) \ + do { \ + glamor_set_transformed_normalize_tcoords_ext( priv, \ + matrix, \ + xscale, \ + yscale, \ + tx1, ty1, tx2, ty2, \ + yInverted, texcoords, \ + 2); \ + } while (0) + + + +#define glamor_set_normalize_tri_tcoords(xscale, \ + yscale, \ + vtx, \ + yInverted, \ + texcoords) \ + do { \ + _glamor_set_normalize_tpoint(xscale, yscale, \ + (vtx)[0], (vtx)[1], \ + texcoords, \ + yInverted); \ + _glamor_set_normalize_tpoint(xscale, yscale, \ + (vtx)[2], (vtx)[3], \ + texcoords+2, \ + yInverted); \ + _glamor_set_normalize_tpoint(xscale, yscale, \ + (vtx)[4], (vtx)[5], \ + texcoords+4, \ + yInverted); \ + } while (0) + +#define glamor_set_repeat_transformed_normalize_tcoords_ext( priv, \ + repeat_type, \ + matrix, \ + xscale, \ + yscale, \ + _x1_, _y1_, \ + _x2_, _y2_, \ + yInverted, \ + texcoords, \ + stride) \ + do { \ + if (likely(priv->type != GLAMOR_TEXTURE_LARGE)) { \ + glamor_set_transformed_normalize_tcoords_ext(priv, matrix, xscale, \ + yscale, _x1_, _y1_, \ + _x2_, _y2_, yInverted, \ + texcoords, stride); \ + } else { \ + float tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4; \ + float ttx1, tty1, ttx2, tty2, ttx3, tty3, ttx4, tty4; \ + DEBUGF("original coords %d %d %d %d\n", _x1_, _y1_, _x2_, _y2_); \ + glamor_transform_point(matrix, tx1, ty1, _x1_, _y1_); \ + glamor_transform_point(matrix, tx2, ty2, _x2_, _y1_); \ + glamor_transform_point(matrix, tx3, ty3, _x2_, _y2_); \ + glamor_transform_point(matrix, tx4, ty4, _x1_, _y2_); \ + DEBUGF("transformed %f %f %f %f %f %f %f %f\n", \ + tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4); \ + glamor_get_repeat_transform_coords((&priv->large), repeat_type, \ + ttx1, tty1, \ + tx1, ty1); \ + glamor_get_repeat_transform_coords((&priv->large), repeat_type, \ + ttx2, tty2, \ + tx2, ty2); \ + glamor_get_repeat_transform_coords((&priv->large), repeat_type, \ + ttx3, tty3, \ + tx3, ty3); \ + glamor_get_repeat_transform_coords((&priv->large), repeat_type, \ + ttx4, tty4, \ + tx4, ty4); \ + DEBUGF("repeat transformed %f %f %f %f %f %f %f %f\n", ttx1, tty1, \ + ttx2, tty2, ttx3, tty3, ttx4, tty4); \ + _glamor_set_normalize_tpoint(xscale, yscale, ttx1, tty1, \ + texcoords, yInverted); \ + _glamor_set_normalize_tpoint(xscale, yscale, ttx2, tty2, \ + texcoords + 1 * stride, yInverted); \ + _glamor_set_normalize_tpoint(xscale, yscale, ttx3, tty3, \ + texcoords + 2 * stride, yInverted); \ + _glamor_set_normalize_tpoint(xscale, yscale, ttx4, tty4, \ + texcoords + 3 * stride, yInverted); \ + } \ + } while (0) + + +#define glamor_set_repeat_transformed_normalize_tcoords( priv, \ + repeat_type, \ + matrix, \ + xscale, \ + yscale, \ + _x1_, _y1_, \ + _x2_, _y2_, \ + yInverted, \ + texcoords) \ + do { \ + glamor_set_repeat_transformed_normalize_tcoords_ext( priv, \ + repeat_type, \ + matrix, \ + xscale, \ + yscale, \ + _x1_, _y1_, \ + _x2_, _y2_, \ + yInverted, \ + texcoords, \ + 2); \ + } while (0) + +#define _glamor_set_normalize_tcoords(xscale, yscale, tx1, \ + ty1, tx2, ty2, \ + yInverted, vertices, stride) \ + do { \ + /* vertices may be write-only, so we use following \ + * temporary variable. */ \ + float _t0_, _t1_, _t2_, _t5_; \ + (vertices)[0] = _t0_ = t_from_x_coord_x(xscale, tx1); \ + (vertices)[1 * stride] = _t2_ = t_from_x_coord_x(xscale, tx2); \ + (vertices)[2 * stride] = _t2_; \ + (vertices)[3 * stride] = _t0_; \ + if (likely(yInverted)) { \ + (vertices)[1] = _t1_ = t_from_x_coord_y_inverted(yscale, ty1); \ + (vertices)[2 * stride + 1] = _t5_ = t_from_x_coord_y_inverted(yscale, ty2);\ + } \ + else { \ + (vertices)[1] = _t1_ = t_from_x_coord_y(yscale, ty1); \ + (vertices)[2 * stride + 1] = _t5_ = t_from_x_coord_y(yscale, ty2);\ + } \ + (vertices)[1 * stride + 1] = _t1_; \ + (vertices)[3 * stride + 1] = _t5_; \ + } while(0) + +#define glamor_set_normalize_tcoords_ext(priv, xscale, yscale, \ + x1, y1, x2, y2, \ + yInverted, vertices, stride) \ + do { \ + if (unlikely(priv->type == GLAMOR_TEXTURE_LARGE)) { \ + float tx1, tx2, ty1, ty2; \ + int fbo_x_off, fbo_y_off; \ + pixmap_priv_get_fbo_off(priv, &fbo_x_off, &fbo_y_off); \ + tx1 = x1 + fbo_x_off; \ + tx2 = x2 + fbo_x_off; \ + ty1 = y1 + fbo_y_off; \ + ty2 = y2 + fbo_y_off; \ + _glamor_set_normalize_tcoords(xscale, yscale, tx1, ty1, \ + tx2, ty2, yInverted, vertices, \ + stride); \ + } else \ + _glamor_set_normalize_tcoords(xscale, yscale, x1, y1, \ + x2, y2, yInverted, vertices, stride);\ + } while(0) + + +#define glamor_set_normalize_tcoords(priv, xscale, yscale, \ + x1, y1, x2, y2, \ + yInverted, vertices) \ + do { \ + glamor_set_normalize_tcoords_ext(priv, xscale, yscale, \ + x1, y1, x2, y2, \ + yInverted, vertices, 2); \ + } while(0) + +#define glamor_set_repeat_normalize_tcoords_ext(priv, repeat_type, \ + xscale, yscale, \ + _x1_, _y1_, _x2_, _y2_, \ + yInverted, vertices, stride)\ + do { \ + if (unlikely(priv->type == GLAMOR_TEXTURE_LARGE)) { \ + float tx1, tx2, ty1, ty2; \ + if (repeat_type == RepeatPad) { \ + tx1 = _x1_ - priv->large.box.x1; \ + ty1 = _y1_ - priv->large.box.y1; \ + tx2 = tx1 + ((_x2_) - (_x1_)); \ + ty2 = ty1 + ((_y2_) - (_y1_)); \ + } else { \ + glamor_get_repeat_coords((&priv->large), repeat_type, \ + tx1, ty1, tx2, ty2, \ + _x1_, _y1_, _x2_, _y2_); \ + } \ + _glamor_set_normalize_tcoords(xscale, yscale, tx1, ty1, \ + tx2, ty2, yInverted, vertices, \ + stride); \ + } else \ + _glamor_set_normalize_tcoords(xscale, yscale, _x1_, _y1_, \ + _x2_, _y2_, yInverted, vertices, \ + stride); \ + } while(0) + + +#define glamor_set_repeat_normalize_tcoords(priv, repeat_type, \ + xscale, yscale, \ + _x1_, _y1_, _x2_, _y2_, \ + yInverted, vertices) \ + do { \ + glamor_set_repeat_normalize_tcoords_ext(priv, repeat_type, \ + xscale, yscale, \ + _x1_, _y1_, _x2_, _y2_, \ + yInverted, vertices, 2); \ + } while(0) + +#define glamor_set_normalize_tcoords_tri_stripe(xscale, yscale, \ + x1, y1, x2, y2, \ + yInverted, vertices) \ + do { \ + (vertices)[0] = t_from_x_coord_x(xscale, x1); \ + (vertices)[2] = t_from_x_coord_x(xscale, x2); \ + (vertices)[6] = (vertices)[2]; \ + (vertices)[4] = (vertices)[0]; \ + if (likely(yInverted)) { \ + (vertices)[1] = t_from_x_coord_y_inverted(yscale, y1); \ + (vertices)[7] = t_from_x_coord_y_inverted(yscale, y2); \ + } \ + else { \ + (vertices)[1] = t_from_x_coord_y(yscale, y1); \ + (vertices)[7] = t_from_x_coord_y(yscale, y2); \ + } \ + (vertices)[3] = (vertices)[1]; \ + (vertices)[5] = (vertices)[7]; \ + } while(0) + +#define glamor_set_tcoords(width, height, x1, y1, x2, y2, \ + yInverted, vertices) \ + do { \ + (vertices)[0] = (x1); \ + (vertices)[2] = (x2); \ + (vertices)[4] = (vertices)[2]; \ + (vertices)[6] = (vertices)[0]; \ + if (likely(yInverted)) { \ + (vertices)[1] = (y1); \ + (vertices)[5] = (y2); \ + } \ + else { \ + (vertices)[1] = height - (y2); \ + (vertices)[5] = height - (y1); \ + } \ + (vertices)[3] = (vertices)[1]; \ + (vertices)[7] = (vertices)[5]; \ + } while(0) + +#define glamor_set_tcoords_ext(width, height, x1, y1, x2, y2, \ + yInverted, vertices, stride) \ + do { \ + (vertices)[0] = (x1); \ + (vertices)[1*stride] = (x2); \ + (vertices)[2*stride] = (vertices)[1*stride]; \ + (vertices)[3*stride] = (vertices)[0]; \ + if (likely(yInverted)) { \ + (vertices)[1] = (y1); \ + (vertices)[2*stride + 1] = (y2); \ + } \ + else { \ + (vertices)[1] = height - (y2); \ + (vertices)[2*stride + 1] = height - (y1); \ + } \ + (vertices)[1*stride + 1] = (vertices)[1]; \ + (vertices)[3*stride + 1] = (vertices)[2*stride + 1]; \ + } while(0) + +#define glamor_set_normalize_one_vcoord(xscale, yscale, x, y, \ + yInverted, vertices) \ + do { \ + (vertices)[0] = v_from_x_coord_x(xscale, x); \ + if (likely(yInverted)) { \ + (vertices)[1] = v_from_x_coord_y_inverted(yscale, y); \ + } else { \ + (vertices)[1] = v_from_x_coord_y(yscale, y); \ + } \ + } while(0) + +#define glamor_set_normalize_tri_vcoords(xscale, yscale, vtx, \ + yInverted, vertices) \ + do { \ + glamor_set_normalize_one_vcoord(xscale, yscale, \ + (vtx)[0], (vtx)[1], \ + yInverted, vertices); \ + glamor_set_normalize_one_vcoord(xscale, yscale, \ + (vtx)[2], (vtx)[3], \ + yInverted, vertices+2); \ + glamor_set_normalize_one_vcoord(xscale, yscale, \ + (vtx)[4], (vtx)[5], \ + yInverted, vertices+4); \ + } while(0) + +#define glamor_set_tcoords_tri_strip(width, height, x1, y1, x2, y2, \ + yInverted, vertices) \ + do { \ + (vertices)[0] = (x1); \ + (vertices)[2] = (x2); \ + (vertices)[6] = (vertices)[2]; \ + (vertices)[4] = (vertices)[0]; \ + if (likely(yInverted)) { \ + (vertices)[1] = (y1); \ + (vertices)[7] = (y2); \ + } \ + else { \ + (vertices)[1] = height - (y2); \ + (vertices)[7] = height - (y1); \ + } \ + (vertices)[3] = (vertices)[1]; \ + (vertices)[5] = (vertices)[7]; \ + } while(0) + +#define glamor_set_normalize_vcoords_ext(priv, xscale, yscale, \ + x1, y1, x2, y2, \ + yInverted, vertices, stride) \ + do { \ + int fbo_x_off, fbo_y_off; \ + /* vertices may be write-only, so we use following \ + * temporary variable. */ \ + float _t0_, _t1_, _t2_, _t5_; \ + pixmap_priv_get_fbo_off(priv, &fbo_x_off, &fbo_y_off); \ + (vertices)[0] = _t0_ = v_from_x_coord_x(xscale, x1 + fbo_x_off); \ + (vertices)[1 * stride] = _t2_ = v_from_x_coord_x(xscale, \ + x2 + fbo_x_off); \ + (vertices)[2 * stride] = _t2_; \ + (vertices)[3 * stride] = _t0_; \ + if (likely(yInverted)) { \ + (vertices)[1] = _t1_ = v_from_x_coord_y_inverted(yscale, \ + y1 + fbo_y_off); \ + (vertices)[2 * stride + 1] = _t5_ = \ + v_from_x_coord_y_inverted(yscale, \ + y2 + fbo_y_off); \ + } \ + else { \ + (vertices)[1] = _t1_ = v_from_x_coord_y(yscale, y1 + fbo_y_off); \ + (vertices)[2 * stride + 1] = _t5_ = v_from_x_coord_y(yscale, \ + y2 + fbo_y_off); \ + } \ + (vertices)[1 * stride + 1] = _t1_; \ + (vertices)[3 * stride + 1] = _t5_; \ + } while(0) + + +#define glamor_set_normalize_vcoords(priv, xscale, yscale, \ + x1, y1, x2, y2, \ + yInverted, vertices) \ + do { \ + glamor_set_normalize_vcoords_ext(priv, xscale, yscale, \ + x1, y1, x2, y2, \ + yInverted, vertices, 2); \ + } while(0) + +#define glamor_set_const_ext(params, nparam, vertices, nverts, stride) \ + do { \ + int _i_ = 0, _j_ = 0; \ + for(; _i_ < nverts; _i_++) { \ + for(_j_ = 0; _j_ < nparam; _j_++) { \ + vertices[stride*_i_ + _j_] = params[_j_]; \ + } \ + } \ + } while(0) + +#define glamor_set_normalize_vcoords_tri_strip(xscale, yscale, \ + x1, y1, x2, y2, \ + yInverted, vertices) \ + do { \ + (vertices)[0] = v_from_x_coord_x(xscale, x1); \ + (vertices)[2] = v_from_x_coord_x(xscale, x2); \ + (vertices)[6] = (vertices)[2]; \ + (vertices)[4] = (vertices)[0]; \ + if (likely(yInverted)) { \ + (vertices)[1] = v_from_x_coord_y_inverted(yscale, y1); \ + (vertices)[7] = v_from_x_coord_y_inverted(yscale, y2); \ + } \ + else { \ + (vertices)[1] = v_from_x_coord_y(yscale, y1); \ + (vertices)[7] = v_from_x_coord_y(yscale, y2); \ + } \ + (vertices)[3] = (vertices)[1]; \ + (vertices)[5] = (vertices)[7]; \ + } while(0) + +#define glamor_set_normalize_pt(xscale, yscale, x, y, \ + yInverted, pt) \ + do { \ + (pt)[0] = t_from_x_coord_x(xscale, x); \ + if (likely(yInverted)) { \ + (pt)[1] = t_from_x_coord_y_inverted(yscale, y); \ + } else { \ + (pt)[1] = t_from_x_coord_y(yscale, y); \ + } \ + } while(0) + +#define glamor_set_circle_centre(width, height, x, y, \ + yInverted, c) \ + do { \ + (c)[0] = (float)x; \ + if (likely(yInverted)) { \ + (c)[1] = (float)y; \ + } else { \ + (c)[1] = (float)height - (float)y; \ + } \ + } while(0) + +inline static void +glamor_calculate_boxes_bound(BoxPtr bound, BoxPtr boxes, int nbox) +{ + int x_min, y_min; + int x_max, y_max; + int i; + x_min = y_min = MAXSHORT; + x_max = y_max = MINSHORT; + for (i = 0; i < nbox; i++) { + if (x_min > boxes[i].x1) + x_min = boxes[i].x1; + if (y_min > boxes[i].y1) + y_min = boxes[i].y1; + + if (x_max < boxes[i].x2) + x_max = boxes[i].x2; + if (y_max < boxes[i].y2) + y_max = boxes[i].y2; + } + bound->x1 = x_min; + bound->y1 = y_min; + bound->x2 = x_max; + bound->y2 = y_max; +} + +inline static void +glamor_translate_boxes(BoxPtr boxes, int nbox, int dx, int dy) +{ + int i; + for (i = 0; i < nbox; i++) { + boxes[i].x1 += dx; + boxes[i].y1 += dy; + boxes[i].x2 += dx; + boxes[i].y2 += dy; + } +} + +static inline Bool +region_is_empty(pixman_region16_t *region) +{ + return region->data && region->data->numRects == 0; +} + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +#define ALIGN(i,m) (((i) + (m) - 1) & ~((m) - 1)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define glamor_check_fbo_size(_glamor_,_w_, _h_) ((_w_) > 0 && (_h_) > 0 \ + && (_w_) <= _glamor_->max_fbo_size \ + && (_h_) <= _glamor_->max_fbo_size) + +/* For 1bpp pixmap, we don't store it as texture. */ +#define glamor_check_pixmap_fbo_depth(_depth_) ( \ + _depth_ == 8 \ + || _depth_ == 15 \ + || _depth_ == 16 \ + || _depth_ == 24 \ + || _depth_ == 30 \ + || _depth_ == 32) + +#define GLAMOR_PIXMAP_PRIV_IS_PICTURE(pixmap_priv) (pixmap_priv && pixmap_priv->base.is_picture == 1) +#define GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv) (pixmap_priv && pixmap_priv->base.gl_fbo == GLAMOR_FBO_NORMAL) +#define GLAMOR_PIXMAP_PRIV_HAS_FBO_DOWNLOADED(pixmap_priv) (pixmap_priv && (pixmap_priv->base.gl_fbo == GLAMOR_FBO_DOWNLOADED)) + +/** + * Borrow from uxa. + */ +static inline CARD32 +format_for_depth(int depth) +{ + switch (depth) { + case 1: + return PICT_a1; + case 4: + return PICT_a4; + case 8: + return PICT_a8; + case 15: + return PICT_x1r5g5b5; + case 16: + return PICT_r5g6b5; + default: + case 24: + return PICT_x8r8g8b8; +#if XORG_VERSION_CURRENT >= 10699900 + case 30: + return PICT_x2r10g10b10; +#endif + case 32: + return PICT_a8r8g8b8; + } +} + +static inline void +gl_iformat_for_depth(int depth, GLenum * format) +{ + switch (depth) { +#ifndef GLAMOR_GLES2 + case 1: + case 8: + *format = GL_ALPHA; + break; +#endif + default: + *format = GL_RGBA; + break; + } +} + +static inline CARD32 +format_for_pixmap(PixmapPtr pixmap) +{ + glamor_pixmap_private *pixmap_priv; + PictFormatShort pict_format; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (GLAMOR_PIXMAP_PRIV_IS_PICTURE(pixmap_priv)) + pict_format = pixmap_priv->base.picture->format; + else + pict_format = format_for_depth(pixmap->drawable.depth); + + return pict_format; +} + +#define REVERT_NONE 0 +#define REVERT_NORMAL 1 +#define REVERT_DOWNLOADING_A1 2 +#define REVERT_UPLOADING_A1 3 +#define REVERT_DOWNLOADING_2_10_10_10 4 +#define REVERT_UPLOADING_2_10_10_10 5 +#define REVERT_DOWNLOADING_1_5_5_5 7 +#define REVERT_UPLOADING_1_5_5_5 8 +#define REVERT_DOWNLOADING_10_10_10_2 9 +#define REVERT_UPLOADING_10_10_10_2 10 + +#define SWAP_NONE_DOWNLOADING 0 +#define SWAP_DOWNLOADING 1 +#define SWAP_UPLOADING 2 +#define SWAP_NONE_UPLOADING 3 + +/* + * Map picture's format to the correct gl texture format and type. + * no_alpha is used to indicate whehter we need to wire alpha to 1. + * + * Although opengl support A1/GL_BITMAP, we still don't use it + * here, it seems that mesa has bugs when uploading a A1 bitmap. + * + * Return 0 if find a matched texture type. Otherwise return -1. + **/ +#ifndef GLAMOR_GLES2 +static inline int +glamor_get_tex_format_type_from_pictformat(PictFormatShort format, + GLenum * tex_format, + GLenum * tex_type, + int *no_alpha, + int *revert, + int *swap_rb, + int is_upload) + +{ + *no_alpha = 0; + *revert = REVERT_NONE; + *swap_rb = is_upload ? SWAP_NONE_UPLOADING : SWAP_NONE_DOWNLOADING; + switch (format) { + case PICT_a1: + *tex_format = GL_ALPHA; + *tex_type = GL_UNSIGNED_BYTE; + *revert = is_upload ? REVERT_UPLOADING_A1 : REVERT_DOWNLOADING_A1; + break; + case PICT_b8g8r8x8: + *no_alpha = 1; + case PICT_b8g8r8a8: + *tex_format = GL_BGRA; + *tex_type = GL_UNSIGNED_INT_8_8_8_8; + break; + + case PICT_x8r8g8b8: + *no_alpha = 1; + case PICT_a8r8g8b8: + *tex_format = GL_BGRA; + *tex_type = GL_UNSIGNED_INT_8_8_8_8_REV; + break; + case PICT_x8b8g8r8: + *no_alpha = 1; + case PICT_a8b8g8r8: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_INT_8_8_8_8_REV; + break; + case PICT_x2r10g10b10: + *no_alpha = 1; + case PICT_a2r10g10b10: + *tex_format = GL_BGRA; + *tex_type = GL_UNSIGNED_INT_2_10_10_10_REV; + break; + case PICT_x2b10g10r10: + *no_alpha = 1; + case PICT_a2b10g10r10: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_INT_2_10_10_10_REV; + break; + + case PICT_r5g6b5: + *tex_format = GL_RGB; + *tex_type = GL_UNSIGNED_SHORT_5_6_5; + break; + case PICT_b5g6r5: + *tex_format = GL_RGB; + *tex_type = GL_UNSIGNED_SHORT_5_6_5_REV; + break; + case PICT_x1b5g5r5: + *no_alpha = 1; + case PICT_a1b5g5r5: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + break; + + case PICT_x1r5g5b5: + *no_alpha = 1; + case PICT_a1r5g5b5: + *tex_format = GL_BGRA; + *tex_type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + break; + case PICT_a8: + *tex_format = GL_ALPHA; + *tex_type = GL_UNSIGNED_BYTE; + break; + case PICT_x4r4g4b4: + *no_alpha = 1; + case PICT_a4r4g4b4: + *tex_format = GL_BGRA; + *tex_type = GL_UNSIGNED_SHORT_4_4_4_4_REV; + break; + + case PICT_x4b4g4r4: + *no_alpha = 1; + case PICT_a4b4g4r4: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_SHORT_4_4_4_4_REV; + break; + + default: + LogMessageVerb(X_INFO, 0, + "fail to get matched format for %x \n", + format); + return -1; + } + return 0; +} + +/* Currently, we use RGBA to represent all formats. */ +inline static int cache_format(GLenum format) +{ + switch (format) { + case GL_ALPHA: + return 1; + case GL_RGBA: + return 0; + default: + return -1; + } +} + +#else +#define IS_LITTLE_ENDIAN (IMAGE_BYTE_ORDER == LSBFirst) + +static inline int +glamor_get_tex_format_type_from_pictformat(PictFormatShort format, + GLenum * tex_format, + GLenum * tex_type, + int *no_alpha, + int *revert, + int *swap_rb, + int is_upload) +{ + int need_swap_rb = 0; + + *no_alpha = 0; + *revert = IS_LITTLE_ENDIAN ? REVERT_NONE : REVERT_NORMAL; + + switch (format) { + case PICT_b8g8r8x8: + *no_alpha = 1; + case PICT_b8g8r8a8: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_BYTE; + need_swap_rb = 1; + *revert = IS_LITTLE_ENDIAN ? REVERT_NORMAL : REVERT_NONE; + break; + + case PICT_x8r8g8b8: + *no_alpha = 1; + case PICT_a8r8g8b8: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_BYTE; + need_swap_rb = 1; + break; + + case PICT_x8b8g8r8: + *no_alpha = 1; + case PICT_a8b8g8r8: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_BYTE; + break; + + case PICT_x2r10g10b10: + *no_alpha = 1; + case PICT_a2r10g10b10: + *tex_format = GL_RGBA; + /* glReadPixmap doesn't support GL_UNSIGNED_INT_10_10_10_2. + * we have to use GL_UNSIGNED_BYTE and do the conversion in + * shader latter.*/ + *tex_type = GL_UNSIGNED_BYTE; + if (is_upload == 1) { + if (!IS_LITTLE_ENDIAN) + *revert = REVERT_UPLOADING_10_10_10_2; + else + *revert = REVERT_UPLOADING_2_10_10_10; + } + else { + if (!IS_LITTLE_ENDIAN) { + *revert = REVERT_DOWNLOADING_10_10_10_2; + } + else { + *revert = REVERT_DOWNLOADING_2_10_10_10; + } + } + need_swap_rb = 1; + + break; + + case PICT_x2b10g10r10: + *no_alpha = 1; + case PICT_a2b10g10r10: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_BYTE; + if (is_upload == 1) { + if (!IS_LITTLE_ENDIAN) + *revert = REVERT_UPLOADING_10_10_10_2; + else + *revert = REVERT_UPLOADING_2_10_10_10; + } + else { + if (!IS_LITTLE_ENDIAN) { + *revert = REVERT_DOWNLOADING_10_10_10_2; + } + else { + *revert = REVERT_DOWNLOADING_2_10_10_10; + } + } + break; + + case PICT_r5g6b5: + *tex_format = GL_RGB; + *tex_type = GL_UNSIGNED_SHORT_5_6_5; + *revert = IS_LITTLE_ENDIAN ? REVERT_NONE : REVERT_NORMAL; + + break; + + case PICT_b5g6r5: + *tex_format = GL_RGB; + *tex_type = GL_UNSIGNED_SHORT_5_6_5; + need_swap_rb = IS_LITTLE_ENDIAN ? 1 : 0;; + break; + + case PICT_x1b5g5r5: + *no_alpha = 1; + case PICT_a1b5g5r5: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_SHORT_5_5_5_1; + if (IS_LITTLE_ENDIAN) { + *revert = is_upload ? REVERT_UPLOADING_1_5_5_5 : REVERT_DOWNLOADING_1_5_5_5; + } else + *revert = REVERT_NONE; + break; + + case PICT_x1r5g5b5: + *no_alpha = 1; + case PICT_a1r5g5b5: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_SHORT_5_5_5_1; + if (IS_LITTLE_ENDIAN) { + *revert = is_upload ? REVERT_UPLOADING_1_5_5_5 : REVERT_DOWNLOADING_1_5_5_5; + } else + *revert = REVERT_NONE; + need_swap_rb = 1; + break; + + case PICT_a1: + *tex_format = GL_ALPHA; + *tex_type = GL_UNSIGNED_BYTE; + *revert = is_upload ? REVERT_UPLOADING_A1 : REVERT_DOWNLOADING_A1; + break; + + case PICT_a8: + *tex_format = GL_ALPHA; + *tex_type = GL_UNSIGNED_BYTE; + *revert = REVERT_NONE; + break; + + case PICT_x4r4g4b4: + *no_alpha = 1; + case PICT_a4r4g4b4: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_SHORT_4_4_4_4; + *revert = IS_LITTLE_ENDIAN ? REVERT_NORMAL : REVERT_NONE; + need_swap_rb = 1; + break; + + case PICT_x4b4g4r4: + *no_alpha = 1; + case PICT_a4b4g4r4: + *tex_format = GL_RGBA; + *tex_type = GL_UNSIGNED_SHORT_4_4_4_4; + *revert = IS_LITTLE_ENDIAN ? REVERT_NORMAL : REVERT_NONE; + break; + + default: + LogMessageVerb(X_INFO, 0, + "fail to get matched format for %x \n", + format); + return -1; + } + + if (need_swap_rb) + *swap_rb = is_upload ? SWAP_UPLOADING : SWAP_DOWNLOADING; + else + *swap_rb = is_upload ? SWAP_NONE_UPLOADING : SWAP_NONE_DOWNLOADING; + return 0; +} + +inline static int cache_format(GLenum format) +{ + switch (format) { + case GL_ALPHA: + return 2; + case GL_RGB: + return 1; + case GL_RGBA: + return 0; + default: + return -1; + } +} + +#endif + + +static inline int +glamor_get_tex_format_type_from_pixmap(PixmapPtr pixmap, + GLenum * format, + GLenum * type, + int *no_alpha, + int *revert, + int *swap_rb, + int is_upload) +{ + glamor_pixmap_private *pixmap_priv; + PictFormatShort pict_format; + + pixmap_priv = glamor_get_pixmap_private(pixmap); + if (GLAMOR_PIXMAP_PRIV_IS_PICTURE(pixmap_priv)) + pict_format = pixmap_priv->base.picture->format; + else + pict_format = format_for_depth(pixmap->drawable.depth); + + return glamor_get_tex_format_type_from_pictformat(pict_format, + format, type, + no_alpha, + revert, + swap_rb, + is_upload); +} + + +/* borrowed from uxa */ +static inline Bool +glamor_get_rgba_from_pixel(CARD32 pixel, + float *red, + float *green, + float *blue, float *alpha, CARD32 format) +{ + int rbits, bbits, gbits, abits; + int rshift, bshift, gshift, ashift; + + rbits = PICT_FORMAT_R(format); + gbits = PICT_FORMAT_G(format); + bbits = PICT_FORMAT_B(format); + abits = PICT_FORMAT_A(format); + + if (PICT_FORMAT_TYPE(format) == PICT_TYPE_A) { + rshift = gshift = bshift = ashift = 0; + } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ARGB) { + bshift = 0; + gshift = bbits; + rshift = gshift + gbits; + ashift = rshift + rbits; + } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_ABGR) { + rshift = 0; + gshift = rbits; + bshift = gshift + gbits; + ashift = bshift + bbits; +#if XORG_VERSION_CURRENT >= 10699900 + } else if (PICT_FORMAT_TYPE(format) == PICT_TYPE_BGRA) { + ashift = 0; + rshift = abits; + if (abits == 0) + rshift = PICT_FORMAT_BPP(format) - (rbits + gbits + + bbits); + gshift = rshift + rbits; + bshift = gshift + gbits; +#endif + } else { + return FALSE; + } +#define COLOR_INT_TO_FLOAT(_fc_, _p_, _s_, _bits_) \ + *_fc_ = (((_p_) >> (_s_)) & (( 1 << (_bits_)) - 1)) \ + / (float)((1<<(_bits_)) - 1) + + if (rbits) + COLOR_INT_TO_FLOAT(red, pixel, rshift, rbits); + else + *red = 0; + + if (gbits) + COLOR_INT_TO_FLOAT(green, pixel, gshift, gbits); + else + *green = 0; + + if (bbits) + COLOR_INT_TO_FLOAT(blue, pixel, bshift, bbits); + else + *blue = 0; + + if (abits) + COLOR_INT_TO_FLOAT(alpha, pixel, ashift, abits); + else + *alpha = 1; + + return TRUE; +} + +inline static Bool glamor_pict_format_is_compatible(PictFormatShort pict_format, int depth) +{ + GLenum iformat; + + gl_iformat_for_depth(depth, &iformat); + switch (iformat) { + case GL_RGBA: + return (pict_format == PICT_a8r8g8b8 || pict_format == PICT_x8r8g8b8); + case GL_ALPHA: + return (pict_format == PICT_a8); + default: + return FALSE; + } +} + +/* return TRUE if we can access this pixmap at DDX driver. */ +inline static Bool glamor_ddx_fallback_check_pixmap(DrawablePtr drawable) +{ + PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); + glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); + return (!pixmap_priv + || (pixmap_priv->type == GLAMOR_TEXTURE_DRM + || pixmap_priv->type == GLAMOR_MEMORY + || pixmap_priv->type == GLAMOR_DRM_ONLY)); +} + +inline static Bool glamor_ddx_fallback_check_gc(GCPtr gc) +{ + PixmapPtr pixmap; + if (!gc) + return TRUE; + switch (gc->fillStyle) { + case FillStippled: + case FillOpaqueStippled: + pixmap = gc->stipple; + break; + case FillTiled: + pixmap = gc->tile.pixmap; + break; + default: + pixmap = NULL; + } + return (!pixmap || glamor_ddx_fallback_check_pixmap(&pixmap->drawable)); +} +inline static Bool glamor_is_large_pixmap(PixmapPtr pixmap) +{ + glamor_pixmap_private *priv; + + priv = glamor_get_pixmap_private(pixmap); + return (priv->type == GLAMOR_TEXTURE_LARGE); +} + +inline static Bool glamor_is_large_picture(PicturePtr picture) +{ + PixmapPtr pixmap; + + if (picture->pDrawable) { + pixmap = glamor_get_drawable_pixmap(picture->pDrawable); + return glamor_is_large_pixmap(pixmap); + } + return FALSE; +} + +inline static Bool glamor_tex_format_is_readable(GLenum format) +{ + return ((format == GL_RGBA || format == GL_RGB || format == GL_ALPHA)); + +} + +static inline void _glamor_dump_pixmap_bits(PixmapPtr pixmap, int x, int y, int w, int h) +{ + int i,j; + unsigned char * p = pixmap->devPrivate.ptr; + int stride = pixmap->devKind; + + p = p + y * stride + x; + + for (i = 0; i < h; i++) + { + ErrorF("line %3d: ", i); + for(j = 0; j < w; j++) + ErrorF("%2d ", (p[j/8] & (1 << (j%8)))>>(j%8)); + p += stride; + ErrorF("\n"); + } +} + +static inline void _glamor_dump_pixmap_byte(PixmapPtr pixmap, int x, int y, int w, int h) +{ + int i,j; + unsigned char * p = pixmap->devPrivate.ptr; + int stride = pixmap->devKind; + + p = p + y * stride + x; + + for (i = 0; i < h; i++) + { + ErrorF("line %3d: ", i); + for(j = 0; j < w; j++) + ErrorF("%2x ", p[j]); + p += stride; + ErrorF("\n"); + } +} + +static inline void _glamor_dump_pixmap_sword(PixmapPtr pixmap, int x, int y, int w, int h) +{ + int i,j; + unsigned short * p = pixmap->devPrivate.ptr; + int stride = pixmap->devKind / 2; + + p = p + y * stride + x; + + for (i = 0; i < h; i++) + { + ErrorF("line %3d: ", i); + for(j = 0; j < w; j++) + ErrorF("%2x ", p[j]); + p += stride; + ErrorF("\n"); + } +} + +static inline void _glamor_dump_pixmap_word(PixmapPtr pixmap, int x, int y, int w, int h) +{ + int i,j; + unsigned int * p = pixmap->devPrivate.ptr; + int stride = pixmap->devKind / 4; + + p = p + y * stride + x; + + for (i = 0; i < h; i++) + { + ErrorF("line %3d: ", i); + for(j = 0; j < w; j++) + ErrorF("%2x ", p[j]); + p += stride; + ErrorF("\n"); + } +} + +static inline void glamor_dump_pixmap(PixmapPtr pixmap, int x, int y, int w, int h) +{ + w = ((x + w) > pixmap->drawable.width) ? (pixmap->drawable.width - x) : w; + h = ((y + h) > pixmap->drawable.height) ? (pixmap->drawable.height - y) : h; + + glamor_prepare_access(&pixmap->drawable, GLAMOR_ACCESS_RO); + switch (pixmap->drawable.depth) { + case 8: + _glamor_dump_pixmap_byte(pixmap, x, y, w, h); + break; + case 15: + case 16: + _glamor_dump_pixmap_sword(pixmap, x, y, w, h); + break; + + case 24: + case 32: + _glamor_dump_pixmap_word(pixmap, x, y, w, h); + break; + case 1: + _glamor_dump_pixmap_bits(pixmap, x, y, w, h); + break; + default: + ErrorF("dump depth %d, not implemented.\n", pixmap->drawable.depth); + } + glamor_finish_access(&pixmap->drawable, GLAMOR_ACCESS_RO); +} + +static inline void _glamor_compare_pixmaps(PixmapPtr pixmap1, PixmapPtr pixmap2, + int x, int y, int w, int h, + PictFormatShort short_format, + int all, int diffs) +{ + int i, j; + unsigned char * p1 = pixmap1->devPrivate.ptr; + unsigned char * p2 = pixmap2->devPrivate.ptr; + int line_need_printed = 0; + int test_code = 0xAABBCCDD; + int little_endian = 0; + unsigned char *p_test; + int bpp = pixmap1->drawable.depth == 8 ? 1 : 4; + int stride = pixmap1->devKind; + + assert(pixmap1->devKind == pixmap2->devKind); + + ErrorF("stride:%d, width:%d, height:%d\n", stride, w, h); + + p1 = p1 + y * stride + x; + p2 = p2 + y * stride + x; + + if (all) { + for (i = 0; i < h; i++) { + ErrorF("line %3d: ", i); + + for (j = 0; j < stride; j++) { + if (j % bpp == 0) + ErrorF("[%d]%2x:%2x ", j / bpp, p1[j], p2[j]); + else + ErrorF("%2x:%2x ", p1[j], p2[j]); + } + + p1 += stride; + p2 += stride; + ErrorF("\n"); + } + } else { + if (short_format == PICT_a8r8g8b8) { + p_test = (unsigned char *) & test_code; + little_endian = (*p_test == 0xDD); + bpp = 4; + + for (i = 0; i < h; i++) { + line_need_printed = 0; + + for (j = 0; j < stride; j++) { + if (p1[j] != p2[j] && (p1[j] - p2[j] > diffs || p2[j] - p1[j] > diffs)) { + if (line_need_printed) { + if (little_endian) { + switch (j % 4) { + case 2: + ErrorF("[%d]RED:%2x:%2x ", j / bpp, p1[j], p2[j]); + break; + case 1: + ErrorF("[%d]GREEN:%2x:%2x ", j / bpp, p1[j], p2[j]); + break; + case 0: + ErrorF("[%d]BLUE:%2x:%2x ", j / bpp, p1[j], p2[j]); + break; + case 3: + ErrorF("[%d]Alpha:%2x:%2x ", j / bpp, p1[j], p2[j]); + break; + } + } else { + switch (j % 4) { + case 1: + ErrorF("[%d]RED:%2x:%2x ", j / bpp, p1[j], p2[j]); + break; + case 2: + ErrorF("[%d]GREEN:%2x:%2x ", j / bpp, p1[j], p2[j]); + break; + case 3: + ErrorF("[%d]BLUE:%2x:%2x ", j / bpp, p1[j], p2[j]); + break; + case 0: + ErrorF("[%d]Alpha:%2x:%2x ", j / bpp, p1[j], p2[j]); + break; + } + } + } else { + line_need_printed = 1; + j = -1; + ErrorF("line %3d: ", i); + continue; + } + } + } + + p1 += stride; + p2 += stride; + ErrorF("\n"); + } + } //more format can be added here. + else { // the default format, just print. + for (i = 0; i < h; i++) { + line_need_printed = 0; + + for (j = 0; j < stride; j++) { + if (p1[j] != p2[j]) { + if (line_need_printed) { + ErrorF("[%d]%2x:%2x ", j / bpp, p1[j], p2[j]); + } else { + line_need_printed = 1; + j = -1; + ErrorF("line %3d: ", i); + continue; + } + } + } + + p1 += stride; + p2 += stride; + ErrorF("\n"); + } + } + } +} + +static inline void glamor_compare_pixmaps(PixmapPtr pixmap1, PixmapPtr pixmap2, + int x, int y, int w, int h, int all, int diffs) +{ + assert(pixmap1->drawable.depth == pixmap2->drawable.depth); + + glamor_prepare_access(&pixmap1->drawable, GLAMOR_ACCESS_RO); + glamor_prepare_access(&pixmap2->drawable, GLAMOR_ACCESS_RO); + + _glamor_compare_pixmaps(pixmap1, pixmap2, x, y, w, h, -1, all, diffs); + + glamor_finish_access(&pixmap1->drawable, GLAMOR_ACCESS_RO); + glamor_finish_access(&pixmap2->drawable, GLAMOR_ACCESS_RO); +} + +/* This function is used to compare two pictures. + If the picture has no drawable, we use fb functions to generate it. */ +static inline void glamor_compare_pictures( ScreenPtr screen, + PicturePtr fst_picture, + PicturePtr snd_picture, + int x_source, int y_source, + int width, int height, + int all, int diffs) +{ + PixmapPtr fst_pixmap; + PixmapPtr snd_pixmap; + int fst_generated, snd_generated; + int error; + int fst_type = -1; + int snd_type = -1; // -1 represent has drawable. + + if (fst_picture->format != snd_picture->format) { + ErrorF("Different picture format can not compare!\n"); + return; + } + + if (!fst_picture->pDrawable) { + fst_type = fst_picture->pSourcePict->type; + } + + if (!snd_picture->pDrawable) { + snd_type = snd_picture->pSourcePict->type; + } + + if ((fst_type != -1) && (snd_type != -1) && (fst_type != snd_type)) { + ErrorF("Different picture type will never be same!\n"); + return; + } + + fst_generated = snd_generated = 0; + + if (!fst_picture->pDrawable) { + PicturePtr pixman_pic; + PixmapPtr pixmap = NULL; + PictFormatShort format; + + format = fst_picture->format; + + pixmap = glamor_create_pixmap(screen, + width, height, + PIXMAN_FORMAT_DEPTH(format), + GLAMOR_CREATE_PIXMAP_CPU); + + pixman_pic = CreatePicture(0, + &pixmap->drawable, + PictureMatchFormat(screen, + PIXMAN_FORMAT_DEPTH(format), format), + 0, 0, serverClient, &error); + + fbComposite(PictOpSrc, fst_picture, NULL, pixman_pic, + x_source, y_source, + 0, 0, + 0, 0, + width, height); + + glamor_destroy_pixmap(pixmap); + + fst_picture = pixman_pic; + fst_generated = 1; + } + + if (!snd_picture->pDrawable) { + PicturePtr pixman_pic; + PixmapPtr pixmap = NULL; + PictFormatShort format; + + format = snd_picture->format; + + pixmap = glamor_create_pixmap(screen, + width, height, + PIXMAN_FORMAT_DEPTH(format), + GLAMOR_CREATE_PIXMAP_CPU); + + pixman_pic = CreatePicture(0, + &pixmap->drawable, + PictureMatchFormat(screen, + PIXMAN_FORMAT_DEPTH(format), format), + 0, 0, serverClient, &error); + + fbComposite(PictOpSrc, snd_picture, NULL, pixman_pic, + x_source, y_source, + 0, 0, + 0, 0, + width, height); + + glamor_destroy_pixmap(pixmap); + + snd_picture = pixman_pic; + snd_generated = 1; + } + + fst_pixmap = glamor_get_drawable_pixmap(fst_picture->pDrawable); + snd_pixmap = glamor_get_drawable_pixmap(snd_picture->pDrawable); + + if (fst_pixmap->drawable.depth != snd_pixmap->drawable.depth) { + if (fst_generated) + glamor_destroy_picture(fst_picture); + if (snd_generated) + glamor_destroy_picture(snd_picture); + + ErrorF("Different pixmap depth can not compare!\n"); + return; + } + + glamor_prepare_access(&fst_pixmap->drawable, GLAMOR_ACCESS_RO); + glamor_prepare_access(&snd_pixmap->drawable, GLAMOR_ACCESS_RO); + + if ((fst_type == SourcePictTypeLinear) || + (fst_type == SourcePictTypeRadial) || + (fst_type == SourcePictTypeConical) || + (snd_type == SourcePictTypeLinear) || + (snd_type == SourcePictTypeRadial) || + (snd_type == SourcePictTypeConical)) { + x_source = y_source = 0; + } + + _glamor_compare_pixmaps(fst_pixmap, snd_pixmap, + x_source, y_source, + width, height, + fst_picture->format, all, diffs); + + glamor_finish_access(&fst_pixmap->drawable, GLAMOR_ACCESS_RO); + glamor_finish_access(&snd_pixmap->drawable, GLAMOR_ACCESS_RO); + + if (fst_generated) + glamor_destroy_picture(fst_picture); + if (snd_generated) + glamor_destroy_picture(snd_picture); + + return; +} + +#ifdef __i386__ +static inline unsigned long __fls(unsigned long x) +{ + asm("bsr %1,%0" + : "=r" (x) + : "rm" (x)); + return x; +} +#else +static inline unsigned long __fls(unsigned long x) +{ + int n; + + if (x == 0) return(0); + n = 0; + if (x <= 0x0000FFFF) {n = n +16; x = x <<16;} + if (x <= 0x00FFFFFF) {n = n + 8; x = x << 8;} + if (x <= 0x0FFFFFFF) {n = n + 4; x = x << 4;} + if (x <= 0x3FFFFFFF) {n = n + 2; x = x << 2;} + if (x <= 0x7FFFFFFF) {n = n + 1;} + return 31 - n; +} +#endif + +static inline void glamor_make_current(ScreenPtr screen) +{ + glamor_egl_make_current(screen); +} + +static inline void glamor_restore_current(ScreenPtr screen) +{ + glamor_egl_restore_context(screen); +} + +#ifdef GLX_USE_SHARED_DISPATCH +static inline glamor_gl_dispatch * +glamor_get_dispatch(glamor_screen_private *glamor_priv) +{ + if (glamor_priv->flags & GLAMOR_USE_EGL_SCREEN) + glamor_make_current(glamor_priv->screen); + + return &glamor_priv->_dispatch; +} + +static inline void +glamor_put_dispatch(glamor_screen_private *glamor_priv) +{ + if (glamor_priv->flags & GLAMOR_USE_EGL_SCREEN) + glamor_restore_current(glamor_priv->screen); +} +#else +#warning "Indirect GLX may be broken, need to implement context switch." +static inline glamor_gl_dispatch * +glamor_get_dispatch(glamor_screen_private *glamor_priv) +{ + return &glamor_priv->_dispatch; +} + +static inline void +glamor_put_dispatch(glamor_screen_private *glamor_priv) +{ +} + +#endif + +#endif diff --git a/xorg-server/glamor/glamor_window.c b/xorg-server/glamor/glamor_window.c new file mode 100644 index 000000000..b67c72880 --- /dev/null +++ b/xorg-server/glamor/glamor_window.c @@ -0,0 +1,103 @@ +/* + * Copyright © 2008 Intel Corporation + * Copyright © 1998 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "glamor_priv.h" + +/** @file glamor_window.c + * + * Screen Change Window Attribute implementation. + */ + + +static void +glamor_fixup_window_pixmap(DrawablePtr pDrawable, PixmapPtr * ppPixmap) +{ + PixmapPtr pPixmap = *ppPixmap; + glamor_pixmap_private *pixmap_priv; + + if (pPixmap->drawable.bitsPerPixel != pDrawable->bitsPerPixel) { + pixmap_priv = glamor_get_pixmap_private(pPixmap); + if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv)) { + glamor_fallback("pixmap %p has no fbo\n", pPixmap); + goto fail; + } + glamor_debug_output(GLAMOR_DEBUG_UNIMPL, + "To be implemented.\n"); + } + return; + + fail: + GLAMOR_PANIC + (" We can't fall back to fbFixupWindowPixmap, as the fb24_32ReformatTile" + " is broken for glamor. \n"); +} + +Bool +glamor_change_window_attributes(WindowPtr pWin, unsigned long mask) +{ + if (mask & CWBackPixmap) { + if (pWin->backgroundState == BackgroundPixmap) + glamor_fixup_window_pixmap(&pWin->drawable, + &pWin-> + background.pixmap); + } + + if (mask & CWBorderPixmap) { + if (pWin->borderIsPixel == FALSE) + glamor_fixup_window_pixmap(&pWin->drawable, + &pWin->border.pixmap); + } + return TRUE; +} + +void +glamor_set_window_pixmap(WindowPtr win, PixmapPtr pPixmap) +{ + ScreenPtr screen = win->drawable.pScreen; + glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); + PixmapPtr old = screen->GetWindowPixmap(win); + + if (pPixmap != old) { + glamor_pixmap_private *pixmap_priv; + PicturePtr pic = NULL; + + pixmap_priv = glamor_get_pixmap_private(old); + if (GLAMOR_PIXMAP_PRIV_IS_PICTURE(pixmap_priv) && pixmap_priv->base.picture->pDrawable == (DrawablePtr)win) { + pic = pixmap_priv->base.picture; + pixmap_priv->base.is_picture = 0; + pixmap_priv->base.picture = NULL; + } + + pixmap_priv = glamor_get_pixmap_private(pPixmap); + if (pixmap_priv) { + pixmap_priv->base.is_picture = !!pic; + pixmap_priv->base.picture = pic; + } + } + + screen->SetWindowPixmap = glamor_priv->saved_procs.set_window_pixmap; + (screen->SetWindowPixmap)(win, pPixmap); + glamor_priv->saved_procs.set_window_pixmap = screen->SetWindowPixmap; + screen->SetWindowPixmap = glamor_set_window_pixmap; +} diff --git a/xorg-server/glamor/glamor_xv.c b/xorg-server/glamor/glamor_xv.c new file mode 100644 index 000000000..a89b4cd3f --- /dev/null +++ b/xorg-server/glamor/glamor_xv.c @@ -0,0 +1,645 @@ +/* + * Copyright © 2013 Red Hat + * + * 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. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * + * some code is derived from the xf86-video-ati radeon driver, mainly + * the calculations. + */ + +/** @file glamor_xv.c + * + * Xv acceleration implementation + */ + +#include "glamor_priv.h" + +#ifdef GLAMOR_XV +#include "xf86xv.h" +#include <X11/extensions/Xv.h> +#include "fourcc.h" +/* Reference color space transform data */ +typedef struct tagREF_TRANSFORM +{ + float RefLuma; + float RefRCb; + float RefRCr; + float RefGCb; + float RefGCr; + float RefBCb; + float RefBCr; +} REF_TRANSFORM; + +#define RTFSaturation(a) (1.0 + ((a)*1.0)/1000.0) +#define RTFBrightness(a) (((a)*1.0)/2000.0) +#define RTFIntensity(a) (((a)*1.0)/2000.0) +#define RTFContrast(a) (1.0 + ((a)*1.0)/1000.0) +#define RTFHue(a) (((a)*3.1416)/1000.0) + +static const char *xv_vs= "attribute vec4 v_position;\n" + "attribute vec4 v_texcoord0;\n" + "varying vec2 tcs;\n" + "void main()\n" "{\n" " gl_Position = v_position;\n" + "tcs = v_texcoord0.xy;\n" + "}\n"; + +static const char *xv_ps = GLAMOR_DEFAULT_PRECISION + "uniform sampler2D y_sampler;\n" + "uniform sampler2D u_sampler;\n" + "uniform sampler2D v_sampler;\n" + "uniform vec4 offsetyco;\n" + "uniform vec4 ucogamma;\n" + "uniform vec4 vco;\n" + "varying vec2 tcs;\n" + "float sample;\n" + "vec4 temp1;\n" + "void main()\n" "{\n" + "sample = texture2D(y_sampler, tcs).w;\n" + "temp1.xyz = offsetyco.www * vec3(sample) + offsetyco.xyz;\n" + "sample = texture2D(u_sampler, tcs).w;\n" + "temp1.xyz = ucogamma.xyz * vec3(sample) + temp1.xyz;\n" + "sample = texture2D(v_sampler, tcs).w;\n" + "temp1.xyz = clamp(vco.xyz * vec3(sample) + temp1.xyz, 0.0, 1.0);\n" + "temp1.w = 1.0;\n" + "gl_FragColor = temp1;\n" + "}\n"; + +void +glamor_init_xv_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + GLint fs_prog, vs_prog; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + glamor_priv->xv_prog = dispatch->glCreateProgram(); + + vs_prog = glamor_compile_glsl_prog(dispatch, GL_VERTEX_SHADER, xv_vs); + fs_prog = glamor_compile_glsl_prog(dispatch, GL_FRAGMENT_SHADER, xv_ps); + dispatch->glAttachShader(glamor_priv->xv_prog, vs_prog); + dispatch->glAttachShader(glamor_priv->xv_prog, fs_prog); + + dispatch->glBindAttribLocation(glamor_priv->xv_prog, + GLAMOR_VERTEX_POS, "v_position"); + dispatch->glBindAttribLocation(glamor_priv->xv_prog, + GLAMOR_VERTEX_SOURCE, "v_texcoord0"); + glamor_link_glsl_prog(dispatch, glamor_priv->xv_prog); + + glamor_put_dispatch(glamor_priv); +} + +void +glamor_fini_xv_shader(ScreenPtr screen) +{ + glamor_screen_private *glamor_priv; + glamor_gl_dispatch *dispatch; + + glamor_priv = glamor_get_screen_private(screen); + dispatch = glamor_get_dispatch(glamor_priv); + + dispatch->glDeleteProgram(glamor_priv->xv_prog); + glamor_put_dispatch(glamor_priv); +} + +#define ClipValue(v,min,max) ((v) < (min) ? (min) : (v) > (max) ? (max) : (v)) +#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE) + +static Atom xvBrightness, xvContrast, xvSaturation, xvHue, xvColorspace, xvGamma; + +#define NUM_ATTRIBUTES 5 +static XF86AttributeRec Attributes_glamor[NUM_ATTRIBUTES+1] = +{ + {XvSettable | XvGettable, -1000, 1000, "XV_BRIGHTNESS"}, + {XvSettable | XvGettable, -1000, 1000, "XV_CONTRAST"}, + {XvSettable | XvGettable, -1000, 1000, "XV_SATURATION"}, + {XvSettable | XvGettable, -1000, 1000, "XV_HUE"}, + {XvSettable | XvGettable, 0, 1, "XV_COLORSPACE"}, + {0, 0, 0, NULL} + }; + +#define NUM_FORMATS 3 + +static XF86VideoFormatRec Formats[NUM_FORMATS] = +{ + {15, TrueColor}, {16, TrueColor}, {24, TrueColor} + }; + +#define NUM_IMAGES 2 + +static XF86ImageRec Images[NUM_IMAGES] = +{ +XVIMAGE_YV12, + XVIMAGE_I420, + }; + +static void +glamor_xv_stop_video(ScrnInfoPtr pScrn, pointer data, Bool cleanup) +{ +glamor_port_private *port_priv = (glamor_port_private *)data; +int i; +if (!cleanup) + return; + +for (i = 0; i < 3; i++) { +if (port_priv->src_pix[i]) { +glamor_destroy_pixmap(port_priv->src_pix[i]); +port_priv->src_pix[i] = NULL; +} +} +} + +static int +glamor_xv_set_port_attribute(ScrnInfoPtr pScrn, + Atom attribute, + INT32 value, + pointer data) +{ +glamor_port_private *port_priv = (glamor_port_private *)data; +if (attribute == xvBrightness) + port_priv->brightness = ClipValue(value, -1000, 1000); +else if (attribute == xvHue) + port_priv->hue = ClipValue(value, -1000, 1000); +else if (attribute == xvContrast) + port_priv->contrast = ClipValue(value, -1000, 1000); +else if (attribute == xvSaturation) + port_priv->saturation = ClipValue(value, -1000, 1000); +else if (attribute == xvGamma) + port_priv->gamma = ClipValue (value, 100, 10000); +else if(attribute == xvColorspace) + port_priv->transform_index = ClipValue (value, 0, 1); +else + return BadMatch; +return Success; +} + +static int +glamor_xv_get_port_attribute(ScrnInfoPtr pScrn, + Atom attribute, + INT32 *value, + pointer data) +{ +glamor_port_private *port_priv = (glamor_port_private *)data; +if (attribute == xvBrightness) + *value = port_priv->brightness; +else if (attribute == xvHue) + *value = port_priv->hue; +else if (attribute == xvContrast) + *value = port_priv->contrast; +else if (attribute == xvSaturation) + *value = port_priv->saturation; +else if (attribute == xvGamma) + *value = port_priv->gamma; +else if(attribute == xvColorspace) + *value = port_priv->transform_index; +else + return BadMatch; + +return Success; +} + +static void +glamor_xv_query_best_size(ScrnInfoPtr pScrn, + Bool motion, + short vid_w, short vid_h, + short drw_w, short drw_h, + unsigned int *p_w, unsigned int *p_h, + pointer data) +{ +*p_w = drw_w; +*p_h = drw_h; +} + +static int +glamor_xv_query_image_attributes(ScrnInfoPtr pScrn, + int id, + unsigned short *w, unsigned short *h, + int *pitches, int *offsets) +{ +int size = 0, tmp; + +if (offsets) offsets[0] = 0; +switch (id) { +case FOURCC_YV12: +case FOURCC_I420: +*h = *h; +*w = *w; +size = *w; +if (pitches) pitches[0] = size; +size *= *h; +if (offsets) offsets[1] = size; +tmp = *w >> 1; +if (pitches) pitches[1] = pitches[2] = tmp; +tmp *= (*h >> 1); +size += tmp; +if (offsets) offsets[2] = size; +size += tmp; +break; +} +return size; +} +/* Parameters for ITU-R BT.601 and ITU-R BT.709 colour spaces + note the difference to the parameters used in overlay are due + to 10bit vs. float calcs */ +static REF_TRANSFORM trans[2] = +{ + {1.1643, 0.0, 1.5960, -0.3918, -0.8129, 2.0172, 0.0}, /* BT.601 */ + {1.1643, 0.0, 1.7927, -0.2132, -0.5329, 2.1124, 0.0} /* BT.709 */ + }; + +static void +glamor_display_textured_video(glamor_port_private *port_priv) +{ +ScreenPtr screen = port_priv->pPixmap->drawable.pScreen; +glamor_screen_private *glamor_priv = + glamor_get_screen_private(screen); +glamor_pixmap_private *pixmap_priv = + glamor_get_pixmap_private(port_priv->pPixmap); +glamor_pixmap_private *src_pixmap_priv[3]; +glamor_gl_dispatch *dispatch; +float vertices[32], texcoords[8]; +BoxPtr box = REGION_RECTS(&port_priv->clip); +int nBox = REGION_NUM_RECTS(&port_priv->clip); +int dst_x_off, dst_y_off; +GLfloat dst_xscale, dst_yscale; +GLfloat src_xscale[3], src_yscale[3]; +int i; +const float Loff = -0.0627; +const float Coff = -0.502; +float uvcosf, uvsinf; +float yco; +float uco[3], vco[3], off[3]; +float bright, cont, gamma; +int ref = port_priv->transform_index; +GLint uloc, sampler_loc; + +cont = RTFContrast(port_priv->contrast); +bright = RTFBrightness(port_priv->brightness); +gamma = (float)port_priv->gamma / 1000.0; +uvcosf = RTFSaturation(port_priv->saturation) * cos(RTFHue(port_priv->hue)); +uvsinf = RTFSaturation(port_priv->saturation) * sin(RTFHue(port_priv->hue)); +/* overlay video also does pre-gamma contrast/sat adjust, should we? */ + +yco = trans[ref].RefLuma * cont; +uco[0] = -trans[ref].RefRCr * uvsinf; +uco[1] = trans[ref].RefGCb * uvcosf - trans[ref].RefGCr * uvsinf; +uco[2] = trans[ref].RefBCb * uvcosf; +vco[0] = trans[ref].RefRCr * uvcosf; +vco[1] = trans[ref].RefGCb * uvsinf + trans[ref].RefGCr * uvcosf; +vco[2] = trans[ref].RefBCb * uvsinf; +off[0] = Loff * yco + Coff * (uco[0] + vco[0]) + bright; +off[1] = Loff * yco + Coff * (uco[1] + vco[1]) + bright; +off[2] = Loff * yco + Coff * (uco[2] + vco[2]) + bright; +gamma = 1.0; + +pixmap_priv_get_dest_scale(pixmap_priv, &dst_xscale, &dst_yscale); +glamor_get_drawable_deltas(port_priv->pDraw, port_priv->pPixmap, &dst_x_off, + &dst_y_off); +glamor_set_destination_pixmap_priv_nc(pixmap_priv); + +for (i = 0; i < 3; i++) { +if (port_priv->src_pix[i]) { +src_pixmap_priv[i] = glamor_get_pixmap_private(port_priv->src_pix[i]); +pixmap_priv_get_scale(src_pixmap_priv[i], &src_xscale[i], &src_yscale[i]); +} +} +dispatch = glamor_get_dispatch(glamor_priv); +dispatch->glUseProgram(glamor_priv->xv_prog); + +uloc = dispatch->glGetUniformLocation(glamor_priv->xv_prog, "offsetyco"); +dispatch->glUniform4f(uloc, off[0], off[1], off[2], yco); +uloc = dispatch->glGetUniformLocation(glamor_priv->xv_prog, "ucogamma"); +dispatch->glUniform4f(uloc, uco[0], uco[1], uco[2], gamma); +uloc = dispatch->glGetUniformLocation(glamor_priv->xv_prog, "vco"); +dispatch->glUniform4f(uloc, vco[0], vco[1], vco[2], 0); + +dispatch->glActiveTexture(GL_TEXTURE0); +dispatch->glBindTexture(GL_TEXTURE_2D, src_pixmap_priv[0]->base.fbo->tex); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_LINEAR); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_LINEAR); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + +dispatch->glActiveTexture(GL_TEXTURE1); +dispatch->glBindTexture(GL_TEXTURE_2D, src_pixmap_priv[1]->base.fbo->tex); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_LINEAR); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_LINEAR); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + +dispatch->glActiveTexture(GL_TEXTURE2); +dispatch->glBindTexture(GL_TEXTURE_2D, src_pixmap_priv[2]->base.fbo->tex); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_LINEAR); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_LINEAR); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); +dispatch->glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + +sampler_loc = dispatch->glGetUniformLocation(glamor_priv->xv_prog, "y_sampler"); +dispatch->glUniform1i(sampler_loc, 0); +sampler_loc = dispatch->glGetUniformLocation(glamor_priv->xv_prog, "u_sampler"); +dispatch->glUniform1i(sampler_loc, 1); +sampler_loc = dispatch->glGetUniformLocation(glamor_priv->xv_prog, "v_sampler"); +dispatch->glUniform1i(sampler_loc, 2); + +dispatch->glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, + GL_FLOAT, GL_FALSE, + 2 * sizeof(float), + texcoords); +dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + +dispatch->glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, + GL_FALSE, 2 * sizeof(float), + vertices); + +dispatch->glEnableVertexAttribArray(GLAMOR_VERTEX_POS); +for (i = 0; i < nBox; i++) { +float off_x = box[i].x1 - port_priv->drw_x; +float off_y = box[i].y1 - port_priv->drw_y; +float diff_x = (float)port_priv->src_w / (float)port_priv->dst_w; +float diff_y = (float)port_priv->src_h / (float)port_priv->dst_h; +float srcx, srcy, srcw, srch; +int dstx, dsty, dstw, dsth; + + +dstx = box[i].x1 + dst_x_off; +dsty = box[i].y1 + dst_y_off; +dstw = box[i].x2 - box[i].x1; +dsth = box[i].y2 - box[i].y1; + +srcx = port_priv->src_x + off_x * diff_x; +srcy = port_priv->src_y + off_y * diff_y; +srcw = (port_priv->src_w * dstw) / (float)port_priv->dst_w; +srch = (port_priv->src_h * dsth) / (float)port_priv->dst_h; + +glamor_set_normalize_vcoords(pixmap_priv, + dst_xscale, dst_yscale, + dstx, + dsty, + dstx + dstw, + dsty + dsth, + glamor_priv->yInverted, + vertices); + +glamor_set_normalize_tcoords(src_pixmap_priv[0], + src_xscale[0], + src_yscale[0], + srcx, + srcy, + srcx + srcw, + srcy + srch, + glamor_priv->yInverted, + texcoords); + +dispatch->glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_POS); +dispatch->glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); + +dispatch->glUseProgram(0); +glamor_put_dispatch(glamor_priv); +DamageDamageRegion(port_priv->pDraw, &port_priv->clip); +} + +static int glamor_xv_put_image(ScrnInfoPtr pScrn, + short src_x, short src_y, + short drw_x, short drw_y, + short src_w, short src_h, + short drw_w, short drw_h, + int id, + unsigned char *buf, + short width, + short height, + Bool sync, + RegionPtr clipBoxes, + pointer data, + DrawablePtr pDrawable) +{ + ScreenPtr screen = xf86ScrnToScreen(pScrn); + glamor_port_private *port_priv = (glamor_port_private *)data; + INT32 x1, x2, y1, y2; + int srcPitch, srcPitch2; + BoxRec dstBox; + int top, nlines; + int s2offset, s3offset, tmp; + + s2offset = s3offset = srcPitch2 = 0; + + /* Clip */ + x1 = src_x; + x2 = src_x + src_w; + y1 = src_y; + y2 = src_y + src_h; + + dstBox.x1 = drw_x; + dstBox.x2 = drw_x + drw_w; + dstBox.y1 = drw_y; + dstBox.y2 = drw_y + drw_h; + if (!xf86XVClipVideoHelper(&dstBox, &x1, &x2, &y1, &y2, clipBoxes, width, height)) + return Success; + + if ((x1 >= x2) || (y1 >= y2)) + return Success; + + srcPitch = width; + srcPitch2 = width >> 1; + + if (!port_priv->src_pix[0] || (width != port_priv->src_pix_w || height != port_priv->src_pix_h)) { + int i; + for (i = 0; i < 3; i++) + if (port_priv->src_pix[i]) + glamor_destroy_pixmap(port_priv->src_pix[i]); + + port_priv->src_pix[0] = glamor_create_pixmap(screen, width, height, 8, 0); + port_priv->src_pix[1] = glamor_create_pixmap(screen, width >> 1, height >> 1, 8, 0); + port_priv->src_pix[2] = glamor_create_pixmap(screen, width >> 1, height >> 1, 8, 0); + port_priv->src_pix_w = width; + port_priv->src_pix_h = height; + + if (!port_priv->src_pix[0] || !port_priv->src_pix[1] || !port_priv->src_pix[2]) + return BadAlloc; + } + + top = (y1 >> 16) & ~1; + nlines = ((y2 + 0xffff) >> 16) - top; + + switch (id) { + case FOURCC_YV12: + case FOURCC_I420: + s2offset = srcPitch * height; + s3offset = s2offset + (srcPitch2 * ((height + 1) >> 1)); + s2offset += ((top >> 1) * srcPitch2); + s3offset += ((top >> 1) * srcPitch2); + if (id == FOURCC_YV12) { + tmp = s2offset; + s2offset = s3offset; + s3offset = tmp; + } + glamor_upload_sub_pixmap_to_texture(port_priv->src_pix[0], + 0, 0, srcPitch, nlines, + port_priv->src_pix[0]->devKind, + buf + (top * srcPitch), 0); + + glamor_upload_sub_pixmap_to_texture(port_priv->src_pix[1], + 0, 0, srcPitch2, (nlines + 1) >> 1, + port_priv->src_pix[1]->devKind, + buf + s2offset, 0); + + glamor_upload_sub_pixmap_to_texture(port_priv->src_pix[2], + 0, 0, srcPitch2, (nlines + 1) >> 1, + port_priv->src_pix[2]->devKind, + buf + s3offset, 0); + break; + default: + return BadMatch; + } + + if (pDrawable->type == DRAWABLE_WINDOW) + port_priv->pPixmap = (*screen->GetWindowPixmap)((WindowPtr)pDrawable); + else + port_priv->pPixmap = (PixmapPtr)pDrawable; + + if (!RegionEqual(&port_priv->clip, clipBoxes)) { + RegionCopy(&port_priv->clip, clipBoxes); + } + + port_priv->src_x = src_x; + port_priv->src_y = src_y; + port_priv->src_w = src_w; + port_priv->src_h = src_h; + port_priv->dst_w = drw_w; + port_priv->dst_h = drw_h; + port_priv->drw_x = drw_x; + port_priv->drw_y = drw_y; + port_priv->w = width; + port_priv->h = height; + port_priv->pDraw = pDrawable; + glamor_display_textured_video(port_priv); + return Success; +} + +static XF86VideoEncodingRec DummyEncodingGLAMOR[1] = +{ + { + 0, + "XV_IMAGE", + 8192, 8192, + {1, 1} + } +}; + +XF86VideoAdaptorPtr +glamor_xv_init(ScreenPtr screen, int num_texture_ports) +{ + glamor_port_private *port_priv; + XF86VideoAdaptorPtr adapt; + int i; + + adapt = calloc(1, sizeof(XF86VideoAdaptorRec) + num_texture_ports * + (sizeof(glamor_port_private) + sizeof(DevUnion))); + if (adapt == NULL) + return NULL; + + xvBrightness = MAKE_ATOM("XV_BRIGHTNESS"); + xvContrast = MAKE_ATOM("XV_CONTRAST"); + xvSaturation = MAKE_ATOM("XV_SATURATION"); + xvHue = MAKE_ATOM("XV_HUE"); + xvGamma = MAKE_ATOM("XV_GAMMA"); + xvColorspace = MAKE_ATOM("XV_COLORSPACE"); + + adapt->type = XvWindowMask | XvInputMask | XvImageMask; + adapt->flags = 0; + adapt->name = "GLAMOR Textured Video"; + adapt->nEncodings = 1; + adapt->pEncodings = DummyEncodingGLAMOR; + + adapt->nFormats = NUM_FORMATS; + adapt->pFormats = Formats; + adapt->nPorts = num_texture_ports; + adapt->pPortPrivates = (DevUnion*)(&adapt[1]); + + adapt->pAttributes = Attributes_glamor; + adapt->nAttributes = NUM_ATTRIBUTES; + + port_priv = (glamor_port_private *)(&adapt->pPortPrivates[num_texture_ports]); + adapt->pImages = Images; + adapt->nImages = NUM_IMAGES; + adapt->PutVideo = NULL; + adapt->PutStill = NULL; + adapt->GetVideo = NULL; + adapt->GetStill = NULL; + adapt->StopVideo = glamor_xv_stop_video; + adapt->SetPortAttribute = glamor_xv_set_port_attribute; + adapt->GetPortAttribute = glamor_xv_get_port_attribute; + adapt->QueryBestSize = glamor_xv_query_best_size; + adapt->PutImage = glamor_xv_put_image; + adapt->ReputImage = NULL; + adapt->QueryImageAttributes = glamor_xv_query_image_attributes; + + for (i = 0; i < num_texture_ports; i++) { + glamor_port_private *pPriv = &port_priv[i]; + + pPriv->brightness = 0; + pPriv->contrast = 0; + pPriv->saturation = 0; + pPriv->hue = 0; + pPriv->gamma = 1000; + pPriv->transform_index = 0; + + REGION_NULL(pScreen, &pPriv->clip); + + adapt->pPortPrivates[i].ptr = (pointer)(pPriv); + } + return adapt; +} +#else +XF86VideoAdaptorPtr +glamor_xv_init(ScreenPtr screen, int num_texture_ports) +{ + return NULL; +} +#endif diff --git a/xorg-server/glamor/glapi.h b/xorg-server/glamor/glapi.h new file mode 100644 index 000000000..d510dac1d --- /dev/null +++ b/xorg-server/glamor/glapi.h @@ -0,0 +1,121 @@ +/* + * Mesa 3-D graphics library + * Version: 7.1 + * + * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, 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 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 + * BRIAN PAUL 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. + */ + + +/** + * \mainpage Mesa GL API Module + * + * \section GLAPIIntroduction Introduction + * + * The Mesa GL API module is responsible for dispatching all the + * gl*() functions. All GL functions are dispatched by jumping through + * the current dispatch table (basically a struct full of function + * pointers.) + * + * A per-thread current dispatch table and per-thread current context + * pointer are managed by this module too. + * + * This module is intended to be non-Mesa-specific so it can be used + * with the X/DRI libGL also. + */ + +#ifndef _GLAPI_H +#define _GLAPI_H + +#define GL_GLEXT_PROTOTYPES + +#if GLAMOR_GLES2 +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#else +#include <GL/gl.h> +#include "GL/glext.h" +#endif + +/* Is this needed? It is incomplete anyway. */ +#ifdef USE_MGL_NAMESPACE +#define _glapi_set_dispatch _mglapi_set_dispatch +#define _glapi_get_dispatch _mglapi_get_dispatch +#define _glapi_set_context _mglapi_set_context +#define _glapi_get_context _mglapi_get_context +#define _glapi_Dispatch _mglapi_Dispatch +#define _glapi_Context _mglapi_Context +#endif + +typedef void (*_glapi_proc)(void); +struct _glapi_table; + + +#if defined (GLX_USE_TLS) + +extern __thread struct _glapi_table * _glapi_tls_Dispatch + __attribute__((tls_model("initial-exec"))); + +extern __thread void * _glapi_tls_Context + __attribute__((tls_model("initial-exec"))); + +extern const struct _glapi_table *_glapi_Dispatch; +extern const void *_glapi_Context; + +# define GET_DISPATCH() _glapi_tls_Dispatch +# define GET_CURRENT_CONTEXT(C) C = (typeof(C)) _glapi_tls_Context +# define SET_CURRENT_CONTEXT(C) _glapi_tls_Context = (void*)C + +#else + +extern struct _glapi_table *_glapi_Dispatch; +extern void *_glapi_Context; + +# ifdef THREADS + +# define GET_DISPATCH() \ + (likely(_glapi_Dispatch) ? _glapi_Dispatch : _glapi_get_dispatch()) + +# define GET_CURRENT_CONTEXT(C) C = (typeof(C)) \ + (likely(_glapi_Context) ? _glapi_Context : _glapi_get_context()) + + +# define SET_CURRENT_CONTEXT(C) do { if (likely(_glapi_Context)) \ + _glapi_Context = (void*)C; \ + else \ + _glapi_set_context(C); } while(0) + +# else + +# define GET_DISPATCH() _glapi_Dispatch +# define GET_CURRENT_CONTEXT(C) C = (typeof(C)) _glapi_Context +# define SET_CURRENT_CONTEXT(C) _glapi_Context = (void*)C + +# endif + +#endif /* defined (GLX_USE_TLS) */ + + +extern void +_glapi_set_context(void *context); + +extern void * +_glapi_get_context(void); + +#endif |