/* * 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 * * some code is derived from the xf86-video-ati radeon driver, mainly * the calculations. */ /** @file glamor_xv.c * * Xv acceleration implementation */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include "glamor_priv.h" #include "glamor_transform.h" #include "glamor_transfer.h" #include #include #include #include #include #include #include #include "../hw/xfree86/common/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) #ifndef DRM_FORMAT_NV12_10 #define DRM_FORMAT_NV12_10 fourcc_code('N', 'A', '1', '2') #endif #ifndef DRM_FORMAT_NV15 #define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') #endif #ifndef DRM_FORMAT_YUV420_8BIT #define DRM_FORMAT_YUV420_8BIT fourcc_code('Y', 'U', '0', '8') #endif #ifndef DRM_FORMAT_YUV420_10BIT #define DRM_FORMAT_YUV420_10BIT fourcc_code('Y', 'U', '1', '0') #endif #ifndef DRM_FORMAT_MOD_VENDOR_ARM #define DRM_FORMAT_MOD_VENDOR_ARM 0x08 #endif #ifndef DRM_FORMAT_MOD_ARM_AFBC #define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) fourcc_mod_code(ARM, __afbc_mode) #endif #ifndef AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 #define AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 (1ULL) #endif #ifndef AFBC_FORMAT_MOD_SPARSE #define AFBC_FORMAT_MOD_SPARSE (((__u64)1) << 6) #endif #define DRM_AFBC_MODIFIER \ (DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_SPARSE) | \ DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16)) #define XV_MAX_DMA_FD 3 static const glamor_facet glamor_facet_xv_planar_2 = { .name = "xv_planar_2", .version = 120, .source_name = "v_texcoord0", .vs_vars = ("attribute vec2 position;\n" "attribute vec2 v_texcoord0;\n" "varying vec2 tcs;\n"), .vs_exec = (GLAMOR_POS(gl_Position, position) " tcs = v_texcoord0;\n"), .fs_vars = ("uniform sampler2D y_sampler;\n" "uniform sampler2D u_sampler;\n" "uniform vec4 offsetyco;\n" "uniform vec4 ucogamma;\n" "uniform vec4 vco;\n" "varying vec2 tcs;\n"), .fs_exec = ( " float sample;\n" " vec2 sample_uv;\n" " vec4 temp1;\n" " sample = texture2D(y_sampler, tcs).w;\n" " temp1.xyz = offsetyco.www * vec3(sample) + offsetyco.xyz;\n" " sample_uv = texture2D(u_sampler, tcs).xy;\n" " temp1.xyz = ucogamma.xyz * vec3(sample_uv.x) + temp1.xyz;\n" " temp1.xyz = clamp(vco.xyz * vec3(sample_uv.y) + temp1.xyz, 0.0, 1.0);\n" " temp1.w = 1.0;\n" " gl_FragColor = temp1;\n" ), }; static const glamor_facet glamor_facet_xv_planar_3 = { .name = "xv_planar_3", .version = 120, .source_name = "v_texcoord0", .vs_vars = ("attribute vec2 position;\n" "attribute vec2 v_texcoord0;\n" "varying vec2 tcs;\n"), .vs_exec = (GLAMOR_POS(gl_Position, position) " tcs = v_texcoord0;\n"), .fs_vars = ("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"), .fs_exec = ( " float sample;\n" " vec4 temp1;\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" ), }; static const glamor_facet glamor_facet_xv_egl_external = { .name = "xv_egl_external", .source_name = "v_texcoord0", .vs_vars = ("attribute vec2 position;\n" "attribute vec2 v_texcoord0;\n" "varying vec2 tcs;\n"), .vs_exec = (GLAMOR_POS(gl_Position, position) " tcs = v_texcoord0;\n"), .fs_extensions = ("#extension GL_OES_EGL_image_external : require\n"), .fs_vars = ("uniform samplerExternalOES sampler;\n" "varying vec2 tcs;\n"), .fs_exec = ( " gl_FragColor = texture2D(sampler, tcs);\n" ), }; #define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE) XvAttributeRec glamor_xv_attributes[] = { {XvSettable | XvGettable, -1000, 1000, (char *)"XV_BRIGHTNESS"}, {XvSettable | XvGettable, -1000, 1000, (char *)"XV_CONTRAST"}, {XvSettable | XvGettable, -1000, 1000, (char *)"XV_SATURATION"}, {XvSettable | XvGettable, -1000, 1000, (char *)"XV_HUE"}, {XvSettable | XvGettable, 0, 1, (char *)"XV_COLORSPACE"}, {XvSettable | XvGettable, 0, 0xFFFFFFFF, (char *)"XV_DMA_CLIENT_ID"}, {XvSettable | XvGettable, 0, 0xFFFFFFFF, (char *)"XV_DMA_HOR_STRIDE"}, {XvSettable | XvGettable, 0, 0xFFFFFFFF, (char *)"XV_DMA_VER_STRIDE"}, {XvSettable | XvGettable, 0, 0xFFFFFFFF, (char *)"XV_DMA_DRM_FOURCC"}, {XvSettable | XvGettable, 0, 1, (char *)"XV_DMA_DRM_AFBC"}, {0, 0, 0, NULL} }; int glamor_xv_num_attributes = ARRAY_SIZE(glamor_xv_attributes) - 1; Atom glamorBrightness, glamorContrast, glamorSaturation, glamorHue, glamorColorspace, glamorGamma, glamorDmaClient, glamorDmaHorStride, glamorDmaVerStride, glamorDmaDrmFourcc, glamorDmaDrmAFBC; XvImageRec glamor_xv_images[] = { XVIMAGE_YV12, XVIMAGE_I420, XVIMAGE_NV12 }; int glamor_xv_num_images = ARRAY_SIZE(glamor_xv_images); static void glamor_init_xv_shader(ScreenPtr screen, int id) { glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); GLint sampler_loc; const glamor_facet *glamor_facet_xv_planar = NULL; switch (id) { case FOURCC_YV12: case FOURCC_I420: glamor_facet_xv_planar = &glamor_facet_xv_planar_3; break; case FOURCC_NV12: glamor_facet_xv_planar = &glamor_facet_xv_planar_2; break; default: break; } glamor_build_program(screen, &glamor_priv->xv_prog, glamor_facet_xv_planar, NULL, NULL, NULL); glUseProgram(glamor_priv->xv_prog.prog); sampler_loc = glGetUniformLocation(glamor_priv->xv_prog.prog, "y_sampler"); glUniform1i(sampler_loc, 0); sampler_loc = glGetUniformLocation(glamor_priv->xv_prog.prog, "u_sampler"); glUniform1i(sampler_loc, 1); switch (id) { case FOURCC_YV12: case FOURCC_I420: sampler_loc = glGetUniformLocation(glamor_priv->xv_prog.prog, "v_sampler"); glUniform1i(sampler_loc, 2); break; case FOURCC_NV12: break; default: break; } } static void glamor_init_xv_shader_egl_external(ScreenPtr screen) { glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); GLint sampler_loc; glamor_build_program(screen, &glamor_priv->xv_prog_ext, &glamor_facet_xv_egl_external, NULL, NULL, NULL); glUseProgram(glamor_priv->xv_prog_ext.prog); sampler_loc = glGetUniformLocation(glamor_priv->xv_prog_ext.prog, "sampler"); glUniform1i(sampler_loc, 0); } #define ClipValue(v,min,max) ((v) < (min) ? (min) : (v) > (max) ? (max) : (v)) static void glamor_xv_set_dma_client(glamor_port_private *port_priv, uint32_t dma_client) { struct sockaddr_un addr; // re-open socket to flush pending messages if (port_priv->dma_client) close(port_priv->dma_socket_fd); port_priv->dma_client = dma_client; if (!dma_client) goto clear; port_priv->dma_socket_fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0); if (port_priv->dma_socket_fd < 0) goto clear; addr.sun_family = AF_LOCAL; snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/.xv_dma_client.%d", port_priv->dma_client); addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; unlink(addr.sun_path); if (bind(port_priv->dma_socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) goto clear; chmod(addr.sun_path, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH); return; clear: if (port_priv->dma_socket_fd > 0) { close(port_priv->dma_socket_fd); port_priv->dma_socket_fd = 0; } port_priv->dma_client = 0; port_priv->dma_hor_stride = 0; port_priv->dma_ver_stride = 0; port_priv->dma_drm_fourcc = 0; port_priv->dma_drm_afbc = 0; } void glamor_xv_stop_video(glamor_port_private *port_priv) { glamor_xv_set_dma_client(port_priv, 0); } static void glamor_xv_free_port_data(glamor_port_private *port_priv) { 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[i] = NULL; } } RegionUninit(&port_priv->clip); RegionNull(&port_priv->clip); } int glamor_xv_set_port_attribute(glamor_port_private *port_priv, Atom attribute, INT32 value) { if (attribute == glamorBrightness) port_priv->brightness = ClipValue(value, -1000, 1000); else if (attribute == glamorHue) port_priv->hue = ClipValue(value, -1000, 1000); else if (attribute == glamorContrast) port_priv->contrast = ClipValue(value, -1000, 1000); else if (attribute == glamorSaturation) port_priv->saturation = ClipValue(value, -1000, 1000); else if (attribute == glamorGamma) port_priv->gamma = ClipValue(value, 100, 10000); else if (attribute == glamorColorspace) port_priv->transform_index = ClipValue(value, 0, 1); else if (attribute == glamorDmaClient) glamor_xv_set_dma_client(port_priv, ClipValue(value, 0, 0xFFFFFFFF)); else if (attribute == glamorDmaHorStride) port_priv->dma_hor_stride = ClipValue(value, 0, 0xFFFFFFFF); else if (attribute == glamorDmaVerStride) port_priv->dma_ver_stride = ClipValue(value, 0, 0xFFFFFFFF); else if (attribute == glamorDmaDrmFourcc) port_priv->dma_drm_fourcc = ClipValue(value, 0, 0xFFFFFFFF); else if (attribute == glamorDmaDrmAFBC) port_priv->dma_drm_afbc = ClipValue(value, 0, 0xFFFFFFFF); else return BadMatch; return Success; } int glamor_xv_get_port_attribute(glamor_port_private *port_priv, Atom attribute, INT32 *value) { if (attribute == glamorBrightness) *value = port_priv->brightness; else if (attribute == glamorHue) *value = port_priv->hue; else if (attribute == glamorContrast) *value = port_priv->contrast; else if (attribute == glamorSaturation) *value = port_priv->saturation; else if (attribute == glamorGamma) *value = port_priv->gamma; else if (attribute == glamorColorspace) *value = port_priv->transform_index; else if (attribute == glamorDmaClient) *value = port_priv->dma_client; else if (attribute == glamorDmaHorStride) *value = port_priv->dma_hor_stride; else if (attribute == glamorDmaVerStride) *value = port_priv->dma_ver_stride; else if (attribute == glamorDmaDrmFourcc) *value = port_priv->dma_drm_fourcc; else if (attribute == glamorDmaDrmAFBC) *value = port_priv->dma_drm_afbc; else return BadMatch; return Success; } int glamor_xv_query_image_attributes(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: *w = ALIGN(*w, 2); *h = ALIGN(*h, 2); size = ALIGN(*w, 4); if (pitches) pitches[0] = size; size *= *h; if (offsets) offsets[1] = size; tmp = ALIGN(*w >> 1, 4); if (pitches) pitches[1] = pitches[2] = tmp; tmp *= (*h >> 1); size += tmp; if (offsets) offsets[2] = size; size += tmp; break; case FOURCC_NV12: *w = ALIGN(*w, 2); *h = ALIGN(*h, 2); size = ALIGN(*w, 4); if (pitches) pitches[0] = size; size *= *h; if (offsets) offsets[1] = size; tmp = ALIGN(*w, 4); if (pitches) pitches[1] = tmp; tmp *= (*h >> 1); 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 */ }; void glamor_xv_render(glamor_port_private *port_priv, int id) { ScreenPtr screen = port_priv->pPixmap->drawable.pScreen; glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); PixmapPtr pixmap = port_priv->pPixmap; glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); glamor_pixmap_private *src_pixmap_priv[3]; BoxPtr box = REGION_RECTS(&port_priv->clip); int nBox = REGION_NUM_RECTS(&port_priv->clip); 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; GLfloat *v; char *vbo_offset; int dst_box_index; DamageRegionAppend(port_priv->pDraw, &port_priv->clip); if (!glamor_priv->xv_prog.prog) glamor_init_xv_shader(screen, id); 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; glamor_set_alu(screen, GXcopy); 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]); } else { src_pixmap_priv[i] = NULL; } } glamor_make_current(glamor_priv); glUseProgram(glamor_priv->xv_prog.prog); uloc = glGetUniformLocation(glamor_priv->xv_prog.prog, "offsetyco"); glUniform4f(uloc, off[0], off[1], off[2], yco); uloc = glGetUniformLocation(glamor_priv->xv_prog.prog, "ucogamma"); glUniform4f(uloc, uco[0], uco[1], uco[2], gamma); uloc = glGetUniformLocation(glamor_priv->xv_prog.prog, "vco"); glUniform4f(uloc, vco[0], vco[1], vco[2], 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, src_pixmap_priv[0]->fbo->tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, src_pixmap_priv[1]->fbo->tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); switch (id) { case FOURCC_YV12: case FOURCC_I420: glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, src_pixmap_priv[2]->fbo->tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); break; case FOURCC_NV12: break; default: break; } glEnableVertexAttribArray(GLAMOR_VERTEX_POS); glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); glEnable(GL_SCISSOR_TEST); v = glamor_get_vbo_space(screen, 3 * 4 * sizeof(GLfloat), &vbo_offset); /* Set up a single primitive covering the area being drawn. We'll * clip it to port_priv->clip using GL scissors instead of just * emitting a GL_QUAD per box, because this way we hopefully avoid * diagonal tearing between the two triangles used to rasterize a * GL_QUAD. */ i = 0; v[i++] = port_priv->drw_x; v[i++] = port_priv->drw_y; v[i++] = port_priv->drw_x + port_priv->dst_w * 2; v[i++] = port_priv->drw_y; v[i++] = port_priv->drw_x; v[i++] = port_priv->drw_y + port_priv->dst_h * 2; v[i++] = t_from_x_coord_x(src_xscale[0], port_priv->src_x); v[i++] = t_from_x_coord_y(src_yscale[0], port_priv->src_y); v[i++] = t_from_x_coord_x(src_xscale[0], port_priv->src_x + port_priv->src_w * 2); v[i++] = t_from_x_coord_y(src_yscale[0], port_priv->src_y); v[i++] = t_from_x_coord_x(src_xscale[0], port_priv->src_x); v[i++] = t_from_x_coord_y(src_yscale[0], port_priv->src_y + port_priv->src_h * 2); glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vbo_offset); glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vbo_offset + 6 * sizeof(GLfloat)); glamor_put_vbo_space(screen); /* Now draw our big triangle, clipped to each of the clip boxes. */ glamor_pixmap_loop(pixmap_priv, dst_box_index) { int dst_off_x, dst_off_y; glamor_set_destination_drawable(port_priv->pDraw, dst_box_index, FALSE, FALSE, glamor_priv->xv_prog.matrix_uniform, &dst_off_x, &dst_off_y); for (i = 0; i < nBox; i++) { int dstx, dsty, dstw, dsth; dstx = box[i].x1 + dst_off_x; dsty = box[i].y1 + dst_off_y; dstw = box[i].x2 - box[i].x1; dsth = box[i].y2 - box[i].y1; glScissor(dstx, dsty, dstw, dsth); glDrawArrays(GL_TRIANGLE_FAN, 0, 3); } } glDisable(GL_SCISSOR_TEST); glDisableVertexAttribArray(GLAMOR_VERTEX_POS); glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); DamageRegionProcessPending(port_priv->pDraw); glamor_xv_free_port_data(port_priv); glamor_pixmap_invalid(pixmap); } static int glamor_xv_render_dma(glamor_port_private *port_priv, int dma_fd) { ScreenPtr screen = port_priv->pPixmap->drawable.pScreen; glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); PixmapPtr pixmap = port_priv->pPixmap; glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); BoxPtr box = REGION_RECTS(&port_priv->clip); int nBox = REGION_NUM_RECTS(&port_priv->clip); GLfloat src_xscale, src_yscale; int i; GLfloat *v; char *vbo_offset; int dst_box_index; int hor_stride = port_priv->dma_hor_stride ? port_priv->dma_hor_stride : port_priv->w; int ver_stride = port_priv->dma_ver_stride ? port_priv->dma_ver_stride : port_priv->h; int width = hor_stride; uint32_t fourcc = port_priv->dma_drm_fourcc ? port_priv->dma_drm_fourcc : DRM_FORMAT_NV12; int afbc = port_priv->dma_drm_afbc; PFNEGLCREATEIMAGEKHRPROC create_image; PFNEGLDESTROYIMAGEKHRPROC destroy_image; PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d; EGLImageKHR image; GLuint texture; /* Mali is using NV15 for NV12_10 */ switch (fourcc) { case DRM_FORMAT_NV12_10: case DRM_FORMAT_NV15: fourcc = DRM_FORMAT_NV15; /* HACK: guess a width from 10B stride */ width = hor_stride / 10 * 8; if (afbc) { fourcc = DRM_FORMAT_YUV420_10BIT; hor_stride *= 1.5; } break; case DRM_FORMAT_NV12: if (afbc) { fourcc = DRM_FORMAT_YUV420_8BIT; hor_stride *= 1.5; } break; case DRM_FORMAT_NV16: if (afbc) { fourcc = DRM_FORMAT_YUYV; hor_stride *= 2; } break; default: ErrorF("glamor xv only support DMA for NV12|NV12_10|NV16\n"); return BadMatch; } const EGLint attrs[] = { EGL_WIDTH, width, EGL_HEIGHT, ver_stride, EGL_LINUX_DRM_FOURCC_EXT, fourcc, EGL_DMA_BUF_PLANE0_FD_EXT, dma_fd, EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, EGL_DMA_BUF_PLANE0_PITCH_EXT, hor_stride, EGL_DMA_BUF_PLANE1_FD_EXT, dma_fd, EGL_DMA_BUF_PLANE1_OFFSET_EXT, hor_stride * ver_stride, EGL_DMA_BUF_PLANE1_PITCH_EXT, hor_stride, EGL_YUV_COLOR_SPACE_HINT_EXT, EGL_ITU_REC601_EXT, EGL_SAMPLE_RANGE_HINT_EXT, EGL_YUV_NARROW_RANGE_EXT, EGL_NONE }; const EGLint attrs_afbc[] = { EGL_WIDTH, width, EGL_HEIGHT, ver_stride, EGL_LINUX_DRM_FOURCC_EXT, fourcc, EGL_DMA_BUF_PLANE0_FD_EXT, dma_fd, EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, EGL_DMA_BUF_PLANE0_PITCH_EXT, hor_stride, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, DRM_AFBC_MODIFIER & 0xFFFFFFFF, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, DRM_AFBC_MODIFIER >> 32, EGL_NONE }; create_image = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR"); destroy_image = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR"); image_target_texture_2d = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress("glEGLImageTargetTexture2DOES"); if (!create_image || !destroy_image || !image_target_texture_2d) { ErrorF("glamor xv without EGL_EXT_image_dma_buf_import\n"); return BadMatch; } glamor_make_current(glamor_priv); image = create_image(glamor_priv->ctx.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, afbc ? attrs_afbc : attrs); if (image == EGL_NO_IMAGE) { ErrorF("glamor xv failed to create egl image\n"); return BadMatch; } DamageRegionAppend(port_priv->pDraw, &port_priv->clip); if (!glamor_priv->xv_prog_ext.prog) glamor_init_xv_shader_egl_external(screen); /* TODO: support contrast/brightness/gamma/saturation/hue */ glamor_set_alu(screen, GXcopy); src_xscale = 1.0 / width; src_yscale = 1.0 / ver_stride; glUseProgram(glamor_priv->xv_prog_ext.prog); glGenTextures(1, &texture); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture); image_target_texture_2d(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glEnableVertexAttribArray(GLAMOR_VERTEX_POS); glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); glEnable(GL_SCISSOR_TEST); v = glamor_get_vbo_space(screen, 3 * 4 * sizeof(GLfloat), &vbo_offset); /* Set up a single primitive covering the area being drawn. We'll * clip it to port_priv->clip using GL scissors instead of just * emitting a GL_QUAD per box, because this way we hopefully avoid * diagonal tearing between the two triangles used to rasterize a * GL_QUAD. */ i = 0; v[i++] = port_priv->drw_x; v[i++] = port_priv->drw_y; v[i++] = port_priv->drw_x + port_priv->dst_w * 2; v[i++] = port_priv->drw_y; v[i++] = port_priv->drw_x; v[i++] = port_priv->drw_y + port_priv->dst_h * 2; v[i++] = t_from_x_coord_x(src_xscale, port_priv->src_x); v[i++] = t_from_x_coord_y(src_yscale, port_priv->src_y); v[i++] = t_from_x_coord_x(src_xscale, port_priv->src_x + port_priv->src_w * 2); v[i++] = t_from_x_coord_y(src_yscale, port_priv->src_y); v[i++] = t_from_x_coord_x(src_xscale, port_priv->src_x); v[i++] = t_from_x_coord_y(src_yscale, port_priv->src_y + port_priv->src_h * 2); glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vbo_offset); glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vbo_offset + 6 * sizeof(GLfloat)); glamor_put_vbo_space(screen); /* Now draw our big triangle, clipped to each of the clip boxes. */ glamor_pixmap_loop(pixmap_priv, dst_box_index) { int dst_off_x, dst_off_y; glamor_set_destination_drawable(port_priv->pDraw, dst_box_index, FALSE, FALSE, glamor_priv->xv_prog_ext.matrix_uniform, &dst_off_x, &dst_off_y); for (i = 0; i < nBox; i++) { int dstx, dsty, dstw, dsth; dstx = box[i].x1 + dst_off_x; dsty = box[i].y1 + dst_off_y; dstw = box[i].x2 - box[i].x1; dsth = box[i].y2 - box[i].y1; glScissor(dstx, dsty, dstw, dsth); glDrawArrays(GL_TRIANGLE_FAN, 0, 3); } } glDisable(GL_SCISSOR_TEST); glDisableVertexAttribArray(GLAMOR_VERTEX_POS); glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); DamageRegionProcessPending(port_priv->pDraw); glamor_xv_free_port_data(port_priv); glDeleteTextures(1, &texture); destroy_image(glamor_priv->ctx.display, image); glamor_pixmap_invalid(pixmap); return Success; } static int glamor_xv_put_dma_image(glamor_port_private *port_priv, DrawablePtr pDrawable, 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, short width, short height, Bool sync, RegionPtr clipBoxes) { ScreenPtr pScreen = pDrawable->pScreen; struct iovec iov; struct msghdr msg; struct cmsghdr *header; char buf[CMSG_SPACE (sizeof (int))]; int dma_fds[XV_MAX_DMA_FD], num_dma_fd; int ret = BadMatch; if (!port_priv->dma_client || port_priv->dma_socket_fd <= 0) return BadMatch; /* Only support NV12 for now */ if (id != FOURCC_NV12) return BadValue; iov.iov_base = buf; iov.iov_len = 1; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; num_dma_fd = 0; while (1) { msg.msg_control = buf; msg.msg_controllen = sizeof(buf); if (recvmsg(port_priv->dma_socket_fd, &msg, 0) < 0) break; /* End with a empty msg */ header = CMSG_FIRSTHDR(&msg); if (!header) break; for (; header != NULL; header = CMSG_NXTHDR(&msg, header)) { if (header->cmsg_level != SOL_SOCKET || header->cmsg_type != SCM_RIGHTS || header->cmsg_len != CMSG_LEN(sizeof(int))) break; dma_fds[num_dma_fd++] = *((int *)CMSG_DATA(header)); } } /* Expected 1 buffer for NV12 */ if (num_dma_fd != 1) goto out; if (pDrawable->type == DRAWABLE_WINDOW) port_priv->pPixmap = pScreen->GetWindowPixmap((WindowPtr) pDrawable); else port_priv->pPixmap = (PixmapPtr) pDrawable; 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; ret = glamor_xv_render_dma(port_priv, dma_fds[0]); if (ret == Success && sync) glamor_finish(pScreen); out: while (num_dma_fd--) close(dma_fds[num_dma_fd]); if (ret != Success) { ErrorF("glamor xv failed to render dma image\n"); glamor_xv_set_dma_client(port_priv, 0); } return ret; } int glamor_xv_put_image(glamor_port_private *port_priv, DrawablePtr pDrawable, 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) { ScreenPtr pScreen = pDrawable->pScreen; glamor_screen_private *glamor_priv = glamor_get_screen_private(pScreen); int srcPitch, srcPitch2; int top, nlines; int s2offset, s3offset, tmp; BoxRec full_box, half_box; if (glamor_xv_put_dma_image(port_priv, pDrawable, src_x, src_y, drw_x, drw_y, src_w, src_h, drw_w, drw_h, id, width, height, sync, clipBoxes) == Success) return Success; s2offset = s3offset = srcPitch2 = 0; if (!port_priv->src_pix[0] || (width != port_priv->src_pix_w || height != port_priv->src_pix_h) || (port_priv->src_pix[2] && id == FOURCC_NV12) || (!port_priv->src_pix[2] && id != FOURCC_NV12)) { int i; if (glamor_priv->xv_prog.prog) { glDeleteProgram(glamor_priv->xv_prog.prog); glamor_priv->xv_prog.prog = 0; } 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(pScreen, width, height, 8, GLAMOR_CREATE_FBO_NO_FBO); switch (id) { case FOURCC_YV12: case FOURCC_I420: port_priv->src_pix[1] = glamor_create_pixmap(pScreen, width >> 1, height >> 1, 8, GLAMOR_CREATE_FBO_NO_FBO); port_priv->src_pix[2] = glamor_create_pixmap(pScreen, width >> 1, height >> 1, 8, GLAMOR_CREATE_FBO_NO_FBO); if (!port_priv->src_pix[2]) return BadAlloc; break; case FOURCC_NV12: port_priv->src_pix[1] = glamor_create_pixmap(pScreen, width >> 1, height >> 1, 16, GLAMOR_CREATE_FBO_NO_FBO | GLAMOR_CREATE_FORMAT_CBCR); port_priv->src_pix[2] = NULL; break; default: return BadMatch; } port_priv->src_pix_w = width; port_priv->src_pix_h = height; if (!port_priv->src_pix[0] || !port_priv->src_pix[1]) return BadAlloc; } top = (src_y) & ~1; nlines = (src_y + src_h) - top; switch (id) { case FOURCC_YV12: case FOURCC_I420: srcPitch = ALIGN(width, 4); srcPitch2 = ALIGN(width >> 1, 4); 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; } full_box.x1 = 0; full_box.y1 = 0; full_box.x2 = width; full_box.y2 = nlines; half_box.x1 = 0; half_box.y1 = 0; half_box.x2 = width >> 1; half_box.y2 = (nlines + 1) >> 1; glamor_upload_boxes(port_priv->src_pix[0], &full_box, 1, 0, 0, 0, 0, buf + (top * srcPitch), srcPitch); glamor_upload_boxes(port_priv->src_pix[1], &half_box, 1, 0, 0, 0, 0, buf + s2offset, srcPitch2); glamor_upload_boxes(port_priv->src_pix[2], &half_box, 1, 0, 0, 0, 0, buf + s3offset, srcPitch2); break; case FOURCC_NV12: srcPitch = ALIGN(width, 4); s2offset = srcPitch * height; s2offset += ((top >> 1) * srcPitch); full_box.x1 = 0; full_box.y1 = 0; full_box.x2 = width; full_box.y2 = nlines; half_box.x1 = 0; half_box.y1 = 0; half_box.x2 = width; half_box.y2 = (nlines + 1) >> 1; glamor_upload_boxes(port_priv->src_pix[0], &full_box, 1, 0, 0, 0, 0, buf + (top * srcPitch), srcPitch); glamor_upload_boxes(port_priv->src_pix[1], &half_box, 1, 0, 0, 0, 0, buf + s2offset, srcPitch); break; default: return BadMatch; } if (pDrawable->type == DRAWABLE_WINDOW) port_priv->pPixmap = pScreen->GetWindowPixmap((WindowPtr) pDrawable); else port_priv->pPixmap = (PixmapPtr) pDrawable; RegionCopy(&port_priv->clip, clipBoxes); port_priv->src_x = src_x; port_priv->src_y = src_y - top; 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_xv_render(port_priv, id); return Success; } void glamor_xv_init_port(glamor_port_private *port_priv) { port_priv->brightness = 0; port_priv->contrast = 0; port_priv->saturation = 0; port_priv->hue = 0; port_priv->gamma = 1000; port_priv->transform_index = 0; port_priv->dma_client = 0; port_priv->dma_socket_fd = 0; port_priv->dma_hor_stride = 0; port_priv->dma_ver_stride = 0; port_priv->dma_drm_fourcc = 0; port_priv->dma_drm_afbc = 0; REGION_NULL(pScreen, &port_priv->clip); } void glamor_xv_core_init(ScreenPtr screen) { glamorBrightness = MAKE_ATOM("XV_BRIGHTNESS"); glamorContrast = MAKE_ATOM("XV_CONTRAST"); glamorSaturation = MAKE_ATOM("XV_SATURATION"); glamorHue = MAKE_ATOM("XV_HUE"); glamorGamma = MAKE_ATOM("XV_GAMMA"); glamorColorspace = MAKE_ATOM("XV_COLORSPACE"); glamorDmaClient = MAKE_ATOM("XV_DMA_CLIENT_ID"); glamorDmaHorStride = MAKE_ATOM("XV_DMA_HOR_STRIDE"); glamorDmaVerStride = MAKE_ATOM("XV_DMA_VER_STRIDE"); glamorDmaDrmFourcc = MAKE_ATOM("XV_DMA_DRM_FOURCC"); glamorDmaDrmAFBC = MAKE_ATOM("XV_DMA_DRM_AFBC"); }