blob: 267ee81a7a6a1b04fe099794cb7a833c07827f02 [file] [log] [blame]
/*
* Copyright (C) 2009 Maciej Cencora <m.cencora@gmail.com>
*
* 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 (including the
* next paragraph) 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 THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
*
*/
/**
* \file
*
* Fragment program compiler. Perform transformations on the intermediate
* representation until the program is in a form where we can translate
* it more or less directly into machine-readable form.
*
* \author Ben Skeggs <darktama@iinet.net.au>
* \author Jerome Glisse <j.glisse@gmail.com>
*/
#include "r300_fragprog_common.h"
#include "shader/program.h"
#include "shader/prog_parameter.h"
#include "shader/prog_print.h"
#include "compiler/radeon_compiler.h"
#include "radeon_mesa_to_rc.h"
#include "r300_state.h"
static GLuint build_dtm(GLuint depthmode)
{
switch(depthmode) {
default:
case GL_LUMINANCE: return 0;
case GL_INTENSITY: return 1;
case GL_ALPHA: return 2;
}
}
static GLuint build_func(GLuint comparefunc)
{
return comparefunc - GL_NEVER;
}
/**
* Collect all external state that is relevant for compiling the given
* fragment program.
*/
static void build_state(
r300ContextPtr r300,
struct gl_fragment_program *fp,
struct r300_fragment_program_external_state *state)
{
int unit;
_mesa_bzero(state, sizeof(*state));
for(unit = 0; unit < 16; ++unit) {
if (fp->Base.ShadowSamplers & (1 << unit)) {
struct gl_texture_object* tex = r300->radeon.glCtx->Texture.Unit[unit]._Current;
state->unit[unit].depth_texture_mode = build_dtm(tex->DepthMode);
state->unit[unit].texture_compare_func = build_func(tex->CompareFunc);
}
}
}
/**
* Transform the program to support fragment.position.
*
* Introduce a small fragment at the start of the program that will be
* the only code that directly reads the FRAG_ATTRIB_WPOS input.
* All other code pieces that reference that input will be rewritten
* to read from a newly allocated temporary.
*
*/
static void insert_WPOS_trailer(struct r300_fragment_program_compiler *compiler, struct r300_fragment_program * fp)
{
int i;
fp->wpos_attr = FRAG_ATTRIB_MAX;
if (!(compiler->Base.Program.InputsRead & FRAG_BIT_WPOS)) {
return;
}
for (i = FRAG_ATTRIB_TEX0; i <= FRAG_ATTRIB_TEX7; ++i)
{
if (!(compiler->Base.Program.InputsRead & (1 << i))) {
fp->wpos_attr = i;
break;
}
}
/* No free texcoord found, fall-back to software rendering */
if (fp->wpos_attr == FRAG_ATTRIB_MAX)
{
compiler->Base.Error = 1;
return;
}
rc_transform_fragment_wpos(&compiler->Base, FRAG_ATTRIB_WPOS, fp->wpos_attr);
}
/**
* Rewrite fragment.fogcoord to use a texture coordinate slot.
* Note that fogcoord is forced into an X001 pattern, and this enforcement
* is done here.
*
* See also the counterpart rewriting for vertex programs.
*/
static void rewriteFog(struct r300_fragment_program_compiler *compiler, struct r300_fragment_program * fp)
{
struct rc_src_register src;
int i;
fp->fog_attr = FRAG_ATTRIB_MAX;
if (!(compiler->Base.Program.InputsRead & FRAG_BIT_FOGC)) {
return;
}
for (i = FRAG_ATTRIB_TEX0; i <= FRAG_ATTRIB_TEX7; ++i)
{
if (!(compiler->Base.Program.InputsRead & (1 << i))) {
fp->fog_attr = i;
break;
}
}
/* No free texcoord found, fall-back to software rendering */
if (fp->fog_attr == FRAG_ATTRIB_MAX)
{
compiler->Base.Error = 1;
return;
}
memset(&src, 0, sizeof(src));
src.File = RC_FILE_INPUT;
src.Index = fp->fog_attr;
src.Swizzle = MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_ZERO, SWIZZLE_ZERO, SWIZZLE_ONE);
rc_move_input(&compiler->Base, FRAG_ATTRIB_FOGC, src);
}
/**
* Reserve hardware temporary registers for the program inputs.
*
* @note This allocation is performed explicitly, because the order of inputs
* is determined by the RS hardware.
*/
static void allocate_hw_inputs(
struct r300_fragment_program_compiler * c,
void (*allocate)(void * data, unsigned input, unsigned hwreg),
void * mydata)
{
GLuint InputsRead = c->Base.Program.InputsRead;
int i;
GLuint hwindex = 0;
/* Primary colour */
if (InputsRead & FRAG_BIT_COL0)
allocate(mydata, FRAG_ATTRIB_COL0, hwindex++);
InputsRead &= ~FRAG_BIT_COL0;
/* Secondary color */
if (InputsRead & FRAG_BIT_COL1)
allocate(mydata, FRAG_ATTRIB_COL1, hwindex++);
InputsRead &= ~FRAG_BIT_COL1;
/* Texcoords */
for (i = 0; i < 8; i++) {
if (InputsRead & (FRAG_BIT_TEX0 << i))
allocate(mydata, FRAG_ATTRIB_TEX0+i, hwindex++);
}
InputsRead &= ~FRAG_BITS_TEX_ANY;
/* Fogcoords treated as a texcoord */
if (InputsRead & FRAG_BIT_FOGC)
allocate(mydata, FRAG_ATTRIB_FOGC, hwindex++);
InputsRead &= ~FRAG_BIT_FOGC;
/* fragment position treated as a texcoord */
if (InputsRead & FRAG_BIT_WPOS)
allocate(mydata, FRAG_ATTRIB_WPOS, hwindex++);
InputsRead &= ~FRAG_BIT_WPOS;
/* Anything else */
if (InputsRead)
rc_error(&c->Base, "Don't know how to handle inputs 0x%x\n", InputsRead);
}
static void translate_fragment_program(GLcontext *ctx, struct r300_fragment_program_cont *cont, struct r300_fragment_program *fp)
{
r300ContextPtr r300 = R300_CONTEXT(ctx);
struct r300_fragment_program_compiler compiler;
rc_init(&compiler.Base);
compiler.Base.Debug = (RADEON_DEBUG & RADEON_PIXEL) ? GL_TRUE : GL_FALSE;
compiler.code = &fp->code;
compiler.state = fp->state;
compiler.is_r500 = (r300->radeon.radeonScreen->chip_family >= CHIP_FAMILY_RV515) ? GL_TRUE : GL_FALSE;
compiler.OutputDepth = FRAG_RESULT_DEPTH;
compiler.OutputColor = FRAG_RESULT_COLOR;
compiler.AllocateHwInputs = &allocate_hw_inputs;
if (compiler.Base.Debug) {
fflush(stderr);
_mesa_printf("Fragment Program: Initial program:\n");
_mesa_print_program(&cont->Base.Base);
fflush(stderr);
}
radeon_mesa_to_rc_program(&compiler.Base, &cont->Base.Base);
insert_WPOS_trailer(&compiler, fp);
rewriteFog(&compiler, fp);
r3xx_compile_fragment_program(&compiler);
if (compiler.is_r500) {
/* We need to support the non-KMS DRM interface, which
* artificially limits the number of instructions and
* constants which are available to us.
*
* See also the comment in r300_context.c where we
* set the MAX_NATIVE_xxx values.
*/
if (fp->code.code.r500.inst_end >= 255 || fp->code.constants.Count > 255)
rc_error(&compiler.Base, "Program is too big (upgrade to r300g to avoid this limitation).\n");
}
fp->error = compiler.Base.Error;
fp->InputsRead = compiler.Base.Program.InputsRead;
rc_destroy(&compiler.Base);
}
struct r300_fragment_program *r300SelectAndTranslateFragmentShader(GLcontext *ctx)
{
r300ContextPtr r300 = R300_CONTEXT(ctx);
struct r300_fragment_program_cont *fp_list;
struct r300_fragment_program *fp;
struct r300_fragment_program_external_state state;
fp_list = (struct r300_fragment_program_cont *)ctx->FragmentProgram._Current;
build_state(r300, ctx->FragmentProgram._Current, &state);
fp = fp_list->progs;
while (fp) {
if (_mesa_memcmp(&fp->state, &state, sizeof(state)) == 0) {
return r300->selected_fp = fp;
}
fp = fp->next;
}
fp = _mesa_calloc(sizeof(struct r300_fragment_program));
fp->state = state;
fp->next = fp_list->progs;
fp_list->progs = fp;
translate_fragment_program(ctx, fp_list, fp);
return r300->selected_fp = fp;
}