If the pixmap is write-only, then use a pbo mapping will not get too much benefit. And even worse, when the software rendering is access this mapped data range, it's much slower than just using a system memory. From the glamor_prepare_access glamor_finish_access view, we have two options here: option 1: 1.0 create a pbo 1.1 copy texture to the pbo 1.2 map the pbo to va 1.3 access the va directly in software rendering. 1.4 bind the pbo as unpack buffer & draw it back to texture. option 2: 2.0 allocate a block memory in system memory space. 2.1 read the texture memory to the system memory. 2.2 access the system memory and do rendering. 2.3 draw the system memory back to texture. In general, 1.1 plush 1.2 is much faster than 2.1. And 1.3 is slower than 2.2. 1.4 is faster than 2.3. If the access mode is read only or read write, option 1 may be fater, but if the access mode is write only. Then most of the time option 1 is much faster. Signed-off-by: Zhigang Gong <zhigang.gong@linux.intel.com>
495 lines
13 KiB
C
495 lines
13 KiB
C
#ifdef HAVE_DIX_CONFIG_H
|
|
#include <dix-config.h>
|
|
#endif
|
|
|
|
#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_set_destination_pixmap_priv_nc(glamor_pixmap_private *pixmap_priv)
|
|
{
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, pixmap_priv->fb);
|
|
|
|
glViewport(0, 0,
|
|
pixmap_priv->container->drawable.width,
|
|
pixmap_priv->container->drawable.height);
|
|
}
|
|
|
|
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)
|
|
{
|
|
glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
|
|
|
|
return glamor_set_destination_pixmap_priv(pixmap_priv);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
glamor_set_alu(unsigned char alu)
|
|
{
|
|
if (alu == GXcopy) {
|
|
glDisable(GL_COLOR_LOGIC_OP);
|
|
return;
|
|
}
|
|
glEnable(GL_COLOR_LOGIC_OP);
|
|
switch (alu) {
|
|
case GXclear:
|
|
glLogicOp(GL_CLEAR);
|
|
break;
|
|
case GXand:
|
|
glLogicOp(GL_AND);
|
|
break;
|
|
case GXandReverse:
|
|
glLogicOp(GL_AND_REVERSE);
|
|
break;
|
|
case GXandInverted:
|
|
glLogicOp(GL_AND_INVERTED);
|
|
break;
|
|
case GXnoop:
|
|
glLogicOp(GL_NOOP);
|
|
break;
|
|
case GXxor:
|
|
glLogicOp(GL_XOR);
|
|
break;
|
|
case GXor:
|
|
glLogicOp(GL_OR);
|
|
break;
|
|
case GXnor:
|
|
glLogicOp(GL_NOR);
|
|
break;
|
|
case GXequiv:
|
|
glLogicOp(GL_EQUIV);
|
|
break;
|
|
case GXinvert:
|
|
glLogicOp(GL_INVERT);
|
|
break;
|
|
case GXorReverse:
|
|
glLogicOp(GL_OR_REVERSE);
|
|
break;
|
|
case GXcopyInverted:
|
|
glLogicOp(GL_COPY_INVERTED);
|
|
break;
|
|
case GXorInverted:
|
|
glLogicOp(GL_OR_INVERTED);
|
|
break;
|
|
case GXnand:
|
|
glLogicOp(GL_NAND);
|
|
break;
|
|
case GXset:
|
|
glLogicOp(GL_SET);
|
|
break;
|
|
default:
|
|
FatalError("unknown logic op\n");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Upload pixmap to a specified texture.
|
|
* This texture may not be the one attached to it.
|
|
**/
|
|
|
|
static void
|
|
__glamor_upload_pixmap_to_texture(PixmapPtr pixmap, GLenum format, GLenum type, GLuint tex)
|
|
{
|
|
glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
|
|
unsigned int stride, row_length;
|
|
void *texels;
|
|
GLenum iformat = GL_RGBA;
|
|
|
|
stride = pixmap->devKind;
|
|
row_length = (stride * 8) / pixmap->drawable.bitsPerPixel;
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
|
|
|
|
if (pixmap_priv->pbo && pixmap_priv->pbo_valid) {
|
|
texels = NULL;
|
|
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, pixmap_priv->pbo);
|
|
}
|
|
else
|
|
texels = pixmap->devPrivate.ptr;
|
|
|
|
glTexImage2D(GL_TEXTURE_2D,
|
|
0,
|
|
iformat,
|
|
pixmap->drawable.width,
|
|
pixmap->drawable.height, 0,
|
|
format, type, texels);
|
|
}
|
|
|
|
|
|
/*
|
|
* Load texture from the pixmap's data pointer and then
|
|
* draw the texture to the fbo, and flip the y axis.
|
|
* */
|
|
|
|
static void
|
|
_glamor_upload_pixmap_to_texture(PixmapPtr pixmap, GLenum format, GLenum type, int ax, int flip)
|
|
{
|
|
|
|
glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
|
|
glamor_screen_private *glamor_priv =
|
|
glamor_get_screen_private(pixmap->drawable.pScreen);
|
|
static float vertices[8] = {-1, -1,
|
|
1, -1,
|
|
1, 1,
|
|
-1, 1};
|
|
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;
|
|
|
|
GLuint tex;
|
|
int need_flip;
|
|
need_flip = (flip && !glamor_priv->yInverted);
|
|
|
|
/* Try fast path firstly, upload the pixmap to the texture attached
|
|
* to the fbo directly. */
|
|
|
|
if (ax == 0 && !need_flip) {
|
|
__glamor_upload_pixmap_to_texture(pixmap, format, type, pixmap_priv->tex);
|
|
return;
|
|
}
|
|
|
|
|
|
if (need_flip)
|
|
ptexcoords = texcoords;
|
|
else
|
|
ptexcoords = texcoords_inv;
|
|
|
|
/* Slow path, we need to flip y or wire alpha to 1. */
|
|
glVertexPointer(2, GL_FLOAT, sizeof(float) * 2, vertices);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(float) * 2, ptexcoords);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, pixmap_priv->fb);
|
|
glViewport(0, 0, pixmap->drawable.width, pixmap->drawable.height);
|
|
|
|
glGenTextures(1, &tex);
|
|
|
|
__glamor_upload_pixmap_to_texture(pixmap, format, type, tex);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glUseProgramObjectARB(glamor_priv->finish_access_prog[ax]);
|
|
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
glUseProgramObjectARB(0);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDeleteTextures(1, &tex);
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Prepare to upload a pixmap to texture memory.
|
|
* ax 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. ax != 0, we need to wire the alpha.
|
|
* */
|
|
static int
|
|
glamor_pixmap_upload_prepare(PixmapPtr pixmap, int ax)
|
|
{
|
|
int need_fbo;
|
|
glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
|
|
glamor_screen_private *glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen);
|
|
|
|
if (!glamor_check_fbo_width_height(pixmap->drawable.width , pixmap->drawable.height)
|
|
|| !glamor_check_fbo_depth(pixmap->drawable.depth)) {
|
|
glamor_fallback("upload failed reason: bad size or depth %d x %d @depth %d \n",
|
|
pixmap->drawable.width, pixmap->drawable.height, pixmap->drawable.depth);
|
|
return -1;
|
|
}
|
|
|
|
if (GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv))
|
|
return 0;
|
|
|
|
if (ax != 0 || !glamor_priv->yInverted)
|
|
need_fbo = 1;
|
|
else
|
|
need_fbo = 0;
|
|
|
|
if (pixmap_priv->tex == 0)
|
|
glGenTextures(1, &pixmap_priv->tex);
|
|
|
|
if (need_fbo) {
|
|
if (pixmap_priv->fb == 0)
|
|
glGenFramebuffersEXT(1, &pixmap_priv->fb);
|
|
glBindTexture(GL_TEXTURE_2D, pixmap_priv->tex);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pixmap->drawable.width,
|
|
pixmap->drawable.height, 0,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, pixmap_priv->fb);
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
|
|
GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_TEXTURE_2D,
|
|
pixmap_priv->tex,
|
|
0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum glamor_pixmap_status
|
|
glamor_upload_pixmap_to_texture(PixmapPtr pixmap)
|
|
{
|
|
GLenum format, type;
|
|
int ax;
|
|
|
|
if (glamor_get_tex_format_type_from_pixmap(pixmap,
|
|
&format,
|
|
&type,
|
|
&ax)) {
|
|
glamor_fallback("Unknown pixmap depth %d.\n", pixmap->drawable.depth);
|
|
return GLAMOR_UPLOAD_FAILED;
|
|
}
|
|
if (glamor_pixmap_upload_prepare(pixmap, ax))
|
|
return GLAMOR_UPLOAD_FAILED;
|
|
glamor_debug_output(GLAMOR_DEBUG_TEXTURE_DYNAMIC_UPLOAD,
|
|
"Uploading pixmap %p %dx%d depth%d.\n",
|
|
pixmap,
|
|
pixmap->drawable.width,
|
|
pixmap->drawable.height,
|
|
pixmap->drawable.depth);
|
|
_glamor_upload_pixmap_to_texture(pixmap, format, type, ax, 1);
|
|
return GLAMOR_UPLOAD_DONE;
|
|
}
|
|
|
|
#if 0
|
|
enum glamor_pixmap_status
|
|
glamor_upload_pixmap_to_texure_from_data(PixmapPtr pixmap, void *data)
|
|
{
|
|
enum glamor_pixmap_status upload_status;
|
|
glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
|
|
|
|
assert(pixmap_priv->pbo_valid == 0);
|
|
assert(pixmap->devPrivate.ptr == NULL);
|
|
pixmap->devPrivate.ptr = data;
|
|
upload_status = glamor_upload_pixmap_to_texture(pixmap);
|
|
pixmap->devPrivate.ptr = NULL;
|
|
return upload_status;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
glamor_restore_pixmap_to_texture(PixmapPtr pixmap)
|
|
{
|
|
GLenum format, type;
|
|
int ax;
|
|
|
|
if (glamor_get_tex_format_type_from_pixmap(pixmap,
|
|
&format,
|
|
&type,
|
|
&ax)) {
|
|
ErrorF("Unknown pixmap depth %d.\n", pixmap->drawable.depth);
|
|
assert(0);
|
|
}
|
|
_glamor_upload_pixmap_to_texture(pixmap, format, type, ax, 1);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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, row_length, y;
|
|
GLenum format, type, gl_access, gl_usage;
|
|
int ax;
|
|
uint8_t *data, *read;
|
|
glamor_screen_private *glamor_priv =
|
|
glamor_get_screen_private(pixmap->drawable.pScreen);
|
|
|
|
|
|
if (!GLAMOR_PIXMAP_PRIV_HAS_FBO(pixmap_priv))
|
|
return TRUE;
|
|
|
|
if (glamor_get_tex_format_type_from_pixmap(pixmap,
|
|
&format,
|
|
&type,
|
|
&ax)) {
|
|
ErrorF("Unknown pixmap depth %d.\n", pixmap->drawable.depth);
|
|
assert(0); // Should never happen.
|
|
return FALSE;
|
|
}
|
|
|
|
pixmap_priv->access_mode = access;
|
|
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;
|
|
|
|
switch (access) {
|
|
case GLAMOR_ACCESS_RO:
|
|
gl_access = GL_READ_ONLY_ARB;
|
|
gl_usage = GL_STREAM_READ_ARB;
|
|
break;
|
|
case GLAMOR_ACCESS_WO:
|
|
data = malloc(stride * pixmap->drawable.height);
|
|
goto done;
|
|
break;
|
|
case GLAMOR_ACCESS_RW:
|
|
gl_access = GL_READ_WRITE_ARB;
|
|
gl_usage = GL_DYNAMIC_DRAW_ARB;
|
|
break;
|
|
default:
|
|
ErrorF("Glamor: Invalid access code. %d\n", access);
|
|
assert(0);
|
|
}
|
|
|
|
row_length = (stride * 8) / pixmap->drawable.bitsPerPixel;
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, pixmap_priv->fb);
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
glPixelStorei(GL_PACK_ROW_LENGTH, row_length);
|
|
|
|
if (GLEW_MESA_pack_invert || glamor_priv->yInverted) {
|
|
|
|
if (!glamor_priv->yInverted)
|
|
glPixelStorei(GL_PACK_INVERT_MESA, 1);
|
|
if (pixmap_priv->pbo == 0)
|
|
glGenBuffersARB (1, &pixmap_priv->pbo);
|
|
glBindBufferARB (GL_PIXEL_PACK_BUFFER_EXT, pixmap_priv->pbo);
|
|
glBufferDataARB (GL_PIXEL_PACK_BUFFER_EXT,
|
|
stride * pixmap->drawable.height,
|
|
NULL, gl_usage);
|
|
glReadPixels (0, 0,
|
|
row_length, pixmap->drawable.height,
|
|
format, type, 0);
|
|
data = glMapBufferARB (GL_PIXEL_PACK_BUFFER_EXT, gl_access);
|
|
pixmap_priv->pbo_valid = TRUE;
|
|
|
|
if (!glamor_priv->yInverted)
|
|
glPixelStorei(GL_PACK_INVERT_MESA, 0);
|
|
glBindBufferARB (GL_PIXEL_PACK_BUFFER_EXT, 0);
|
|
} else {
|
|
data = malloc(stride * pixmap->drawable.height);
|
|
assert(data);
|
|
if (access != GLAMOR_ACCESS_WO) {
|
|
if (pixmap_priv->pbo == 0)
|
|
glGenBuffersARB(1, &pixmap_priv->pbo);
|
|
glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, pixmap_priv->pbo);
|
|
glBufferDataARB(GL_PIXEL_PACK_BUFFER_EXT,
|
|
stride * pixmap->drawable.height,
|
|
NULL, GL_STREAM_READ_ARB);
|
|
glReadPixels (0, 0, row_length, pixmap->drawable.height,
|
|
format, type, 0);
|
|
read = glMapBufferARB(GL_PIXEL_PACK_BUFFER_EXT, GL_READ_ONLY_ARB);
|
|
|
|
for (y = 0; y < pixmap->drawable.height; y++)
|
|
memcpy(data + y * stride,
|
|
read + (pixmap->drawable.height - y - 1) * stride, stride);
|
|
glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_EXT);
|
|
glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
|
|
pixmap_priv->pbo_valid = FALSE;
|
|
glDeleteBuffersARB(1, &pixmap_priv->pbo);
|
|
pixmap_priv->pbo = 0;
|
|
}
|
|
}
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
done:
|
|
pixmap->devPrivate.ptr = data;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
_glamor_destroy_upload_pixmap(PixmapPtr pixmap)
|
|
{
|
|
glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
|
|
|
|
assert(pixmap_priv->gl_fbo == 0);
|
|
if (pixmap_priv->fb)
|
|
glDeleteFramebuffersEXT(1, &pixmap_priv->fb);
|
|
if (pixmap_priv->tex)
|
|
glDeleteTextures(1, &pixmap_priv->tex);
|
|
if (pixmap_priv->pbo)
|
|
glDeleteBuffersARB(1, &pixmap_priv->pbo);
|
|
pixmap_priv->fb = pixmap_priv->tex = pixmap_priv->pbo = 0;
|
|
|
|
}
|
|
|
|
void glamor_destroy_upload_pixmap(PixmapPtr pixmap)
|
|
{
|
|
_glamor_destroy_upload_pixmap(pixmap);
|
|
}
|
|
|