[gl] Use GLSL when available for glyph rendering.

This reduces the CPU work in translating fixed function state to
shaders, but currently is a slight cost on GM45 because we end up
changing shaders more frequently since other parts of the pipeline are
doing fixed function still.
This commit is contained in:
Eric Anholt 2010-02-02 19:23:17 -08:00
parent 672973caa0
commit f475351f75
4 changed files with 239 additions and 35 deletions

View file

@ -1,6 +1,7 @@
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Chris Wilson
* Copyright © 2010 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@ -42,13 +43,6 @@
#define GLYPH_CACHE_MIN_SIZE 4
#define GLYPH_CACHE_MAX_SIZE 128
typedef enum _cairo_gl_glyphs_shader {
CAIRO_GL_GLYPHS_SHADER_UNSET,
CAIRO_GL_GLYPHS_SHADER_NORMAL,
CAIRO_GL_GLYPHS_SHADER_CA_SOURCE_ALPHA,
CAIRO_GL_GLYPHS_SHADER_CA_SOURCE,
} cairo_gl_glyphs_shader_t;
typedef struct _cairo_gl_glyph_private {
cairo_rtree_node_t node;
cairo_gl_glyph_cache_t *cache;
@ -142,6 +136,128 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_gl_glyphs_setup_shaders (cairo_gl_context_t *ctx)
{
static const char *vs_source_constant =
"varying vec2 mask_texcoords;\n"
"void main()\n"
"{\n"
" gl_Position = ftransform();\n"
" mask_texcoords = gl_MultiTexCoord1.xy;\n"
"}\n";
static const char *vs_source_texture =
"varying vec2 source_texcoords;\n"
"varying vec2 mask_texcoords;\n"
"void main()\n"
"{\n"
" gl_Position = ftransform();\n"
" source_texcoords = gl_MultiTexCoord0.xy;\n"
" mask_texcoords = gl_MultiTexCoord1.xy;\n"
"}\n";
static const char *glyphs_source_constant =
"uniform vec4 constant_source;\n"
"vec4 get_source()\n"
"{\n"
" return constant_source;\n"
"}\n";
static const char *glyphs_source_texture =
"uniform sampler2D source_sampler;\n"
"varying vec2 source_texcoords;\n"
"vec4 get_source()\n"
"{\n"
" return texture2D(source_sampler, source_texcoords);\n"
"}\n";
static const char *glyphs_source_texture_alpha =
"uniform sampler2D source_sampler;\n"
"varying vec2 source_texcoords;\n"
"vec4 get_source()\n"
"{\n"
" return vec4(0, 0, 0, texture2D(source_sampler, source_texcoords).a);\n"
"}\n";
static const char *glyphs_in_normal =
"uniform sampler2D mask_sampler;\n"
"varying vec2 mask_texcoords;\n"
"void main()\n"
"{\n"
" vec4 mask = texture2D(mask_sampler, mask_texcoords);\n"
" gl_FragColor = get_source() * mask.a;\n"
"}\n";
static const char *glyphs_in_component_alpha_source =
"uniform sampler2D mask_sampler;\n"
"varying vec2 mask_texcoords;\n"
"void main()\n"
"{\n"
" vec4 mask = texture2D(mask_sampler, mask_texcoords);\n"
" gl_FragColor = get_source() * mask;\n"
"}\n";
static const char *glyphs_in_component_alpha_alpha =
"uniform sampler2D mask_sampler;\n"
"varying vec2 mask_texcoords;\n"
"void main()\n"
"{\n"
" vec4 mask = texture2D(mask_sampler, mask_texcoords);\n"
" gl_FragColor = get_source().a * mask;\n"
"}\n";
const char *glyphs_source_sources[CAIRO_GL_GLYPHS_SOURCE_COUNT] = {
glyphs_source_constant,
glyphs_source_texture,
glyphs_source_texture_alpha,
};
const char *glyphs_in_sources[CAIRO_GL_GLYPHS_SHADER_COUNT] = {
glyphs_in_normal,
glyphs_in_component_alpha_source,
glyphs_in_component_alpha_alpha,
};
enum cairo_gl_glyphs_shader_source source;
enum cairo_gl_glyphs_shader in;
for (source = 0; source < CAIRO_GL_GLYPHS_SOURCE_COUNT; source++) {
for (in = 0; in < CAIRO_GL_GLYPHS_SHADER_COUNT; in++) {
const char *source_source = glyphs_source_sources[source];
const char *in_source = glyphs_in_sources[in];
char *fs_source = _cairo_malloc (strlen(source_source) +
strlen(in_source) +
1);
const char *vs_source;
cairo_status_t status;
cairo_gl_shader_program_t *program = &ctx->glyphs_shaders[source][in];
if (source == CAIRO_GL_GLYPHS_SOURCE_CONSTANT)
vs_source = vs_source_constant;
else
vs_source = vs_source_texture;
if (!fs_source)
return CAIRO_STATUS_NO_MEMORY;
sprintf(fs_source, "%s%s", source_source, in_source);
init_shader_program (program);
status = create_shader_program (program,
vs_source,
fs_source);
free (fs_source);
if (_cairo_status_is_error (status))
return status;
_cairo_gl_use_program (program);
bind_texture_to_shader (program->program, "mask_sampler", 1);
if (source == CAIRO_GL_GLYPHS_SOURCE_CONSTANT) {
vs_source = vs_source_constant;
} else {
bind_texture_to_shader (program->program, "source_sampler", 0);
vs_source = vs_source_texture;
}
}
}
_cairo_gl_use_program (NULL);
return CAIRO_STATUS_SUCCESS;
}
static cairo_gl_glyph_private_t *
_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache,
cairo_scaled_glyph_t *scaled_glyph)
@ -254,9 +370,9 @@ typedef struct _cairo_gl_glyphs_setup
} cairo_gl_glyphs_setup_t;
static void
_cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx,
cairo_gl_glyphs_setup_t *setup,
cairo_gl_glyphs_shader_t shader)
_cairo_gl_glyphs_set_shader_fixed (cairo_gl_context_t *ctx,
cairo_gl_glyphs_setup_t *setup,
cairo_gl_glyphs_shader_t shader)
{
if (setup->shader == shader)
return;
@ -296,6 +412,46 @@ _cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx,
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
}
static void
_cairo_gl_glyphs_set_shader_glsl (cairo_gl_context_t *ctx,
cairo_gl_glyphs_setup_t *setup,
cairo_gl_glyphs_shader_t shader)
{
enum cairo_gl_glyphs_shader_source glyphs_source;
if (setup->shader == shader)
return;
if (setup->composite->src.type == OPERAND_CONSTANT) {
glyphs_source = CAIRO_GL_GLYPHS_SOURCE_CONSTANT;
} else {
if (setup->composite->src.operand.texture.surface->base.content !=
CAIRO_CONTENT_ALPHA)
{
glyphs_source = CAIRO_GL_GLYPHS_SOURCE_TEXTURE;
} else {
glyphs_source = CAIRO_GL_GLYPHS_SOURCE_TEXTURE_ALPHA;
}
}
setup->composite->shader = &ctx->glyphs_shaders[glyphs_source][shader];
_cairo_gl_use_program (setup->composite->shader);
_cairo_gl_set_src_operand (ctx, setup->composite);
setup->shader = shader;
}
static void
_cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx,
cairo_gl_glyphs_setup_t *setup,
cairo_gl_glyphs_shader_t shader)
{
if (ctx->using_glsl_glyphs)
_cairo_gl_glyphs_set_shader_glsl (ctx, setup, shader);
else
_cairo_gl_glyphs_set_shader_fixed (ctx, setup, shader);
}
static void
_cairo_gl_glyphs_draw (cairo_gl_context_t *ctx,
cairo_gl_glyphs_setup_t *setup)
@ -440,6 +596,8 @@ _render_glyphs (cairo_gl_surface_t *dst,
*has_component_alpha = FALSE;
memset (&composite_setup, 0, sizeof(composite_setup));
status = _cairo_gl_operand_init (&composite_setup.src, source, dst,
glyph_extents->x, glyph_extents->y,
dst_x, dst_y,
@ -452,6 +610,13 @@ _render_glyphs (cairo_gl_surface_t *dst,
if (unlikely (status))
return status;
if (ctx->using_glsl && !ctx->glsl_glyphs_inited) {
cairo_status_t status = _cairo_gl_glyphs_setup_shaders (ctx);
if (!_cairo_status_is_error (status))
ctx->using_glsl_glyphs = TRUE;
ctx->glsl_glyphs_inited = TRUE;
}
_cairo_gl_set_destination (dst);
_cairo_scaled_font_freeze_cache (scaled_font);
@ -480,7 +645,7 @@ _render_glyphs (cairo_gl_surface_t *dst,
if (setup.vbo_size > 4096)
setup.vbo_size = 4096;
setup.op = op;
setup.shader = CAIRO_GL_GLYPHS_SHADER_UNSET;
setup.shader = CAIRO_GL_GLYPHS_SHADER_COUNT; /* unset */
glGenBuffersARB (1, &vbo);
glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo);
@ -596,6 +761,7 @@ _render_glyphs (cairo_gl_surface_t *dst,
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
glActiveTexture (GL_TEXTURE1);
glDisable (GL_TEXTURE_2D);
_cairo_gl_use_program (NULL);
if (vbo != 0) {
glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0);
@ -878,3 +1044,4 @@ _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
sizeof (cairo_gl_glyph_private_t),
NULL);
}

View file

@ -87,6 +87,20 @@ typedef struct cairo_gl_shader_program {
cairo_bool_t build_failure;
} cairo_gl_shader_program_t;
enum cairo_gl_glyphs_shader_source {
CAIRO_GL_GLYPHS_SOURCE_CONSTANT,
CAIRO_GL_GLYPHS_SOURCE_TEXTURE,
CAIRO_GL_GLYPHS_SOURCE_TEXTURE_ALPHA,
CAIRO_GL_GLYPHS_SOURCE_COUNT,
};
typedef enum cairo_gl_glyphs_shader {
CAIRO_GL_GLYPHS_SHADER_NORMAL,
CAIRO_GL_GLYPHS_SHADER_CA_SOURCE,
CAIRO_GL_GLYPHS_SHADER_CA_SOURCE_ALPHA,
CAIRO_GL_GLYPHS_SHADER_COUNT,
} cairo_gl_glyphs_shader_t;
typedef struct _cairo_gl_context {
cairo_device_t base;
@ -94,9 +108,13 @@ typedef struct _cairo_gl_context {
GLint max_framebuffer_size;
GLint max_texture_size;
cairo_bool_t using_glsl;
cairo_bool_t glsl_glyphs_inited;
cairo_bool_t using_glsl_glyphs;
cairo_bool_t using_shaders;
cairo_gl_shader_program_t fill_rectangles_shader;
cairo_gl_shader_program_t glyphs_shaders[CAIRO_GL_GLYPHS_SOURCE_COUNT]
[CAIRO_GL_GLYPHS_SHADER_COUNT];
cairo_gl_surface_t *current_target;
cairo_gl_surface_t *glyphs_temporary_mask;
@ -134,6 +152,7 @@ typedef struct cairo_gl_composite_operand {
typedef struct _cairo_gl_composite_setup {
cairo_gl_composite_operand_t src;
cairo_gl_composite_operand_t mask;
cairo_gl_shader_program_t *shader;
} cairo_gl_composite_setup_t;
cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend;

View file

@ -317,7 +317,10 @@ bind_texture_to_shader_arb (GLuint program, const char *name, GLuint tex_unit)
static void
use_program_arb (cairo_gl_shader_program_t *program)
{
glUseProgramObjectARB (program->program);
if (program)
glUseProgramObjectARB (program->program);
else
glUseProgramObjectARB (0);
}
/* OpenGL Core 2.0 API. */
@ -488,7 +491,10 @@ bind_texture_to_shader_core_2_0 (GLuint program, const char *name, GLuint tex_un
static void
use_program_core_2_0 (cairo_gl_shader_program_t *program)
{
glUseProgram (program->program);
if (program)
glUseProgram (program->program);
else
glUseProgram (0);
}
static const shader_impl_t shader_impl_core_2_0 = {

View file

@ -117,7 +117,8 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
if (GLEW_VERSION_2_0 ||
(GLEW_ARB_fragment_shader &&
GLEW_ARB_vertex_shader &&
GLEW_ARB_shader_objects)) {
GLEW_ARB_shader_objects))
{
ctx->using_glsl = TRUE;
}
@ -1160,32 +1161,43 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
switch (setup->src.type) {
case OPERAND_CONSTANT:
_cairo_gl_set_tex_combine_constant_color (ctx, 0,
setup->src.operand.constant.color);
if (setup->shader) {
bind_vec4_to_shader(setup->shader->program,
"constant_source",
setup->src.operand.constant.color[0],
setup->src.operand.constant.color[1],
setup->src.operand.constant.color[2],
setup->src.operand.constant.color[3]);
} else {
_cairo_gl_set_tex_combine_constant_color (ctx, 0,
setup->src.operand.constant.color);
}
break;
case OPERAND_TEXTURE:
_cairo_gl_set_texture_surface (0, setup->src.operand.texture.tex,
src_attributes);
/* Set up the constant color we use to set color to 0 if needed. */
glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color);
/* Set up the combiner to just set color to the sampled texture. */
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
if (!setup->shader) {
/* Set up the constant color we use to set color to 0 if needed. */
glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color);
/* Set up the combiner to just set color to the sampled texture. */
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
/* Force the src color to 0 if the surface should be alpha-only.
* We may have a teximage with color bits if the implementation doesn't
* support GL_ALPHA FBOs.
*/
if (setup->src.operand.texture.surface->base.content !=
CAIRO_CONTENT_ALPHA)
glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0);
else
glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
break;
/* Force the src color to 0 if the surface should be
* alpha-only. We may have a teximage with color bits if
* the implementation doesn't support GL_ALPHA FBOs.
*/
if (setup->src.operand.texture.surface->base.content !=
CAIRO_CONTENT_ALPHA)
glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0);
else
glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
break;
}
}
}