mirror of
https://github.com/brl/mutter.git
synced 2024-11-22 16:10:41 -05:00
cogl-material: Use fragment programs instead of texture env combiners
THIS IS A WORK IN PROGRESS Mesa is building a big shader when using ARB_texture_env_combine. The idea is to bypass that computation, do it ourselves and cache the compiled program in a CoglMaterial. For now that feature can be enabled by setting the COGL_PIPELINE environment variable to "arbfp". COGL_SHOW_FP_SOURCE can be set to a non empty string to dump the fragment program source too. TODO: * fog (really easy, using OPTION) * support tex env combiner operands, DOT3, ADD_SIGNED, INTERPOLATE combine modes (need refactoring the generation of temporary variables) (not too hard) * alpha testing for GLES 2.0?
This commit is contained in:
parent
56dd71dba0
commit
8ee099d204
@ -113,6 +113,15 @@ typedef enum _CoglMaterialFlags
|
||||
COGL_MATERIAL_FLAG_DEFAULT_BLEND = 1L<<5
|
||||
} CoglMaterialFlags;
|
||||
|
||||
/* ARBfp1.0 program (Fog + ARB_texture_env_combine) */
|
||||
typedef struct _CoglMaterialProgram
|
||||
{
|
||||
GString *source;
|
||||
GLuint gl_program;
|
||||
|
||||
gboolean *sampled;
|
||||
} CoglMaterialProgram;
|
||||
|
||||
struct _CoglMaterial
|
||||
{
|
||||
CoglHandleObject _parent;
|
||||
@ -145,6 +154,8 @@ struct _CoglMaterial
|
||||
GLint blend_src_factor_rgb;
|
||||
GLint blend_dst_factor_rgb;
|
||||
|
||||
CoglMaterialProgram program;
|
||||
|
||||
GList *layers;
|
||||
unsigned int n_layers;
|
||||
};
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "cogl-journal-private.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
@ -56,6 +57,11 @@
|
||||
#define glBlendEquation ctx->drv.pf_glBlendEquation
|
||||
#define glBlendColor ctx->drv.pf_glBlendColor
|
||||
#define glBlendEquationSeparate ctx->drv.pf_glBlendEquationSeparate
|
||||
|
||||
#define glProgramString ctx->drv.pf_glProgramString
|
||||
#define glBindProgram ctx->drv.pf_glBindProgram
|
||||
#define glDeleteProgram ctx->drv.pf_glDeletePrograms
|
||||
#define glGenPrograms ctx->drv.pf_glGenPrograms
|
||||
#endif
|
||||
|
||||
/* This isn't defined in the GLES headers */
|
||||
@ -123,6 +129,9 @@ _cogl_material_init_default_material (void)
|
||||
material->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA;
|
||||
material->flags |= COGL_MATERIAL_FLAG_DEFAULT_BLEND;
|
||||
|
||||
material->program.source = NULL;
|
||||
material->program.gl_program = -1;
|
||||
|
||||
material->layers = NULL;
|
||||
material->n_layers = 0;
|
||||
|
||||
@ -1337,7 +1346,6 @@ _cogl_material_layer_ensure_mipmaps (CoglHandle layer_handle)
|
||||
_cogl_texture_ensure_mipmaps (layer->texture);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
_cogl_material_set_wrap_modes_for_layer (CoglMaterialLayer *layer,
|
||||
int layer_num,
|
||||
@ -1403,6 +1411,328 @@ _cogl_material_set_wrap_modes_for_layer (CoglMaterialLayer *layer,
|
||||
wrap_mode_r);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cogl_material_program_is_used (void)
|
||||
{
|
||||
static gint use_arbfp = -1;
|
||||
|
||||
if (G_UNLIKELY (use_arbfp == -1))
|
||||
{
|
||||
const gchar *env_var = g_getenv ("COGL_PIPELINE");
|
||||
use_arbfp = FALSE;
|
||||
|
||||
if (g_strcmp0 (env_var, "arbfp") == 0)
|
||||
{
|
||||
if (_cogl_features_available_private (COGL_FEATURE_PRIVATE_ARB_FP))
|
||||
{
|
||||
g_message ("Using an ARBfp pipeline");
|
||||
use_arbfp = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("ARB_fragment_program is not available for your "
|
||||
"platform");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return use_arbfp;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cogl_material_program_show_source (void)
|
||||
{
|
||||
static gint show_source = -1;
|
||||
|
||||
if (G_UNLIKELY (show_source == -1))
|
||||
{
|
||||
const gchar *env_var = g_getenv ("COGL_SHOW_FP_SOURCE");
|
||||
|
||||
if (env_var != NULL && env_var[0] != '\0')
|
||||
show_source = TRUE;
|
||||
else
|
||||
show_source = FALSE;
|
||||
}
|
||||
|
||||
return show_source;
|
||||
}
|
||||
|
||||
static const char *
|
||||
combine_func_to_arbfp_instruction (GLint op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case GL_ADD:
|
||||
return "ADD_SAT";
|
||||
case GL_MODULATE:
|
||||
return "MUL";
|
||||
case GL_REPLACE:
|
||||
return "MOV";
|
||||
case GL_SUBTRACT:
|
||||
return "SUB_SAT";
|
||||
case GL_ADD_SIGNED:
|
||||
case GL_DOT3_RGB:
|
||||
case GL_DOT3_RGBA:
|
||||
case GL_INTERPOLATE:
|
||||
default:
|
||||
/* get away with it now */
|
||||
g_message ("*** oops got %d", op);
|
||||
return "MUL";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
gl_target_to_arbfp_string (GLenum gl_target)
|
||||
{
|
||||
if (gl_target == GL_TEXTURE_1D)
|
||||
return "1D";
|
||||
else if (gl_target == GL_TEXTURE_2D)
|
||||
return "2D";
|
||||
else if (gl_target == GL_TEXTURE_RECTANGLE_ARB)
|
||||
return "RECT";
|
||||
else
|
||||
return "2D";
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cogl_material_program_is_unit_sampled (CoglMaterialProgram *program,
|
||||
unsigned int sampler_nb)
|
||||
{
|
||||
return program->sampled[sampler_nb];
|
||||
}
|
||||
|
||||
static char *
|
||||
cogl_material_layer_get_arbfp_arg (CoglMaterialLayer *layer,
|
||||
CoglTextureUnit *unit,
|
||||
CoglBlendStringChannelMask mask,
|
||||
int arg,
|
||||
gboolean *need_sampler,
|
||||
unsigned int *sampler_nb)
|
||||
{
|
||||
char buffer[32];
|
||||
unsigned int texture_unit;
|
||||
GLint *src;
|
||||
|
||||
if (mask == COGL_BLEND_STRING_CHANNEL_MASK_ALPHA)
|
||||
src = layer->texture_combine_alpha_src;
|
||||
else
|
||||
src = layer->texture_combine_rgb_src;
|
||||
|
||||
if (src[arg] == GL_PRIMARY_COLOR)
|
||||
{
|
||||
g_sprintf (buffer, "fragment.color.primary");
|
||||
*need_sampler = FALSE;
|
||||
}
|
||||
else if (src[arg] == GL_PREVIOUS)
|
||||
{
|
||||
int unit_nb = unit->index - 1;
|
||||
|
||||
if (unit_nb >= 0)
|
||||
{
|
||||
*need_sampler = TRUE;
|
||||
*sampler_nb = unit->index - 1;
|
||||
g_sprintf (buffer, "texture[%d]", *sampler_nb);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_sprintf (buffer, "fragment.color.primary");
|
||||
*need_sampler = FALSE;
|
||||
}
|
||||
}
|
||||
else if (src[arg] == GL_TEXTURE)
|
||||
{
|
||||
*need_sampler = TRUE;
|
||||
*sampler_nb = unit->index;
|
||||
g_sprintf (buffer, "texture[%d]", *sampler_nb);
|
||||
}
|
||||
else
|
||||
{
|
||||
texture_unit = src[arg] - GL_TEXTURE0;
|
||||
if (texture_unit < _cogl_get_max_texture_image_units ())
|
||||
{
|
||||
*need_sampler = TRUE;
|
||||
*sampler_nb = texture_unit;
|
||||
g_sprintf (buffer, "texture[%d]", *sampler_nb);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note that this should not happend */
|
||||
g_warning ("Cannot figure out ARBfp1.0 argument defaulting to "
|
||||
"primary color");
|
||||
g_sprintf (buffer, "fragment.color.primary");
|
||||
*need_sampler = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return g_strdup (buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
cogl_material_program_gen (CoglMaterialProgram *program,
|
||||
CoglMaterialLayer *layer,
|
||||
CoglTextureUnit *unit,
|
||||
CoglBlendStringChannelMask mask,
|
||||
GLenum gl_target)
|
||||
{
|
||||
gboolean need_sampler;
|
||||
unsigned int sampler_nb;
|
||||
GLint func;
|
||||
unsigned int nb_args, i;
|
||||
char *arg[3];
|
||||
const gchar *swizzle[2][3] =
|
||||
{
|
||||
/* RGB ALPHA RGBA*/
|
||||
{ ".xyz", ".a", "" },
|
||||
{ "" , ".a", "" }
|
||||
};
|
||||
|
||||
if (mask == COGL_BLEND_STRING_CHANNEL_MASK_ALPHA)
|
||||
{
|
||||
func = layer->texture_combine_alpha_func;
|
||||
}
|
||||
else
|
||||
func = layer->texture_combine_rgb_func;
|
||||
|
||||
nb_args = get_n_args_for_combine_func (func);
|
||||
|
||||
for (i = 0; i < nb_args; i++)
|
||||
{
|
||||
arg[i] = cogl_material_layer_get_arbfp_arg (layer,
|
||||
unit,
|
||||
mask,
|
||||
i,
|
||||
&need_sampler,
|
||||
&sampler_nb);
|
||||
if (need_sampler)
|
||||
{
|
||||
if (!cogl_material_program_is_unit_sampled (program, sampler_nb))
|
||||
{
|
||||
g_string_append_printf (program->source,
|
||||
"TEMP texel%d;\n",
|
||||
sampler_nb);
|
||||
g_string_append_printf (program->source,
|
||||
"TEX texel%d,fragment.texcoord[%d],"
|
||||
"%s,%s;\n",
|
||||
sampler_nb,
|
||||
unit->index,
|
||||
arg[i],
|
||||
gl_target_to_arbfp_string (gl_target));
|
||||
program->sampled[sampler_nb] = TRUE;
|
||||
}
|
||||
arg[i] = g_strdup_printf ("texel%d", sampler_nb);
|
||||
}
|
||||
}
|
||||
|
||||
if (nb_args == 1)
|
||||
{
|
||||
g_string_append_printf (program->source,
|
||||
"%s output%s,%s%s;\n",
|
||||
combine_func_to_arbfp_instruction (func),
|
||||
swizzle[0][mask],
|
||||
arg[0], swizzle[1][mask]);
|
||||
}
|
||||
else if (nb_args == 2)
|
||||
{
|
||||
g_string_append_printf (program->source,
|
||||
"%s output%s,%s%s,%s%s;\n",
|
||||
combine_func_to_arbfp_instruction (func),
|
||||
swizzle[0][mask],
|
||||
arg[0], swizzle[1][mask],
|
||||
arg[1], swizzle[1][mask]);
|
||||
}
|
||||
|
||||
for (i = 0; i < nb_args; i++)
|
||||
g_free (arg[i]);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
cogl_material_program_update (CoglMaterialProgram *program,
|
||||
CoglMaterialLayer *layer,
|
||||
CoglTextureUnit *unit,
|
||||
CoglLayerInfo *gl_layer_info,
|
||||
GLenum gl_target)
|
||||
{
|
||||
CoglBlendStringChannelMask mask;
|
||||
|
||||
if (!(layer->flags & COGL_MATERIAL_LAYER_FLAG_DIRTY))
|
||||
return;
|
||||
|
||||
#ifndef DISABLE_MATERIAL_CACHE
|
||||
if (!(gl_layer_info &&
|
||||
gl_layer_info->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE &&
|
||||
(layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE)))
|
||||
#endif
|
||||
{
|
||||
if (program->source == NULL)
|
||||
{
|
||||
program->source = g_string_new ("!!ARBfp1.0\n");
|
||||
g_string_append (program->source, "TEMP output;\n");
|
||||
program->sampled = g_new0 (gboolean,
|
||||
_cogl_get_max_texture_image_units ());
|
||||
}
|
||||
|
||||
if (layer->texture_combine_rgb_func == layer->texture_combine_alpha_func)
|
||||
{
|
||||
mask = COGL_BLEND_STRING_CHANNEL_MASK_RGBA;
|
||||
|
||||
cogl_material_program_gen (program,
|
||||
layer,
|
||||
unit,
|
||||
mask,
|
||||
gl_target);
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB;
|
||||
cogl_material_program_gen (program,
|
||||
layer,
|
||||
unit,
|
||||
mask,
|
||||
gl_target);
|
||||
mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA;
|
||||
cogl_material_program_gen (program,
|
||||
layer,
|
||||
unit,
|
||||
mask,
|
||||
gl_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cogl_material_program_flush (CoglMaterialProgram *program)
|
||||
{
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (program->source)
|
||||
{
|
||||
g_string_append (program->source, "MOV result.color,output;\n");
|
||||
g_string_append (program->source, "END\n");
|
||||
|
||||
if (cogl_material_program_show_source ())
|
||||
g_message ("material program:\n%s", program->source->str);
|
||||
|
||||
GE (glGenPrograms (1, &program->gl_program));
|
||||
|
||||
GE (glBindProgram (GL_FRAGMENT_PROGRAM_ARB, program->gl_program));
|
||||
GE (glProgramString (GL_FRAGMENT_PROGRAM_ARB,
|
||||
GL_PROGRAM_FORMAT_ASCII_ARB,
|
||||
program->source->len,
|
||||
program->source->str));
|
||||
GE (glEnable (GL_FRAGMENT_PROGRAM_ARB));
|
||||
|
||||
g_string_free (program->source, TRUE);
|
||||
program->source = NULL;
|
||||
g_free (program->sampled);
|
||||
program->sampled = NULL;
|
||||
}
|
||||
else if (program->gl_program != -1)
|
||||
{
|
||||
GE (glEnable (GL_FRAGMENT_PROGRAM_ARB));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* _cogl_material_flush_layers_gl_state:
|
||||
* @fallback_mask: is a bitmask of the material layers that need to be
|
||||
@ -1464,6 +1794,9 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (cogl_material_program_is_used ())
|
||||
glDisable (GL_FRAGMENT_PROGRAM_ARB);
|
||||
|
||||
for (tmp = material->layers, i = 0;
|
||||
tmp != NULL && i < _cogl_get_max_texture_image_units ();
|
||||
tmp = tmp->next, i++)
|
||||
@ -1605,7 +1938,20 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
|
||||
}
|
||||
}
|
||||
|
||||
_cogl_material_layer_flush_gl_sampler_state (layer, unit, gl_layer_info);
|
||||
if (cogl_material_program_is_used ())
|
||||
{
|
||||
cogl_material_program_update (&material->program,
|
||||
layer,
|
||||
unit,
|
||||
gl_layer_info,
|
||||
gl_target);
|
||||
}
|
||||
else
|
||||
{
|
||||
_cogl_material_layer_flush_gl_sampler_state (layer,
|
||||
unit,
|
||||
gl_layer_info);
|
||||
}
|
||||
|
||||
new_gl_layer_info.handle = layer_handle;
|
||||
new_gl_layer_info.flags = layer->flags;
|
||||
@ -1635,6 +1981,10 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
|
||||
gl_layer_info->disabled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (cogl_material_program_is_used ())
|
||||
cogl_material_program_flush (&material->program);
|
||||
|
||||
}
|
||||
|
||||
#ifndef HAVE_COGL_GLES
|
||||
|
Loading…
Reference in New Issue
Block a user