blob: 9abbdc1ea99a94924bfd8e389b5fcc466ba90f6a [file] [log] [blame]
/**************************************************************************
*
* Copyright 2008 VMware, Inc.
* 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, sub license, 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 NON-INFRINGEMENT.
* IN NO EVENT SHALL VMWARE 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.
*
**************************************************************************/
/**
* TGSI program transformation utility.
*
* Authors: Brian Paul
*/
#include "util/u_debug.h"
#include "util/log.h"
#include "tgsi_transform.h"
/**
* Increments the next-token index if the tgsi_build_* succeeded, or extends the
* token array and returns true to request a re-emit of the tgsi_build_* by the
* caller.
*/
static bool
need_re_emit(struct tgsi_transform_context *ctx, uint32_t emitted, struct tgsi_header orig_header)
{
if (emitted > 0) {
ctx->ti += emitted;
return false;
} else {
uint32_t new_len = ctx->max_tokens_out * 2;
if (new_len < ctx->max_tokens_out) {
ctx->fail = true;
return false;
}
struct tgsi_token *new_tokens = tgsi_alloc_tokens(new_len);
if (!new_tokens) {
ctx->fail = true;
return false;
}
memcpy(new_tokens, ctx->tokens_out, sizeof(struct tgsi_token) * ctx->ti);
tgsi_free_tokens(ctx->tokens_out);
ctx->tokens_out = new_tokens;
ctx->max_tokens_out = new_len;
/* Point the header at the resized tokens. */
ctx->header = (struct tgsi_header *)new_tokens;
/* The failing emit may have incremented header/body size, reset it to its state before our attempt. */
*ctx->header = orig_header;
return true;
}
}
static void
emit_instruction(struct tgsi_transform_context *ctx,
const struct tgsi_full_instruction *inst)
{
uint32_t emitted;
struct tgsi_header orig_header = *ctx->header;
do {
emitted = tgsi_build_full_instruction(inst,
ctx->tokens_out + ctx->ti,
ctx->header,
ctx->max_tokens_out - ctx->ti);
} while (need_re_emit(ctx, emitted, orig_header));
}
static void
emit_declaration(struct tgsi_transform_context *ctx,
const struct tgsi_full_declaration *decl)
{
uint32_t emitted;
struct tgsi_header orig_header = *ctx->header;
do {
emitted = tgsi_build_full_declaration(decl,
ctx->tokens_out + ctx->ti,
ctx->header,
ctx->max_tokens_out - ctx->ti);
} while (need_re_emit(ctx, emitted, orig_header));
}
static void
emit_immediate(struct tgsi_transform_context *ctx,
const struct tgsi_full_immediate *imm)
{
uint32_t emitted;
struct tgsi_header orig_header = *ctx->header;
do {
emitted = tgsi_build_full_immediate(imm,
ctx->tokens_out + ctx->ti,
ctx->header,
ctx->max_tokens_out - ctx->ti);
} while (need_re_emit(ctx, emitted, orig_header));
}
static void
emit_property(struct tgsi_transform_context *ctx,
const struct tgsi_full_property *prop)
{
uint32_t emitted;
struct tgsi_header orig_header = *ctx->header;
do {
emitted = tgsi_build_full_property(prop,
ctx->tokens_out + ctx->ti,
ctx->header,
ctx->max_tokens_out - ctx->ti);
} while (need_re_emit(ctx, emitted, orig_header));
}
/**
* Apply user-defined transformations to the input shader to produce
* the output shader.
* For example, a register search-and-replace operation could be applied
* by defining a transform_instruction() callback that examined and changed
* the instruction src/dest regs.
*
* \return new tgsi tokens, or NULL on failure
*/
struct tgsi_token *
tgsi_transform_shader(const struct tgsi_token *tokens_in,
uint initial_tokens_len,
struct tgsi_transform_context *ctx)
{
boolean first_instruction = TRUE;
boolean epilog_emitted = FALSE;
int cond_stack = 0;
int call_stack = 0;
/* input shader */
struct tgsi_parse_context parse;
/* output shader */
struct tgsi_processor *processor;
/* Always include space for the header. */
initial_tokens_len = MAX2(initial_tokens_len, 2);
/**
** callback context init
**/
ctx->emit_instruction = emit_instruction;
ctx->emit_declaration = emit_declaration;
ctx->emit_immediate = emit_immediate;
ctx->emit_property = emit_property;
ctx->tokens_out = tgsi_alloc_tokens(initial_tokens_len);
ctx->max_tokens_out = initial_tokens_len;
ctx->fail = false;
if (!ctx->tokens_out) {
mesa_loge("failed to allocate %d tokens\n", initial_tokens_len);
return NULL;
}
/**
** Setup to begin parsing input shader
**/
if (tgsi_parse_init( &parse, tokens_in ) != TGSI_PARSE_OK) {
debug_printf("tgsi_parse_init() failed in tgsi_transform_shader()!\n");
return NULL;
}
ctx->processor = parse.FullHeader.Processor.Processor;
/**
** Setup output shader
**/
ctx->header = (struct tgsi_header *)ctx->tokens_out;
*ctx->header = tgsi_build_header();
processor = (struct tgsi_processor *) (ctx->tokens_out + 1);
*processor = tgsi_build_processor( ctx->processor, ctx->header );
ctx->ti = 2;
/**
** Loop over incoming program tokens/instructions
*/
while( !tgsi_parse_end_of_tokens( &parse ) ) {
tgsi_parse_token( &parse );
switch( parse.FullToken.Token.Type ) {
case TGSI_TOKEN_TYPE_INSTRUCTION:
{
struct tgsi_full_instruction *fullinst
= &parse.FullToken.FullInstruction;
enum tgsi_opcode opcode = fullinst->Instruction.Opcode;
if (first_instruction && ctx->prolog) {
ctx->prolog(ctx);
}
/*
* XXX Note: we handle the case of ret in main.
* However, the output redirections done by transform
* have their limits with control flow and will generally
* not work correctly. e.g.
* if (cond) {
* oColor = x;
* ret;
* }
* oColor = y;
* end;
* If the color output is redirected to a temp and modified
* by a transform, this will not work (the oColor assignment
* in the conditional will never make it to the actual output).
*/
if ((opcode == TGSI_OPCODE_END || opcode == TGSI_OPCODE_RET) &&
call_stack == 0 && ctx->epilog && !epilog_emitted) {
if (opcode == TGSI_OPCODE_RET && cond_stack != 0) {
assert(!"transform ignoring RET in main");
} else {
assert(cond_stack == 0);
/* Emit caller's epilog */
ctx->epilog(ctx);
epilog_emitted = TRUE;
}
/* Emit END (or RET) */
ctx->emit_instruction(ctx, fullinst);
}
else {
switch (opcode) {
case TGSI_OPCODE_IF:
case TGSI_OPCODE_UIF:
case TGSI_OPCODE_SWITCH:
case TGSI_OPCODE_BGNLOOP:
cond_stack++;
break;
case TGSI_OPCODE_CAL:
call_stack++;
break;
case TGSI_OPCODE_ENDIF:
case TGSI_OPCODE_ENDSWITCH:
case TGSI_OPCODE_ENDLOOP:
assert(cond_stack > 0);
cond_stack--;
break;
case TGSI_OPCODE_ENDSUB:
assert(call_stack > 0);
call_stack--;
break;
case TGSI_OPCODE_BGNSUB:
case TGSI_OPCODE_RET:
default:
break;
}
if (ctx->transform_instruction)
ctx->transform_instruction(ctx, fullinst);
else
ctx->emit_instruction(ctx, fullinst);
}
first_instruction = FALSE;
}
break;
case TGSI_TOKEN_TYPE_DECLARATION:
{
struct tgsi_full_declaration *fulldecl
= &parse.FullToken.FullDeclaration;
if (ctx->transform_declaration)
ctx->transform_declaration(ctx, fulldecl);
else
ctx->emit_declaration(ctx, fulldecl);
}
break;
case TGSI_TOKEN_TYPE_IMMEDIATE:
{
struct tgsi_full_immediate *fullimm
= &parse.FullToken.FullImmediate;
if (ctx->transform_immediate)
ctx->transform_immediate(ctx, fullimm);
else
ctx->emit_immediate(ctx, fullimm);
}
break;
case TGSI_TOKEN_TYPE_PROPERTY:
{
struct tgsi_full_property *fullprop
= &parse.FullToken.FullProperty;
if (ctx->transform_property)
ctx->transform_property(ctx, fullprop);
else
ctx->emit_property(ctx, fullprop);
}
break;
default:
assert( 0 );
}
}
assert(call_stack == 0);
tgsi_parse_free (&parse);
if (ctx->fail) {
tgsi_free_tokens(ctx->tokens_out);
return NULL;
}
return ctx->tokens_out;
}
#include "tgsi_text.h"
extern int tgsi_transform_foo( struct tgsi_token *tokens_out,
uint max_tokens_out );
/* This function exists only so that tgsi_text_translate() doesn't get
* magic-ed out of the libtgsi.a archive by the build system. Don't
* remove unless you know this has been fixed - check on mingw/scons
* builds as well.
*/
int
tgsi_transform_foo( struct tgsi_token *tokens_out,
uint max_tokens_out )
{
const char *text =
"FRAG\n"
"DCL IN[0], COLOR, CONSTANT\n"
"DCL OUT[0], COLOR\n"
" 0: MOV OUT[0], IN[0]\n"
" 1: END";
return tgsi_text_translate( text,
tokens_out,
max_tokens_out );
}