4110 lines
123 KiB
C
4110 lines
123 KiB
C
/*
|
|
* Mesa 3-D graphics library
|
|
* Version: 6.5.1
|
|
*
|
|
* Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#define DEBUG_PARSING 0
|
|
|
|
/**
|
|
* \file arbprogparse.c
|
|
* ARB_*_program parser core
|
|
* \author Karl Rasche
|
|
*/
|
|
|
|
#include "glheader.h"
|
|
#include "imports.h"
|
|
#include "arbprogparse.h"
|
|
#include "grammar_mesa.h"
|
|
#include "program.h"
|
|
#include "prog_parameter.h"
|
|
#include "prog_statevars.h"
|
|
#include "context.h"
|
|
#include "macros.h"
|
|
#include "mtypes.h"
|
|
#include "prog_instruction.h"
|
|
|
|
|
|
/* For ARB programs, use the NV instruction limits */
|
|
#define MAX_INSTRUCTIONS MAX2(MAX_NV_FRAGMENT_PROGRAM_INSTRUCTIONS, \
|
|
MAX_NV_VERTEX_PROGRAM_INSTRUCTIONS)
|
|
|
|
|
|
/**
|
|
* This is basically a union of the vertex_program and fragment_program
|
|
* structs that we can use to parse the program into
|
|
*
|
|
* XXX we can probably get rid of this entirely someday.
|
|
*/
|
|
struct arb_program
|
|
{
|
|
struct gl_program Base;
|
|
|
|
GLuint Position; /* Just used for error reporting while parsing */
|
|
GLuint MajorVersion;
|
|
GLuint MinorVersion;
|
|
|
|
/* ARB_vertex_progmra options */
|
|
GLboolean HintPositionInvariant;
|
|
|
|
/* ARB_fragment_progmra options */
|
|
GLenum PrecisionOption; /* GL_DONT_CARE, GL_NICEST or GL_FASTEST */
|
|
GLenum FogOption; /* GL_NONE, GL_LINEAR, GL_EXP or GL_EXP2 */
|
|
|
|
/* ARB_fragment_program specifics */
|
|
GLbitfield TexturesUsed[MAX_TEXTURE_IMAGE_UNITS];
|
|
GLuint NumAluInstructions;
|
|
GLuint NumTexInstructions;
|
|
GLuint NumTexIndirections;
|
|
|
|
GLboolean UsesKill;
|
|
};
|
|
|
|
|
|
|
|
/* TODO:
|
|
* Fragment Program Stuff:
|
|
* -----------------------------------------------------
|
|
*
|
|
* - things from Michal's email
|
|
* + overflow on atoi
|
|
* + not-overflowing floats (don't use parse_integer..)
|
|
* + can remove range checking in arbparse.c
|
|
*
|
|
* - check all limits of number of various variables
|
|
* + parameters
|
|
*
|
|
* - test! test! test!
|
|
*
|
|
* Vertex Program Stuff:
|
|
* -----------------------------------------------------
|
|
* - Optimize param array usage and count limits correctly, see spec,
|
|
* section 2.14.3.7
|
|
* + Record if an array is reference absolutly or relatively (or both)
|
|
* + For absolute arrays, store a bitmap of accesses
|
|
* + For single parameters, store an access flag
|
|
* + After parsing, make a parameter cleanup and merging pass, where
|
|
* relative arrays are layed out first, followed by abs arrays, and
|
|
* finally single state.
|
|
* + Remap offsets for param src and dst registers
|
|
* + Now we can properly count parameter usage
|
|
*
|
|
* - Multiple state binding errors in param arrays (see spec, just before
|
|
* section 2.14.3.3)
|
|
* - grep for XXX
|
|
*
|
|
* Mesa Stuff
|
|
* -----------------------------------------------------
|
|
* - User clipping planes vs. PositionInvariant
|
|
* - Is it sufficient to just multiply by the mvp to transform in the
|
|
* PositionInvariant case? Or do we need something more involved?
|
|
*
|
|
* - vp_src swizzle is GLubyte, fp_src swizzle is GLuint
|
|
* - fetch state listed in program_parameters list
|
|
* + WTF should this go???
|
|
* + currently in nvvertexec.c and s_nvfragprog.c
|
|
*
|
|
* - allow for multiple address registers (and fetch address regs properly)
|
|
*
|
|
* Cosmetic Stuff
|
|
* -----------------------------------------------------
|
|
* - remove any leftover unused grammer.c stuff (dict_ ?)
|
|
* - fix grammer.c error handling so its not static
|
|
* - #ifdef around stuff pertaining to extentions
|
|
*
|
|
* Outstanding Questions:
|
|
* -----------------------------------------------------
|
|
* - ARB_matrix_palette / ARB_vertex_blend -- not supported
|
|
* what gets hacked off because of this:
|
|
* + VERTEX_ATTRIB_MATRIXINDEX
|
|
* + VERTEX_ATTRIB_WEIGHT
|
|
* + MATRIX_MODELVIEW
|
|
* + MATRIX_PALETTE
|
|
*
|
|
* - When can we fetch env/local params from their own register files, and
|
|
* when to we have to fetch them into the main state register file?
|
|
* (think arrays)
|
|
*
|
|
* Grammar Changes:
|
|
* -----------------------------------------------------
|
|
*/
|
|
|
|
/* Changes since moving the file to shader directory
|
|
|
|
2004-III-4 ------------------------------------------------------------
|
|
- added #include "grammar_mesa.h"
|
|
- removed grammar specific code part (it resides now in grammar.c)
|
|
- added GL_ARB_fragment_program_shadow tokens
|
|
- modified #include "arbparse_syn.h"
|
|
- major changes inside _mesa_parse_arb_program()
|
|
- check the program string for '\0' characters
|
|
- copy the program string to a one-byte-longer location to have
|
|
it null-terminated
|
|
- position invariance test (not writing to result.position) moved
|
|
to syntax part
|
|
*/
|
|
|
|
typedef GLubyte *production;
|
|
|
|
|
|
/**
|
|
* This is the text describing the rules to parse the grammar
|
|
*/
|
|
LONGSTRING static char arb_grammar_text[] =
|
|
#include "arbprogram_syn.h"
|
|
;
|
|
|
|
/**
|
|
* These should match up with the values defined in arbprogram.syn
|
|
*/
|
|
|
|
/*
|
|
Changes:
|
|
- changed and merged V_* and F_* opcode values to OP_*.
|
|
- added GL_ARB_fragment_program_shadow specific tokens (michal)
|
|
*/
|
|
#define REVISION 0x09
|
|
|
|
/* program type */
|
|
#define FRAGMENT_PROGRAM 0x01
|
|
#define VERTEX_PROGRAM 0x02
|
|
|
|
/* program section */
|
|
#define OPTION 0x01
|
|
#define INSTRUCTION 0x02
|
|
#define DECLARATION 0x03
|
|
#define END 0x04
|
|
|
|
/* GL_ARB_fragment_program option */
|
|
#define ARB_PRECISION_HINT_FASTEST 0x00
|
|
#define ARB_PRECISION_HINT_NICEST 0x01
|
|
#define ARB_FOG_EXP 0x02
|
|
#define ARB_FOG_EXP2 0x03
|
|
#define ARB_FOG_LINEAR 0x04
|
|
|
|
/* GL_ARB_vertex_program option */
|
|
#define ARB_POSITION_INVARIANT 0x05
|
|
|
|
/* GL_ARB_fragment_program_shadow option */
|
|
#define ARB_FRAGMENT_PROGRAM_SHADOW 0x06
|
|
|
|
/* GL_ARB_draw_buffers option */
|
|
#define ARB_DRAW_BUFFERS 0x07
|
|
|
|
/* GL_ARB_fragment_program instruction class */
|
|
#define OP_ALU_INST 0x00
|
|
#define OP_TEX_INST 0x01
|
|
|
|
/* GL_ARB_vertex_program instruction class */
|
|
/* OP_ALU_INST */
|
|
|
|
/* GL_ARB_fragment_program instruction type */
|
|
#define OP_ALU_VECTOR 0x00
|
|
#define OP_ALU_SCALAR 0x01
|
|
#define OP_ALU_BINSC 0x02
|
|
#define OP_ALU_BIN 0x03
|
|
#define OP_ALU_TRI 0x04
|
|
#define OP_ALU_SWZ 0x05
|
|
#define OP_TEX_SAMPLE 0x06
|
|
#define OP_TEX_KIL 0x07
|
|
|
|
/* GL_ARB_vertex_program instruction type */
|
|
#define OP_ALU_ARL 0x08
|
|
/* OP_ALU_VECTOR */
|
|
/* OP_ALU_SCALAR */
|
|
/* OP_ALU_BINSC */
|
|
/* OP_ALU_BIN */
|
|
/* OP_ALU_TRI */
|
|
/* OP_ALU_SWZ */
|
|
|
|
/* GL_ARB_fragment_program instruction code */
|
|
#define OP_ABS 0x00
|
|
#define OP_ABS_SAT 0x1B
|
|
#define OP_FLR 0x09
|
|
#define OP_FLR_SAT 0x26
|
|
#define OP_FRC 0x0A
|
|
#define OP_FRC_SAT 0x27
|
|
#define OP_LIT 0x0C
|
|
#define OP_LIT_SAT 0x2A
|
|
#define OP_MOV 0x11
|
|
#define OP_MOV_SAT 0x30
|
|
#define OP_COS 0x1F
|
|
#define OP_COS_SAT 0x20
|
|
#define OP_EX2 0x07
|
|
#define OP_EX2_SAT 0x25
|
|
#define OP_LG2 0x0B
|
|
#define OP_LG2_SAT 0x29
|
|
#define OP_RCP 0x14
|
|
#define OP_RCP_SAT 0x33
|
|
#define OP_RSQ 0x15
|
|
#define OP_RSQ_SAT 0x34
|
|
#define OP_SIN 0x38
|
|
#define OP_SIN_SAT 0x39
|
|
#define OP_SCS 0x35
|
|
#define OP_SCS_SAT 0x36
|
|
#define OP_POW 0x13
|
|
#define OP_POW_SAT 0x32
|
|
#define OP_ADD 0x01
|
|
#define OP_ADD_SAT 0x1C
|
|
#define OP_DP3 0x03
|
|
#define OP_DP3_SAT 0x21
|
|
#define OP_DP4 0x04
|
|
#define OP_DP4_SAT 0x22
|
|
#define OP_DPH 0x05
|
|
#define OP_DPH_SAT 0x23
|
|
#define OP_DST 0x06
|
|
#define OP_DST_SAT 0x24
|
|
#define OP_MAX 0x0F
|
|
#define OP_MAX_SAT 0x2E
|
|
#define OP_MIN 0x10
|
|
#define OP_MIN_SAT 0x2F
|
|
#define OP_MUL 0x12
|
|
#define OP_MUL_SAT 0x31
|
|
#define OP_SGE 0x16
|
|
#define OP_SGE_SAT 0x37
|
|
#define OP_SLT 0x17
|
|
#define OP_SLT_SAT 0x3A
|
|
#define OP_SUB 0x18
|
|
#define OP_SUB_SAT 0x3B
|
|
#define OP_XPD 0x1A
|
|
#define OP_XPD_SAT 0x43
|
|
#define OP_CMP 0x1D
|
|
#define OP_CMP_SAT 0x1E
|
|
#define OP_LRP 0x2B
|
|
#define OP_LRP_SAT 0x2C
|
|
#define OP_MAD 0x0E
|
|
#define OP_MAD_SAT 0x2D
|
|
#define OP_SWZ 0x19
|
|
#define OP_SWZ_SAT 0x3C
|
|
#define OP_TEX 0x3D
|
|
#define OP_TEX_SAT 0x3E
|
|
#define OP_TXB 0x3F
|
|
#define OP_TXB_SAT 0x40
|
|
#define OP_TXP 0x41
|
|
#define OP_TXP_SAT 0x42
|
|
#define OP_KIL 0x28
|
|
|
|
/* GL_ARB_vertex_program instruction code */
|
|
#define OP_ARL 0x02
|
|
/* OP_ABS */
|
|
/* OP_FLR */
|
|
/* OP_FRC */
|
|
/* OP_LIT */
|
|
/* OP_MOV */
|
|
/* OP_EX2 */
|
|
#define OP_EXP 0x08
|
|
/* OP_LG2 */
|
|
#define OP_LOG 0x0D
|
|
/* OP_RCP */
|
|
/* OP_RSQ */
|
|
/* OP_POW */
|
|
/* OP_ADD */
|
|
/* OP_DP3 */
|
|
/* OP_DP4 */
|
|
/* OP_DPH */
|
|
/* OP_DST */
|
|
/* OP_MAX */
|
|
/* OP_MIN */
|
|
/* OP_MUL */
|
|
/* OP_SGE */
|
|
/* OP_SLT */
|
|
/* OP_SUB */
|
|
/* OP_XPD */
|
|
/* OP_MAD */
|
|
/* OP_SWZ */
|
|
|
|
/* fragment attribute binding */
|
|
#define FRAGMENT_ATTRIB_COLOR 0x01
|
|
#define FRAGMENT_ATTRIB_TEXCOORD 0x02
|
|
#define FRAGMENT_ATTRIB_FOGCOORD 0x03
|
|
#define FRAGMENT_ATTRIB_POSITION 0x04
|
|
|
|
/* vertex attribute binding */
|
|
#define VERTEX_ATTRIB_POSITION 0x01
|
|
#define VERTEX_ATTRIB_WEIGHT 0x02
|
|
#define VERTEX_ATTRIB_NORMAL 0x03
|
|
#define VERTEX_ATTRIB_COLOR 0x04
|
|
#define VERTEX_ATTRIB_FOGCOORD 0x05
|
|
#define VERTEX_ATTRIB_TEXCOORD 0x06
|
|
#define VERTEX_ATTRIB_MATRIXINDEX 0x07
|
|
#define VERTEX_ATTRIB_GENERIC 0x08
|
|
|
|
/* fragment result binding */
|
|
#define FRAGMENT_RESULT_COLOR 0x01
|
|
#define FRAGMENT_RESULT_DEPTH 0x02
|
|
|
|
/* vertex result binding */
|
|
#define VERTEX_RESULT_POSITION 0x01
|
|
#define VERTEX_RESULT_COLOR 0x02
|
|
#define VERTEX_RESULT_FOGCOORD 0x03
|
|
#define VERTEX_RESULT_POINTSIZE 0x04
|
|
#define VERTEX_RESULT_TEXCOORD 0x05
|
|
|
|
/* texture target */
|
|
#define TEXTARGET_1D 0x01
|
|
#define TEXTARGET_2D 0x02
|
|
#define TEXTARGET_3D 0x03
|
|
#define TEXTARGET_RECT 0x04
|
|
#define TEXTARGET_CUBE 0x05
|
|
/* GL_ARB_fragment_program_shadow */
|
|
#define TEXTARGET_SHADOW1D 0x06
|
|
#define TEXTARGET_SHADOW2D 0x07
|
|
#define TEXTARGET_SHADOWRECT 0x08
|
|
|
|
/* face type */
|
|
#define FACE_FRONT 0x00
|
|
#define FACE_BACK 0x01
|
|
|
|
/* color type */
|
|
#define COLOR_PRIMARY 0x00
|
|
#define COLOR_SECONDARY 0x01
|
|
|
|
/* component */
|
|
#define COMPONENT_X 0x00
|
|
#define COMPONENT_Y 0x01
|
|
#define COMPONENT_Z 0x02
|
|
#define COMPONENT_W 0x03
|
|
#define COMPONENT_0 0x04
|
|
#define COMPONENT_1 0x05
|
|
|
|
/* array index type */
|
|
#define ARRAY_INDEX_ABSOLUTE 0x00
|
|
#define ARRAY_INDEX_RELATIVE 0x01
|
|
|
|
/* matrix name */
|
|
#define MATRIX_MODELVIEW 0x01
|
|
#define MATRIX_PROJECTION 0x02
|
|
#define MATRIX_MVP 0x03
|
|
#define MATRIX_TEXTURE 0x04
|
|
#define MATRIX_PALETTE 0x05
|
|
#define MATRIX_PROGRAM 0x06
|
|
|
|
/* matrix modifier */
|
|
#define MATRIX_MODIFIER_IDENTITY 0x00
|
|
#define MATRIX_MODIFIER_INVERSE 0x01
|
|
#define MATRIX_MODIFIER_TRANSPOSE 0x02
|
|
#define MATRIX_MODIFIER_INVTRANS 0x03
|
|
|
|
/* constant type */
|
|
#define CONSTANT_SCALAR 0x01
|
|
#define CONSTANT_VECTOR 0x02
|
|
|
|
/* program param type */
|
|
#define PROGRAM_PARAM_ENV 0x01
|
|
#define PROGRAM_PARAM_LOCAL 0x02
|
|
|
|
/* register type */
|
|
#define REGISTER_ATTRIB 0x01
|
|
#define REGISTER_PARAM 0x02
|
|
#define REGISTER_RESULT 0x03
|
|
#define REGISTER_ESTABLISHED_NAME 0x04
|
|
|
|
/* param binding */
|
|
#define PARAM_NULL 0x00
|
|
#define PARAM_ARRAY_ELEMENT 0x01
|
|
#define PARAM_STATE_ELEMENT 0x02
|
|
#define PARAM_PROGRAM_ELEMENT 0x03
|
|
#define PARAM_PROGRAM_ELEMENTS 0x04
|
|
#define PARAM_CONSTANT 0x05
|
|
|
|
/* param state property */
|
|
#define STATE_MATERIAL_PARSER 0x01
|
|
#define STATE_LIGHT_PARSER 0x02
|
|
#define STATE_LIGHT_MODEL 0x03
|
|
#define STATE_LIGHT_PROD 0x04
|
|
#define STATE_FOG 0x05
|
|
#define STATE_MATRIX_ROWS 0x06
|
|
/* GL_ARB_fragment_program */
|
|
#define STATE_TEX_ENV 0x07
|
|
#define STATE_DEPTH 0x08
|
|
/* GL_ARB_vertex_program */
|
|
#define STATE_TEX_GEN 0x09
|
|
#define STATE_CLIP_PLANE 0x0A
|
|
#define STATE_POINT 0x0B
|
|
|
|
/* state material property */
|
|
#define MATERIAL_AMBIENT 0x01
|
|
#define MATERIAL_DIFFUSE 0x02
|
|
#define MATERIAL_SPECULAR 0x03
|
|
#define MATERIAL_EMISSION 0x04
|
|
#define MATERIAL_SHININESS 0x05
|
|
|
|
/* state light property */
|
|
#define LIGHT_AMBIENT 0x01
|
|
#define LIGHT_DIFFUSE 0x02
|
|
#define LIGHT_SPECULAR 0x03
|
|
#define LIGHT_POSITION 0x04
|
|
#define LIGHT_ATTENUATION 0x05
|
|
#define LIGHT_HALF 0x06
|
|
#define LIGHT_SPOT_DIRECTION 0x07
|
|
|
|
/* state light model property */
|
|
#define LIGHT_MODEL_AMBIENT 0x01
|
|
#define LIGHT_MODEL_SCENECOLOR 0x02
|
|
|
|
/* state light product property */
|
|
#define LIGHT_PROD_AMBIENT 0x01
|
|
#define LIGHT_PROD_DIFFUSE 0x02
|
|
#define LIGHT_PROD_SPECULAR 0x03
|
|
|
|
/* state texture environment property */
|
|
#define TEX_ENV_COLOR 0x01
|
|
|
|
/* state texture generation coord property */
|
|
#define TEX_GEN_EYE 0x01
|
|
#define TEX_GEN_OBJECT 0x02
|
|
|
|
/* state fog property */
|
|
#define FOG_COLOR 0x01
|
|
#define FOG_PARAMS 0x02
|
|
|
|
/* state depth property */
|
|
#define DEPTH_RANGE 0x01
|
|
|
|
/* state point parameters property */
|
|
#define POINT_SIZE 0x01
|
|
#define POINT_ATTENUATION 0x02
|
|
|
|
/* declaration */
|
|
#define ATTRIB 0x01
|
|
#define PARAM 0x02
|
|
#define TEMP 0x03
|
|
#define OUTPUT 0x04
|
|
#define ALIAS 0x05
|
|
/* GL_ARB_vertex_program */
|
|
#define ADDRESS 0x06
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* From here on down is the semantic checking portion
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Variable Table Handling functions
|
|
*/
|
|
typedef enum
|
|
{
|
|
vt_none,
|
|
vt_address,
|
|
vt_attrib,
|
|
vt_param,
|
|
vt_temp,
|
|
vt_output,
|
|
vt_alias
|
|
} var_type;
|
|
|
|
|
|
/**
|
|
* Setting an explicit field for each of the binding properties is a bit
|
|
* wasteful of space, but it should be much more clear when reading later on..
|
|
*/
|
|
struct var_cache
|
|
{
|
|
const GLubyte *name; /* don't free() - no need */
|
|
var_type type;
|
|
GLuint address_binding; /* The index of the address register we should
|
|
* be using */
|
|
GLuint attrib_binding; /* For type vt_attrib, see nvfragprog.h for values */
|
|
GLuint attrib_is_generic; /* If the attrib was specified through a generic
|
|
* vertex attrib */
|
|
GLuint temp_binding; /* The index of the temp register we are to use */
|
|
GLuint output_binding; /* Output/result register number */
|
|
struct var_cache *alias_binding; /* For type vt_alias, points to the var_cache entry
|
|
* that this is aliased to */
|
|
GLuint param_binding_type; /* {PROGRAM_STATE_VAR, PROGRAM_LOCAL_PARAM,
|
|
* PROGRAM_ENV_PARAM} */
|
|
GLuint param_binding_begin; /* This is the offset into the program_parameter_list where
|
|
* the tokens representing our bound state (or constants)
|
|
* start */
|
|
GLuint param_binding_length; /* This is how many entries in the the program_parameter_list
|
|
* we take up with our state tokens or constants. Note that
|
|
* this is _not_ the same as the number of param registers
|
|
* we eventually use */
|
|
struct var_cache *next;
|
|
};
|
|
|
|
static GLvoid
|
|
var_cache_create (struct var_cache **va)
|
|
{
|
|
*va = (struct var_cache *) _mesa_malloc (sizeof (struct var_cache));
|
|
if (*va) {
|
|
(**va).name = NULL;
|
|
(**va).type = vt_none;
|
|
(**va).attrib_binding = ~0;
|
|
(**va).attrib_is_generic = 0;
|
|
(**va).temp_binding = ~0;
|
|
(**va).output_binding = ~0;
|
|
(**va).param_binding_type = ~0;
|
|
(**va).param_binding_begin = ~0;
|
|
(**va).param_binding_length = ~0;
|
|
(**va).alias_binding = NULL;
|
|
(**va).next = NULL;
|
|
}
|
|
}
|
|
|
|
static GLvoid
|
|
var_cache_destroy (struct var_cache **va)
|
|
{
|
|
if (*va) {
|
|
var_cache_destroy (&(**va).next);
|
|
_mesa_free (*va);
|
|
*va = NULL;
|
|
}
|
|
}
|
|
|
|
static GLvoid
|
|
var_cache_append (struct var_cache **va, struct var_cache *nv)
|
|
{
|
|
if (*va)
|
|
var_cache_append (&(**va).next, nv);
|
|
else
|
|
*va = nv;
|
|
}
|
|
|
|
static struct var_cache *
|
|
var_cache_find (struct var_cache *va, const GLubyte * name)
|
|
{
|
|
/*struct var_cache *first = va;*/
|
|
|
|
while (va) {
|
|
if (!_mesa_strcmp ( (const char*) name, (const char*) va->name)) {
|
|
if (va->type == vt_alias)
|
|
return va->alias_binding;
|
|
return va;
|
|
}
|
|
|
|
va = va->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Called when an error is detected while parsing/compiling a program.
|
|
* Sets the ctx->Program.ErrorString field to descript and records a
|
|
* GL_INVALID_OPERATION error.
|
|
* \param position position of error in program string
|
|
* \param descrip verbose error description
|
|
*/
|
|
static void
|
|
program_error(GLcontext *ctx, GLint position, const char *descrip)
|
|
{
|
|
if (descrip) {
|
|
const char *prefix = "glProgramString(", *suffix = ")";
|
|
char *str = (char *) _mesa_malloc(_mesa_strlen(descrip) +
|
|
_mesa_strlen(prefix) +
|
|
_mesa_strlen(suffix) + 1);
|
|
if (str) {
|
|
_mesa_sprintf(str, "%s%s%s", prefix, descrip, suffix);
|
|
_mesa_error(ctx, GL_INVALID_OPERATION, str);
|
|
_mesa_free(str);
|
|
}
|
|
}
|
|
_mesa_set_program_error(ctx, position, descrip);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* constructs an integer from 4 GLubytes in LE format
|
|
*/
|
|
static GLuint
|
|
parse_position (const GLubyte ** inst)
|
|
{
|
|
GLuint value;
|
|
|
|
value = (GLuint) (*(*inst)++);
|
|
value += (GLuint) (*(*inst)++) * 0x100;
|
|
value += (GLuint) (*(*inst)++) * 0x10000;
|
|
value += (GLuint) (*(*inst)++) * 0x1000000;
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* This will, given a string, lookup the string as a variable name in the
|
|
* var cache. If the name is found, the var cache node corresponding to the
|
|
* var name is returned. If it is not found, a new entry is allocated
|
|
*
|
|
* \param I Points into the binary array where the string identifier begins
|
|
* \param found 1 if the string was found in the var_cache, 0 if it was allocated
|
|
* \return The location on the var_cache corresponding the the string starting at I
|
|
*/
|
|
static struct var_cache *
|
|
parse_string (const GLubyte ** inst, struct var_cache **vc_head,
|
|
struct arb_program *Program, GLuint * found)
|
|
{
|
|
const GLubyte *i = *inst;
|
|
struct var_cache *va = NULL;
|
|
(void) Program;
|
|
|
|
*inst += _mesa_strlen ((char *) i) + 1;
|
|
|
|
va = var_cache_find (*vc_head, i);
|
|
|
|
if (va) {
|
|
*found = 1;
|
|
return va;
|
|
}
|
|
|
|
*found = 0;
|
|
var_cache_create (&va);
|
|
va->name = (const GLubyte *) i;
|
|
|
|
var_cache_append (vc_head, va);
|
|
|
|
return va;
|
|
}
|
|
|
|
static char *
|
|
parse_string_without_adding (const GLubyte ** inst, struct arb_program *Program)
|
|
{
|
|
const GLubyte *i = *inst;
|
|
(void) Program;
|
|
|
|
*inst += _mesa_strlen ((char *) i) + 1;
|
|
|
|
return (char *) i;
|
|
}
|
|
|
|
/**
|
|
* \return -1 if we parse '-', return 1 otherwise
|
|
*/
|
|
static GLint
|
|
parse_sign (const GLubyte ** inst)
|
|
{
|
|
/*return *(*inst)++ != '+'; */
|
|
|
|
if (**inst == '-') {
|
|
(*inst)++;
|
|
return -1;
|
|
}
|
|
else if (**inst == '+') {
|
|
(*inst)++;
|
|
return 1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* parses and returns signed integer
|
|
*/
|
|
static GLint
|
|
parse_integer (const GLubyte ** inst, struct arb_program *Program)
|
|
{
|
|
GLint sign;
|
|
GLint value;
|
|
|
|
/* check if *inst points to '+' or '-'
|
|
* if yes, grab the sign and increment *inst
|
|
*/
|
|
sign = parse_sign (inst);
|
|
|
|
/* now check if *inst points to 0
|
|
* if yes, increment the *inst and return the default value
|
|
*/
|
|
if (**inst == 0) {
|
|
(*inst)++;
|
|
return 0;
|
|
}
|
|
|
|
/* parse the integer as you normally would do it */
|
|
value = _mesa_atoi (parse_string_without_adding (inst, Program));
|
|
|
|
/* now, after terminating 0 there is a position
|
|
* to parse it - parse_position()
|
|
*/
|
|
Program->Position = parse_position (inst);
|
|
|
|
return value * sign;
|
|
}
|
|
|
|
/**
|
|
Accumulate this string of digits, and return them as
|
|
a large integer represented in floating point (for range).
|
|
If scale is not NULL, also accumulates a power-of-ten
|
|
integer scale factor that represents the number of digits
|
|
in the string.
|
|
*/
|
|
static GLdouble
|
|
parse_float_string(const GLubyte ** inst, struct arb_program *Program, GLdouble *scale)
|
|
{
|
|
GLdouble value = 0.0;
|
|
GLdouble oscale = 1.0;
|
|
|
|
if (**inst == 0) { /* this string of digits is empty-- do nothing */
|
|
(*inst)++;
|
|
}
|
|
else { /* nonempty string-- parse out the digits */
|
|
while (**inst >= '0' && **inst <= '9') {
|
|
GLubyte digit = *((*inst)++);
|
|
value = value * 10.0 + (GLint) (digit - '0');
|
|
oscale *= 10.0;
|
|
}
|
|
assert(**inst == 0); /* integer string should end with 0 */
|
|
(*inst)++; /* skip over terminating 0 */
|
|
Program->Position = parse_position(inst); /* skip position (from integer) */
|
|
}
|
|
if (scale)
|
|
*scale = oscale;
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
Parse an unsigned floating-point number from this stream of tokenized
|
|
characters. Example floating-point formats supported:
|
|
12.34
|
|
12
|
|
0.34
|
|
.34
|
|
12.34e-4
|
|
*/
|
|
static GLfloat
|
|
parse_float (const GLubyte ** inst, struct arb_program *Program)
|
|
{
|
|
GLint exponent;
|
|
GLdouble whole, fraction, fracScale = 1.0;
|
|
|
|
whole = parse_float_string(inst, Program, 0);
|
|
fraction = parse_float_string(inst, Program, &fracScale);
|
|
|
|
/* Parse signed exponent */
|
|
exponent = parse_integer(inst, Program); /* This is the exponent */
|
|
|
|
/* Assemble parts of floating-point number: */
|
|
return (GLfloat) ((whole + fraction / fracScale) *
|
|
_mesa_pow(10.0, (GLfloat) exponent));
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
static GLfloat
|
|
parse_signed_float (const GLubyte ** inst, struct arb_program *Program)
|
|
{
|
|
GLint sign = parse_sign (inst);
|
|
GLfloat value = parse_float (inst, Program);
|
|
return value * sign;
|
|
}
|
|
|
|
/**
|
|
* This picks out a constant value from the parsed array. The constant vector is r
|
|
* returned in the *values array, which should be of length 4.
|
|
*
|
|
* \param values - The 4 component vector with the constant value in it
|
|
*/
|
|
static GLvoid
|
|
parse_constant (const GLubyte ** inst, GLfloat *values, struct arb_program *Program,
|
|
GLboolean use)
|
|
{
|
|
GLuint components, i;
|
|
|
|
|
|
switch (*(*inst)++) {
|
|
case CONSTANT_SCALAR:
|
|
if (use == GL_TRUE) {
|
|
values[0] =
|
|
values[1] =
|
|
values[2] = values[3] = parse_float (inst, Program);
|
|
}
|
|
else {
|
|
values[0] =
|
|
values[1] =
|
|
values[2] = values[3] = parse_signed_float (inst, Program);
|
|
}
|
|
|
|
break;
|
|
case CONSTANT_VECTOR:
|
|
values[0] = values[1] = values[2] = 0;
|
|
values[3] = 1;
|
|
components = *(*inst)++;
|
|
for (i = 0; i < components; i++) {
|
|
values[i] = parse_signed_float (inst, Program);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \param offset The offset from the address register that we should
|
|
* address
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_relative_offset(GLcontext *ctx, const GLubyte **inst,
|
|
struct arb_program *Program, GLint *offset)
|
|
{
|
|
(void) ctx;
|
|
*offset = parse_integer(inst, Program);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \param color 0 if color type is primary, 1 if color type is secondary
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_color_type (GLcontext * ctx, const GLubyte ** inst, struct arb_program *Program,
|
|
GLint * color)
|
|
{
|
|
(void) ctx; (void) Program;
|
|
*color = *(*inst)++ != COLOR_PRIMARY;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get an integer corresponding to a generic vertex attribute.
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_generic_attrib_num(GLcontext *ctx, const GLubyte ** inst,
|
|
struct arb_program *Program, GLuint *attrib)
|
|
{
|
|
GLint i = parse_integer(inst, Program);
|
|
|
|
if ((i < 0) || (i >= MAX_VERTEX_PROGRAM_ATTRIBS))
|
|
{
|
|
program_error(ctx, Program->Position,
|
|
"Invalid generic vertex attribute index");
|
|
return 1;
|
|
}
|
|
|
|
*attrib = (GLuint) i;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* \param color The index of the color buffer to write into
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_output_color_num (GLcontext * ctx, const GLubyte ** inst,
|
|
struct arb_program *Program, GLuint * color)
|
|
{
|
|
GLint i = parse_integer (inst, Program);
|
|
|
|
if ((i < 0) || (i >= (int)ctx->Const.MaxDrawBuffers)) {
|
|
program_error(ctx, Program->Position, "Invalid draw buffer index");
|
|
return 1;
|
|
}
|
|
|
|
*color = (GLuint) i;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* \param coord The texture unit index
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_texcoord_num (GLcontext * ctx, const GLubyte ** inst,
|
|
struct arb_program *Program, GLuint * coord)
|
|
{
|
|
GLint i = parse_integer (inst, Program);
|
|
|
|
if ((i < 0) || (i >= (int)ctx->Const.MaxTextureUnits)) {
|
|
program_error(ctx, Program->Position, "Invalid texture unit index");
|
|
return 1;
|
|
}
|
|
|
|
*coord = (GLuint) i;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \param coord The weight index
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_weight_num (GLcontext * ctx, const GLubyte ** inst, struct arb_program *Program,
|
|
GLint * coord)
|
|
{
|
|
*coord = parse_integer (inst, Program);
|
|
|
|
if ((*coord < 0) || (*coord >= 1)) {
|
|
program_error(ctx, Program->Position, "Invalid weight index");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \param coord The clip plane index
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_clipplane_num (GLcontext * ctx, const GLubyte ** inst,
|
|
struct arb_program *Program, GLint * coord)
|
|
{
|
|
*coord = parse_integer (inst, Program);
|
|
|
|
if ((*coord < 0) || (*coord >= (GLint) ctx->Const.MaxClipPlanes)) {
|
|
program_error(ctx, Program->Position, "Invalid clip plane index");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* \return 0 on front face, 1 on back face
|
|
*/
|
|
static GLuint
|
|
parse_face_type (const GLubyte ** inst)
|
|
{
|
|
switch (*(*inst)++) {
|
|
case FACE_FRONT:
|
|
return 0;
|
|
|
|
case FACE_BACK:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Given a matrix and a modifier token on the binary array, return tokens
|
|
* that _mesa_fetch_state() [program.c] can understand.
|
|
*
|
|
* \param matrix - the matrix we are talking about
|
|
* \param matrix_idx - the index of the matrix we have (for texture & program matricies)
|
|
* \param matrix_modifier - the matrix modifier (trans, inv, etc)
|
|
* \return 0 on sucess, 1 on failure
|
|
*/
|
|
static GLuint
|
|
parse_matrix (GLcontext * ctx, const GLubyte ** inst, struct arb_program *Program,
|
|
GLint * matrix, GLint * matrix_idx, GLint * matrix_modifier)
|
|
{
|
|
GLubyte mat = *(*inst)++;
|
|
|
|
*matrix_idx = 0;
|
|
|
|
switch (mat) {
|
|
case MATRIX_MODELVIEW:
|
|
*matrix = STATE_MODELVIEW;
|
|
*matrix_idx = parse_integer (inst, Program);
|
|
if (*matrix_idx > 0) {
|
|
program_error(ctx, Program->Position,
|
|
"ARB_vertex_blend not supported");
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case MATRIX_PROJECTION:
|
|
*matrix = STATE_PROJECTION;
|
|
break;
|
|
|
|
case MATRIX_MVP:
|
|
*matrix = STATE_MVP;
|
|
break;
|
|
|
|
case MATRIX_TEXTURE:
|
|
*matrix = STATE_TEXTURE;
|
|
*matrix_idx = parse_integer (inst, Program);
|
|
if (*matrix_idx >= (GLint) ctx->Const.MaxTextureUnits) {
|
|
program_error(ctx, Program->Position, "Invalid Texture Unit");
|
|
/* bad *matrix_id */
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
/* This is not currently supported (ARB_matrix_palette) */
|
|
case MATRIX_PALETTE:
|
|
*matrix_idx = parse_integer (inst, Program);
|
|
program_error(ctx, Program->Position,
|
|
"ARB_matrix_palette not supported");
|
|
return 1;
|
|
break;
|
|
|
|
case MATRIX_PROGRAM:
|
|
*matrix = STATE_PROGRAM;
|
|
*matrix_idx = parse_integer (inst, Program);
|
|
if (*matrix_idx >= (GLint) ctx->Const.MaxProgramMatrices) {
|
|
program_error(ctx, Program->Position, "Invalid Program Matrix");
|
|
/* bad *matrix_idx */
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (*(*inst)++) {
|
|
case MATRIX_MODIFIER_IDENTITY:
|
|
*matrix_modifier = 0;
|
|
break;
|
|
case MATRIX_MODIFIER_INVERSE:
|
|
*matrix_modifier = STATE_MATRIX_INVERSE;
|
|
break;
|
|
case MATRIX_MODIFIER_TRANSPOSE:
|
|
*matrix_modifier = STATE_MATRIX_TRANSPOSE;
|
|
break;
|
|
case MATRIX_MODIFIER_INVTRANS:
|
|
*matrix_modifier = STATE_MATRIX_INVTRANS;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* This parses a state string (rather, the binary version of it) into
|
|
* a 6-token sequence as described in _mesa_fetch_state() [program.c]
|
|
*
|
|
* \param inst - the start in the binary arry to start working from
|
|
* \param state_tokens - the storage for the 6-token state description
|
|
* \return - 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_state_single_item (GLcontext * ctx, const GLubyte ** inst,
|
|
struct arb_program *Program, GLint * state_tokens)
|
|
{
|
|
switch (*(*inst)++) {
|
|
case STATE_MATERIAL_PARSER:
|
|
state_tokens[0] = STATE_MATERIAL;
|
|
state_tokens[1] = parse_face_type (inst);
|
|
switch (*(*inst)++) {
|
|
case MATERIAL_AMBIENT:
|
|
state_tokens[2] = STATE_AMBIENT;
|
|
break;
|
|
case MATERIAL_DIFFUSE:
|
|
state_tokens[2] = STATE_DIFFUSE;
|
|
break;
|
|
case MATERIAL_SPECULAR:
|
|
state_tokens[2] = STATE_SPECULAR;
|
|
break;
|
|
case MATERIAL_EMISSION:
|
|
state_tokens[2] = STATE_EMISSION;
|
|
break;
|
|
case MATERIAL_SHININESS:
|
|
state_tokens[2] = STATE_SHININESS;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_LIGHT_PARSER:
|
|
state_tokens[0] = STATE_LIGHT;
|
|
state_tokens[1] = parse_integer (inst, Program);
|
|
|
|
/* Check the value of state_tokens[1] against the # of lights */
|
|
if (state_tokens[1] >= (GLint) ctx->Const.MaxLights) {
|
|
program_error(ctx, Program->Position, "Invalid Light Number");
|
|
/* bad state_tokens[1] */
|
|
return 1;
|
|
}
|
|
|
|
switch (*(*inst)++) {
|
|
case LIGHT_AMBIENT:
|
|
state_tokens[2] = STATE_AMBIENT;
|
|
break;
|
|
case LIGHT_DIFFUSE:
|
|
state_tokens[2] = STATE_DIFFUSE;
|
|
break;
|
|
case LIGHT_SPECULAR:
|
|
state_tokens[2] = STATE_SPECULAR;
|
|
break;
|
|
case LIGHT_POSITION:
|
|
state_tokens[2] = STATE_POSITION;
|
|
break;
|
|
case LIGHT_ATTENUATION:
|
|
state_tokens[2] = STATE_ATTENUATION;
|
|
break;
|
|
case LIGHT_HALF:
|
|
state_tokens[2] = STATE_HALF;
|
|
break;
|
|
case LIGHT_SPOT_DIRECTION:
|
|
state_tokens[2] = STATE_SPOT_DIRECTION;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_LIGHT_MODEL:
|
|
switch (*(*inst)++) {
|
|
case LIGHT_MODEL_AMBIENT:
|
|
state_tokens[0] = STATE_LIGHTMODEL_AMBIENT;
|
|
break;
|
|
case LIGHT_MODEL_SCENECOLOR:
|
|
state_tokens[0] = STATE_LIGHTMODEL_SCENECOLOR;
|
|
state_tokens[1] = parse_face_type (inst);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_LIGHT_PROD:
|
|
state_tokens[0] = STATE_LIGHTPROD;
|
|
state_tokens[1] = parse_integer (inst, Program);
|
|
|
|
/* Check the value of state_tokens[1] against the # of lights */
|
|
if (state_tokens[1] >= (GLint) ctx->Const.MaxLights) {
|
|
program_error(ctx, Program->Position, "Invalid Light Number");
|
|
/* bad state_tokens[1] */
|
|
return 1;
|
|
}
|
|
|
|
state_tokens[2] = parse_face_type (inst);
|
|
switch (*(*inst)++) {
|
|
case LIGHT_PROD_AMBIENT:
|
|
state_tokens[3] = STATE_AMBIENT;
|
|
break;
|
|
case LIGHT_PROD_DIFFUSE:
|
|
state_tokens[3] = STATE_DIFFUSE;
|
|
break;
|
|
case LIGHT_PROD_SPECULAR:
|
|
state_tokens[3] = STATE_SPECULAR;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case STATE_FOG:
|
|
switch (*(*inst)++) {
|
|
case FOG_COLOR:
|
|
state_tokens[0] = STATE_FOG_COLOR;
|
|
break;
|
|
case FOG_PARAMS:
|
|
state_tokens[0] = STATE_FOG_PARAMS;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_TEX_ENV:
|
|
state_tokens[1] = parse_integer (inst, Program);
|
|
switch (*(*inst)++) {
|
|
case TEX_ENV_COLOR:
|
|
state_tokens[0] = STATE_TEXENV_COLOR;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_TEX_GEN:
|
|
{
|
|
GLuint type, coord;
|
|
|
|
state_tokens[0] = STATE_TEXGEN;
|
|
/*state_tokens[1] = parse_integer (inst, Program);*/ /* Texture Unit */
|
|
|
|
if (parse_texcoord_num (ctx, inst, Program, &coord))
|
|
return 1;
|
|
state_tokens[1] = coord;
|
|
|
|
/* EYE or OBJECT */
|
|
type = *(*inst++);
|
|
|
|
/* 0 - s, 1 - t, 2 - r, 3 - q */
|
|
coord = *(*inst++);
|
|
|
|
if (type == TEX_GEN_EYE) {
|
|
switch (coord) {
|
|
case COMPONENT_X:
|
|
state_tokens[2] = STATE_TEXGEN_EYE_S;
|
|
break;
|
|
case COMPONENT_Y:
|
|
state_tokens[2] = STATE_TEXGEN_EYE_T;
|
|
break;
|
|
case COMPONENT_Z:
|
|
state_tokens[2] = STATE_TEXGEN_EYE_R;
|
|
break;
|
|
case COMPONENT_W:
|
|
state_tokens[2] = STATE_TEXGEN_EYE_Q;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (coord) {
|
|
case COMPONENT_X:
|
|
state_tokens[2] = STATE_TEXGEN_OBJECT_S;
|
|
break;
|
|
case COMPONENT_Y:
|
|
state_tokens[2] = STATE_TEXGEN_OBJECT_T;
|
|
break;
|
|
case COMPONENT_Z:
|
|
state_tokens[2] = STATE_TEXGEN_OBJECT_R;
|
|
break;
|
|
case COMPONENT_W:
|
|
state_tokens[2] = STATE_TEXGEN_OBJECT_Q;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATE_DEPTH:
|
|
switch (*(*inst)++) {
|
|
case DEPTH_RANGE:
|
|
state_tokens[0] = STATE_DEPTH_RANGE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_CLIP_PLANE:
|
|
state_tokens[0] = STATE_CLIPPLANE;
|
|
state_tokens[1] = parse_integer (inst, Program);
|
|
if (parse_clipplane_num (ctx, inst, Program, &state_tokens[1]))
|
|
return 1;
|
|
break;
|
|
|
|
case STATE_POINT:
|
|
switch (*(*inst++)) {
|
|
case POINT_SIZE:
|
|
state_tokens[0] = STATE_POINT_SIZE;
|
|
break;
|
|
|
|
case POINT_ATTENUATION:
|
|
state_tokens[0] = STATE_POINT_ATTENUATION;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* XXX: I think this is the correct format for a matrix row */
|
|
case STATE_MATRIX_ROWS:
|
|
state_tokens[0] = STATE_MATRIX;
|
|
if (parse_matrix
|
|
(ctx, inst, Program, &state_tokens[1], &state_tokens[2],
|
|
&state_tokens[5]))
|
|
return 1;
|
|
|
|
state_tokens[3] = parse_integer (inst, Program); /* The first row to grab */
|
|
|
|
if ((**inst) != 0) { /* Either the last row, 0 */
|
|
state_tokens[4] = parse_integer (inst, Program);
|
|
if (state_tokens[4] < state_tokens[3]) {
|
|
program_error(ctx, Program->Position,
|
|
"Second matrix index less than the first");
|
|
/* state_tokens[4] vs. state_tokens[3] */
|
|
return 1;
|
|
}
|
|
}
|
|
else {
|
|
state_tokens[4] = state_tokens[3];
|
|
(*inst)++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* This parses a state string (rather, the binary version of it) into
|
|
* a 6-token similar for the state fetching code in program.c
|
|
*
|
|
* One might ask, why fetch these parameters into just like you fetch
|
|
* state when they are already stored in other places?
|
|
*
|
|
* Because of array offsets -> We can stick env/local parameters in the
|
|
* middle of a parameter array and then index someplace into the array
|
|
* when we execute.
|
|
*
|
|
* One optimization might be to only do this for the cases where the
|
|
* env/local parameters end up inside of an array, and leave the
|
|
* single parameters (or arrays of pure env/local pareameters) in their
|
|
* respective register files.
|
|
*
|
|
* For ENV parameters, the format is:
|
|
* state_tokens[0] = STATE_FRAGMENT_PROGRAM / STATE_VERTEX_PROGRAM
|
|
* state_tokens[1] = STATE_ENV
|
|
* state_tokens[2] = the parameter index
|
|
*
|
|
* for LOCAL parameters, the format is:
|
|
* state_tokens[0] = STATE_FRAGMENT_PROGRAM / STATE_VERTEX_PROGRAM
|
|
* state_tokens[1] = STATE_LOCAL
|
|
* state_tokens[2] = the parameter index
|
|
*
|
|
* \param inst - the start in the binary arry to start working from
|
|
* \param state_tokens - the storage for the 6-token state description
|
|
* \return - 0 on sucess, 1 on failure
|
|
*/
|
|
static GLuint
|
|
parse_program_single_item (GLcontext * ctx, const GLubyte ** inst,
|
|
struct arb_program *Program, GLint * state_tokens)
|
|
{
|
|
if (Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB)
|
|
state_tokens[0] = STATE_FRAGMENT_PROGRAM;
|
|
else
|
|
state_tokens[0] = STATE_VERTEX_PROGRAM;
|
|
|
|
|
|
switch (*(*inst)++) {
|
|
case PROGRAM_PARAM_ENV:
|
|
state_tokens[1] = STATE_ENV;
|
|
state_tokens[2] = parse_integer (inst, Program);
|
|
|
|
/* Check state_tokens[2] against the number of ENV parameters available */
|
|
if (((Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) &&
|
|
(state_tokens[2] >= (GLint) ctx->Const.FragmentProgram.MaxEnvParams))
|
|
||
|
|
((Program->Base.Target == GL_VERTEX_PROGRAM_ARB) &&
|
|
(state_tokens[2] >= (GLint) ctx->Const.VertexProgram.MaxEnvParams))) {
|
|
program_error(ctx, Program->Position,
|
|
"Invalid Program Env Parameter");
|
|
/* bad state_tokens[2] */
|
|
return 1;
|
|
}
|
|
|
|
break;
|
|
|
|
case PROGRAM_PARAM_LOCAL:
|
|
state_tokens[1] = STATE_LOCAL;
|
|
state_tokens[2] = parse_integer (inst, Program);
|
|
|
|
/* Check state_tokens[2] against the number of LOCAL parameters available */
|
|
if (((Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) &&
|
|
(state_tokens[2] >= (GLint) ctx->Const.FragmentProgram.MaxLocalParams))
|
|
||
|
|
((Program->Base.Target == GL_VERTEX_PROGRAM_ARB) &&
|
|
(state_tokens[2] >= (GLint) ctx->Const.VertexProgram.MaxLocalParams))) {
|
|
program_error(ctx, Program->Position,
|
|
"Invalid Program Local Parameter");
|
|
/* bad state_tokens[2] */
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* For ARB_vertex_program, programs are not allowed to use both an explicit
|
|
* vertex attribute and a generic vertex attribute corresponding to the same
|
|
* state. See section 2.14.3.1 of the GL_ARB_vertex_program spec.
|
|
*
|
|
* This will walk our var_cache and make sure that nobody does anything fishy.
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
generic_attrib_check(struct var_cache *vc_head)
|
|
{
|
|
int a;
|
|
struct var_cache *curr;
|
|
GLboolean explicitAttrib[MAX_VERTEX_PROGRAM_ATTRIBS],
|
|
genericAttrib[MAX_VERTEX_PROGRAM_ATTRIBS];
|
|
|
|
for (a=0; a<MAX_VERTEX_PROGRAM_ATTRIBS; a++) {
|
|
explicitAttrib[a] = GL_FALSE;
|
|
genericAttrib[a] = GL_FALSE;
|
|
}
|
|
|
|
curr = vc_head;
|
|
while (curr) {
|
|
if (curr->type == vt_attrib) {
|
|
if (curr->attrib_is_generic)
|
|
genericAttrib[ curr->attrib_binding ] = GL_TRUE;
|
|
else
|
|
explicitAttrib[ curr->attrib_binding ] = GL_TRUE;
|
|
}
|
|
|
|
curr = curr->next;
|
|
}
|
|
|
|
for (a=0; a<MAX_VERTEX_PROGRAM_ATTRIBS; a++) {
|
|
if ((explicitAttrib[a]) && (genericAttrib[a]))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* This will handle the binding side of an ATTRIB var declaration
|
|
*
|
|
* \param inputReg returns the input register index, one of the
|
|
* VERT_ATTRIB_* or FRAG_ATTRIB_* values.
|
|
* \return returns 0 on success, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_attrib_binding(GLcontext * ctx, const GLubyte ** inst,
|
|
struct arb_program *Program,
|
|
GLuint *inputReg, GLuint *is_generic)
|
|
{
|
|
GLint err = 0;
|
|
|
|
*is_generic = 0;
|
|
|
|
if (Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) {
|
|
switch (*(*inst)++) {
|
|
case FRAGMENT_ATTRIB_COLOR:
|
|
{
|
|
GLint coord;
|
|
err = parse_color_type (ctx, inst, Program, &coord);
|
|
*inputReg = FRAG_ATTRIB_COL0 + coord;
|
|
}
|
|
break;
|
|
case FRAGMENT_ATTRIB_TEXCOORD:
|
|
{
|
|
GLuint texcoord;
|
|
err = parse_texcoord_num (ctx, inst, Program, &texcoord);
|
|
*inputReg = FRAG_ATTRIB_TEX0 + texcoord;
|
|
}
|
|
break;
|
|
case FRAGMENT_ATTRIB_FOGCOORD:
|
|
*inputReg = FRAG_ATTRIB_FOGC;
|
|
break;
|
|
case FRAGMENT_ATTRIB_POSITION:
|
|
*inputReg = FRAG_ATTRIB_WPOS;
|
|
break;
|
|
default:
|
|
err = 1;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (*(*inst)++) {
|
|
case VERTEX_ATTRIB_POSITION:
|
|
*inputReg = VERT_ATTRIB_POS;
|
|
break;
|
|
|
|
case VERTEX_ATTRIB_WEIGHT:
|
|
{
|
|
GLint weight;
|
|
err = parse_weight_num (ctx, inst, Program, &weight);
|
|
*inputReg = VERT_ATTRIB_WEIGHT;
|
|
#if 1
|
|
/* hack for Warcraft (see bug 8060) */
|
|
_mesa_warning(ctx, "Application error: vertex program uses 'vertex.weight' but GL_ARB_vertex_blend not supported.");
|
|
break;
|
|
#else
|
|
program_error(ctx, Program->Position,
|
|
"ARB_vertex_blend not supported");
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
case VERTEX_ATTRIB_NORMAL:
|
|
*inputReg = VERT_ATTRIB_NORMAL;
|
|
break;
|
|
|
|
case VERTEX_ATTRIB_COLOR:
|
|
{
|
|
GLint color;
|
|
err = parse_color_type (ctx, inst, Program, &color);
|
|
if (color) {
|
|
*inputReg = VERT_ATTRIB_COLOR1;
|
|
}
|
|
else {
|
|
*inputReg = VERT_ATTRIB_COLOR0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VERTEX_ATTRIB_FOGCOORD:
|
|
*inputReg = VERT_ATTRIB_FOG;
|
|
break;
|
|
|
|
case VERTEX_ATTRIB_TEXCOORD:
|
|
{
|
|
GLuint unit;
|
|
err = parse_texcoord_num (ctx, inst, Program, &unit);
|
|
*inputReg = VERT_ATTRIB_TEX0 + unit;
|
|
}
|
|
break;
|
|
|
|
case VERTEX_ATTRIB_MATRIXINDEX:
|
|
/* Not supported at this time */
|
|
{
|
|
const char *msg = "ARB_palette_matrix not supported";
|
|
parse_integer (inst, Program);
|
|
program_error(ctx, Program->Position, msg);
|
|
}
|
|
return 1;
|
|
|
|
case VERTEX_ATTRIB_GENERIC:
|
|
{
|
|
GLuint attrib;
|
|
err = parse_generic_attrib_num(ctx, inst, Program, &attrib);
|
|
if (!err) {
|
|
*is_generic = 1;
|
|
/* Add VERT_ATTRIB_GENERIC0 here because ARB_vertex_program's
|
|
* attributes do not alias the conventional vertex
|
|
* attributes.
|
|
*/
|
|
if (attrib > 0)
|
|
*inputReg = attrib + VERT_ATTRIB_GENERIC0;
|
|
else
|
|
*inputReg = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
err = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (err) {
|
|
program_error(ctx, Program->Position, "Bad attribute binding");
|
|
}
|
|
|
|
Program->Base.InputsRead |= (1 << *inputReg);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/**
|
|
* This translates between a binary token for an output variable type
|
|
* and the mesa token for the same thing.
|
|
*
|
|
* \param inst The parsed tokens
|
|
* \param outputReg Returned index/number of the output register,
|
|
* one of the VERT_RESULT_* or FRAG_RESULT_* values.
|
|
*/
|
|
static GLuint
|
|
parse_result_binding(GLcontext *ctx, const GLubyte **inst,
|
|
GLuint *outputReg, struct arb_program *Program)
|
|
{
|
|
const GLubyte token = *(*inst)++;
|
|
|
|
switch (token) {
|
|
case FRAGMENT_RESULT_COLOR:
|
|
if (Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) {
|
|
GLuint out_color;
|
|
|
|
/* This gets result of the color buffer we're supposed to
|
|
* draw into. This pertains to GL_ARB_draw_buffers.
|
|
*/
|
|
parse_output_color_num(ctx, inst, Program, &out_color);
|
|
ASSERT(out_color < MAX_DRAW_BUFFERS);
|
|
*outputReg = FRAG_RESULT_COLR;
|
|
}
|
|
else {
|
|
/* for vtx programs, this is VERTEX_RESULT_POSITION */
|
|
*outputReg = VERT_RESULT_HPOS;
|
|
}
|
|
break;
|
|
|
|
case FRAGMENT_RESULT_DEPTH:
|
|
if (Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) {
|
|
/* for frag programs, this is FRAGMENT_RESULT_DEPTH */
|
|
*outputReg = FRAG_RESULT_DEPR;
|
|
}
|
|
else {
|
|
/* for vtx programs, this is VERTEX_RESULT_COLOR */
|
|
GLint color_type;
|
|
GLuint face_type = parse_face_type(inst);
|
|
GLint err = parse_color_type(ctx, inst, Program, &color_type);
|
|
if (err)
|
|
return 1;
|
|
|
|
if (face_type) {
|
|
/* back face */
|
|
if (color_type) {
|
|
*outputReg = VERT_RESULT_BFC1; /* secondary color */
|
|
}
|
|
else {
|
|
*outputReg = VERT_RESULT_BFC0; /* primary color */
|
|
}
|
|
}
|
|
else {
|
|
/* front face */
|
|
if (color_type) {
|
|
*outputReg = VERT_RESULT_COL1; /* secondary color */
|
|
}
|
|
/* primary color */
|
|
else {
|
|
*outputReg = VERT_RESULT_COL0; /* primary color */
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VERTEX_RESULT_FOGCOORD:
|
|
*outputReg = VERT_RESULT_FOGC;
|
|
break;
|
|
|
|
case VERTEX_RESULT_POINTSIZE:
|
|
*outputReg = VERT_RESULT_PSIZ;
|
|
break;
|
|
|
|
case VERTEX_RESULT_TEXCOORD:
|
|
{
|
|
GLuint unit;
|
|
if (parse_texcoord_num (ctx, inst, Program, &unit))
|
|
return 1;
|
|
*outputReg = VERT_RESULT_TEX0 + unit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
Program->Base.OutputsWritten |= (1 << *outputReg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* This handles the declaration of ATTRIB variables
|
|
*
|
|
* XXX: Still needs
|
|
* parse_vert_attrib_binding(), or something like that
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLint
|
|
parse_attrib (GLcontext * ctx, const GLubyte ** inst, struct var_cache **vc_head,
|
|
struct arb_program *Program)
|
|
{
|
|
GLuint found;
|
|
char *error_msg;
|
|
struct var_cache *attrib_var;
|
|
|
|
attrib_var = parse_string (inst, vc_head, Program, &found);
|
|
Program->Position = parse_position (inst);
|
|
if (found) {
|
|
error_msg = (char *)
|
|
_mesa_malloc (_mesa_strlen ((char *) attrib_var->name) + 40);
|
|
_mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
|
|
attrib_var->name);
|
|
program_error(ctx, Program->Position, error_msg);
|
|
_mesa_free (error_msg);
|
|
return 1;
|
|
}
|
|
|
|
attrib_var->type = vt_attrib;
|
|
|
|
if (parse_attrib_binding(ctx, inst, Program, &attrib_var->attrib_binding,
|
|
&attrib_var->attrib_is_generic))
|
|
return 1;
|
|
|
|
if (generic_attrib_check(*vc_head)) {
|
|
program_error(ctx, Program->Position,
|
|
"Cannot use both a generic vertex attribute "
|
|
"and a specific attribute of the same type");
|
|
return 1;
|
|
}
|
|
|
|
Program->Base.NumAttributes++;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \param use -- TRUE if we're called when declaring implicit parameters,
|
|
* FALSE if we're declaraing variables. This has to do with
|
|
* if we get a signed or unsigned float for scalar constants
|
|
*/
|
|
static GLuint
|
|
parse_param_elements (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache *param_var,
|
|
struct arb_program *Program, GLboolean use)
|
|
{
|
|
GLint idx;
|
|
GLuint err = 0;
|
|
GLint state_tokens[6];
|
|
GLfloat const_values[4];
|
|
|
|
switch (*(*inst)++) {
|
|
case PARAM_STATE_ELEMENT:
|
|
if (parse_state_single_item (ctx, inst, Program, state_tokens))
|
|
return 1;
|
|
|
|
/* If we adding STATE_MATRIX that has multiple rows, we need to
|
|
* unroll it and call _mesa_add_state_reference() for each row
|
|
*/
|
|
if ((state_tokens[0] == STATE_MATRIX)
|
|
&& (state_tokens[3] != state_tokens[4])) {
|
|
GLint row;
|
|
GLint first_row = state_tokens[3];
|
|
GLint last_row = state_tokens[4];
|
|
|
|
for (row = first_row; row <= last_row; row++) {
|
|
state_tokens[3] = state_tokens[4] = row;
|
|
|
|
idx = _mesa_add_state_reference(Program->Base.Parameters,
|
|
state_tokens);
|
|
if (param_var->param_binding_begin == ~0U)
|
|
param_var->param_binding_begin = idx;
|
|
param_var->param_binding_length++;
|
|
Program->Base.NumParameters++;
|
|
}
|
|
}
|
|
else {
|
|
idx = _mesa_add_state_reference(Program->Base.Parameters,
|
|
state_tokens);
|
|
if (param_var->param_binding_begin == ~0U)
|
|
param_var->param_binding_begin = idx;
|
|
param_var->param_binding_length++;
|
|
Program->Base.NumParameters++;
|
|
}
|
|
break;
|
|
|
|
case PARAM_PROGRAM_ELEMENT:
|
|
if (parse_program_single_item (ctx, inst, Program, state_tokens))
|
|
return 1;
|
|
idx = _mesa_add_state_reference (Program->Base.Parameters, state_tokens);
|
|
if (param_var->param_binding_begin == ~0U)
|
|
param_var->param_binding_begin = idx;
|
|
param_var->param_binding_length++;
|
|
Program->Base.NumParameters++;
|
|
|
|
/* Check if there is more: 0 -> we're done, else its an integer */
|
|
if (**inst) {
|
|
GLuint out_of_range, new_idx;
|
|
GLuint start_idx = state_tokens[2] + 1;
|
|
GLuint end_idx = parse_integer (inst, Program);
|
|
|
|
out_of_range = 0;
|
|
if (Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) {
|
|
if (((state_tokens[1] == STATE_ENV)
|
|
&& (end_idx >= ctx->Const.FragmentProgram.MaxEnvParams))
|
|
|| ((state_tokens[1] == STATE_LOCAL)
|
|
&& (end_idx >=
|
|
ctx->Const.FragmentProgram.MaxLocalParams)))
|
|
out_of_range = 1;
|
|
}
|
|
else {
|
|
if (((state_tokens[1] == STATE_ENV)
|
|
&& (end_idx >= ctx->Const.VertexProgram.MaxEnvParams))
|
|
|| ((state_tokens[1] == STATE_LOCAL)
|
|
&& (end_idx >=
|
|
ctx->Const.VertexProgram.MaxLocalParams)))
|
|
out_of_range = 1;
|
|
}
|
|
if (out_of_range) {
|
|
program_error(ctx, Program->Position,
|
|
"Invalid Program Parameter"); /*end_idx*/
|
|
return 1;
|
|
}
|
|
|
|
for (new_idx = start_idx; new_idx <= end_idx; new_idx++) {
|
|
state_tokens[2] = new_idx;
|
|
idx = _mesa_add_state_reference(Program->Base.Parameters,
|
|
state_tokens);
|
|
param_var->param_binding_length++;
|
|
Program->Base.NumParameters++;
|
|
}
|
|
}
|
|
else {
|
|
(*inst)++;
|
|
}
|
|
break;
|
|
|
|
case PARAM_CONSTANT:
|
|
parse_constant (inst, const_values, Program, use);
|
|
idx = _mesa_add_named_constant(Program->Base.Parameters,
|
|
(char *) param_var->name,
|
|
const_values, 4);
|
|
if (param_var->param_binding_begin == ~0U)
|
|
param_var->param_binding_begin = idx;
|
|
param_var->param_binding_length++;
|
|
Program->Base.NumParameters++;
|
|
break;
|
|
|
|
default:
|
|
program_error(ctx, Program->Position,
|
|
"Unexpected token (in parse_param_elements())");
|
|
return 1;
|
|
}
|
|
|
|
/* Make sure we haven't blown past our parameter limits */
|
|
if (((Program->Base.Target == GL_VERTEX_PROGRAM_ARB) &&
|
|
(Program->Base.NumParameters >=
|
|
ctx->Const.VertexProgram.MaxLocalParams))
|
|
|| ((Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB)
|
|
&& (Program->Base.NumParameters >=
|
|
ctx->Const.FragmentProgram.MaxLocalParams))) {
|
|
program_error(ctx, Program->Position, "Too many parameter variables");
|
|
return 1;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/**
|
|
* This picks out PARAM program parameter bindings.
|
|
*
|
|
* XXX: This needs to be stressed & tested
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_param (GLcontext * ctx, const GLubyte ** inst, struct var_cache **vc_head,
|
|
struct arb_program *Program)
|
|
{
|
|
GLuint found, err;
|
|
GLint specified_length;
|
|
struct var_cache *param_var;
|
|
|
|
err = 0;
|
|
param_var = parse_string (inst, vc_head, Program, &found);
|
|
Program->Position = parse_position (inst);
|
|
|
|
if (found) {
|
|
char *error_msg = (char *)
|
|
_mesa_malloc (_mesa_strlen ((char *) param_var->name) + 40);
|
|
_mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
|
|
param_var->name);
|
|
program_error (ctx, Program->Position, error_msg);
|
|
_mesa_free (error_msg);
|
|
return 1;
|
|
}
|
|
|
|
specified_length = parse_integer (inst, Program);
|
|
|
|
if (specified_length < 0) {
|
|
program_error(ctx, Program->Position, "Negative parameter array length");
|
|
return 1;
|
|
}
|
|
|
|
param_var->type = vt_param;
|
|
param_var->param_binding_length = 0;
|
|
|
|
/* Right now, everything is shoved into the main state register file.
|
|
*
|
|
* In the future, it would be nice to leave things ENV/LOCAL params
|
|
* in their respective register files, if possible
|
|
*/
|
|
param_var->param_binding_type = PROGRAM_STATE_VAR;
|
|
|
|
/* Remember to:
|
|
* * - add each guy to the parameter list
|
|
* * - increment the param_var->param_binding_len
|
|
* * - store the param_var->param_binding_begin for the first one
|
|
* * - compare the actual len to the specified len at the end
|
|
*/
|
|
while (**inst != PARAM_NULL) {
|
|
if (parse_param_elements (ctx, inst, param_var, Program, GL_FALSE))
|
|
return 1;
|
|
}
|
|
|
|
/* Test array length here! */
|
|
if (specified_length) {
|
|
if (specified_length != (int)param_var->param_binding_length) {
|
|
program_error(ctx, Program->Position,
|
|
"Declared parameter array length does not match parameter list");
|
|
}
|
|
}
|
|
|
|
(*inst)++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static GLuint
|
|
parse_param_use (GLcontext * ctx, const GLubyte ** inst, struct var_cache **vc_head,
|
|
struct arb_program *Program, struct var_cache **new_var)
|
|
{
|
|
struct var_cache *param_var;
|
|
|
|
/* First, insert a dummy entry into the var_cache */
|
|
var_cache_create (¶m_var);
|
|
param_var->name = (const GLubyte *) " ";
|
|
param_var->type = vt_param;
|
|
|
|
param_var->param_binding_length = 0;
|
|
/* Don't fill in binding_begin; We use the default value of -1
|
|
* to tell if its already initialized, elsewhere.
|
|
*
|
|
* param_var->param_binding_begin = 0;
|
|
*/
|
|
param_var->param_binding_type = PROGRAM_STATE_VAR;
|
|
|
|
var_cache_append (vc_head, param_var);
|
|
|
|
/* Then fill it with juicy parameter goodness */
|
|
if (parse_param_elements (ctx, inst, param_var, Program, GL_TRUE))
|
|
return 1;
|
|
|
|
*new_var = param_var;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* This handles the declaration of TEMP variables
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_temp (GLcontext * ctx, const GLubyte ** inst, struct var_cache **vc_head,
|
|
struct arb_program *Program)
|
|
{
|
|
GLuint found;
|
|
struct var_cache *temp_var;
|
|
|
|
while (**inst != 0) {
|
|
temp_var = parse_string (inst, vc_head, Program, &found);
|
|
Program->Position = parse_position (inst);
|
|
if (found) {
|
|
char *error_msg = (char *)
|
|
_mesa_malloc (_mesa_strlen ((char *) temp_var->name) + 40);
|
|
_mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
|
|
temp_var->name);
|
|
program_error(ctx, Program->Position, error_msg);
|
|
_mesa_free (error_msg);
|
|
return 1;
|
|
}
|
|
|
|
temp_var->type = vt_temp;
|
|
|
|
if (((Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) &&
|
|
(Program->Base.NumTemporaries >=
|
|
ctx->Const.FragmentProgram.MaxTemps))
|
|
|| ((Program->Base.Target == GL_VERTEX_PROGRAM_ARB)
|
|
&& (Program->Base.NumTemporaries >=
|
|
ctx->Const.VertexProgram.MaxTemps))) {
|
|
program_error(ctx, Program->Position,
|
|
"Too many TEMP variables declared");
|
|
return 1;
|
|
}
|
|
|
|
temp_var->temp_binding = Program->Base.NumTemporaries;
|
|
Program->Base.NumTemporaries++;
|
|
}
|
|
(*inst)++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* This handles variables of the OUTPUT variety
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_output (GLcontext * ctx, const GLubyte ** inst, struct var_cache **vc_head,
|
|
struct arb_program *Program)
|
|
{
|
|
GLuint found;
|
|
struct var_cache *output_var;
|
|
GLuint err;
|
|
|
|
output_var = parse_string (inst, vc_head, Program, &found);
|
|
Program->Position = parse_position (inst);
|
|
if (found) {
|
|
char *error_msg = (char *)
|
|
_mesa_malloc (_mesa_strlen ((char *) output_var->name) + 40);
|
|
_mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
|
|
output_var->name);
|
|
program_error (ctx, Program->Position, error_msg);
|
|
_mesa_free (error_msg);
|
|
return 1;
|
|
}
|
|
|
|
output_var->type = vt_output;
|
|
|
|
err = parse_result_binding(ctx, inst, &output_var->output_binding, Program);
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* This handles variables of the ALIAS kind
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_alias (GLcontext * ctx, const GLubyte ** inst, struct var_cache **vc_head,
|
|
struct arb_program *Program)
|
|
{
|
|
GLuint found;
|
|
struct var_cache *temp_var;
|
|
|
|
temp_var = parse_string (inst, vc_head, Program, &found);
|
|
Program->Position = parse_position (inst);
|
|
|
|
if (found) {
|
|
char *error_msg = (char *)
|
|
_mesa_malloc (_mesa_strlen ((char *) temp_var->name) + 40);
|
|
_mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
|
|
temp_var->name);
|
|
program_error(ctx, Program->Position, error_msg);
|
|
_mesa_free (error_msg);
|
|
return 1;
|
|
}
|
|
|
|
temp_var->type = vt_alias;
|
|
temp_var->alias_binding = parse_string (inst, vc_head, Program, &found);
|
|
Program->Position = parse_position (inst);
|
|
|
|
if (!found)
|
|
{
|
|
char *error_msg = (char *)
|
|
_mesa_malloc (_mesa_strlen ((char *) temp_var->name) + 40);
|
|
_mesa_sprintf (error_msg, "Alias value %s is not defined",
|
|
temp_var->alias_binding->name);
|
|
program_error (ctx, Program->Position, error_msg);
|
|
_mesa_free (error_msg);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* This handles variables of the ADDRESS kind
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_address (GLcontext * ctx, const GLubyte ** inst, struct var_cache **vc_head,
|
|
struct arb_program *Program)
|
|
{
|
|
GLuint found;
|
|
struct var_cache *temp_var;
|
|
|
|
while (**inst != 0) {
|
|
temp_var = parse_string (inst, vc_head, Program, &found);
|
|
Program->Position = parse_position (inst);
|
|
if (found) {
|
|
char *error_msg = (char *)
|
|
_mesa_malloc (_mesa_strlen ((char *) temp_var->name) + 40);
|
|
_mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
|
|
temp_var->name);
|
|
program_error (ctx, Program->Position, error_msg);
|
|
_mesa_free (error_msg);
|
|
return 1;
|
|
}
|
|
|
|
temp_var->type = vt_address;
|
|
|
|
if (Program->Base.NumAddressRegs >=
|
|
ctx->Const.VertexProgram.MaxAddressRegs) {
|
|
const char *msg = "Too many ADDRESS variables declared";
|
|
program_error(ctx, Program->Position, msg);
|
|
return 1;
|
|
}
|
|
|
|
temp_var->address_binding = Program->Base.NumAddressRegs;
|
|
Program->Base.NumAddressRegs++;
|
|
}
|
|
(*inst)++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse a program declaration
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLint
|
|
parse_declaration (GLcontext * ctx, const GLubyte ** inst, struct var_cache **vc_head,
|
|
struct arb_program *Program)
|
|
{
|
|
GLint err = 0;
|
|
|
|
switch (*(*inst)++) {
|
|
case ADDRESS:
|
|
err = parse_address (ctx, inst, vc_head, Program);
|
|
break;
|
|
|
|
case ALIAS:
|
|
err = parse_alias (ctx, inst, vc_head, Program);
|
|
break;
|
|
|
|
case ATTRIB:
|
|
err = parse_attrib (ctx, inst, vc_head, Program);
|
|
break;
|
|
|
|
case OUTPUT:
|
|
err = parse_output (ctx, inst, vc_head, Program);
|
|
break;
|
|
|
|
case PARAM:
|
|
err = parse_param (ctx, inst, vc_head, Program);
|
|
break;
|
|
|
|
case TEMP:
|
|
err = parse_temp (ctx, inst, vc_head, Program);
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* Handle the parsing out of a masked destination register, either for a
|
|
* vertex or fragment program.
|
|
*
|
|
* If we are a vertex program, make sure we don't write to
|
|
* result.position if we have specified that the program is
|
|
* position invariant
|
|
*
|
|
* \param File - The register file we write to
|
|
* \param Index - The register index we write to
|
|
* \param WriteMask - The mask controlling which components we write (1->write)
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_masked_dst_reg (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head, struct arb_program *Program,
|
|
enum register_file *File, GLuint *Index, GLint *WriteMask)
|
|
{
|
|
GLuint tmp, result;
|
|
struct var_cache *dst;
|
|
|
|
/* We either have a result register specified, or a
|
|
* variable that may or may not be writable
|
|
*/
|
|
switch (*(*inst)++) {
|
|
case REGISTER_RESULT:
|
|
if (parse_result_binding(ctx, inst, Index, Program))
|
|
return 1;
|
|
*File = PROGRAM_OUTPUT;
|
|
break;
|
|
|
|
case REGISTER_ESTABLISHED_NAME:
|
|
dst = parse_string (inst, vc_head, Program, &result);
|
|
Program->Position = parse_position (inst);
|
|
|
|
/* If the name has never been added to our symbol table, we're hosed */
|
|
if (!result) {
|
|
program_error(ctx, Program->Position, "0: Undefined variable");
|
|
return 1;
|
|
}
|
|
|
|
switch (dst->type) {
|
|
case vt_output:
|
|
*File = PROGRAM_OUTPUT;
|
|
*Index = dst->output_binding;
|
|
break;
|
|
|
|
case vt_temp:
|
|
*File = PROGRAM_TEMPORARY;
|
|
*Index = dst->temp_binding;
|
|
break;
|
|
|
|
/* If the var type is not vt_output or vt_temp, no go */
|
|
default:
|
|
program_error(ctx, Program->Position,
|
|
"Destination register is read only");
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
program_error(ctx, Program->Position,
|
|
"Unexpected opcode in parse_masked_dst_reg()");
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* Position invariance test */
|
|
/* This test is done now in syntax portion - when position invariance OPTION
|
|
is specified, "result.position" rule is disabled so there is no way
|
|
to write the position
|
|
*/
|
|
/*if ((Program->HintPositionInvariant) && (*File == PROGRAM_OUTPUT) &&
|
|
(*Index == 0)) {
|
|
program_error(ctx, Program->Position,
|
|
"Vertex program specified position invariance and wrote vertex position");
|
|
}*/
|
|
|
|
/* And then the mask.
|
|
* w,a -> bit 0
|
|
* z,b -> bit 1
|
|
* y,g -> bit 2
|
|
* x,r -> bit 3
|
|
*
|
|
* ==> Need to reverse the order of bits for this!
|
|
*/
|
|
tmp = (GLint) *(*inst)++;
|
|
*WriteMask = (((tmp>>3) & 0x1) |
|
|
((tmp>>1) & 0x2) |
|
|
((tmp<<1) & 0x4) |
|
|
((tmp<<3) & 0x8));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Handle the parsing of a address register
|
|
*
|
|
* \param Index - The register index we write to
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_address_reg (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head,
|
|
struct arb_program *Program, GLint * Index)
|
|
{
|
|
struct var_cache *dst;
|
|
GLuint result;
|
|
|
|
*Index = 0; /* XXX */
|
|
|
|
dst = parse_string (inst, vc_head, Program, &result);
|
|
Program->Position = parse_position (inst);
|
|
|
|
/* If the name has never been added to our symbol table, we're hosed */
|
|
if (!result) {
|
|
program_error(ctx, Program->Position, "Undefined variable");
|
|
return 1;
|
|
}
|
|
|
|
if (dst->type != vt_address) {
|
|
program_error(ctx, Program->Position, "Variable is not of type ADDRESS");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if 0 /* unused */
|
|
/**
|
|
* Handle the parsing out of a masked address register
|
|
*
|
|
* \param Index - The register index we write to
|
|
* \param WriteMask - The mask controlling which components we write (1->write)
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_masked_address_reg (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head,
|
|
struct arb_program *Program, GLint * Index,
|
|
GLboolean * WriteMask)
|
|
{
|
|
if (parse_address_reg (ctx, inst, vc_head, Program, Index))
|
|
return 1;
|
|
|
|
/* This should be 0x8 */
|
|
(*inst)++;
|
|
|
|
/* Writemask of .x is implied */
|
|
WriteMask[0] = 1;
|
|
WriteMask[1] = WriteMask[2] = WriteMask[3] = 0;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Parse out a swizzle mask.
|
|
*
|
|
* Basically convert COMPONENT_X/Y/Z/W to SWIZZLE_X/Y/Z/W
|
|
*
|
|
* The len parameter allows us to grab 4 components for a vector
|
|
* swizzle, or just 1 component for a scalar src register selection
|
|
*/
|
|
static void
|
|
parse_swizzle_mask(const GLubyte ** inst, GLubyte *swizzle, GLint len)
|
|
{
|
|
GLint i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
swizzle[i] = i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
switch (*(*inst)++) {
|
|
case COMPONENT_X:
|
|
swizzle[i] = SWIZZLE_X;
|
|
break;
|
|
case COMPONENT_Y:
|
|
swizzle[i] = SWIZZLE_Y;
|
|
break;
|
|
case COMPONENT_Z:
|
|
swizzle[i] = SWIZZLE_Z;
|
|
break;
|
|
case COMPONENT_W:
|
|
swizzle[i] = SWIZZLE_W;
|
|
break;
|
|
default:
|
|
_mesa_problem(NULL, "bad component in parse_swizzle_mask()");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse an extended swizzle mask which is a sequence of
|
|
* four x/y/z/w/0/1 tokens.
|
|
* \return swizzle four swizzle values
|
|
* \return negateMask four element bitfield
|
|
*/
|
|
static void
|
|
parse_extended_swizzle_mask(const GLubyte **inst, GLubyte swizzle[4],
|
|
GLubyte *negateMask)
|
|
{
|
|
GLint i;
|
|
|
|
*negateMask = 0x0;
|
|
for (i = 0; i < 4; i++) {
|
|
GLubyte swz;
|
|
if (parse_sign(inst) == -1)
|
|
*negateMask |= (1 << i);
|
|
|
|
swz = *(*inst)++;
|
|
|
|
switch (swz) {
|
|
case COMPONENT_0:
|
|
swizzle[i] = SWIZZLE_ZERO;
|
|
break;
|
|
case COMPONENT_1:
|
|
swizzle[i] = SWIZZLE_ONE;
|
|
break;
|
|
case COMPONENT_X:
|
|
swizzle[i] = SWIZZLE_X;
|
|
break;
|
|
case COMPONENT_Y:
|
|
swizzle[i] = SWIZZLE_Y;
|
|
break;
|
|
case COMPONENT_Z:
|
|
swizzle[i] = SWIZZLE_Z;
|
|
break;
|
|
case COMPONENT_W:
|
|
swizzle[i] = SWIZZLE_W;
|
|
break;
|
|
default:
|
|
_mesa_problem(NULL, "bad case in parse_extended_swizzle_mask()");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static GLuint
|
|
parse_src_reg (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head,
|
|
struct arb_program *Program,
|
|
enum register_file * File, GLint * Index,
|
|
GLboolean *IsRelOffset )
|
|
{
|
|
struct var_cache *src;
|
|
GLuint binding, is_generic, found;
|
|
GLint offset;
|
|
|
|
*IsRelOffset = 0;
|
|
|
|
/* And the binding for the src */
|
|
switch (*(*inst)++) {
|
|
case REGISTER_ATTRIB:
|
|
if (parse_attrib_binding
|
|
(ctx, inst, Program, &binding, &is_generic))
|
|
return 1;
|
|
*File = PROGRAM_INPUT;
|
|
*Index = binding;
|
|
|
|
/* We need to insert a dummy variable into the var_cache so we can
|
|
* catch generic vertex attrib aliasing errors
|
|
*/
|
|
var_cache_create(&src);
|
|
src->type = vt_attrib;
|
|
src->name = (const GLubyte *) "Dummy Attrib Variable";
|
|
src->attrib_binding = binding;
|
|
src->attrib_is_generic = is_generic;
|
|
var_cache_append(vc_head, src);
|
|
if (generic_attrib_check(*vc_head)) {
|
|
program_error(ctx, Program->Position,
|
|
"Cannot use both a generic vertex attribute "
|
|
"and a specific attribute of the same type");
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case REGISTER_PARAM:
|
|
switch (**inst) {
|
|
case PARAM_ARRAY_ELEMENT:
|
|
(*inst)++;
|
|
src = parse_string (inst, vc_head, Program, &found);
|
|
Program->Position = parse_position (inst);
|
|
|
|
if (!found) {
|
|
program_error(ctx, Program->Position,
|
|
"2: Undefined variable"); /* src->name */
|
|
return 1;
|
|
}
|
|
|
|
*File = (enum register_file) src->param_binding_type;
|
|
|
|
switch (*(*inst)++) {
|
|
case ARRAY_INDEX_ABSOLUTE:
|
|
offset = parse_integer (inst, Program);
|
|
|
|
if ((offset < 0)
|
|
|| (offset >= (int)src->param_binding_length)) {
|
|
program_error(ctx, Program->Position,
|
|
"Index out of range");
|
|
/* offset, src->name */
|
|
return 1;
|
|
}
|
|
|
|
*Index = src->param_binding_begin + offset;
|
|
break;
|
|
|
|
case ARRAY_INDEX_RELATIVE:
|
|
{
|
|
GLint addr_reg_idx, rel_off;
|
|
|
|
/* First, grab the address regiseter */
|
|
if (parse_address_reg (ctx, inst, vc_head, Program, &addr_reg_idx))
|
|
return 1;
|
|
|
|
/* And the .x */
|
|
((*inst)++);
|
|
((*inst)++);
|
|
((*inst)++);
|
|
((*inst)++);
|
|
|
|
/* Then the relative offset */
|
|
if (parse_relative_offset(ctx, inst, Program, &rel_off)) return 1;
|
|
|
|
/* And store it properly */
|
|
*Index = src->param_binding_begin + rel_off;
|
|
*IsRelOffset = 1;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (parse_param_use (ctx, inst, vc_head, Program, &src))
|
|
return 1;
|
|
|
|
*File = (enum register_file) src->param_binding_type;
|
|
*Index = src->param_binding_begin;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case REGISTER_ESTABLISHED_NAME:
|
|
src = parse_string (inst, vc_head, Program, &found);
|
|
Program->Position = parse_position (inst);
|
|
|
|
/* If the name has never been added to our symbol table, we're hosed */
|
|
if (!found) {
|
|
program_error(ctx, Program->Position,
|
|
"3: Undefined variable"); /* src->name */
|
|
return 1;
|
|
}
|
|
|
|
switch (src->type) {
|
|
case vt_attrib:
|
|
*File = PROGRAM_INPUT;
|
|
*Index = src->attrib_binding;
|
|
break;
|
|
|
|
/* XXX: We have to handle offsets someplace in here! -- or are those above? */
|
|
case vt_param:
|
|
*File = (enum register_file) src->param_binding_type;
|
|
*Index = src->param_binding_begin;
|
|
break;
|
|
|
|
case vt_temp:
|
|
*File = PROGRAM_TEMPORARY;
|
|
*Index = src->temp_binding;
|
|
break;
|
|
|
|
/* If the var type is vt_output no go */
|
|
default:
|
|
program_error(ctx, Program->Position,
|
|
"destination register is read only");
|
|
/* bad src->name */
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
program_error(ctx, Program->Position,
|
|
"Unknown token in parse_src_reg");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse fragment program vector source register.
|
|
*/
|
|
static GLuint
|
|
parse_fp_vector_src_reg(GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head,
|
|
struct arb_program *program,
|
|
struct prog_src_register *reg)
|
|
{
|
|
enum register_file file;
|
|
GLint index;
|
|
GLboolean negate;
|
|
GLubyte swizzle[4];
|
|
GLboolean isRelOffset;
|
|
|
|
/* Grab the sign */
|
|
negate = (parse_sign (inst) == -1) ? 0xf : 0x0;
|
|
|
|
/* And the src reg */
|
|
if (parse_src_reg(ctx, inst, vc_head, program, &file, &index, &isRelOffset))
|
|
return 1;
|
|
|
|
/* finally, the swizzle */
|
|
parse_swizzle_mask(inst, swizzle, 4);
|
|
|
|
reg->File = file;
|
|
reg->Index = index;
|
|
reg->NegateBase = negate;
|
|
reg->Swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse fragment program destination register.
|
|
* \return 1 if error, 0 if no error.
|
|
*/
|
|
static GLuint
|
|
parse_fp_dst_reg(GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head, struct arb_program *Program,
|
|
struct prog_dst_register *reg )
|
|
{
|
|
GLint mask;
|
|
GLuint idx;
|
|
enum register_file file;
|
|
|
|
if (parse_masked_dst_reg (ctx, inst, vc_head, Program, &file, &idx, &mask))
|
|
return 1;
|
|
|
|
reg->File = file;
|
|
reg->Index = idx;
|
|
reg->WriteMask = mask;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parse fragment program scalar src register.
|
|
* \return 1 if error, 0 if no error.
|
|
*/
|
|
static GLuint
|
|
parse_fp_scalar_src_reg (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head,
|
|
struct arb_program *Program,
|
|
struct prog_src_register *reg )
|
|
{
|
|
enum register_file File;
|
|
GLint Index;
|
|
GLubyte Negate;
|
|
GLubyte Swizzle[4];
|
|
GLboolean IsRelOffset;
|
|
|
|
/* Grab the sign */
|
|
Negate = (parse_sign (inst) == -1) ? 0x1 : 0x0;
|
|
|
|
/* And the src reg */
|
|
if (parse_src_reg (ctx, inst, vc_head, Program, &File, &Index, &IsRelOffset))
|
|
return 1;
|
|
|
|
/* finally, the swizzle */
|
|
parse_swizzle_mask(inst, Swizzle, 1);
|
|
|
|
reg->File = File;
|
|
reg->Index = Index;
|
|
reg->NegateBase = Negate;
|
|
reg->Swizzle = (Swizzle[0] << 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* This is a big mother that handles getting opcodes into the instruction
|
|
* and handling the src & dst registers for fragment program instructions
|
|
* \return 1 if error, 0 if no error
|
|
*/
|
|
static GLuint
|
|
parse_fp_instruction (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head, struct arb_program *Program,
|
|
struct prog_instruction *fp)
|
|
{
|
|
GLint a;
|
|
GLuint texcoord;
|
|
GLubyte instClass, type, code;
|
|
GLboolean rel;
|
|
|
|
_mesa_init_instructions(fp, 1);
|
|
|
|
/* Record the position in the program string for debugging */
|
|
fp->StringPos = Program->Position;
|
|
|
|
/* OP_ALU_INST or OP_TEX_INST */
|
|
instClass = *(*inst)++;
|
|
|
|
/* OP_ALU_{VECTOR, SCALAR, BINSC, BIN, TRI, SWZ},
|
|
* OP_TEX_{SAMPLE, KIL}
|
|
*/
|
|
type = *(*inst)++;
|
|
|
|
/* The actual opcode name */
|
|
code = *(*inst)++;
|
|
|
|
/* Increment the correct count */
|
|
switch (instClass) {
|
|
case OP_ALU_INST:
|
|
Program->NumAluInstructions++;
|
|
break;
|
|
case OP_TEX_INST:
|
|
Program->NumTexInstructions++;
|
|
break;
|
|
}
|
|
|
|
switch (type) {
|
|
case OP_ALU_VECTOR:
|
|
switch (code) {
|
|
case OP_ABS_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_ABS:
|
|
fp->Opcode = OPCODE_ABS;
|
|
break;
|
|
|
|
case OP_FLR_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_FLR:
|
|
fp->Opcode = OPCODE_FLR;
|
|
break;
|
|
|
|
case OP_FRC_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_FRC:
|
|
fp->Opcode = OPCODE_FRC;
|
|
break;
|
|
|
|
case OP_LIT_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_LIT:
|
|
fp->Opcode = OPCODE_LIT;
|
|
break;
|
|
|
|
case OP_MOV_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_MOV:
|
|
fp->Opcode = OPCODE_MOV;
|
|
break;
|
|
}
|
|
|
|
if (parse_fp_dst_reg (ctx, inst, vc_head, Program, &fp->DstReg))
|
|
return 1;
|
|
|
|
if (parse_fp_vector_src_reg(ctx, inst, vc_head, Program, &fp->SrcReg[0]))
|
|
return 1;
|
|
break;
|
|
|
|
case OP_ALU_SCALAR:
|
|
switch (code) {
|
|
case OP_COS_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_COS:
|
|
fp->Opcode = OPCODE_COS;
|
|
break;
|
|
|
|
case OP_EX2_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_EX2:
|
|
fp->Opcode = OPCODE_EX2;
|
|
break;
|
|
|
|
case OP_LG2_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_LG2:
|
|
fp->Opcode = OPCODE_LG2;
|
|
break;
|
|
|
|
case OP_RCP_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_RCP:
|
|
fp->Opcode = OPCODE_RCP;
|
|
break;
|
|
|
|
case OP_RSQ_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_RSQ:
|
|
fp->Opcode = OPCODE_RSQ;
|
|
break;
|
|
|
|
case OP_SIN_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_SIN:
|
|
fp->Opcode = OPCODE_SIN;
|
|
break;
|
|
|
|
case OP_SCS_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_SCS:
|
|
|
|
fp->Opcode = OPCODE_SCS;
|
|
break;
|
|
}
|
|
|
|
if (parse_fp_dst_reg (ctx, inst, vc_head, Program, &fp->DstReg))
|
|
return 1;
|
|
|
|
if (parse_fp_scalar_src_reg(ctx, inst, vc_head, Program, &fp->SrcReg[0]))
|
|
return 1;
|
|
break;
|
|
|
|
case OP_ALU_BINSC:
|
|
switch (code) {
|
|
case OP_POW_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_POW:
|
|
fp->Opcode = OPCODE_POW;
|
|
break;
|
|
}
|
|
|
|
if (parse_fp_dst_reg(ctx, inst, vc_head, Program, &fp->DstReg))
|
|
return 1;
|
|
|
|
for (a = 0; a < 2; a++) {
|
|
if (parse_fp_scalar_src_reg(ctx, inst, vc_head, Program, &fp->SrcReg[a]))
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
|
|
case OP_ALU_BIN:
|
|
switch (code) {
|
|
case OP_ADD_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_ADD:
|
|
fp->Opcode = OPCODE_ADD;
|
|
break;
|
|
|
|
case OP_DP3_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_DP3:
|
|
fp->Opcode = OPCODE_DP3;
|
|
break;
|
|
|
|
case OP_DP4_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_DP4:
|
|
fp->Opcode = OPCODE_DP4;
|
|
break;
|
|
|
|
case OP_DPH_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_DPH:
|
|
fp->Opcode = OPCODE_DPH;
|
|
break;
|
|
|
|
case OP_DST_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_DST:
|
|
fp->Opcode = OPCODE_DST;
|
|
break;
|
|
|
|
case OP_MAX_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_MAX:
|
|
fp->Opcode = OPCODE_MAX;
|
|
break;
|
|
|
|
case OP_MIN_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_MIN:
|
|
fp->Opcode = OPCODE_MIN;
|
|
break;
|
|
|
|
case OP_MUL_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_MUL:
|
|
fp->Opcode = OPCODE_MUL;
|
|
break;
|
|
|
|
case OP_SGE_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_SGE:
|
|
fp->Opcode = OPCODE_SGE;
|
|
break;
|
|
|
|
case OP_SLT_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_SLT:
|
|
fp->Opcode = OPCODE_SLT;
|
|
break;
|
|
|
|
case OP_SUB_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_SUB:
|
|
fp->Opcode = OPCODE_SUB;
|
|
break;
|
|
|
|
case OP_XPD_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_XPD:
|
|
fp->Opcode = OPCODE_XPD;
|
|
break;
|
|
}
|
|
|
|
if (parse_fp_dst_reg (ctx, inst, vc_head, Program, &fp->DstReg))
|
|
return 1;
|
|
for (a = 0; a < 2; a++) {
|
|
if (parse_fp_vector_src_reg(ctx, inst, vc_head, Program, &fp->SrcReg[a]))
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case OP_ALU_TRI:
|
|
switch (code) {
|
|
case OP_CMP_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_CMP:
|
|
fp->Opcode = OPCODE_CMP;
|
|
break;
|
|
|
|
case OP_LRP_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_LRP:
|
|
fp->Opcode = OPCODE_LRP;
|
|
break;
|
|
|
|
case OP_MAD_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_MAD:
|
|
fp->Opcode = OPCODE_MAD;
|
|
break;
|
|
}
|
|
|
|
if (parse_fp_dst_reg (ctx, inst, vc_head, Program, &fp->DstReg))
|
|
return 1;
|
|
|
|
for (a = 0; a < 3; a++) {
|
|
if (parse_fp_vector_src_reg(ctx, inst, vc_head, Program, &fp->SrcReg[a]))
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case OP_ALU_SWZ:
|
|
switch (code) {
|
|
case OP_SWZ_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_SWZ:
|
|
fp->Opcode = OPCODE_SWZ;
|
|
break;
|
|
}
|
|
if (parse_fp_dst_reg (ctx, inst, vc_head, Program, &fp->DstReg))
|
|
return 1;
|
|
|
|
{
|
|
GLubyte swizzle[4];
|
|
GLubyte negateMask;
|
|
enum register_file file;
|
|
GLint index;
|
|
|
|
if (parse_src_reg(ctx, inst, vc_head, Program, &file, &index, &rel))
|
|
return 1;
|
|
parse_extended_swizzle_mask(inst, swizzle, &negateMask);
|
|
fp->SrcReg[0].File = file;
|
|
fp->SrcReg[0].Index = index;
|
|
fp->SrcReg[0].NegateBase = negateMask;
|
|
fp->SrcReg[0].Swizzle = MAKE_SWIZZLE4(swizzle[0],
|
|
swizzle[1],
|
|
swizzle[2],
|
|
swizzle[3]);
|
|
}
|
|
break;
|
|
|
|
case OP_TEX_SAMPLE:
|
|
switch (code) {
|
|
case OP_TEX_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_TEX:
|
|
fp->Opcode = OPCODE_TEX;
|
|
break;
|
|
|
|
case OP_TXP_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_TXP:
|
|
fp->Opcode = OPCODE_TXP;
|
|
break;
|
|
|
|
case OP_TXB_SAT:
|
|
fp->SaturateMode = SATURATE_ZERO_ONE;
|
|
case OP_TXB:
|
|
fp->Opcode = OPCODE_TXB;
|
|
break;
|
|
}
|
|
|
|
if (parse_fp_dst_reg (ctx, inst, vc_head, Program, &fp->DstReg))
|
|
return 1;
|
|
|
|
if (parse_fp_vector_src_reg(ctx, inst, vc_head, Program, &fp->SrcReg[0]))
|
|
return 1;
|
|
|
|
/* texImageUnit */
|
|
if (parse_texcoord_num (ctx, inst, Program, &texcoord))
|
|
return 1;
|
|
fp->TexSrcUnit = texcoord;
|
|
|
|
/* texTarget */
|
|
switch (*(*inst)++) {
|
|
case TEXTARGET_1D:
|
|
fp->TexSrcTarget = TEXTURE_1D_INDEX;
|
|
break;
|
|
case TEXTARGET_2D:
|
|
fp->TexSrcTarget = TEXTURE_2D_INDEX;
|
|
break;
|
|
case TEXTARGET_3D:
|
|
fp->TexSrcTarget = TEXTURE_3D_INDEX;
|
|
break;
|
|
case TEXTARGET_RECT:
|
|
fp->TexSrcTarget = TEXTURE_RECT_INDEX;
|
|
break;
|
|
case TEXTARGET_CUBE:
|
|
fp->TexSrcTarget = TEXTURE_CUBE_INDEX;
|
|
break;
|
|
case TEXTARGET_SHADOW1D:
|
|
case TEXTARGET_SHADOW2D:
|
|
case TEXTARGET_SHADOWRECT:
|
|
/* TODO ARB_fragment_program_shadow code */
|
|
break;
|
|
}
|
|
Program->TexturesUsed[texcoord] |= (1 << fp->TexSrcTarget);
|
|
/* Check that both "2D" and "CUBE" (for example) aren't both used */
|
|
if (_mesa_bitcount(Program->TexturesUsed[texcoord]) > 1) {
|
|
program_error(ctx, Program->Position,
|
|
"multiple targets used on one texture image unit");
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case OP_TEX_KIL:
|
|
Program->UsesKill = 1;
|
|
if (parse_fp_vector_src_reg(ctx, inst, vc_head, Program, &fp->SrcReg[0]))
|
|
return 1;
|
|
fp->Opcode = OPCODE_KIL;
|
|
break;
|
|
default:
|
|
_mesa_problem(ctx, "bad type 0x%x in parse_fp_instruction()", type);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static GLuint
|
|
parse_vp_dst_reg(GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head, struct arb_program *Program,
|
|
struct prog_dst_register *reg )
|
|
{
|
|
GLint mask;
|
|
GLuint idx;
|
|
enum register_file file;
|
|
|
|
if (parse_masked_dst_reg(ctx, inst, vc_head, Program, &file, &idx, &mask))
|
|
return 1;
|
|
|
|
reg->File = file;
|
|
reg->Index = idx;
|
|
reg->WriteMask = mask;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Handle the parsing out of a masked address register
|
|
*
|
|
* \param Index - The register index we write to
|
|
* \param WriteMask - The mask controlling which components we write (1->write)
|
|
*
|
|
* \return 0 on sucess, 1 on error
|
|
*/
|
|
static GLuint
|
|
parse_vp_address_reg (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head,
|
|
struct arb_program *Program,
|
|
struct prog_dst_register *reg)
|
|
{
|
|
GLint idx;
|
|
|
|
if (parse_address_reg (ctx, inst, vc_head, Program, &idx))
|
|
return 1;
|
|
|
|
/* This should be 0x8 */
|
|
(*inst)++;
|
|
|
|
reg->File = PROGRAM_ADDRESS;
|
|
reg->Index = idx;
|
|
|
|
/* Writemask of .x is implied */
|
|
reg->WriteMask = 0x1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse vertex program vector source register.
|
|
*/
|
|
static GLuint
|
|
parse_vp_vector_src_reg(GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head,
|
|
struct arb_program *program,
|
|
struct prog_src_register *reg )
|
|
{
|
|
enum register_file file;
|
|
GLint index;
|
|
GLubyte negateMask;
|
|
GLubyte swizzle[4];
|
|
GLboolean isRelOffset;
|
|
|
|
/* Grab the sign */
|
|
negateMask = (parse_sign (inst) == -1) ? 0xf : 0x0;
|
|
|
|
/* And the src reg */
|
|
if (parse_src_reg (ctx, inst, vc_head, program, &file, &index, &isRelOffset))
|
|
return 1;
|
|
|
|
/* finally, the swizzle */
|
|
parse_swizzle_mask(inst, swizzle, 4);
|
|
|
|
reg->File = file;
|
|
reg->Index = index;
|
|
reg->Swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1],
|
|
swizzle[2], swizzle[3]);
|
|
reg->NegateBase = negateMask;
|
|
reg->RelAddr = isRelOffset;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static GLuint
|
|
parse_vp_scalar_src_reg (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head,
|
|
struct arb_program *Program,
|
|
struct prog_src_register *reg )
|
|
{
|
|
enum register_file File;
|
|
GLint Index;
|
|
GLubyte Negate;
|
|
GLubyte Swizzle[4];
|
|
GLboolean IsRelOffset;
|
|
|
|
/* Grab the sign */
|
|
Negate = (parse_sign (inst) == -1) ? 0x1 : 0x0;
|
|
|
|
/* And the src reg */
|
|
if (parse_src_reg (ctx, inst, vc_head, Program, &File, &Index, &IsRelOffset))
|
|
return 1;
|
|
|
|
/* finally, the swizzle */
|
|
parse_swizzle_mask(inst, Swizzle, 1);
|
|
|
|
reg->File = File;
|
|
reg->Index = Index;
|
|
reg->Swizzle = (Swizzle[0] << 0);
|
|
reg->NegateBase = Negate;
|
|
reg->RelAddr = IsRelOffset;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* This is a big mother that handles getting opcodes into the instruction
|
|
* and handling the src & dst registers for vertex program instructions
|
|
*/
|
|
static GLuint
|
|
parse_vp_instruction (GLcontext * ctx, const GLubyte ** inst,
|
|
struct var_cache **vc_head, struct arb_program *Program,
|
|
struct prog_instruction *vp)
|
|
{
|
|
GLint a;
|
|
GLubyte type, code;
|
|
|
|
/* OP_ALU_{ARL, VECTOR, SCALAR, BINSC, BIN, TRI, SWZ} */
|
|
type = *(*inst)++;
|
|
|
|
/* The actual opcode name */
|
|
code = *(*inst)++;
|
|
|
|
_mesa_init_instructions(vp, 1);
|
|
/* Record the position in the program string for debugging */
|
|
vp->StringPos = Program->Position;
|
|
|
|
switch (type) {
|
|
/* XXX: */
|
|
case OP_ALU_ARL:
|
|
vp->Opcode = OPCODE_ARL;
|
|
|
|
/* Remember to set SrcReg.RelAddr; */
|
|
|
|
/* Get the masked address register [dst] */
|
|
if (parse_vp_address_reg(ctx, inst, vc_head, Program, &vp->DstReg))
|
|
return 1;
|
|
|
|
vp->DstReg.File = PROGRAM_ADDRESS;
|
|
|
|
/* Get a scalar src register */
|
|
if (parse_vp_scalar_src_reg(ctx, inst, vc_head, Program, &vp->SrcReg[0]))
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case OP_ALU_VECTOR:
|
|
switch (code) {
|
|
case OP_ABS:
|
|
vp->Opcode = OPCODE_ABS;
|
|
break;
|
|
case OP_FLR:
|
|
vp->Opcode = OPCODE_FLR;
|
|
break;
|
|
case OP_FRC:
|
|
vp->Opcode = OPCODE_FRC;
|
|
break;
|
|
case OP_LIT:
|
|
vp->Opcode = OPCODE_LIT;
|
|
break;
|
|
case OP_MOV:
|
|
vp->Opcode = OPCODE_MOV;
|
|
break;
|
|
}
|
|
|
|
if (parse_vp_dst_reg(ctx, inst, vc_head, Program, &vp->DstReg))
|
|
return 1;
|
|
|
|
if (parse_vp_vector_src_reg(ctx, inst, vc_head, Program, &vp->SrcReg[0]))
|
|
return 1;
|
|
break;
|
|
|
|
case OP_ALU_SCALAR:
|
|
switch (code) {
|
|
case OP_EX2:
|
|
vp->Opcode = OPCODE_EX2;
|
|
break;
|
|
case OP_EXP:
|
|
vp->Opcode = OPCODE_EXP;
|
|
break;
|
|
case OP_LG2:
|
|
vp->Opcode = OPCODE_LG2;
|
|
break;
|
|
case OP_LOG:
|
|
vp->Opcode = OPCODE_LOG;
|
|
break;
|
|
case OP_RCP:
|
|
vp->Opcode = OPCODE_RCP;
|
|
break;
|
|
case OP_RSQ:
|
|
vp->Opcode = OPCODE_RSQ;
|
|
break;
|
|
}
|
|
if (parse_vp_dst_reg(ctx, inst, vc_head, Program, &vp->DstReg))
|
|
return 1;
|
|
|
|
if (parse_vp_scalar_src_reg(ctx, inst, vc_head, Program, &vp->SrcReg[0]))
|
|
return 1;
|
|
break;
|
|
|
|
case OP_ALU_BINSC:
|
|
switch (code) {
|
|
case OP_POW:
|
|
vp->Opcode = OPCODE_POW;
|
|
break;
|
|
}
|
|
if (parse_vp_dst_reg(ctx, inst, vc_head, Program, &vp->DstReg))
|
|
return 1;
|
|
|
|
for (a = 0; a < 2; a++) {
|
|
if (parse_vp_scalar_src_reg(ctx, inst, vc_head, Program, &vp->SrcReg[a]))
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case OP_ALU_BIN:
|
|
switch (code) {
|
|
case OP_ADD:
|
|
vp->Opcode = OPCODE_ADD;
|
|
break;
|
|
case OP_DP3:
|
|
vp->Opcode = OPCODE_DP3;
|
|
break;
|
|
case OP_DP4:
|
|
vp->Opcode = OPCODE_DP4;
|
|
break;
|
|
case OP_DPH:
|
|
vp->Opcode = OPCODE_DPH;
|
|
break;
|
|
case OP_DST:
|
|
vp->Opcode = OPCODE_DST;
|
|
break;
|
|
case OP_MAX:
|
|
vp->Opcode = OPCODE_MAX;
|
|
break;
|
|
case OP_MIN:
|
|
vp->Opcode = OPCODE_MIN;
|
|
break;
|
|
case OP_MUL:
|
|
vp->Opcode = OPCODE_MUL;
|
|
break;
|
|
case OP_SGE:
|
|
vp->Opcode = OPCODE_SGE;
|
|
break;
|
|
case OP_SLT:
|
|
vp->Opcode = OPCODE_SLT;
|
|
break;
|
|
case OP_SUB:
|
|
vp->Opcode = OPCODE_SUB;
|
|
break;
|
|
case OP_XPD:
|
|
vp->Opcode = OPCODE_XPD;
|
|
break;
|
|
}
|
|
if (parse_vp_dst_reg(ctx, inst, vc_head, Program, &vp->DstReg))
|
|
return 1;
|
|
|
|
for (a = 0; a < 2; a++) {
|
|
if (parse_vp_vector_src_reg(ctx, inst, vc_head, Program, &vp->SrcReg[a]))
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case OP_ALU_TRI:
|
|
switch (code) {
|
|
case OP_MAD:
|
|
vp->Opcode = OPCODE_MAD;
|
|
break;
|
|
}
|
|
|
|
if (parse_vp_dst_reg(ctx, inst, vc_head, Program, &vp->DstReg))
|
|
return 1;
|
|
|
|
for (a = 0; a < 3; a++) {
|
|
if (parse_vp_vector_src_reg(ctx, inst, vc_head, Program, &vp->SrcReg[a]))
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case OP_ALU_SWZ:
|
|
switch (code) {
|
|
case OP_SWZ:
|
|
vp->Opcode = OPCODE_SWZ;
|
|
break;
|
|
}
|
|
{
|
|
GLubyte swizzle[4];
|
|
GLubyte negateMask;
|
|
GLboolean relAddr;
|
|
enum register_file file;
|
|
GLint index;
|
|
|
|
if (parse_vp_dst_reg(ctx, inst, vc_head, Program, &vp->DstReg))
|
|
return 1;
|
|
|
|
if (parse_src_reg(ctx, inst, vc_head, Program, &file, &index, &relAddr))
|
|
return 1;
|
|
parse_extended_swizzle_mask (inst, swizzle, &negateMask);
|
|
vp->SrcReg[0].File = file;
|
|
vp->SrcReg[0].Index = index;
|
|
vp->SrcReg[0].NegateBase = negateMask;
|
|
vp->SrcReg[0].Swizzle = MAKE_SWIZZLE4(swizzle[0],
|
|
swizzle[1],
|
|
swizzle[2],
|
|
swizzle[3]);
|
|
vp->SrcReg[0].RelAddr = relAddr;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if DEBUG_PARSING
|
|
|
|
static GLvoid
|
|
print_state_token (GLint token)
|
|
{
|
|
switch (token) {
|
|
case STATE_MATERIAL:
|
|
fprintf (stderr, "STATE_MATERIAL ");
|
|
break;
|
|
case STATE_LIGHT:
|
|
fprintf (stderr, "STATE_LIGHT ");
|
|
break;
|
|
|
|
case STATE_LIGHTMODEL_AMBIENT:
|
|
fprintf (stderr, "STATE_AMBIENT ");
|
|
break;
|
|
|
|
case STATE_LIGHTMODEL_SCENECOLOR:
|
|
fprintf (stderr, "STATE_SCENECOLOR ");
|
|
break;
|
|
|
|
case STATE_LIGHTPROD:
|
|
fprintf (stderr, "STATE_LIGHTPROD ");
|
|
break;
|
|
|
|
case STATE_TEXGEN:
|
|
fprintf (stderr, "STATE_TEXGEN ");
|
|
break;
|
|
|
|
case STATE_FOG_COLOR:
|
|
fprintf (stderr, "STATE_FOG_COLOR ");
|
|
break;
|
|
|
|
case STATE_FOG_PARAMS:
|
|
fprintf (stderr, "STATE_FOG_PARAMS ");
|
|
break;
|
|
|
|
case STATE_CLIPPLANE:
|
|
fprintf (stderr, "STATE_CLIPPLANE ");
|
|
break;
|
|
|
|
case STATE_POINT_SIZE:
|
|
fprintf (stderr, "STATE_POINT_SIZE ");
|
|
break;
|
|
|
|
case STATE_POINT_ATTENUATION:
|
|
fprintf (stderr, "STATE_ATTENUATION ");
|
|
break;
|
|
|
|
case STATE_MATRIX:
|
|
fprintf (stderr, "STATE_MATRIX ");
|
|
break;
|
|
|
|
case STATE_MODELVIEW:
|
|
fprintf (stderr, "STATE_MODELVIEW ");
|
|
break;
|
|
|
|
case STATE_PROJECTION:
|
|
fprintf (stderr, "STATE_PROJECTION ");
|
|
break;
|
|
|
|
case STATE_MVP:
|
|
fprintf (stderr, "STATE_MVP ");
|
|
break;
|
|
|
|
case STATE_TEXTURE:
|
|
fprintf (stderr, "STATE_TEXTURE ");
|
|
break;
|
|
|
|
case STATE_PROGRAM:
|
|
fprintf (stderr, "STATE_PROGRAM ");
|
|
break;
|
|
|
|
case STATE_MATRIX_INVERSE:
|
|
fprintf (stderr, "STATE_INVERSE ");
|
|
break;
|
|
|
|
case STATE_MATRIX_TRANSPOSE:
|
|
fprintf (stderr, "STATE_TRANSPOSE ");
|
|
break;
|
|
|
|
case STATE_MATRIX_INVTRANS:
|
|
fprintf (stderr, "STATE_INVTRANS ");
|
|
break;
|
|
|
|
case STATE_AMBIENT:
|
|
fprintf (stderr, "STATE_AMBIENT ");
|
|
break;
|
|
|
|
case STATE_DIFFUSE:
|
|
fprintf (stderr, "STATE_DIFFUSE ");
|
|
break;
|
|
|
|
case STATE_SPECULAR:
|
|
fprintf (stderr, "STATE_SPECULAR ");
|
|
break;
|
|
|
|
case STATE_EMISSION:
|
|
fprintf (stderr, "STATE_EMISSION ");
|
|
break;
|
|
|
|
case STATE_SHININESS:
|
|
fprintf (stderr, "STATE_SHININESS ");
|
|
break;
|
|
|
|
case STATE_HALF:
|
|
fprintf (stderr, "STATE_HALF ");
|
|
break;
|
|
|
|
case STATE_POSITION:
|
|
fprintf (stderr, "STATE_POSITION ");
|
|
break;
|
|
|
|
case STATE_ATTENUATION:
|
|
fprintf (stderr, "STATE_ATTENUATION ");
|
|
break;
|
|
|
|
case STATE_SPOT_DIRECTION:
|
|
fprintf (stderr, "STATE_DIRECTION ");
|
|
break;
|
|
|
|
case STATE_TEXGEN_EYE_S:
|
|
fprintf (stderr, "STATE_TEXGEN_EYE_S ");
|
|
break;
|
|
|
|
case STATE_TEXGEN_EYE_T:
|
|
fprintf (stderr, "STATE_TEXGEN_EYE_T ");
|
|
break;
|
|
|
|
case STATE_TEXGEN_EYE_R:
|
|
fprintf (stderr, "STATE_TEXGEN_EYE_R ");
|
|
break;
|
|
|
|
case STATE_TEXGEN_EYE_Q:
|
|
fprintf (stderr, "STATE_TEXGEN_EYE_Q ");
|
|
break;
|
|
|
|
case STATE_TEXGEN_OBJECT_S:
|
|
fprintf (stderr, "STATE_TEXGEN_EYE_S ");
|
|
break;
|
|
|
|
case STATE_TEXGEN_OBJECT_T:
|
|
fprintf (stderr, "STATE_TEXGEN_OBJECT_T ");
|
|
break;
|
|
|
|
case STATE_TEXGEN_OBJECT_R:
|
|
fprintf (stderr, "STATE_TEXGEN_OBJECT_R ");
|
|
break;
|
|
|
|
case STATE_TEXGEN_OBJECT_Q:
|
|
fprintf (stderr, "STATE_TEXGEN_OBJECT_Q ");
|
|
break;
|
|
|
|
case STATE_TEXENV_COLOR:
|
|
fprintf (stderr, "STATE_TEXENV_COLOR ");
|
|
break;
|
|
|
|
case STATE_DEPTH_RANGE:
|
|
fprintf (stderr, "STATE_DEPTH_RANGE ");
|
|
break;
|
|
|
|
case STATE_VERTEX_PROGRAM:
|
|
fprintf (stderr, "STATE_VERTEX_PROGRAM ");
|
|
break;
|
|
|
|
case STATE_FRAGMENT_PROGRAM:
|
|
fprintf (stderr, "STATE_FRAGMENT_PROGRAM ");
|
|
break;
|
|
|
|
case STATE_ENV:
|
|
fprintf (stderr, "STATE_ENV ");
|
|
break;
|
|
|
|
case STATE_LOCAL:
|
|
fprintf (stderr, "STATE_LOCAL ");
|
|
break;
|
|
|
|
}
|
|
fprintf (stderr, "[%d] ", token);
|
|
}
|
|
|
|
|
|
static GLvoid
|
|
debug_variables (GLcontext * ctx, struct var_cache *vc_head,
|
|
struct arb_program *Program)
|
|
{
|
|
struct var_cache *vc;
|
|
GLint a, b;
|
|
|
|
fprintf (stderr, "debug_variables, vc_head: %x\n", vc_head);
|
|
|
|
/* First of all, print out the contents of the var_cache */
|
|
vc = vc_head;
|
|
while (vc) {
|
|
fprintf (stderr, "[%x]\n", vc);
|
|
switch (vc->type) {
|
|
case vt_none:
|
|
fprintf (stderr, "UNDEFINED %s\n", vc->name);
|
|
break;
|
|
case vt_attrib:
|
|
fprintf (stderr, "ATTRIB %s\n", vc->name);
|
|
fprintf (stderr, " binding: 0x%x\n", vc->attrib_binding);
|
|
break;
|
|
case vt_param:
|
|
fprintf (stderr, "PARAM %s begin: %d len: %d\n", vc->name,
|
|
vc->param_binding_begin, vc->param_binding_length);
|
|
b = vc->param_binding_begin;
|
|
for (a = 0; a < vc->param_binding_length; a++) {
|
|
fprintf (stderr, "%s\n",
|
|
Program->Parameters->Parameters[a + b].Name);
|
|
if (Program->Parameters->Parameters[a + b].Type == STATE) {
|
|
print_state_token (Program->Parameters->Parameters[a + b].
|
|
StateIndexes[0]);
|
|
print_state_token (Program->Parameters->Parameters[a + b].
|
|
StateIndexes[1]);
|
|
print_state_token (Program->Parameters->Parameters[a + b].
|
|
StateIndexes[2]);
|
|
print_state_token (Program->Parameters->Parameters[a + b].
|
|
StateIndexes[3]);
|
|
print_state_token (Program->Parameters->Parameters[a + b].
|
|
StateIndexes[4]);
|
|
print_state_token (Program->Parameters->Parameters[a + b].
|
|
StateIndexes[5]);
|
|
}
|
|
else
|
|
fprintf (stderr, "%f %f %f %f\n",
|
|
Program->Parameters->Parameters[a + b].Values[0],
|
|
Program->Parameters->Parameters[a + b].Values[1],
|
|
Program->Parameters->Parameters[a + b].Values[2],
|
|
Program->Parameters->Parameters[a + b].Values[3]);
|
|
}
|
|
break;
|
|
case vt_temp:
|
|
fprintf (stderr, "TEMP %s\n", vc->name);
|
|
fprintf (stderr, " binding: 0x%x\n", vc->temp_binding);
|
|
break;
|
|
case vt_output:
|
|
fprintf (stderr, "OUTPUT %s\n", vc->name);
|
|
fprintf (stderr, " binding: 0x%x\n", vc->output_binding);
|
|
break;
|
|
case vt_alias:
|
|
fprintf (stderr, "ALIAS %s\n", vc->name);
|
|
fprintf (stderr, " binding: 0x%x (%s)\n",
|
|
vc->alias_binding, vc->alias_binding->name);
|
|
break;
|
|
}
|
|
vc = vc->next;
|
|
}
|
|
}
|
|
|
|
#endif /* DEBUG_PARSING */
|
|
|
|
|
|
/**
|
|
* The main loop for parsing a fragment or vertex program
|
|
*
|
|
* \return 1 on error, 0 on success
|
|
*/
|
|
static GLint
|
|
parse_instructions(GLcontext * ctx, const GLubyte * inst,
|
|
struct var_cache **vc_head, struct arb_program *Program)
|
|
{
|
|
const GLuint maxInst = (Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB)
|
|
? ctx->Const.FragmentProgram.MaxInstructions
|
|
: ctx->Const.VertexProgram.MaxInstructions;
|
|
GLint err = 0;
|
|
|
|
ASSERT(MAX_INSTRUCTIONS >= maxInst);
|
|
|
|
Program->MajorVersion = (GLuint) * inst++;
|
|
Program->MinorVersion = (GLuint) * inst++;
|
|
|
|
while (*inst != END) {
|
|
switch (*inst++) {
|
|
|
|
case OPTION:
|
|
switch (*inst++) {
|
|
case ARB_PRECISION_HINT_FASTEST:
|
|
Program->PrecisionOption = GL_FASTEST;
|
|
break;
|
|
|
|
case ARB_PRECISION_HINT_NICEST:
|
|
Program->PrecisionOption = GL_NICEST;
|
|
break;
|
|
|
|
case ARB_FOG_EXP:
|
|
Program->FogOption = GL_EXP;
|
|
break;
|
|
|
|
case ARB_FOG_EXP2:
|
|
Program->FogOption = GL_EXP2;
|
|
break;
|
|
|
|
case ARB_FOG_LINEAR:
|
|
Program->FogOption = GL_LINEAR;
|
|
break;
|
|
|
|
case ARB_POSITION_INVARIANT:
|
|
if (Program->Base.Target == GL_VERTEX_PROGRAM_ARB)
|
|
Program->HintPositionInvariant = GL_TRUE;
|
|
break;
|
|
|
|
case ARB_FRAGMENT_PROGRAM_SHADOW:
|
|
if (Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) {
|
|
/* TODO ARB_fragment_program_shadow code */
|
|
}
|
|
break;
|
|
|
|
case ARB_DRAW_BUFFERS:
|
|
if (Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) {
|
|
/* do nothing for now */
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case INSTRUCTION:
|
|
/* check length */
|
|
if (Program->Base.NumInstructions + 1 >= maxInst) {
|
|
program_error(ctx, Program->Position,
|
|
"Max instruction count exceeded");
|
|
return 1;
|
|
}
|
|
Program->Position = parse_position (&inst);
|
|
/* parse the current instruction */
|
|
if (Program->Base.Target == GL_FRAGMENT_PROGRAM_ARB) {
|
|
err = parse_fp_instruction (ctx, &inst, vc_head, Program,
|
|
&Program->Base.Instructions[Program->Base.NumInstructions]);
|
|
}
|
|
else {
|
|
err = parse_vp_instruction (ctx, &inst, vc_head, Program,
|
|
&Program->Base.Instructions[Program->Base.NumInstructions]);
|
|
}
|
|
|
|
/* increment instuction count */
|
|
Program->Base.NumInstructions++;
|
|
break;
|
|
|
|
case DECLARATION:
|
|
err = parse_declaration (ctx, &inst, vc_head, Program);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (err)
|
|
break;
|
|
}
|
|
|
|
/* Finally, tag on an OPCODE_END instruction */
|
|
{
|
|
const GLuint numInst = Program->Base.NumInstructions;
|
|
_mesa_init_instructions(Program->Base.Instructions + numInst, 1);
|
|
Program->Base.Instructions[numInst].Opcode = OPCODE_END;
|
|
/* YYY Wrong Position in program, whatever, at least not random -> crash
|
|
Program->Position = parse_position (&inst);
|
|
*/
|
|
Program->Base.Instructions[numInst].StringPos = Program->Position;
|
|
}
|
|
Program->Base.NumInstructions++;
|
|
|
|
/*
|
|
* Initialize native counts to logical counts. The device driver may
|
|
* change them if program is translated into a hardware program.
|
|
*/
|
|
Program->Base.NumNativeInstructions = Program->Base.NumInstructions;
|
|
Program->Base.NumNativeTemporaries = Program->Base.NumTemporaries;
|
|
Program->Base.NumNativeParameters = Program->Base.NumParameters;
|
|
Program->Base.NumNativeAttributes = Program->Base.NumAttributes;
|
|
Program->Base.NumNativeAddressRegs = Program->Base.NumAddressRegs;
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/* XXX temporary */
|
|
__extension__ static char core_grammar_text[] =
|
|
#include "grammar_syn.h"
|
|
;
|
|
|
|
|
|
/**
|
|
* Set a grammar parameter.
|
|
* \param name the grammar parameter
|
|
* \param value the new parameter value
|
|
* \return 0 if OK, 1 if error
|
|
*/
|
|
static int
|
|
set_reg8 (GLcontext *ctx, grammar id, const char *name, GLubyte value)
|
|
{
|
|
char error_msg[300];
|
|
GLint error_pos;
|
|
|
|
if (grammar_set_reg8 (id, (const byte *) name, value))
|
|
return 0;
|
|
|
|
grammar_get_last_error ((byte *) error_msg, 300, &error_pos);
|
|
_mesa_set_program_error (ctx, error_pos, error_msg);
|
|
_mesa_error (ctx, GL_INVALID_OPERATION, "Grammar Register Error");
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Enable support for the given language option in the parser.
|
|
* \return 1 if OK, 0 if error
|
|
*/
|
|
static int
|
|
enable_ext(GLcontext *ctx, grammar id, const char *name)
|
|
{
|
|
return !set_reg8(ctx, id, name, 1);
|
|
}
|
|
|
|
|
|
/**
|
|
* Enable parser extensions based on which OpenGL extensions are supported
|
|
* by this rendering context.
|
|
*
|
|
* \return GL_TRUE if OK, GL_FALSE if error.
|
|
*/
|
|
static GLboolean
|
|
enable_parser_extensions(GLcontext *ctx, grammar id)
|
|
{
|
|
#if 0
|
|
/* These are not supported at this time */
|
|
if ((ctx->Extensions.ARB_vertex_blend ||
|
|
ctx->Extensions.EXT_vertex_weighting)
|
|
&& !enable_ext(ctx, id, "vertex_blend"))
|
|
return GL_FALSE;
|
|
if (ctx->Extensions.ARB_matrix_palette
|
|
&& !enable_ext(ctx, id, "matrix_palette"))
|
|
return GL_FALSE;
|
|
if (ctx->Extensions.ARB_fragment_program_shadow
|
|
&& !enable_ext(ctx, id, "fragment_program_shadow"))
|
|
return GL_FALSE;
|
|
#endif
|
|
if (ctx->Extensions.EXT_point_parameters
|
|
&& !enable_ext(ctx, id, "point_parameters"))
|
|
return GL_FALSE;
|
|
if (ctx->Extensions.EXT_secondary_color
|
|
&& !enable_ext(ctx, id, "secondary_color"))
|
|
return GL_FALSE;
|
|
if (ctx->Extensions.EXT_fog_coord
|
|
&& !enable_ext(ctx, id, "fog_coord"))
|
|
return GL_FALSE;
|
|
if (ctx->Extensions.NV_texture_rectangle
|
|
&& !enable_ext(ctx, id, "texture_rectangle"))
|
|
return GL_FALSE;
|
|
if (ctx->Extensions.ARB_draw_buffers
|
|
&& !enable_ext(ctx, id, "draw_buffers"))
|
|
return GL_FALSE;
|
|
|
|
#if 1
|
|
/* hack for Warcraft (see bug 8060) */
|
|
enable_ext(ctx, id, "vertex_blend");
|
|
#endif
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* This kicks everything off.
|
|
*
|
|
* \param ctx - The GL Context
|
|
* \param str - The program string
|
|
* \param len - The program string length
|
|
* \param program - The arb_program struct to return all the parsed info in
|
|
* \return GL_TRUE on sucess, GL_FALSE on error
|
|
*/
|
|
static GLboolean
|
|
_mesa_parse_arb_program(GLcontext *ctx, GLenum target,
|
|
const GLubyte *str, GLsizei len,
|
|
struct arb_program *program)
|
|
{
|
|
GLint a, err, error_pos;
|
|
char error_msg[300];
|
|
GLuint parsed_len;
|
|
struct var_cache *vc_head;
|
|
grammar arbprogram_syn_id;
|
|
GLubyte *parsed, *inst;
|
|
GLubyte *strz = NULL;
|
|
static int arbprogram_syn_is_ok = 0; /* XXX temporary */
|
|
|
|
/* set the program target before parsing */
|
|
program->Base.Target = target;
|
|
|
|
/* Reset error state */
|
|
_mesa_set_program_error(ctx, -1, NULL);
|
|
|
|
/* check if arb_grammar_text (arbprogram.syn) is syntactically correct */
|
|
if (!arbprogram_syn_is_ok) {
|
|
/* One-time initialization of parsing system */
|
|
grammar grammar_syn_id;
|
|
GLuint parsed_len;
|
|
|
|
grammar_syn_id = grammar_load_from_text ((byte *) core_grammar_text);
|
|
if (grammar_syn_id == 0) {
|
|
grammar_get_last_error ((byte *) error_msg, 300, &error_pos);
|
|
/* XXX this is not a GL error - it's an implementation bug! - FIX */
|
|
_mesa_set_program_error (ctx, error_pos, error_msg);
|
|
_mesa_error (ctx, GL_INVALID_OPERATION,
|
|
"glProgramStringARB(Error loading grammar rule set)");
|
|
return GL_FALSE;
|
|
}
|
|
|
|
err = !grammar_check(grammar_syn_id, (byte *) arb_grammar_text,
|
|
&parsed, &parsed_len);
|
|
|
|
/* 'parsed' is unused here */
|
|
_mesa_free (parsed);
|
|
parsed = NULL;
|
|
|
|
/* NOTE: we can't destroy grammar_syn_id right here because
|
|
* grammar_destroy() can reset the last error
|
|
*/
|
|
if (err) {
|
|
/* XXX this is not a GL error - it's an implementation bug! - FIX */
|
|
grammar_get_last_error ((byte *) error_msg, 300, &error_pos);
|
|
_mesa_set_program_error (ctx, error_pos, error_msg);
|
|
_mesa_error (ctx, GL_INVALID_OPERATION,
|
|
"glProgramString(Error loading grammar rule set");
|
|
grammar_destroy (grammar_syn_id);
|
|
return GL_FALSE;
|
|
}
|
|
|
|
grammar_destroy (grammar_syn_id);
|
|
|
|
arbprogram_syn_is_ok = 1;
|
|
}
|
|
|
|
/* create the grammar object */
|
|
arbprogram_syn_id = grammar_load_from_text ((byte *) arb_grammar_text);
|
|
if (arbprogram_syn_id == 0) {
|
|
/* XXX this is not a GL error - it's an implementation bug! - FIX */
|
|
grammar_get_last_error ((GLubyte *) error_msg, 300, &error_pos);
|
|
_mesa_set_program_error (ctx, error_pos, error_msg);
|
|
_mesa_error (ctx, GL_INVALID_OPERATION,
|
|
"glProgramString(Error loading grammer rule set)");
|
|
return GL_FALSE;
|
|
}
|
|
|
|
/* Set program_target register value */
|
|
if (set_reg8 (ctx, arbprogram_syn_id, "program_target",
|
|
program->Base.Target == GL_FRAGMENT_PROGRAM_ARB ? 0x10 : 0x20)) {
|
|
grammar_destroy (arbprogram_syn_id);
|
|
return GL_FALSE;
|
|
}
|
|
|
|
if (!enable_parser_extensions(ctx, arbprogram_syn_id)) {
|
|
grammar_destroy(arbprogram_syn_id);
|
|
return GL_FALSE;
|
|
}
|
|
|
|
/* check for NULL character occurences */
|
|
{
|
|
GLint i;
|
|
for (i = 0; i < len; i++) {
|
|
if (str[i] == '\0') {
|
|
program_error(ctx, i, "illegal character");
|
|
grammar_destroy (arbprogram_syn_id);
|
|
return GL_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* copy the program string to a null-terminated string */
|
|
strz = (GLubyte *) _mesa_malloc (len + 1);
|
|
if (!strz) {
|
|
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glProgramStringARB");
|
|
grammar_destroy (arbprogram_syn_id);
|
|
return GL_FALSE;
|
|
}
|
|
_mesa_memcpy (strz, str, len);
|
|
strz[len] = '\0';
|
|
|
|
/* do a fast check on program string - initial production buffer is 4K */
|
|
err = !grammar_fast_check(arbprogram_syn_id, strz,
|
|
&parsed, &parsed_len, 0x1000);
|
|
|
|
/* Syntax parse error */
|
|
if (err) {
|
|
grammar_get_last_error((GLubyte *) error_msg, 300, &error_pos);
|
|
program_error(ctx, error_pos, error_msg);
|
|
|
|
#if DEBUG_PARSING
|
|
/* useful for debugging */
|
|
do {
|
|
int line, col;
|
|
char *s;
|
|
fprintf(stderr, "program: %s\n", (char *) strz);
|
|
fprintf(stderr, "Error Pos: %d\n", ctx->program.ErrorPos);
|
|
s = (char *) _mesa_find_line_column(strz, strz+ctx->program.ErrorPos,
|
|
&line, &col);
|
|
fprintf(stderr, "line %d col %d: %s\n", line, col, s);
|
|
} while (0)
|
|
#endif
|
|
|
|
_mesa_free(strz);
|
|
_mesa_free(parsed);
|
|
|
|
grammar_destroy (arbprogram_syn_id);
|
|
return GL_FALSE;
|
|
}
|
|
|
|
grammar_destroy (arbprogram_syn_id);
|
|
|
|
/*
|
|
* Program string is syntactically correct at this point
|
|
* Parse the tokenized version of the program now, generating
|
|
* vertex/fragment program instructions.
|
|
*/
|
|
|
|
/* Initialize the arb_program struct */
|
|
program->Base.String = strz;
|
|
program->Base.Instructions = _mesa_alloc_instructions(MAX_INSTRUCTIONS);
|
|
program->Base.NumInstructions =
|
|
program->Base.NumTemporaries =
|
|
program->Base.NumParameters =
|
|
program->Base.NumAttributes = program->Base.NumAddressRegs = 0;
|
|
program->Base.Parameters = _mesa_new_parameter_list ();
|
|
program->Base.InputsRead = 0x0;
|
|
program->Base.OutputsWritten = 0x0;
|
|
program->Position = 0;
|
|
program->MajorVersion = program->MinorVersion = 0;
|
|
program->PrecisionOption = GL_DONT_CARE;
|
|
program->FogOption = GL_NONE;
|
|
program->HintPositionInvariant = GL_FALSE;
|
|
for (a = 0; a < MAX_TEXTURE_IMAGE_UNITS; a++)
|
|
program->TexturesUsed[a] = 0x0;
|
|
program->NumAluInstructions =
|
|
program->NumTexInstructions =
|
|
program->NumTexIndirections = 0;
|
|
program->UsesKill = 0;
|
|
|
|
vc_head = NULL;
|
|
err = GL_FALSE;
|
|
|
|
/* Start examining the tokens in the array */
|
|
inst = parsed;
|
|
|
|
/* Check the grammer rev */
|
|
if (*inst++ != REVISION) {
|
|
program_error (ctx, 0, "Grammar version mismatch");
|
|
err = GL_TRUE;
|
|
}
|
|
else {
|
|
/* ignore program target */
|
|
inst++;
|
|
err = parse_instructions(ctx, inst, &vc_head, program);
|
|
}
|
|
|
|
/*debug_variables(ctx, vc_head, program); */
|
|
|
|
/* We're done with the parsed binary array */
|
|
var_cache_destroy (&vc_head);
|
|
|
|
_mesa_free (parsed);
|
|
|
|
/* Reallocate the instruction array from size [MAX_INSTRUCTIONS]
|
|
* to size [ap.Base.NumInstructions].
|
|
*/
|
|
program->Base.Instructions
|
|
= _mesa_realloc_instructions(program->Base.Instructions,
|
|
MAX_INSTRUCTIONS,
|
|
program->Base.NumInstructions);
|
|
|
|
return !err;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
_mesa_parse_arb_fragment_program(GLcontext* ctx, GLenum target,
|
|
const GLvoid *str, GLsizei len,
|
|
struct gl_fragment_program *program)
|
|
{
|
|
struct arb_program ap;
|
|
GLuint i;
|
|
|
|
ASSERT(target == GL_FRAGMENT_PROGRAM_ARB);
|
|
if (!_mesa_parse_arb_program(ctx, target, (const GLubyte*) str, len, &ap)) {
|
|
/* Error in the program. Just return. */
|
|
return;
|
|
}
|
|
|
|
/* Copy the relevant contents of the arb_program struct into the
|
|
* fragment_program struct.
|
|
*/
|
|
program->Base.String = ap.Base.String;
|
|
program->Base.NumInstructions = ap.Base.NumInstructions;
|
|
program->Base.NumTemporaries = ap.Base.NumTemporaries;
|
|
program->Base.NumParameters = ap.Base.NumParameters;
|
|
program->Base.NumAttributes = ap.Base.NumAttributes;
|
|
program->Base.NumAddressRegs = ap.Base.NumAddressRegs;
|
|
program->Base.NumNativeInstructions = ap.Base.NumNativeInstructions;
|
|
program->Base.NumNativeTemporaries = ap.Base.NumNativeTemporaries;
|
|
program->Base.NumNativeParameters = ap.Base.NumNativeParameters;
|
|
program->Base.NumNativeAttributes = ap.Base.NumNativeAttributes;
|
|
program->Base.NumNativeAddressRegs = ap.Base.NumNativeAddressRegs;
|
|
program->NumAluInstructions = ap.NumAluInstructions;
|
|
program->NumTexInstructions = ap.NumTexInstructions;
|
|
program->NumTexIndirections = ap.NumTexIndirections;
|
|
program->NumNativeAluInstructions = ap.NumAluInstructions;
|
|
program->NumNativeTexInstructions = ap.NumTexInstructions;
|
|
program->NumNativeTexIndirections = ap.NumTexIndirections;
|
|
program->Base.InputsRead = ap.Base.InputsRead;
|
|
program->Base.OutputsWritten = ap.Base.OutputsWritten;
|
|
for (i = 0; i < MAX_TEXTURE_IMAGE_UNITS; i++)
|
|
program->TexturesUsed[i] = ap.TexturesUsed[i];
|
|
program->FogOption = ap.FogOption;
|
|
|
|
if (program->Base.Instructions)
|
|
_mesa_free(program->Base.Instructions);
|
|
program->Base.Instructions = ap.Base.Instructions;
|
|
|
|
if (program->Base.Parameters)
|
|
_mesa_free_parameter_list(program->Base.Parameters);
|
|
program->Base.Parameters = ap.Base.Parameters;
|
|
|
|
#if DEBUG_FP
|
|
_mesa_printf("____________Fragment program %u ________\n", program->Base.ID);
|
|
_mesa_print_program(&program->Base);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Parse the vertex program string. If success, update the given
|
|
* vertex_program object with the new program. Else, leave the vertex_program
|
|
* object unchanged.
|
|
*/
|
|
void
|
|
_mesa_parse_arb_vertex_program(GLcontext *ctx, GLenum target,
|
|
const GLvoid *str, GLsizei len,
|
|
struct gl_vertex_program *program)
|
|
{
|
|
struct arb_program ap;
|
|
|
|
ASSERT(target == GL_VERTEX_PROGRAM_ARB);
|
|
|
|
if (!_mesa_parse_arb_program(ctx, target, (const GLubyte*) str, len, &ap)) {
|
|
/* Error in the program. Just return. */
|
|
return;
|
|
}
|
|
|
|
/* Copy the relevant contents of the arb_program struct into the
|
|
* vertex_program struct.
|
|
*/
|
|
program->Base.String = ap.Base.String;
|
|
program->Base.NumInstructions = ap.Base.NumInstructions;
|
|
program->Base.NumTemporaries = ap.Base.NumTemporaries;
|
|
program->Base.NumParameters = ap.Base.NumParameters;
|
|
program->Base.NumAttributes = ap.Base.NumAttributes;
|
|
program->Base.NumAddressRegs = ap.Base.NumAddressRegs;
|
|
program->Base.NumNativeInstructions = ap.Base.NumNativeInstructions;
|
|
program->Base.NumNativeTemporaries = ap.Base.NumNativeTemporaries;
|
|
program->Base.NumNativeParameters = ap.Base.NumNativeParameters;
|
|
program->Base.NumNativeAttributes = ap.Base.NumNativeAttributes;
|
|
program->Base.NumNativeAddressRegs = ap.Base.NumNativeAddressRegs;
|
|
program->Base.InputsRead = ap.Base.InputsRead;
|
|
program->Base.OutputsWritten = ap.Base.OutputsWritten;
|
|
program->IsPositionInvariant = ap.HintPositionInvariant;
|
|
|
|
if (program->Base.Instructions)
|
|
_mesa_free(program->Base.Instructions);
|
|
program->Base.Instructions = ap.Base.Instructions;
|
|
|
|
if (program->Base.Parameters)
|
|
_mesa_free_parameter_list(program->Base.Parameters);
|
|
program->Base.Parameters = ap.Base.Parameters;
|
|
|
|
#if DEBUG_VP
|
|
_mesa_printf("____________Vertex program %u __________\n", program->Base.ID);
|
|
_mesa_print_program(&program->Base);
|
|
#endif
|
|
}
|