Merge branch 'mesa_7_7_branch'
[mesa.git] / src / gallium / drivers / r300 / r300_fs.c
index 4b304306d0f9027db1a0d076c36789c338653b68..79b01bb4dc26ce97534c915f9dc015647b4ea792 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright 2008 Corbin Simpson <MostAwesomeDude@gmail.com>
  *                Joakim Sindholt <opensource@zhasha.com>
+ * Copyright 2009 Marek Olšák <maraeo@gmail.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  * USE OR OTHER DEALINGS IN THE SOFTWARE. */
 
+#include "tgsi/tgsi_dump.h"
+
+#include "r300_context.h"
+#include "r300_screen.h"
 #include "r300_fs.h"
+#include "r300_tgsi_to_rc.h"
 
-void r300_translate_fragment_shader(struct r300_context* r300,
-                                    struct r300_fragment_shader* fs)
+#include "radeon_code.h"
+#include "radeon_compiler.h"
+
+/* Convert info about FS input semantics to r300_shader_semantics. */
+static void r300_shader_read_fs_inputs(struct tgsi_shader_info* info,
+                                       struct r300_shader_semantics* fs_inputs)
 {
-    struct tgsi_parse_context parser;
     int i;
-    boolean is_r500 = r300_screen(r300->context.screen)->caps->is_r500;
-    struct r300_constant_buffer* consts =
-        &r300->shader_constants[PIPE_SHADER_FRAGMENT];
+    unsigned index;
 
-    struct r300_fs_asm* assembler = CALLOC_STRUCT(r300_fs_asm);
-    if (assembler == NULL) {
-        return;
-    }
-    /* Setup starting offset for immediates. */
-    assembler->imm_offset = consts->user_count;
-    /* Enable depth writes, if needed. */
-    assembler->writes_depth = fs->info.writes_z;
-
-    /* Make sure we start at the beginning of the shader. */
-    if (is_r500) {
-        ((struct r5xx_fragment_shader*)fs)->instruction_count = 0;
-    }
+    r300_shader_semantics_reset(fs_inputs);
 
-    tgsi_parse_init(&parser, fs->state.tokens);
+    for (i = 0; i < info->num_inputs; i++) {
+        index = info->input_semantic_index[i];
 
-    while (!tgsi_parse_end_of_tokens(&parser)) {
-        tgsi_parse_token(&parser);
+        switch (info->input_semantic_name[i]) {
+            case TGSI_SEMANTIC_COLOR:
+                assert(index <= ATTR_COLOR_COUNT);
+                fs_inputs->color[index] = i;
+                break;
 
-        /* This is seriously the lamest way to create fragment programs ever.
-         * I blame TGSI. */
-        switch (parser.FullToken.Token.Type) {
-            case TGSI_TOKEN_TYPE_DECLARATION:
-                /* Allocated registers sitting at the beginning
-                 * of the program. */
-                r300_fs_declare(assembler, &parser.FullToken.FullDeclaration);
+            case TGSI_SEMANTIC_GENERIC:
+                assert(index <= ATTR_GENERIC_COUNT);
+                fs_inputs->generic[index] = i;
                 break;
-            case TGSI_TOKEN_TYPE_IMMEDIATE:
-                debug_printf("r300: Emitting immediate to constant buffer, "
-                        "position %d\n",
-                        assembler->imm_offset + assembler->imm_count);
-                /* I am not amused by the length of these. */
-                for (i = 0; i < 4; i++) {
-                    consts->constants[assembler->imm_offset +
-                        assembler->imm_count][i] =
-                        parser.FullToken.FullImmediate.u.ImmediateFloat32[i]
-                        .Float;
-                }
-                assembler->imm_count++;
+
+            case TGSI_SEMANTIC_FOG:
+                assert(index == 0);
+                fs_inputs->fog = i;
+                break;
+
+            default:
+                assert(0);
+        }
+    }
+}
+
+
+static void find_output_registers(struct r300_fragment_program_compiler * compiler,
+                                  struct r300_fragment_shader * fs)
+{
+    unsigned i;
+
+    /* Mark the outputs as not present initially */
+    compiler->OutputColor = fs->info.num_outputs;
+    compiler->OutputDepth = fs->info.num_outputs;
+
+    /* Now see where they really are. */
+    for(i = 0; i < fs->info.num_outputs; ++i) {
+        switch(fs->info.output_semantic_name[i]) {
+            case TGSI_SEMANTIC_COLOR:
+                compiler->OutputColor = i;
                 break;
-            case TGSI_TOKEN_TYPE_INSTRUCTION:
-                if (is_r500) {
-                    r5xx_fs_instruction((struct r5xx_fragment_shader*)fs,
-                            assembler, &parser.FullToken.FullInstruction);
-                } else {
-                    r3xx_fs_instruction((struct r3xx_fragment_shader*)fs,
-                            assembler, &parser.FullToken.FullInstruction);
-                }
+            case TGSI_SEMANTIC_POSITION:
+                compiler->OutputDepth = i;
                 break;
         }
     }
+}
 
-    debug_printf("r300: fs: %d texs and %d colors, first free reg is %d\n",
-            assembler->tex_count, assembler->color_count,
-            assembler->tex_count + assembler->color_count);
-
-    consts->count = consts->user_count + assembler->imm_count;
-    fs->uses_imms = assembler->imm_count;
-    debug_printf("r300: fs: %d total constants, "
-            "%d from user and %d from immediates\n", consts->count,
-            consts->user_count, assembler->imm_count);
-    r3xx_fs_finalize(fs, assembler);
-    if (is_r500) {
-        r5xx_fs_finalize((struct r5xx_fragment_shader*)fs, assembler);
+static void allocate_hardware_inputs(
+    struct r300_fragment_program_compiler * c,
+    void (*allocate)(void * data, unsigned input, unsigned hwreg),
+    void * mydata)
+{
+    struct r300_shader_semantics* inputs =
+        &((struct r300_fragment_shader*)c->UserData)->inputs;
+    int i, reg = 0;
+
+    /* Allocate input registers. */
+    for (i = 0; i < ATTR_COLOR_COUNT; i++) {
+        if (inputs->color[i] != ATTR_UNUSED) {
+            allocate(mydata, inputs->color[i], reg++);
+        }
+    }
+    for (i = 0; i < ATTR_GENERIC_COUNT; i++) {
+        if (inputs->generic[i] != ATTR_UNUSED) {
+            allocate(mydata, inputs->generic[i], reg++);
+        }
+    }
+    if (inputs->fog != ATTR_UNUSED) {
+        allocate(mydata, inputs->fog, reg++);
     }
+}
+
+void r300_translate_fragment_shader(struct r300_context* r300,
+                                    struct r300_fragment_shader* fs)
+{
+    struct r300_fragment_program_compiler compiler;
+    struct tgsi_to_rc ttr;
+
+    /* Initialize. */
+    r300_shader_read_fs_inputs(&fs->info, &fs->inputs);
+
+    /* Setup the compiler. */
+    memset(&compiler, 0, sizeof(compiler));
+    rc_init(&compiler.Base);
+    compiler.Base.Debug = DBG_ON(r300, DBG_FP);
+
+    compiler.code = &fs->code;
+    compiler.is_r500 = r300_screen(r300->context.screen)->caps->is_r500;
+    compiler.AllocateHwInputs = &allocate_hardware_inputs;
+    compiler.UserData = fs;
+
+    /* XXX: Program compilation depends on texture compare modes,
+     * which are sampler state. Therefore, programs need to be recompiled
+     * depending on this state as in the classic Mesa driver.
+     *
+     * This is not yet handled correctly.
+     */
+
+    find_output_registers(&compiler, fs);
+
+    if (compiler.Base.Debug) {
+        debug_printf("r300: Initial fragment program\n");
+        tgsi_dump(fs->state.tokens, 0);
+    }
+
+    /* Translate TGSI to our internal representation */
+    ttr.compiler = &compiler.Base;
+    ttr.info = &fs->info;
+
+    r300_tgsi_to_rc(&ttr, fs->state.tokens);
 
-    tgsi_dump(fs->state.tokens, 0);
-    /* XXX finish r300 dumper too */
-    if (is_r500) {
-        r5xx_fs_dump((struct r5xx_fragment_shader*)fs);
+    /* Invoke the compiler */
+    r3xx_compile_fragment_program(&compiler);
+    if (compiler.Base.Error) {
+        /* XXX failover maybe? */
+        DBG(r300, DBG_FP, "r300: Error compiling fragment program: %s\n",
+            compiler.Base.ErrorMsg);
+        assert(0);
     }
 
-    tgsi_parse_free(&parser);
-    FREE(assembler);
+    /* And, finally... */
+    rc_destroy(&compiler.Base);
+    fs->translated = TRUE;
 }