diff options
author | Eric Anholt <[email protected]> | 2011-12-19 10:53:10 -0800 |
---|---|---|
committer | Eric Anholt <[email protected]> | 2012-03-16 11:55:38 -0700 |
commit | 31866308fcf989df992ace28b5b986c3d3770e90 (patch) | |
tree | 28db9a1b780624214ef409b78108f809842dfb3f /src/mesa/drivers/dri/i965/brw_fs_emit.cpp | |
parent | bddb2edab616d30f7894cfff7071a70d273a848e (diff) |
i965/fs: Jump from discard statements to the end of the program when done.
From the GLSL 1.30 spec:
The discard keyword is only allowed within fragment shaders. It
can be used within a fragment shader to abandon the operation on
the current fragment. This keyword causes the fragment to be
discarded and no updates to any buffers will occur. Control flow
exits the shader, and subsequent implicit or explicit derivatives
are undefined when this control flow is non-uniform (meaning
different fragments within the primitive take different control
paths).
v2: Don't emit the final HALT if no other HALTs were emitted.
Reviewed-by: Kenneth Graunke <[email protected]> (v1)
Diffstat (limited to 'src/mesa/drivers/dri/i965/brw_fs_emit.cpp')
-rw-r--r-- | src/mesa/drivers/dri/i965/brw_fs_emit.cpp | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/src/mesa/drivers/dri/i965/brw_fs_emit.cpp b/src/mesa/drivers/dri/i965/brw_fs_emit.cpp index 0c32f085114..702c5251421 100644 --- a/src/mesa/drivers/dri/i965/brw_fs_emit.cpp +++ b/src/mesa/drivers/dri/i965/brw_fs_emit.cpp @@ -37,11 +37,55 @@ extern "C" { #include "glsl/ir_print_visitor.h" void +fs_visitor::patch_discard_jumps_to_fb_writes() +{ + if (intel->gen < 6 || this->discard_halt_patches.is_empty()) + return; + + /* There is a somewhat strange undocumented requirement of using + * HALT, according to the simulator. If some channel has HALTed to + * a particular UIP, then by the end of the program, every channel + * must have HALTed to that UIP. Furthermore, the tracking is a + * stack, so you can't do the final halt of a UIP after starting + * halting to a new UIP. + * + * Symptoms of not emitting this instruction on actual hardware + * included GPU hangs and sparkly rendering on the piglit discard + * tests. + */ + struct brw_instruction *last_halt = gen6_HALT(p); + last_halt->bits3.break_cont.uip = 2; + last_halt->bits3.break_cont.jip = 2; + + int ip = p->nr_insn; + + foreach_list(node, &this->discard_halt_patches) { + ip_record *patch_ip = (ip_record *)node; + struct brw_instruction *patch = &p->store[patch_ip->ip]; + int br = (intel->gen >= 5) ? 2 : 1; + + /* HALT takes a distance from the pre-incremented IP, so '1' + * would be the next instruction after jmpi. + */ + assert(patch->header.opcode == BRW_OPCODE_HALT); + patch->bits3.break_cont.uip = (ip - patch_ip->ip) * br; + } + + this->discard_halt_patches.make_empty(); +} + +void fs_visitor::generate_fb_write(fs_inst *inst) { bool eot = inst->eot; struct brw_reg implied_header; + /* Note that the jumps emitted to this point mean that the g0 -> + * base_mrf setup must be inside of this function, so that we jump + * to a point containing it. + */ + patch_discard_jumps_to_fb_writes(); + /* Header is 2 regs, g0 and g1 are the contents. g0 will be implied * move, here's g1. */ @@ -482,6 +526,17 @@ fs_visitor::generate_discard(fs_inst *inst) brw_set_mask_control(p, BRW_MASK_DISABLE); brw_AND(p, g1, f0, g1); brw_pop_insn_state(p); + + /* GLSL 1.30+ say that discarded channels should stop executing + * (so, for example, an infinite loop that would otherwise in + * just that channel does not occur. + * + * This HALT will be patched up at FB write time to point UIP at + * the end of the program, and at brw_uip_jip() JIP will be set + * to the end of the current block (or the program). + */ + this->discard_halt_patches.push_tail(new(mem_ctx) ip_record(p->nr_insn)); + gen6_HALT(p); } else { struct brw_reg g0 = retype(brw_vec1_grf(0, 0), BRW_REGISTER_TYPE_UW); |