411 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			411 lines
		
	
	
		
			13 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_uniform);
 | |
| }
 | |
| 
 | |
| static const glamor_facet glamor_fill_tile = {
 | |
|     .name = "tile",
 | |
|     .vs_exec =  "       fill_pos = (fill_offset + primitive.xy + pos) / fill_size;\n",
 | |
|     .fs_exec =  "       gl_FragColor = texture2D(sampler, fill_pos);\n",
 | |
|     .locations = glamor_program_location_fill,
 | |
|     .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_uniform);
 | |
| }
 | |
| 
 | |
| static const glamor_facet glamor_fill_stipple = {
 | |
|     .name = "stipple",
 | |
|     .vs_exec =  "       fill_pos = (fill_offset + primitive.xy + pos) / fill_size;\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_fill,
 | |
|     .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;\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_fill,
 | |
|     .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_fill,
 | |
|         .vs_vars = ("uniform vec2 fill_offset;\n"
 | |
|                     "uniform vec2 fill_size;\n"
 | |
|                     "varying vec2 fill_pos;\n"),
 | |
|         .fs_vars = ("uniform sampler2D sampler;\n"
 | |
|                     "uniform vec2 fill_size;\n"
 | |
|                     "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",
 | |
|     },
 | |
| };
 | |
| 
 | |
| #define NUM_LOCATION_VARS       (sizeof location_vars / sizeof location_vars[0])
 | |
| 
 | |
| 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 < NUM_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 < NUM_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"                                /* 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 */
 | |
|     GLAMOR_DEFAULT_PRECISION
 | |
|     "%s"                                /* prim fs_vars */
 | |
|     "%s"                                /* fill fs_vars */
 | |
|     "%s"                                /* location fs_vars */
 | |
|     "void main() {\n"
 | |
|     "%s"                                /* prim fs_exec */
 | |
|     "%s"                                /* fill fs_exec */
 | |
|     "}\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)
 | |
| {
 | |
|     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;
 | |
| 
 | |
|     if (!fill)
 | |
|         fill = &facet_null_fill;
 | |
| 
 | |
|     locations |= fill->locations;
 | |
|     flags |= fill->flags;
 | |
|     version = MAX(version, fill->version);
 | |
| 
 | |
|     if (version > glamor_priv->glsl_version)
 | |
|         goto fail;
 | |
| 
 | |
|     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),
 | |
|                  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),
 | |
|                  str(prim->fs_vars),
 | |
|                  str(fill->fs_vars),
 | |
|                  fs_vars,
 | |
|                  str(prim->fs_exec),
 | |
|                  str(fill->fs_exec)) < 0)
 | |
|         fs_prog_string = NULL;
 | |
| 
 | |
|     if (!vs_prog_string || !fs_prog_string)
 | |
|         goto fail;
 | |
| 
 | |
| #if DBG
 | |
|     ErrorF("\nPrograms for %s %s\nVertex shader:\n\n%s\n\nFragment Shader:\n\n%s",
 | |
|            prim->name, fill->name, vs_prog_string, fs_prog_string);
 | |
| #endif
 | |
| 
 | |
|     prog->prog = glCreateProgram();
 | |
|     prog->flags = flags;
 | |
|     prog->locations = locations;
 | |
|     prog->prim_use = prim->use;
 | |
|     prog->fill_use = fill->use;
 | |
| 
 | |
|     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);
 | |
|     }
 | |
| 
 | |
|     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_fill, "fill_offset");
 | |
|     prog->fill_size_uniform = glamor_get_uniform(prog, glamor_program_location_fill, "fill_size");
 | |
|     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");
 | |
| 
 | |
|     if (glGetError() != GL_NO_ERROR)
 | |
|         goto fail;
 | |
| 
 | |
|     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))
 | |
|             return NULL;
 | |
|     }
 | |
| 
 | |
|     if (!glamor_use_program(pixmap, gc, prog, NULL))
 | |
|         return NULL;
 | |
| 
 | |
|     return prog;
 | |
| }
 |