xserver-multidpi/glamor/glamor_program.c

701 lines
23 KiB
C

/*
* Copyright © 2014 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 the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS 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"
#include "glamor_transform.h"
#include "glamor_program.h"
static Bool
use_solid(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg)
{
return glamor_set_solid(pixmap, gc, TRUE, prog->fg_uniform);
}
const glamor_facet glamor_fill_solid = {
.name = "solid",
.fs_exec = " gl_FragColor = fg;\n",
.locations = glamor_program_location_fg,
.use = use_solid,
};
static Bool
use_tile(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg)
{
return glamor_set_tiled(pixmap, gc, prog->fill_offset_uniform, prog->fill_size_inv_uniform);
}
static const glamor_facet glamor_fill_tile = {
.name = "tile",
.vs_exec = " fill_pos = (fill_offset + primitive.xy + pos) * fill_size_inv;\n",
.fs_exec = " gl_FragColor = texture2D(sampler, fill_pos);\n",
.locations = glamor_program_location_fillsamp | glamor_program_location_fillpos,
.use = use_tile,
};
static Bool
use_stipple(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg)
{
return glamor_set_stippled(pixmap, gc, prog->fg_uniform,
prog->fill_offset_uniform,
prog->fill_size_inv_uniform);
}
static const glamor_facet glamor_fill_stipple = {
.name = "stipple",
.vs_exec = " fill_pos = (fill_offset + primitive.xy + pos) * fill_size_inv;\n",
.fs_exec = (" float a = texture2D(sampler, fill_pos).w;\n"
" if (a == 0.0)\n"
" discard;\n"
" gl_FragColor = fg;\n"),
.locations = glamor_program_location_fg | glamor_program_location_fillsamp | glamor_program_location_fillpos,
.use = use_stipple,
};
static Bool
use_opaque_stipple(PixmapPtr pixmap, GCPtr gc, glamor_program *prog, void *arg)
{
if (!use_stipple(pixmap, gc, prog, arg))
return FALSE;
glamor_set_color(pixmap, gc->bgPixel, prog->bg_uniform);
return TRUE;
}
static const glamor_facet glamor_fill_opaque_stipple = {
.name = "opaque_stipple",
.vs_exec = " fill_pos = (fill_offset + primitive.xy + pos) * fill_size_inv;\n",
.fs_exec = (" float a = texture2D(sampler, fill_pos).w;\n"
" if (a == 0.0)\n"
" gl_FragColor = bg;\n"
" else\n"
" gl_FragColor = fg;\n"),
.locations = glamor_program_location_fg | glamor_program_location_bg | glamor_program_location_fillsamp | glamor_program_location_fillpos,
.use = use_opaque_stipple
};
static const glamor_facet *glamor_facet_fill[4] = {
&glamor_fill_solid,
&glamor_fill_tile,
&glamor_fill_stipple,
&glamor_fill_opaque_stipple,
};
typedef struct {
glamor_program_location location;
const char *vs_vars;
const char *fs_vars;
} glamor_location_var;
static glamor_location_var location_vars[] = {
{
.location = glamor_program_location_fg,
.fs_vars = "uniform vec4 fg;\n"
},
{
.location = glamor_program_location_bg,
.fs_vars = "uniform vec4 bg;\n"
},
{
.location = glamor_program_location_fillsamp,
.fs_vars = "uniform sampler2D sampler;\n"
},
{
.location = glamor_program_location_fillpos,
.vs_vars = ("uniform vec2 fill_offset;\n"
"uniform vec2 fill_size_inv;\n"
"varying vec2 fill_pos;\n"),
.fs_vars = ("varying vec2 fill_pos;\n")
},
{
.location = glamor_program_location_font,
.fs_vars = "uniform usampler2D font;\n",
},
{
.location = glamor_program_location_bitplane,
.fs_vars = ("uniform uvec4 bitplane;\n"
"uniform vec4 bitmul;\n"),
},
{
.location = glamor_program_location_dash,
.vs_vars = "uniform float dash_length;\n",
.fs_vars = "uniform sampler2D dash;\n",
},
{
.location = glamor_program_location_atlas,
.fs_vars = "uniform sampler2D atlas;\n",
},
};
static char *
add_var(char *cur, const char *add)
{
char *new;
if (!add)
return cur;
new = realloc(cur, strlen(cur) + strlen(add) + 1);
if (!new) {
free(cur);
return NULL;
}
strcat(new, add);
return new;
}
static char *
vs_location_vars(glamor_program_location locations)
{
int l;
char *vars = strdup("");
for (l = 0; vars && l < ARRAY_SIZE(location_vars); l++)
if (locations & location_vars[l].location)
vars = add_var(vars, location_vars[l].vs_vars);
return vars;
}
static char *
fs_location_vars(glamor_program_location locations)
{
int l;
char *vars = strdup("");
for (l = 0; vars && l < ARRAY_SIZE(location_vars); l++)
if (locations & location_vars[l].location)
vars = add_var(vars, location_vars[l].fs_vars);
return vars;
}
static const char vs_template[] =
"%s" /* version */
"%s" /* exts */
"%s" /* defines */
"%s" /* prim vs_vars */
"%s" /* fill vs_vars */
"%s" /* location vs_vars */
GLAMOR_DECLARE_MATRIX
"void main() {\n"
"%s" /* prim vs_exec, outputs 'pos' and gl_Position */
"%s" /* fill vs_exec */
"}\n";
static const char fs_template[] =
"%s" /* version */
"%s" /* exts */
GLAMOR_DEFAULT_PRECISION
"%s" /* defines */
"%s" /* prim fs_vars */
"%s" /* fill fs_vars */
"%s" /* location fs_vars */
"void main() {\n"
"%s" /* prim fs_exec */
"%s" /* fill fs_exec */
"%s" /* combine */
"}\n";
static const char *
str(const char *s)
{
if (!s)
return "";
return s;
}
static const glamor_facet facet_null_fill = {
.name = ""
};
#define DBG 0
static GLint
glamor_get_uniform(glamor_program *prog,
glamor_program_location location,
const char *name)
{
GLint uniform;
if (location && (prog->locations & location) == 0)
return -2;
uniform = glGetUniformLocation(prog->prog, name);
#if DBG
ErrorF("%s uniform %d\n", name, uniform);
#endif
return uniform;
}
Bool
glamor_build_program(ScreenPtr screen,
glamor_program *prog,
const glamor_facet *prim,
const glamor_facet *fill,
const char *combine,
const char *defines)
{
glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
glamor_program_location locations = prim->locations;
glamor_program_flag flags = prim->flags;
int version = prim->version;
char *version_string = NULL;
char *fs_vars = NULL;
char *vs_vars = NULL;
char *vs_prog_string;
char *fs_prog_string;
GLint fs_prog, vs_prog;
Bool gpu_shader4 = FALSE;
if (!fill)
fill = &facet_null_fill;
locations |= fill->locations;
flags |= fill->flags;
version = MAX(version, fill->version);
if (version > glamor_priv->glsl_version) {
if (version == 130 && !glamor_priv->use_gpu_shader4)
goto fail;
else {
version = 120;
gpu_shader4 = TRUE;
}
}
vs_vars = vs_location_vars(locations);
fs_vars = fs_location_vars(locations);
if (!vs_vars)
goto fail;
if (!fs_vars)
goto fail;
if (version) {
if (asprintf(&version_string, "#version %d\n", version) < 0)
version_string = NULL;
if (!version_string)
goto fail;
}
if (asprintf(&vs_prog_string,
vs_template,
str(version_string),
gpu_shader4 ? "#extension GL_EXT_gpu_shader4 : require\n" : "",
str(defines),
str(prim->vs_vars),
str(fill->vs_vars),
vs_vars,
str(prim->vs_exec),
str(fill->vs_exec)) < 0)
vs_prog_string = NULL;
if (asprintf(&fs_prog_string,
fs_template,
str(version_string),
gpu_shader4 ? "#extension GL_EXT_gpu_shader4 : require\n#define texelFetch texelFetch2D\n#define uint unsigned int\n" : "",
str(defines),
str(prim->fs_vars),
str(fill->fs_vars),
fs_vars,
str(prim->fs_exec),
str(fill->fs_exec),
str(combine)) < 0)
fs_prog_string = NULL;
if (!vs_prog_string || !fs_prog_string)
goto fail;
prog->prog = glCreateProgram();
#if DBG
ErrorF("\n\tProgram %d for %s %s\n\tVertex shader:\n\n\t================\n%s\n\n\tFragment Shader:\n\n%s\t================\n",
prog->prog, prim->name, fill->name, vs_prog_string, fs_prog_string);
#endif
prog->flags = flags;
prog->locations = locations;
prog->prim_use = prim->use;
prog->prim_use_render = prim->use_render;
prog->fill_use = fill->use;
prog->fill_use_render = fill->use_render;
vs_prog = glamor_compile_glsl_prog(GL_VERTEX_SHADER, vs_prog_string);
fs_prog = glamor_compile_glsl_prog(GL_FRAGMENT_SHADER, fs_prog_string);
free(vs_prog_string);
free(fs_prog_string);
glAttachShader(prog->prog, vs_prog);
glDeleteShader(vs_prog);
glAttachShader(prog->prog, fs_prog);
glDeleteShader(fs_prog);
glBindAttribLocation(prog->prog, GLAMOR_VERTEX_POS, "primitive");
if (prim->source_name) {
#if DBG
ErrorF("Bind GLAMOR_VERTEX_SOURCE to %s\n", prim->source_name);
#endif
glBindAttribLocation(prog->prog, GLAMOR_VERTEX_SOURCE, prim->source_name);
}
if (prog->alpha == glamor_program_alpha_dual_blend) {
glBindFragDataLocationIndexed(prog->prog, 0, 0, "color0");
glBindFragDataLocationIndexed(prog->prog, 0, 1, "color1");
}
glamor_link_glsl_prog(screen, prog->prog, "%s_%s", prim->name, fill->name);
prog->matrix_uniform = glamor_get_uniform(prog, glamor_program_location_none, "v_matrix");
prog->fg_uniform = glamor_get_uniform(prog, glamor_program_location_fg, "fg");
prog->bg_uniform = glamor_get_uniform(prog, glamor_program_location_bg, "bg");
prog->fill_offset_uniform = glamor_get_uniform(prog, glamor_program_location_fillpos, "fill_offset");
prog->fill_size_inv_uniform = glamor_get_uniform(prog, glamor_program_location_fillpos, "fill_size_inv");
prog->font_uniform = glamor_get_uniform(prog, glamor_program_location_font, "font");
prog->bitplane_uniform = glamor_get_uniform(prog, glamor_program_location_bitplane, "bitplane");
prog->bitmul_uniform = glamor_get_uniform(prog, glamor_program_location_bitplane, "bitmul");
prog->dash_uniform = glamor_get_uniform(prog, glamor_program_location_dash, "dash");
prog->dash_length_uniform = glamor_get_uniform(prog, glamor_program_location_dash, "dash_length");
prog->atlas_uniform = glamor_get_uniform(prog, glamor_program_location_atlas, "atlas");
free(version_string);
free(fs_vars);
free(vs_vars);
return TRUE;
fail:
prog->failed = 1;
if (prog->prog) {
glDeleteProgram(prog->prog);
prog->prog = 0;
}
free(version_string);
free(fs_vars);
free(vs_vars);
return FALSE;
}
Bool
glamor_use_program(PixmapPtr pixmap,
GCPtr gc,
glamor_program *prog,
void *arg)
{
glUseProgram(prog->prog);
if (prog->prim_use && !prog->prim_use(pixmap, gc, prog, arg))
return FALSE;
if (prog->fill_use && !prog->fill_use(pixmap, gc, prog, arg))
return FALSE;
return TRUE;
}
glamor_program *
glamor_use_program_fill(PixmapPtr pixmap,
GCPtr gc,
glamor_program_fill *program_fill,
const glamor_facet *prim)
{
ScreenPtr screen = pixmap->drawable.pScreen;
glamor_program *prog = &program_fill->progs[gc->fillStyle];
int fill_style = gc->fillStyle;
const glamor_facet *fill;
if (prog->failed)
return FALSE;
if (!prog->prog) {
fill = glamor_facet_fill[fill_style];
if (!fill)
return NULL;
if (!glamor_build_program(screen, prog, prim, fill, NULL, NULL))
return NULL;
}
if (!glamor_use_program(pixmap, gc, prog, NULL))
return NULL;
return prog;
}
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},
};
static void
glamor_set_blend(CARD8 op, glamor_program_alpha alpha, PicturePtr dst)
{
glamor_screen_private *glamor_priv = glamor_get_screen_private(dst->pDrawable->pScreen);
GLenum src_blend, dst_blend;
struct blendinfo *op_info;
switch (alpha) {
case glamor_program_alpha_ca_first:
op = PictOpOutReverse;
break;
case glamor_program_alpha_ca_second:
op = PictOpAdd;
break;
default:
break;
}
if (!glamor_priv->is_gles)
glDisable(GL_COLOR_LOGIC_OP);
if (op == PictOpSrc)
return;
op_info = &composite_op_info[op];
src_blend = op_info->source_blend;
dst_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(dst->format) == 0 && op_info->dest_alpha) {
if (src_blend == GL_DST_ALPHA)
src_blend = GL_ONE;
else if (src_blend == GL_ONE_MINUS_DST_ALPHA)
src_blend = GL_ZERO;
}
/* Set up the source alpha value for blending in component alpha mode. */
if (alpha == glamor_program_alpha_dual_blend) {
switch (dst_blend) {
case GL_SRC_ALPHA:
dst_blend = GL_SRC1_COLOR;
break;
case GL_ONE_MINUS_SRC_ALPHA:
dst_blend = GL_ONE_MINUS_SRC1_COLOR;
break;
}
} else if (alpha != glamor_program_alpha_normal) {
switch (dst_blend) {
case GL_SRC_ALPHA:
dst_blend = GL_SRC_COLOR;
break;
case GL_ONE_MINUS_SRC_ALPHA:
dst_blend = GL_ONE_MINUS_SRC_COLOR;
break;
}
}
glEnable(GL_BLEND);
glBlendFunc(src_blend, dst_blend);
}
static Bool
use_source_solid(CARD8 op, PicturePtr src, PicturePtr dst, glamor_program *prog)
{
PictSolidFill *solid = &src->pSourcePict->solidFill;
float color[4];
glamor_get_rgba_from_color(&solid->fullcolor, color);
glamor_set_blend(op, prog->alpha, dst);
glUniform4fv(prog->fg_uniform, 1, color);
return TRUE;
}
static const glamor_facet glamor_source_solid = {
.name = "render_solid",
.fs_exec = " vec4 source = fg;\n",
.locations = glamor_program_location_fg,
.use_render = use_source_solid,
};
static Bool
use_source_picture(CARD8 op, PicturePtr src, PicturePtr dst, glamor_program *prog)
{
glamor_set_blend(op, prog->alpha, dst);
return glamor_set_texture((PixmapPtr) src->pDrawable,
glamor_picture_red_is_alpha(dst),
0, 0,
prog->fill_offset_uniform,
prog->fill_size_inv_uniform);
}
static const glamor_facet glamor_source_picture = {
.name = "render_picture",
.vs_exec = " fill_pos = (fill_offset + primitive.xy + pos) * fill_size_inv;\n",
.fs_exec = " vec4 source = texture2D(sampler, fill_pos);\n",
.locations = glamor_program_location_fillsamp | glamor_program_location_fillpos,
.use_render = use_source_picture,
};
static Bool
use_source_1x1_picture(CARD8 op, PicturePtr src, PicturePtr dst, glamor_program *prog)
{
glamor_set_blend(op, prog->alpha, dst);
return glamor_set_texture_pixmap((PixmapPtr) src->pDrawable,
glamor_picture_red_is_alpha(dst));
}
static const glamor_facet glamor_source_1x1_picture = {
.name = "render_picture",
.fs_exec = " vec4 source = texture2D(sampler, vec2(0.5));\n",
.locations = glamor_program_location_fillsamp,
.use_render = use_source_1x1_picture,
};
static const glamor_facet *glamor_facet_source[glamor_program_source_count] = {
[glamor_program_source_solid] = &glamor_source_solid,
[glamor_program_source_picture] = &glamor_source_picture,
[glamor_program_source_1x1_picture] = &glamor_source_1x1_picture,
};
static const char *glamor_combine[] = {
[glamor_program_alpha_normal] = " gl_FragColor = source * mask.a;\n",
[glamor_program_alpha_ca_first] = " gl_FragColor = source.a * mask;\n",
[glamor_program_alpha_ca_second] = " gl_FragColor = source * mask;\n",
[glamor_program_alpha_dual_blend] = " color0 = source * mask;\n"
" color1 = source.a * mask;\n"
};
static Bool
glamor_setup_one_program_render(ScreenPtr screen,
glamor_program *prog,
glamor_program_source source_type,
glamor_program_alpha alpha,
const glamor_facet *prim,
const char *defines)
{
if (prog->failed)
return FALSE;
if (!prog->prog) {
const glamor_facet *fill = glamor_facet_source[source_type];
if (!fill)
return FALSE;
prog->alpha = alpha;
if (!glamor_build_program(screen, prog, prim, fill, glamor_combine[alpha], defines))
return FALSE;
}
return TRUE;
}
glamor_program *
glamor_setup_program_render(CARD8 op,
PicturePtr src,
PicturePtr mask,
PicturePtr dst,
glamor_program_render *program_render,
const glamor_facet *prim,
const char *defines)
{
ScreenPtr screen = dst->pDrawable->pScreen;
glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
glamor_program_alpha alpha;
glamor_program_source source_type;
glamor_program *prog;
if (op > ARRAY_SIZE(composite_op_info))
return NULL;
if (glamor_is_component_alpha(mask)) {
if (glamor_priv->has_dual_blend) {
alpha = glamor_program_alpha_dual_blend;
} else {
/* This only works for PictOpOver */
if (op != PictOpOver)
return NULL;
alpha = glamor_program_alpha_ca_first;
}
} else
alpha = glamor_program_alpha_normal;
if (src->pDrawable) {
/* Can't do transforms, alphamaps or sourcing from non-pixmaps yet */
if (src->transform || src->alphaMap || src->pDrawable->type != DRAWABLE_PIXMAP)
return NULL;
if (src->pDrawable->width == 1 && src->pDrawable->height == 1 && src->repeat)
source_type = glamor_program_source_1x1_picture;
else
source_type = glamor_program_source_picture;
} else {
SourcePictPtr sp = src->pSourcePict;
if (!sp)
return NULL;
switch (sp->type) {
case SourcePictTypeSolidFill:
source_type = glamor_program_source_solid;
break;
default:
return NULL;
}
}
prog = &program_render->progs[source_type][alpha];
if (!glamor_setup_one_program_render(screen, prog, source_type, alpha, prim, defines))
return NULL;
if (alpha == glamor_program_alpha_ca_first) {
/* Make sure we can also build the second program before
* deciding to use this path.
*/
if (!glamor_setup_one_program_render(screen,
&program_render->progs[source_type][glamor_program_alpha_ca_second],
source_type, glamor_program_alpha_ca_second, prim,
defines))
return NULL;
}
return prog;
}
Bool
glamor_use_program_render(glamor_program *prog,
CARD8 op,
PicturePtr src,
PicturePtr dst)
{
glUseProgram(prog->prog);
if (prog->prim_use_render && !prog->prim_use_render(op, src, dst, prog))
return FALSE;
if (prog->fill_use_render && !prog->fill_use_render(op, src, dst, prog))
return FALSE;
return TRUE;
}