mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-06-04 07:28:18 +02:00
[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:
parent
672973caa0
commit
f475351f75
4 changed files with 239 additions and 35 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue