mesa: Split the shader texture update logic from fixed function.

I want to avoid walking the entire long array texture image units, but the
obvious way to do so means walking program samplers, and thus hitting the
units in a random order.

This change replaces the previous behavior of only setting up the fallback
texture for a fragment shader with setting up the fallback texture for any
shader that's missing a complete texture of the right target in its unit.

Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
This commit is contained in:
Eric Anholt 2014-04-23 16:27:56 -07:00
parent e5e50fae6a
commit a580b500ed
1 changed files with 169 additions and 99 deletions

View File

@ -40,7 +40,7 @@
#include "teximage.h"
#include "texstate.h"
#include "mtypes.h"
#include "bitset.h"
/**
@ -515,6 +515,154 @@ update_texgen(struct gl_context *ctx)
}
}
static void
update_program_texture_state(struct gl_context *ctx, struct gl_program **prog,
BITSET_WORD *enabled_texture_units)
{
GLuint unit;
int i;
for (unit = 0; unit < ctx->Const.MaxCombinedTextureImageUnits; unit++) {
struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
GLbitfield enabledTargets = 0x0;
GLuint texIndex;
for (i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog[i])
enabledTargets |= prog[i]->TexturesUsed[unit];
}
if (enabledTargets == 0x0) {
/* neither vertex nor fragment processing uses this unit */
continue;
}
for (texIndex = 0; texIndex < NUM_TEXTURE_TARGETS; texIndex++) {
if (enabledTargets & (1 << texIndex)) {
struct gl_texture_object *texObj = texUnit->CurrentTex[texIndex];
struct gl_sampler_object *sampler = texUnit->Sampler ?
texUnit->Sampler : &texObj->Sampler;
if (!_mesa_is_texture_complete(texObj, sampler)) {
_mesa_test_texobj_completeness(ctx, texObj);
}
if (_mesa_is_texture_complete(texObj, sampler)) {
_mesa_reference_texobj(&texUnit->_Current, texObj);
break;
}
}
}
if (texIndex == NUM_TEXTURE_TARGETS) {
/* If we get here it means the shader is expecting a texture
* object, but there isn't one (or it's incomplete). Use the
* fallback texture.
*/
struct gl_texture_object *texObj;
gl_texture_index texTarget;
texTarget = (gl_texture_index) (ffs(enabledTargets) - 1);
texObj = _mesa_get_fallback_texture(ctx, texTarget);
assert(texObj);
if (!texObj) {
/* invalid fallback texture: don't enable the texture unit */
continue;
}
_mesa_reference_texobj(&texUnit->_Current, texObj);
}
/* if we get here, we know this texture unit is enabled */
BITSET_SET(enabled_texture_units, unit);
ctx->Texture._MaxEnabledTexImageUnit =
MAX2(ctx->Texture._MaxEnabledTexImageUnit, (int)unit);
}
if (prog[MESA_SHADER_FRAGMENT]) {
const GLuint coordMask = (1 << MAX_TEXTURE_COORD_UNITS) - 1;
ctx->Texture._EnabledCoordUnits |=
(prog[MESA_SHADER_FRAGMENT]->InputsRead >> VARYING_SLOT_TEX0) &
coordMask;
}
}
static void
update_ff_texture_state(struct gl_context *ctx,
BITSET_WORD *enabled_texture_units)
{
int unit;
for (unit = 0; unit < ctx->Const.MaxTextureUnits; unit++) {
struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
GLuint texIndex;
if (texUnit->Enabled == 0x0)
continue;
/* If a shader already dictated what texture target was used for this
* unit, just go along with it.
*/
if (BITSET_TEST(enabled_texture_units, unit))
continue;
/* From the GL 4.4 compat specification, section 16.2 ("Texture Application"):
*
* "Texturing is enabled or disabled using the generic Enable and
* Disable commands, respectively, with the symbolic constants
* TEXTURE_1D, TEXTURE_2D, TEXTURE_RECTANGLE, TEXTURE_3D, or
* TEXTURE_CUBE_MAP to enable the one-, two-, rectangular,
* three-dimensional, or cube map texture, respectively. If more
* than one of these textures is enabled, the first one enabled
* from the following list is used:
*
* cube map texture
* three-dimensional texture
* rectangular texture
* two-dimensional texture
* one-dimensional texture"
*
* Note that the TEXTURE_x_INDEX values are in high to low priority.
* Also:
*
* "If a texture unit is disabled or has an invalid or incomplete
* texture (as defined in section 8.17) bound to it, then blending
* is disabled for that texture unit. If the texture environment
* for a given enabled texture unit references a disabled texture
* unit, or an invalid or incomplete texture that is bound to
* another unit, then the results of texture blending are
* undefined."
*/
for (texIndex = 0; texIndex < NUM_TEXTURE_TARGETS; texIndex++) {
if (texUnit->Enabled & (1 << texIndex)) {
struct gl_texture_object *texObj = texUnit->CurrentTex[texIndex];
struct gl_sampler_object *sampler = texUnit->Sampler ?
texUnit->Sampler : &texObj->Sampler;
if (!_mesa_is_texture_complete(texObj, sampler)) {
_mesa_test_texobj_completeness(ctx, texObj);
}
if (_mesa_is_texture_complete(texObj, sampler)) {
_mesa_reference_texobj(&texUnit->_Current, texObj);
break;
}
}
}
if (texIndex == NUM_TEXTURE_TARGETS)
continue;
/* if we get here, we know this texture unit is enabled */
BITSET_SET(enabled_texture_units, unit);
ctx->Texture._MaxEnabledTexImageUnit =
MAX2(ctx->Texture._MaxEnabledTexImageUnit, (int)unit);
ctx->Texture._EnabledCoordUnits |= 1 << unit;
update_tex_combine(ctx, texUnit);
}
}
/**
* \note This routine refers to derived texture matrix values to
* compute the ENABLE_TEXMAT flags, but is only called on
@ -526,10 +674,10 @@ update_texgen(struct gl_context *ctx)
static void
update_texture_state( struct gl_context *ctx )
{
GLuint unit;
struct gl_program *prog[MESA_SHADER_STAGES];
GLbitfield enabledFragUnits = 0x0;
int i;
int old_max_unit = ctx->Texture._MaxEnabledTexImageUnit;
BITSET_DECLARE(enabled_texture_units, MAX_COMBINED_TEXTURE_IMAGE_UNITS);
for (i = 0; i < MESA_SHADER_STAGES; i++) {
if (ctx->_Shader->CurrentProgram[i] &&
@ -550,107 +698,29 @@ update_texture_state( struct gl_context *ctx )
ctx->Texture._TexMatEnabled = 0x0;
ctx->Texture._TexGenEnabled = 0x0;
ctx->Texture._MaxEnabledTexImageUnit = -1;
ctx->Texture._EnabledCoordUnits = 0x0;
/*
* Update texture unit state.
memset(&enabled_texture_units, 0, sizeof(enabled_texture_units));
/* First, walk over our programs pulling in all the textures for them.
* Programs dictate specific texture targets to be enabled, and for a draw
* call to be valid they can't conflict about which texture targets are
* used.
*/
for (unit = 0; unit < ctx->Const.MaxCombinedTextureImageUnits; unit++) {
struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
GLbitfield enabledTargetsByStage[MESA_SHADER_STAGES];
GLbitfield enabledTargets = 0x0;
GLuint texIndex;
update_program_texture_state(ctx, prog, enabled_texture_units);
/* Get the bitmask of texture target enables.
* enableBits will be a mask of the TEXTURE_*_BIT flags indicating
* which texture targets are enabled (fixed function) or referenced
* by a fragment program/program. When multiple flags are set, we'll
* settle on the one with highest priority (see below).
*/
for (i = 0; i < MESA_SHADER_STAGES; i++) {
if (prog[i])
enabledTargetsByStage[i] = prog[i]->TexturesUsed[unit];
else if (i == MESA_SHADER_FRAGMENT)
enabledTargetsByStage[i] = texUnit->Enabled;
else
enabledTargetsByStage[i] = 0;
enabledTargets |= enabledTargetsByStage[i];
}
/* Also pull in any textures necessary for fixed function fragment shading.
*/
if (!prog[MESA_SHADER_FRAGMENT])
update_ff_texture_state(ctx, enabled_texture_units);
if (enabledTargets == 0x0) {
_mesa_reference_texobj(&texUnit->_Current, NULL);
/* neither vertex nor fragment processing uses this unit */
continue;
}
/* Look for the highest priority texture target that's enabled (or used
* by the vert/frag shaders) and "complete". That's the one we'll use
* for texturing.
*
* Note that the TEXTURE_x_INDEX values are in high to low priority.
*/
for (texIndex = 0; texIndex < NUM_TEXTURE_TARGETS; texIndex++) {
if (enabledTargets & (1 << texIndex)) {
struct gl_texture_object *texObj = texUnit->CurrentTex[texIndex];
struct gl_sampler_object *sampler = texUnit->Sampler ?
texUnit->Sampler : &texObj->Sampler;
if (!_mesa_is_texture_complete(texObj, sampler)) {
_mesa_test_texobj_completeness(ctx, texObj);
}
if (_mesa_is_texture_complete(texObj, sampler)) {
_mesa_reference_texobj(&texUnit->_Current, texObj);
break;
}
}
}
if (texIndex == NUM_TEXTURE_TARGETS) {
if (prog[MESA_SHADER_FRAGMENT]) {
/* If we get here it means the shader is expecting a texture
* object, but there isn't one (or it's incomplete). Use the
* fallback texture.
*/
struct gl_texture_object *texObj;
gl_texture_index texTarget;
texTarget = (gl_texture_index) (ffs(enabledTargets) - 1);
texObj = _mesa_get_fallback_texture(ctx, texTarget);
assert(texObj);
if (!texObj) {
/* invalid fallback texture: don't enable the texture unit */
continue;
}
_mesa_reference_texobj(&texUnit->_Current, texObj);
}
else {
/* fixed-function: texture unit is really disabled */
_mesa_reference_texobj(&texUnit->_Current, NULL);
continue;
}
}
/* if we get here, we know this texture unit is enabled */
ctx->Texture._MaxEnabledTexImageUnit = unit;
if (enabledTargetsByStage[MESA_SHADER_FRAGMENT])
enabledFragUnits |= (1 << unit);
if (!prog[MESA_SHADER_FRAGMENT])
update_tex_combine(ctx, texUnit);
/* Now, clear out the _Current of any disabled texture units. */
for (i = 0; i <= ctx->Texture._MaxEnabledTexImageUnit; i++) {
if (!BITSET_TEST(enabled_texture_units, i))
_mesa_reference_texobj(&ctx->Texture.Unit[i]._Current, NULL);
}
/* Determine which texture coordinate sets are actually needed */
if (prog[MESA_SHADER_FRAGMENT]) {
const GLuint coordMask = (1 << MAX_TEXTURE_COORD_UNITS) - 1;
ctx->Texture._EnabledCoordUnits
= (prog[MESA_SHADER_FRAGMENT]->InputsRead >> VARYING_SLOT_TEX0) &
coordMask;
}
else {
ctx->Texture._EnabledCoordUnits = enabledFragUnits;
for (i = ctx->Texture._MaxEnabledTexImageUnit + 1; i <= old_max_unit; i++) {
_mesa_reference_texobj(&ctx->Texture.Unit[i]._Current, NULL);
}
if (!prog[MESA_SHADER_FRAGMENT] || !prog[MESA_SHADER_VERTEX])