/* * Copyright © 2014-2015 Broadcom * SPDX-License-Identifier: MIT */ #include #include "nir_to_rc.h" #include "compiler/nir/nir.h" #include "compiler/nir/nir_builder.h" #include "compiler/nir/nir_deref.h" #include "compiler/nir/nir_legacy.h" #include "compiler/radeon_code.h" #include "compiler/radeon_compiler.h" #include "compiler/radeon_program.h" #include "compiler/radeon_program_constants.h" #include "pipe/p_screen.h" #include "pipe/p_state.h" #include "util/compiler.h" #include "util/u_debug.h" #include "util/u_dynarray.h" #include "util/u_math.h" #include "util/u_memory.h" #include "r300_nir.h" #include "r300_screen.h" struct ntr_immediate { float values[4]; }; struct ntr_compile { nir_shader *s; struct r300_shader_semantics *semantics; /* Options */ bool lower_fabs; /* r300's RC backend only tracks a single ADDR[0].x; logical address * indexing collapses to that one register. */ struct rc_dst_register addr_reg; /* if condition set up at the end of a block, for ntr_emit_if(). */ struct rc_src_register if_cond; /* Temps for our NIR SSA and register values. */ struct rc_dst_register *reg_temp; struct rc_src_register *ssa_temp; unsigned num_temps; /* Map from NIR driver_location to RC input register. */ struct rc_src_register *input_index_map; /* RC-side state for direct emission. */ struct radeon_compiler *compiler; /* one struct ntr_immediate per NIR load_const */ struct util_dynarray immediates; /* UBO size in vec4s (0 if no UBO) */ unsigned ubo_size; /* offset for IMMEDIATE indices in the compiler's constants table */ unsigned immediate_offset; /* FS output tracking. */ int fs_output_color_index[4]; int fs_output_depth_index; int fs_output_index[FRAG_RESULT_MAX]; unsigned num_outputs; }; struct ntr_alu_dst { struct rc_dst_register reg; rc_saturate_mode saturate; }; static unsigned ntr_rc_register_index(struct ntr_compile *c, int index); static struct rc_dst_register ntr_temp(struct ntr_compile *c) { return (struct rc_dst_register) { .File = RC_FILE_TEMPORARY, .Index = ntr_rc_register_index(c, c->num_temps++), .WriteMask = RC_MASK_XYZW, }; } static void ntr_emit_cf_list(struct ntr_compile *c, struct exec_list *list); static unsigned ntr_rc_register_index(struct ntr_compile *c, int index) { /* Negative offsets to relative addressing should have been lowered in NIR. */ assert(index >= 0); if (index >= RC_REGISTER_MAX_INDEX) rc_error(c->compiler, "r300: Register index too high.\n"); return index; } static struct rc_dst_register ntr_dst_register(struct ntr_compile *c, rc_register_file file, int index) { return (struct rc_dst_register) { .File = file, .Index = ntr_rc_register_index(c, index), .WriteMask = RC_MASK_XYZW, }; } static struct rc_dst_register ntr_writemask(struct rc_dst_register dst, unsigned writemask) { dst.WriteMask &= writemask; return dst; } static struct ntr_alu_dst ntr_alu_dst(struct rc_dst_register reg, rc_saturate_mode saturate) { return (struct ntr_alu_dst) { .reg = reg, .saturate = saturate, }; } static struct ntr_alu_dst ntr_alu_dst_saturate(struct ntr_alu_dst dst) { dst.saturate = RC_SATURATE_ZERO_ONE; return dst; } static void ntr_set_dst_index(struct ntr_compile *c, struct rc_dst_register *dst, int index) { dst->Index = ntr_rc_register_index(c, index); } static struct rc_src_register ntr_src_register(struct ntr_compile *c, rc_register_file file, int index) { return (struct rc_src_register) { .File = file, .Index = ntr_rc_register_index(c, index), .Swizzle = RC_SWIZZLE_XYZW, .Negate = RC_MASK_NONE, }; } static void ntr_set_src_index(struct ntr_compile *c, struct rc_src_register *src, int index) { src->Index = ntr_rc_register_index(c, index); } static struct rc_src_register ntr_src_from_dst(struct rc_dst_register dst) { return (struct rc_src_register) { .File = dst.File, .Index = dst.Index, .Swizzle = RC_SWIZZLE_XYZW, .Negate = RC_MASK_NONE, }; } static struct rc_src_register ntr_swizzle(struct rc_src_register src, int x, int y, int z, int w) { int swizzle[4] = {x, y, z, w}; unsigned old_swizzle = src.Swizzle; unsigned old_negate = src.Negate; src.Swizzle = 0; src.Negate = RC_MASK_NONE; for (unsigned i = 0; i < 4; i++) { assert(swizzle[i] < 4); SET_SWZ(src.Swizzle, i, GET_SWZ(old_swizzle, swizzle[i])); if (old_negate & (1 << swizzle[i])) src.Negate |= 1 << i; } return src; } static struct rc_src_register ntr_scalar(struct rc_src_register src, int x) { return ntr_swizzle(src, x, x, x, x); } static struct rc_src_register ntr_abs(struct rc_src_register src) { src.Abs = true; src.Negate = RC_MASK_NONE; return src; } static struct rc_src_register ntr_negate(struct rc_src_register src) { src.Negate ^= RC_MASK_XYZW; return src; } static struct rc_src_register ntr_src_indirect(struct rc_src_register src) { src.RelAddr = true; return src; } static void ntr_rc_tex_sampler(struct ntr_compile *c, struct rc_sub_instruction *inst, unsigned sampler_index, bool reladdr) { if (reladdr) rc_error(c->compiler, "r300: Relative addressing of sampler operands is unsupported.\n"); inst->TexSrcUnit = ntr_rc_register_index(c, sampler_index); } static struct rc_sub_instruction * rc_sub_instruction(struct ntr_compile *c, rc_opcode opcode, const struct rc_dst_register *dst, rc_saturate_mode saturate, const struct rc_src_register *src0, const struct rc_src_register *src1, const struct rc_src_register *src2) { const struct rc_opcode_info *opcode_info = rc_get_opcode_info(opcode); assert(opcode_info->NumSrcRegs <= 3); assert(opcode_info->HasDstReg == (dst != NULL)); assert(opcode_info->NumSrcRegs >= (src2 ? 3 : src1 ? 2 : src0 ? 1 : 0)); struct rc_instruction *rc_inst = rc_insert_new_instruction(c->compiler, c->compiler->Program.Instructions.Prev); struct rc_sub_instruction *inst = &rc_inst->U.I; inst->Opcode = opcode; if (dst) { inst->DstReg = *dst; inst->SaturateMode = saturate; } if (src0) inst->SrcReg[0] = *src0; if (src1) inst->SrcReg[1] = *src1; if (src2) inst->SrcReg[2] = *src2; return inst; } static struct rc_sub_instruction * ntr_emit_alu_op(struct ntr_compile *c, rc_opcode opcode, struct ntr_alu_dst dst, const struct rc_src_register *src0, const struct rc_src_register *src1, const struct rc_src_register *src2) { return rc_sub_instruction(c, opcode, &dst.reg, dst.saturate, src0, src1, src2); } static inline void ntr_emit_alu_op1(struct ntr_compile *c, rc_opcode opcode, struct ntr_alu_dst dst, struct rc_src_register src0) { ntr_emit_alu_op(c, opcode, dst, &src0, NULL, NULL); } static inline void ntr_emit_alu_op2(struct ntr_compile *c, rc_opcode opcode, struct ntr_alu_dst dst, struct rc_src_register src0, struct rc_src_register src1) { ntr_emit_alu_op(c, opcode, dst, &src0, &src1, NULL); } static inline void ntr_emit_alu_op3(struct ntr_compile *c, rc_opcode opcode, struct ntr_alu_dst dst, struct rc_src_register src0, struct rc_src_register src1, struct rc_src_register src2) { ntr_emit_alu_op(c, opcode, dst, &src0, &src1, &src2); } #define NTR_OP00(name, op) \ static inline void ntr_##name(struct ntr_compile *c) \ { \ rc_sub_instruction(c, op, NULL, RC_SATURATE_NONE, NULL, NULL, NULL); \ } #define NTR_OP01(name, op) \ static inline void ntr_##name(struct ntr_compile *c, struct rc_src_register src0)\ { \ rc_sub_instruction(c, op, NULL, RC_SATURATE_NONE, &src0, NULL, NULL); \ } #define NTR_OP11(name, op) \ static inline void ntr_##name(struct ntr_compile *c, struct rc_dst_register dst, \ struct rc_src_register src0) \ { \ rc_sub_instruction(c, op, &dst, RC_SATURATE_NONE, &src0, NULL, NULL); \ } NTR_OP00(KILL, RC_OPCODE_KILP) NTR_OP00(BGNLOOP, RC_OPCODE_BGNLOOP) NTR_OP00(BRK, RC_OPCODE_BRK) NTR_OP00(CONT, RC_OPCODE_CONT) NTR_OP00(ELSE, RC_OPCODE_ELSE) NTR_OP00(ENDIF, RC_OPCODE_ENDIF) NTR_OP00(ENDLOOP, RC_OPCODE_ENDLOOP) NTR_OP01(KILL_IF, RC_OPCODE_KIL) NTR_OP01(IF, RC_OPCODE_IF) NTR_OP11(MOV, RC_OPCODE_MOV) NTR_OP11(ARL, RC_OPCODE_ARL) NTR_OP11(DDX, RC_OPCODE_DDX) NTR_OP11(DDY, RC_OPCODE_DDY) /** * Interprets a nir_load_const used as a NIR src as a uint. * * For non-native-integers drivers, nir_load_const_instrs used by an integer ALU * instruction (or in a phi-web used by an integer ALU instruction) were * converted to floats and the ALU instruction swapped to the float equivalent. * However, this means that integer load_consts used by intrinsics (which don't * normally get that conversion) may have been reformatted to be floats. Given * that all of our intrinsic nir_src_as_uint() calls are expected to be small, * we can just look and see if they look like floats and convert them back to * ints. */ static uint32_t ntr_src_as_uint(struct ntr_compile *c, nir_src src) { uint32_t val = nir_src_as_uint(src); if (val >= fui(1.0)) val = (uint32_t)uif(val); return val; } static void ntr_read_input_output(struct ntr_compile *c, gl_varying_slot location, unsigned base) { if (base >= c->semantics->num_total) c->semantics->num_total = base + 1; switch (location) { case VARYING_SLOT_POS: if (c->s->info.stage == MESA_SHADER_VERTEX) c->semantics->pos = base; else c->semantics->wpos = base; break; case VARYING_SLOT_PSIZ: c->semantics->psize = base; break; case VARYING_SLOT_COL0: c->semantics->color[0] = base; break; case VARYING_SLOT_COL1: c->semantics->color[1] = base; break; case VARYING_SLOT_BFC0: c->semantics->bcolor[0] = base; break; case VARYING_SLOT_BFC1: c->semantics->bcolor[1] = base; break; case VARYING_SLOT_FOGC: c->semantics->fog = base; break; case VARYING_SLOT_FACE: assert(c->s->info.stage == MESA_SHADER_FRAGMENT); c->semantics->face = base; break; case VARYING_SLOT_EDGE: assert(c->s->info.stage == MESA_SHADER_VERTEX); fprintf(stderr, "r300 VP: cannot handle edgeflag output.\n"); break; default: if (location >= VARYING_SLOT_VAR0 && location <= VARYING_SLOT_VAR31) { unsigned index = location - VARYING_SLOT_VAR0; if (c->semantics->generic[index] == ATTR_UNUSED) c->semantics->num_generic++; c->semantics->generic[index] = base; } else { printf("Unhandled varying slot: %u\n", location); UNREACHABLE("Unhandled varying slot"); } break; } } static unsigned ntr_fs_output_index(struct ntr_compile *c, gl_frag_result location, unsigned dual_source_blend_index) { int color_index = mesa_frag_result_get_color_index(location); if (dual_source_blend_index) { assert(location == FRAG_RESULT_COLOR || location == FRAG_RESULT_DATA0); color_index += dual_source_blend_index; } int *output_index; if (color_index >= 0 && color_index < ARRAY_SIZE(c->fs_output_color_index)) { output_index = &c->fs_output_color_index[color_index]; } else if (location == FRAG_RESULT_DEPTH) { output_index = &c->fs_output_depth_index; } else { assert(location < FRAG_RESULT_MAX); output_index = &c->fs_output_index[location]; } if (*output_index < 0) *output_index = c->num_outputs++; return *output_index; } static struct rc_dst_register ntr_output_decl(struct ntr_compile *c, nir_intrinsic_instr *instr, uint32_t *frac) { nir_io_semantics semantics = nir_intrinsic_io_semantics(instr); int base = nir_intrinsic_base(instr); *frac = nir_intrinsic_component(instr); struct rc_dst_register out; if (c->s->info.stage == MESA_SHADER_FRAGMENT) { switch (semantics.location) { case FRAG_RESULT_DEPTH: *frac = 2; /* z write is the to the .z channel in TGSI */ break; case FRAG_RESULT_STENCIL: *frac = 1; break; default: break; } out = ntr_dst_register(c, RC_FILE_OUTPUT, ntr_fs_output_index(c, semantics.location, semantics.dual_source_blend_index)); } else { ntr_read_input_output(c, semantics.location, base); out = ntr_dst_register(c, RC_FILE_OUTPUT, base); c->num_outputs = MAX2(c->num_outputs, base + semantics.num_slots); } unsigned write_mask; if (nir_intrinsic_has_write_mask(instr)) write_mask = nir_intrinsic_write_mask(instr); else write_mask = ((1 << instr->num_components) - 1) << *frac; write_mask = write_mask << *frac; return ntr_writemask(out, write_mask); } static bool ntr_try_store_in_tgsi_output_with_use(struct ntr_compile *c, struct rc_dst_register *dst, nir_src *src) { if (nir_src_is_if(src)) return false; if (nir_src_use_instr(src)->type != nir_instr_type_intrinsic) return false; nir_intrinsic_instr *intr = nir_instr_as_intrinsic(nir_src_use_instr(src)); if (intr->intrinsic != nir_intrinsic_store_output || !nir_src_is_const(intr->src[1])) { return false; } uint32_t frac; *dst = ntr_output_decl(c, intr, &frac); ntr_set_dst_index(c, dst, dst->Index + ntr_src_as_uint(c, intr->src[1])); return frac == 0; } /* If this reg is used only for storing an output, then in the simple * cases we can write directly to the TGSI output instead of having * store_output emit its own MOV. */ static bool ntr_try_store_reg_in_tgsi_output(struct ntr_compile *c, struct rc_dst_register *dst, nir_intrinsic_instr *reg_decl) { assert(reg_decl->intrinsic == nir_intrinsic_decl_reg); /* Look for a single use for try_store_in_tgsi_output */ nir_src *use = NULL; nir_foreach_reg_load (src, reg_decl) { nir_intrinsic_instr *load = nir_instr_as_intrinsic(nir_src_use_instr(src)); nir_foreach_use_including_if (load_use, &load->def) { /* We can only have one use */ if (use != NULL) return false; use = load_use; } } if (use == NULL) return false; return ntr_try_store_in_tgsi_output_with_use(c, dst, use); } /* If this SSA def is used only for storing an output, then in the simple * cases we can write directly to the TGSI output instead of having * store_output emit its own MOV. */ static bool ntr_try_store_ssa_in_tgsi_output(struct ntr_compile *c, struct rc_dst_register *dst, nir_def *def) { if (!list_is_singular(&def->uses)) return false; nir_foreach_use_including_if (use, def) { return ntr_try_store_in_tgsi_output_with_use(c, dst, use); } UNREACHABLE("We have one use"); } static void ntr_setup_inputs(struct ntr_compile *c) { if (c->s->info.stage != MESA_SHADER_FRAGMENT) return; unsigned num_inputs = 0; nir_foreach_shader_in_variable (var, c->s) { const struct glsl_type *type = var->type; unsigned array_len = glsl_count_attribute_slots(type, false); num_inputs = MAX2(num_inputs, var->data.driver_location + array_len); } c->input_index_map = ralloc_array(c, struct rc_src_register, num_inputs); nir_foreach_shader_in_variable (var, c->s) { const struct glsl_type *type = var->type; unsigned array_len = glsl_count_attribute_slots(type, false); struct rc_src_register decl; decl = ntr_src_register(c, RC_FILE_INPUT, var->data.driver_location); if (var->data.location == VARYING_SLOT_FACE) { struct rc_dst_register temp = ntr_temp(c); /* tgsi docs say that floating point FACE will be positive for * frontface and negative for backface, but realistically * GLSL-to-TGSI had been doing MOV_SAT to turn it into 0.0 vs 1.0. * Copy that behavior, since some drivers (r300) have been doing a * 0.0 vs 1.0 backface (and I don't think anybody has a non-1.0 * front face). */ rc_sub_instruction(c, RC_OPCODE_MOV, &temp, RC_SATURATE_ZERO_ONE, &decl, NULL, NULL); decl = ntr_src_from_dst(temp); } for (unsigned i = 0; i < array_len; i++) { c->input_index_map[var->data.driver_location + i] = decl; ntr_set_src_index(c, &c->input_index_map[var->data.driver_location + i], decl.Index + i); } } } static int ntr_sort_by_location(const nir_variable *a, const nir_variable *b) { return a->data.location - b->data.location; } /* Preallocate FS output registers in a deterministic order. */ static void ntr_setup_outputs(struct ntr_compile *c) { if (c->s->info.stage != MESA_SHADER_FRAGMENT) return; nir_sort_variables_with_modes(c->s, ntr_sort_by_location, nir_var_shader_out); nir_foreach_shader_out_variable (var, c->s) { ntr_fs_output_index(c, var->data.location, var->data.index); } } static rc_texture_target rc_texture_target_from_sampler_dim(enum glsl_sampler_dim dim, bool is_array) { /* r300 has no array, multisample or buffer textures. */ assert(!is_array); switch (dim) { case GLSL_SAMPLER_DIM_1D: return RC_TEXTURE_1D; case GLSL_SAMPLER_DIM_2D: case GLSL_SAMPLER_DIM_EXTERNAL: return RC_TEXTURE_2D; case GLSL_SAMPLER_DIM_3D: return RC_TEXTURE_3D; case GLSL_SAMPLER_DIM_CUBE: return RC_TEXTURE_CUBE; case GLSL_SAMPLER_DIM_RECT: return RC_TEXTURE_RECT; default: UNREACHABLE("unknown sampler dim"); } } static void ntr_setup_uniforms(struct ntr_compile *c) { /* lower_uniforms_to_ubo lowered non-sampler uniforms to UBOs. */ unsigned size = 0; nir_foreach_variable_with_modes (var, c->s, nir_var_mem_ubo) { int ubo = var->data.driver_location; assert(ubo == 0 && size == 0); size = glsl_get_explicit_size(var->interface_type, false); } c->ubo_size = size ? DIV_ROUND_UP(size, 16) : 0; } static void ntr_setup_registers(struct ntr_compile *c) { assert(c->num_temps == 0); /* After that, allocate non-array regs in our virtual space that we'll * register-allocate before RC emit. */ nir_foreach_reg_decl_safe (nir_reg, nir_shader_get_entrypoint(c->s)) { assert(nir_intrinsic_num_array_elems(nir_reg) == 0); unsigned num_components = nir_intrinsic_num_components(nir_reg); unsigned index = nir_reg->def.index; struct rc_dst_register decl; uint32_t write_mask = BITFIELD_MASK(num_components); if (!ntr_try_store_reg_in_tgsi_output(c, &decl, nir_reg)) { decl = ntr_writemask(ntr_temp(c), write_mask); } c->reg_temp[index] = decl; } } static struct rc_src_register ntr_get_load_const_src(struct ntr_compile *c, nir_load_const_instr *instr) { int num_components = instr->def.num_components; /* Always emit a full vec4 immediate per load_const and let the * RC backend's constant packing handle it later. */ float values[4] = {0}; assert(instr->def.bit_size == 32); for (int i = 0; i < num_components; i++) values[i] = uif(instr->value[i].u32); unsigned index = util_dynarray_num_elements(&c->immediates, struct ntr_immediate); struct ntr_immediate *slot = util_dynarray_grow(&c->immediates, struct ntr_immediate, 1); memcpy(slot->values, values, sizeof(values)); return ntr_src_register(c, RC_FILE_CONSTANT, c->immediate_offset + index); } static struct rc_src_register ntr_reladdr(struct ntr_compile *c, struct rc_src_register addr) { ntr_ARL(c, c->addr_reg, addr); return ntr_scalar(ntr_src_from_dst(c->addr_reg), 0); } /* Forward declare for recursion with indirects */ static struct rc_src_register ntr_get_src(struct ntr_compile *c, nir_src src); static struct rc_src_register ntr_get_chased_src(struct ntr_compile *c, nir_legacy_src *src) { if (src->is_ssa) { if (nir_def_is_const(src->ssa)) return ntr_get_load_const_src(c, nir_def_as_load_const(src->ssa)); return c->ssa_temp[src->ssa->index]; } else { struct rc_dst_register reg_temp = c->reg_temp[src->reg.handle->index]; ntr_set_dst_index(c, ®_temp, reg_temp.Index + src->reg.base_offset); if (src->reg.indirect) { struct rc_src_register offset = ntr_get_src(c, nir_src_for_ssa(src->reg.indirect)); ntr_reladdr(c, offset); return ntr_src_indirect(ntr_src_from_dst(reg_temp)); } else { return ntr_src_from_dst(reg_temp); } } } static struct rc_src_register ntr_get_src(struct ntr_compile *c, nir_src src) { nir_legacy_src chased = nir_legacy_chase_src(&src); return ntr_get_chased_src(c, &chased); } static struct rc_src_register ntr_get_alu_src(struct ntr_compile *c, nir_alu_instr *instr, int i) { /* The RC source modifier fields we use here are 32-bit float modifiers. * Keep integer and f64 modifiers explicit in NIR. * * The lower_fabs requests that we not have native source modifiers * for fabs, and instead emit MAX(a,-a) for nir_op_fabs. */ nir_legacy_alu_src src = nir_legacy_chase_alu_src(&instr->src[i], !c->lower_fabs); struct rc_src_register usrc = ntr_get_chased_src(c, &src.src); usrc = ntr_swizzle(usrc, src.swizzle[0], src.swizzle[1], src.swizzle[2], src.swizzle[3]); if (src.fabs) usrc = ntr_abs(usrc); if (src.fneg) usrc = ntr_negate(usrc); return usrc; } /* Reswizzles a source so that the unset channels in the write mask still refer * to one of the channels present in the write mask. */ static struct rc_src_register ntr_swizzle_for_write_mask(struct rc_src_register src, uint32_t write_mask) { assert(write_mask); int first_chan = ffs(write_mask) - 1; return ntr_swizzle(src, (write_mask & RC_MASK_X) ? RC_SWIZZLE_X : first_chan, (write_mask & RC_MASK_Y) ? RC_SWIZZLE_Y : first_chan, (write_mask & RC_MASK_Z) ? RC_SWIZZLE_Z : first_chan, (write_mask & RC_MASK_W) ? RC_SWIZZLE_W : first_chan); } static struct rc_dst_register ntr_get_ssa_def_decl(struct ntr_compile *c, nir_def *ssa) { uint32_t writemask; /* Fix writemask for nir_intrinsic_load_ubo_vec4 according to uses. */ if (nir_def_is_intrinsic(ssa) && nir_def_as_intrinsic(ssa)->intrinsic == nir_intrinsic_load_ubo_vec4) writemask = nir_def_components_read(ssa); else writemask = BITSET_MASK(ssa->num_components); struct rc_dst_register dst; if (!ntr_try_store_ssa_in_tgsi_output(c, &dst, ssa)) dst = ntr_temp(c); c->ssa_temp[ssa->index] = ntr_swizzle_for_write_mask(ntr_src_from_dst(dst), writemask); return ntr_writemask(dst, writemask); } static struct rc_dst_register ntr_get_chased_dest_decl(struct ntr_compile *c, nir_legacy_dest *dest) { if (dest->is_ssa) return ntr_get_ssa_def_decl(c, dest->ssa); else return c->reg_temp[dest->reg.handle->index]; } static struct rc_dst_register ntr_get_chased_dest(struct ntr_compile *c, nir_legacy_dest *dest) { struct rc_dst_register dst = ntr_get_chased_dest_decl(c, dest); if (!dest->is_ssa) { ntr_set_dst_index(c, &dst, dst.Index + dest->reg.base_offset); if (dest->reg.indirect) { struct rc_src_register offset = ntr_get_src(c, nir_src_for_ssa(dest->reg.indirect)); ntr_reladdr(c, offset); rc_error(c->compiler, "r300: Relative addressing of destination operands is unsupported.\n"); } } return dst; } static struct rc_dst_register ntr_get_dest(struct ntr_compile *c, nir_def *def) { nir_legacy_dest chased = nir_legacy_chase_dest(def); return ntr_get_chased_dest(c, &chased); } static struct ntr_alu_dst ntr_get_alu_dest(struct ntr_compile *c, nir_def *def) { nir_legacy_alu_dest chased = nir_legacy_chase_alu_dest(def); struct rc_dst_register dst = ntr_get_chased_dest(c, &chased.dest); rc_saturate_mode saturate = chased.fsat ? RC_SATURATE_ZERO_ONE : RC_SATURATE_NONE; /* Only registers get write masks */ if (!chased.dest.is_ssa) dst = ntr_writemask(dst, chased.write_mask); return ntr_alu_dst(dst, saturate); } /* For an SSA dest being populated by a constant src, replace the storage with * a copy of the source reference. */ static void ntr_store_def(struct ntr_compile *c, nir_def *def, struct rc_src_register src) { if (!src.RelAddr) { switch (src.File) { case RC_FILE_INPUT: case RC_FILE_CONSTANT: c->ssa_temp[def->index] = src; return; default: break; } } ntr_MOV(c, ntr_get_ssa_def_decl(c, def), src); } static void ntr_store(struct ntr_compile *c, nir_def *def, struct rc_src_register src) { nir_legacy_dest chased = nir_legacy_chase_dest(def); if (chased.is_ssa) ntr_store_def(c, chased.ssa, src); else { struct rc_dst_register dst = ntr_get_chased_dest(c, &chased); ntr_MOV(c, dst, src); } } /* TODO: we should be probably scalarizing all the opcodes we need earlier in NIR. */ static void ntr_emit_scalar(struct ntr_compile *c, rc_opcode op, struct ntr_alu_dst dst, struct rc_src_register src0, const struct rc_src_register *src1) { /* POW is the only 2-operand scalar op. */ bool has_src1 = op == RC_OPCODE_POW; assert(has_src1 == (src1 != NULL)); for (unsigned i = 0; i < 4; i++) { if (dst.reg.WriteMask & (1 << i)) { struct ntr_alu_dst scalar_dst = dst; struct rc_src_register scalar_src0 = ntr_scalar(src0, i); struct rc_src_register scalar_src1; scalar_dst.reg = ntr_writemask(scalar_dst.reg, 1 << i); if (has_src1) scalar_src1 = ntr_scalar(*src1, i); ntr_emit_alu_op(c, op, scalar_dst, &scalar_src0, has_src1 ? &scalar_src1 : NULL, NULL); } } } static void ntr_emit_alu(struct ntr_compile *c, nir_alu_instr *instr) { struct rc_src_register src[4]; struct ntr_alu_dst dst; unsigned i; int num_srcs = nir_op_infos[instr->op].num_inputs; /* Don't try to translate folded fsat since their source won't be valid */ if (instr->op == nir_op_fsat && nir_legacy_fsat_folds(instr)) return; assert(num_srcs <= ARRAY_SIZE(src)); for (i = 0; i < num_srcs; i++) src[i] = ntr_get_alu_src(c, instr, i); dst = ntr_get_alu_dest(c, &instr->def); static const rc_opcode op_map[] = { [nir_op_mov] = RC_OPCODE_MOV, [nir_op_fdot2_replicated] = RC_OPCODE_DP2, [nir_op_fdot3_replicated] = RC_OPCODE_DP3, [nir_op_fdot4_replicated] = RC_OPCODE_DP4, [nir_op_ffract] = RC_OPCODE_FRC, [nir_op_fround_even] = RC_OPCODE_ROUND, [nir_op_slt] = RC_OPCODE_SLT, [nir_op_sge] = RC_OPCODE_SGE, [nir_op_seq] = RC_OPCODE_SEQ, [nir_op_sne] = RC_OPCODE_SNE, [nir_op_fadd] = RC_OPCODE_ADD, [nir_op_fmul] = RC_OPCODE_MUL, [nir_op_fmin] = RC_OPCODE_MIN, [nir_op_fmax] = RC_OPCODE_MAX, [nir_op_fmad] = RC_OPCODE_MAD, }; if (instr->op < ARRAY_SIZE(op_map) && op_map[instr->op] > 0) { /* The normal path for NIR to TGSI ALU op translation */ const struct rc_opcode_info *opcode_info = rc_get_opcode_info(op_map[instr->op]); ntr_emit_alu_op(c, op_map[instr->op], dst, opcode_info->NumSrcRegs > 0 ? &src[0] : NULL, opcode_info->NumSrcRegs > 1 ? &src[1] : NULL, opcode_info->NumSrcRegs > 2 ? &src[2] : NULL); } else { /* Special cases for NIR to TGSI ALU op translation. */ /* TODO: Use something like the ntr_store() path for the MOV calls so we * don't emit extra MOVs for swizzles/srcmods of inputs/const/imm. */ switch (instr->op) { case nir_op_fabs: /* Try to eliminate */ if (!c->lower_fabs && nir_legacy_float_mod_folds(instr)) break; if (c->lower_fabs) ntr_emit_alu_op2(c, RC_OPCODE_MAX, dst, src[0], ntr_negate(src[0])); else ntr_emit_alu_op1(c, RC_OPCODE_MOV, dst, ntr_abs(src[0])); break; case nir_op_fsat: ntr_emit_alu_op1(c, RC_OPCODE_MOV, ntr_alu_dst_saturate(dst), src[0]); break; case nir_op_fneg: /* Try to eliminate */ if (nir_legacy_float_mod_folds(instr)) break; ntr_emit_alu_op1(c, RC_OPCODE_MOV, dst, ntr_negate(src[0])); break; /* NOTE: TGSI 32-bit math ops have the old "one source channel * replicated to all dst channels" behavior, while 64 is normal mapping * of src channels to dst. */ case nir_op_frcp: ntr_emit_scalar(c, RC_OPCODE_RCP, dst, src[0], NULL); break; case nir_op_frsq: ntr_emit_scalar(c, RC_OPCODE_RSQ, dst, src[0], NULL); break; case nir_op_fexp2: ntr_emit_scalar(c, RC_OPCODE_EX2, dst, src[0], NULL); break; case nir_op_flog2: ntr_emit_scalar(c, RC_OPCODE_LG2, dst, src[0], NULL); break; case nir_op_fsin: ntr_emit_scalar(c, RC_OPCODE_SIN, dst, src[0], NULL); break; case nir_op_fcos: ntr_emit_scalar(c, RC_OPCODE_COS, dst, src[0], NULL); break; case nir_op_fsub: ntr_emit_alu_op2(c, RC_OPCODE_ADD, dst, src[0], ntr_negate(src[1])); break; case nir_op_fpow: ntr_emit_scalar(c, RC_OPCODE_POW, dst, src[0], &src[1]); break; case nir_op_fcsel: /* Implement this as CMP(-abs(src0), src1, src2). */ ntr_emit_alu_op3(c, RC_OPCODE_CMP, dst, ntr_negate(ntr_abs(src[0])), src[1], src[2]); break; case nir_op_fcsel_gt: ntr_emit_alu_op3(c, RC_OPCODE_CMP, dst, ntr_negate(src[0]), src[1], src[2]); break; case nir_op_fcsel_ge: /* Implement this as if !(src0 < 0.0) was identical to src0 >= 0.0. */ ntr_emit_alu_op3(c, RC_OPCODE_CMP, dst, src[0], src[2], src[1]); break; default: fprintf(stderr, "Unknown NIR opcode: %s\n", nir_op_infos[instr->op].name); UNREACHABLE("Unknown NIR opcode"); } } } static struct rc_src_register ntr_src_offset(struct ntr_compile *c, struct rc_src_register nsrc, nir_src src) { if (nir_src_is_const(src)) { ntr_set_src_index(c, &nsrc, nsrc.Index + ntr_src_as_uint(c, src)); return nsrc; } else { ntr_reladdr(c, ntr_get_src(c, src)); return ntr_src_indirect(nsrc); } } static struct rc_dst_register ntr_dst_offset(struct ntr_compile *c, struct rc_dst_register dst, nir_src src) { /* Indirect adressing should have been lowered earlier in NIR. */ assert(nir_src_is_const(src)); ntr_set_dst_index(c, &dst, dst.Index + ntr_src_as_uint(c, src)); return dst; } /* Some load operations in NIR will have a fractional offset that we need to * swizzle down before storing to the result register. */ static struct rc_src_register ntr_shift_by_frac(struct rc_src_register src, unsigned frac, unsigned num_components) { return ntr_swizzle(src, frac, frac + MIN2(num_components - 1, 1), frac + MIN2(num_components - 1, 2), frac + MIN2(num_components - 1, 3)); } static void ntr_emit_load_ubo(struct ntr_compile *c, nir_intrinsic_instr *instr) { struct rc_src_register src = ntr_src_register(c, RC_FILE_CONSTANT, 0); /* r300 only exposes a single UBO and any indirect UBO array indexing * has been lowered before we get here. */ assert(nir_src_is_const(instr->src[0])); assert(ntr_src_as_uint(c, instr->src[0]) == 0); /* !pipe_caps.load_constbuf: Just emit it as a vec4 reference to the const * file. */ ntr_set_src_index(c, &src, nir_intrinsic_base(instr)); if (nir_src_is_const(instr->src[1])) { ntr_set_src_index(c, &src, src.Index + ntr_src_as_uint(c, instr->src[1])); } else { ntr_reladdr(c, ntr_get_src(c, instr->src[1])); src = ntr_src_indirect(src); } int start_component = nir_intrinsic_component(instr); src = ntr_shift_by_frac(src, start_component, instr->num_components); ntr_store(c, &instr->def, src); } static void ntr_emit_load_input(struct ntr_compile *c, nir_intrinsic_instr *instr) { uint32_t frac = nir_intrinsic_component(instr); uint32_t num_components = instr->num_components; unsigned base = nir_intrinsic_base(instr); struct rc_src_register input; nir_io_semantics semantics = nir_intrinsic_io_semantics(instr); if (c->s->info.stage == MESA_SHADER_FRAGMENT) ntr_read_input_output(c, semantics.location, base); if (c->s->info.stage == MESA_SHADER_VERTEX) { input = ntr_src_register(c, RC_FILE_INPUT, base); } else { input = c->input_index_map[base]; } input = ntr_shift_by_frac(input, frac, num_components); switch (instr->intrinsic) { case nir_intrinsic_load_input: input = ntr_src_offset(c, input, instr->src[0]); ntr_store(c, &instr->def, input); break; case nir_intrinsic_load_interpolated_input: { input = ntr_src_offset(c, input, instr->src[1]); nir_intrinsic_instr *bary_instr = nir_def_as_intrinsic(instr->src[0].ssa); switch (bary_instr->intrinsic) { case nir_intrinsic_load_barycentric_pixel: /* The barycentric load matches the interpolation on the input * declaration, so we can use it directly. */ ntr_store(c, &instr->def, input); break; case nir_intrinsic_load_barycentric_centroid: /* On r300 interpolation is fixed by the rasterizer state; the NIR * lowering pairs centroid intrinsics with centroid-declared inputs, * so we never need an explicit INTERP instruction. */ ntr_store(c, &instr->def, input); break; default: UNREACHABLE("bad barycentric interp intrinsic\n"); } break; } default: UNREACHABLE("bad load input intrinsic\n"); } } static void ntr_emit_store_output(struct ntr_compile *c, nir_intrinsic_instr *instr) { struct rc_src_register src = ntr_get_src(c, instr->src[0]); if (src.File == RC_FILE_OUTPUT) { /* If our src is the output file, that's an indication that we were able * to emit the output stores in the generating instructions and we have * nothing to do here. */ return; } uint32_t frac; struct rc_dst_register out = ntr_output_decl(c, instr, &frac); out = ntr_dst_offset(c, out, instr->src[1]); uint8_t swizzle[4] = {0, 0, 0, 0}; for (int i = frac; i < 4; i++) { if (out.WriteMask & (1 << i)) swizzle[i] = i - frac; } src = ntr_swizzle(src, swizzle[0], swizzle[1], swizzle[2], swizzle[3]); ntr_MOV(c, out, src); } static void ntr_emit_intrinsic(struct ntr_compile *c, nir_intrinsic_instr *instr) { switch (instr->intrinsic) { case nir_intrinsic_load_ubo: case nir_intrinsic_load_ubo_vec4: ntr_emit_load_ubo(c, instr); break; case nir_intrinsic_load_frag_coord: case nir_intrinsic_load_point_coord: case nir_intrinsic_load_front_face: UNREACHABLE("system value should have been lowered to a varying"); break; case nir_intrinsic_load_input: case nir_intrinsic_load_interpolated_input: ntr_emit_load_input(c, instr); break; case nir_intrinsic_store_output: ntr_emit_store_output(c, instr); break; case nir_intrinsic_terminate: ntr_KILL(c); break; case nir_intrinsic_terminate_if: { struct rc_src_register cond = ntr_scalar(ntr_get_src(c, instr->src[0]), 0); /* For !native_integers, the bool got lowered to 1.0 or 0.0. */ ntr_KILL_IF(c, ntr_negate(cond)); break; } /* In TGSI we don't actually generate the barycentric coords, and * emit the corresponding INTERP_CENTROID instruction in * ntr_emit_load_input. The barycentric loads themselves are * therefore consumed there. */ case nir_intrinsic_load_barycentric_pixel: case nir_intrinsic_load_barycentric_centroid: break; case nir_intrinsic_ddx: case nir_intrinsic_ddx_coarse: ntr_DDX(c, ntr_get_dest(c, &instr->def), ntr_get_src(c, instr->src[0])); return; case nir_intrinsic_ddy: case nir_intrinsic_ddy_coarse: ntr_DDY(c, ntr_get_dest(c, &instr->def), ntr_get_src(c, instr->src[0])); return; case nir_intrinsic_decl_reg: case nir_intrinsic_load_reg: case nir_intrinsic_load_reg_indirect: case nir_intrinsic_store_reg: case nir_intrinsic_store_reg_indirect: /* fully consumed */ break; default: fprintf(stderr, "Unknown intrinsic: "); nir_print_instr(&instr->instr, stderr); fprintf(stderr, "\n"); break; } } struct ntr_tex_operand_state { struct rc_src_register srcs[3]; unsigned i; }; static void ntr_push_tex_arg(struct ntr_compile *c, nir_tex_instr *instr, nir_tex_src_type tex_src_type, struct ntr_tex_operand_state *s) { int tex_src = nir_tex_instr_src_index(instr, tex_src_type); if (tex_src < 0) return; nir_src *src = &instr->src[tex_src].src; s->srcs[s->i++] = ntr_get_src(c, *src); } static void ntr_emit_texture(struct ntr_compile *c, nir_tex_instr *instr) { struct rc_dst_register dst = ntr_get_dest(c, &instr->def); assert(!instr->is_shadow); rc_texture_target target = rc_texture_target_from_sampler_dim(instr->sampler_dim, instr->is_array); unsigned tex_opcode; int tex_handle_src = nir_tex_instr_src_index(instr, nir_tex_src_texture_handle); int sampler_handle_src = nir_tex_instr_src_index(instr, nir_tex_src_sampler_handle); unsigned sampler_index = instr->sampler_index; bool sampler_reladdr = false; if (tex_handle_src >= 0 && sampler_handle_src >= 0) { /* It seems we can't get separate tex/sampler on GL, just use one of the handles */ struct rc_src_register sampler = ntr_get_src(c, instr->src[tex_handle_src].src); sampler_index = sampler.Index; sampler_reladdr = sampler.RelAddr; assert(nir_tex_instr_src_index(instr, nir_tex_src_sampler_offset) == -1); } else { assert(tex_handle_src == -1 && sampler_handle_src == -1); int sampler_src = nir_tex_instr_src_index(instr, nir_tex_src_sampler_offset); if (sampler_src >= 0) { struct rc_src_register reladdr = ntr_get_src(c, instr->src[sampler_src].src); ntr_reladdr(c, reladdr); sampler_reladdr = true; } } switch (instr->op) { case nir_texop_tex: if (nir_tex_instr_src_size(instr, nir_tex_instr_src_index(instr, nir_tex_src_backend1)) > MAX2(instr->coord_components, 2) + instr->is_shadow) tex_opcode = RC_OPCODE_TXP; else tex_opcode = RC_OPCODE_TEX; break; case nir_texop_txl: tex_opcode = RC_OPCODE_TXL; break; case nir_texop_txb: tex_opcode = RC_OPCODE_TXB; break; case nir_texop_txd: tex_opcode = RC_OPCODE_TXD; break; default: UNREACHABLE("unsupported tex op"); } struct ntr_tex_operand_state s = {.i = 0}; ntr_push_tex_arg(c, instr, nir_tex_src_backend1, &s); ntr_push_tex_arg(c, instr, nir_tex_src_backend2, &s); /* The RC backend doesn't have TEX2/TXB2/TXL2-style opcodes; the * shadow comparator that would need a second backend slot is * already lowered before nir_to_rc by nir_lower_tex_shadow. */ assert(s.i <= 1); if (instr->op == nir_texop_txd) { /* Derivs appear in their own src args */ int ddx = nir_tex_instr_src_index(instr, nir_tex_src_ddx); int ddy = nir_tex_instr_src_index(instr, nir_tex_src_ddy); s.srcs[s.i++] = ntr_get_src(c, instr->src[ddx].src); s.srcs[s.i++] = ntr_get_src(c, instr->src[ddy].src); } assert(s.i == rc_get_opcode_info(tex_opcode)->NumSrcRegs); struct rc_sub_instruction *inst = rc_sub_instruction(c, tex_opcode, &dst, RC_SATURATE_NONE, &s.srcs[0], s.i > 1 ? &s.srcs[1] : NULL, s.i > 2 ? &s.srcs[2] : NULL); inst->TexSrcTarget = target; ntr_rc_tex_sampler(c, inst, sampler_index, sampler_reladdr); inst->TexSwizzle = RC_SWIZZLE_XYZW; } static void ntr_emit_jump(struct ntr_compile *c, nir_jump_instr *jump) { switch (jump->type) { case nir_jump_break: ntr_BRK(c); break; case nir_jump_continue: ntr_CONT(c); break; default: fprintf(stderr, "Unknown jump instruction: "); nir_print_instr(&jump->instr, stderr); fprintf(stderr, "\n"); abort(); } } static void ntr_emit_ssa_undef(struct ntr_compile *c, nir_undef_instr *instr) { /* Nothing to do but make sure that we have some storage to deref. */ (void)ntr_get_ssa_def_decl(c, &instr->def); } static void ntr_emit_instr(struct ntr_compile *c, nir_instr *instr) { switch (instr->type) { case nir_instr_type_deref: /* ignored, will be walked by nir_intrinsic_image_*_deref. */ break; case nir_instr_type_alu: ntr_emit_alu(c, nir_instr_as_alu(instr)); break; case nir_instr_type_intrinsic: ntr_emit_intrinsic(c, nir_instr_as_intrinsic(instr)); break; case nir_instr_type_load_const: /* Nothing to do here, as load consts are done directly from * ntr_get_src() (since many constant NIR srcs will often get folded * directly into a register file index instead of as a TGSI src). */ break; case nir_instr_type_tex: ntr_emit_texture(c, nir_instr_as_tex(instr)); break; case nir_instr_type_jump: ntr_emit_jump(c, nir_instr_as_jump(instr)); break; case nir_instr_type_undef: ntr_emit_ssa_undef(c, nir_instr_as_undef(instr)); break; default: fprintf(stderr, "Unknown NIR instr type: "); nir_print_instr(instr, stderr); fprintf(stderr, "\n"); abort(); } } static void ntr_emit_if(struct ntr_compile *c, nir_if *if_stmt) { ntr_IF(c, c->if_cond); ntr_emit_cf_list(c, &if_stmt->then_list); if (!nir_cf_list_is_empty_block(&if_stmt->else_list)) { ntr_ELSE(c); ntr_emit_cf_list(c, &if_stmt->else_list); } ntr_ENDIF(c); } static void ntr_emit_loop(struct ntr_compile *c, nir_loop *loop) { assert(!nir_loop_has_continue_construct(loop)); ntr_BGNLOOP(c); ntr_emit_cf_list(c, &loop->body); ntr_ENDLOOP(c); } static void ntr_emit_block(struct ntr_compile *c, nir_block *block) { nir_foreach_instr (instr, block) { ntr_emit_instr(c, instr); } /* Set up the if condition for ntr_emit_if(). */ nir_if *nif = nir_block_get_following_if(block); if (nif) c->if_cond = ntr_get_src(c, nif->condition); } static void ntr_emit_cf_list(struct ntr_compile *c, struct exec_list *list) { foreach_list_typed (nir_cf_node, node, node, list) { switch (node->type) { case nir_cf_node_block: ntr_emit_block(c, nir_cf_node_as_block(node)); break; case nir_cf_node_if: ntr_emit_if(c, nir_cf_node_as_if(node)); break; case nir_cf_node_loop: ntr_emit_loop(c, nir_cf_node_as_loop(node)); break; default: UNREACHABLE("unknown CF type"); } } } static void ntr_add_constants(struct ntr_compile *c) { /* Add UBO constants first (they show up before immediates in the * compiler's constants table; the immediate offset compensates for * this). */ for (unsigned i = 0; i < c->ubo_size; i++) { struct rc_constant constant; memset(&constant, 0, sizeof(constant)); constant.Type = RC_CONSTANT_EXTERNAL; constant.UseMask = RC_MASK_XYZW; constant.u.External = i; rc_constants_add(&c->compiler->Program.Constants, &constant); } assert(c->compiler->Program.Constants.Count == c->immediate_offset); util_dynarray_foreach (&c->immediates, struct ntr_immediate, imm) { struct rc_constant constant; constant.Type = RC_CONSTANT_IMMEDIATE; constant.UseMask = RC_MASK_XYZW; for (unsigned j = 0; j < 4; j++) constant.u.Immediate[j] = imm->values[j]; rc_constants_add(&c->compiler->Program.Constants, &constant); } } static void ntr_emit_impl(struct ntr_compile *c, nir_function_impl *impl) { c->ssa_temp = rzalloc_array(c, struct rc_src_register, impl->ssa_alloc); c->reg_temp = rzalloc_array(c, struct rc_dst_register, impl->ssa_alloc); ntr_setup_registers(c); ntr_setup_uniforms(c); c->immediate_offset = c->compiler->Program.Constants.Count + c->ubo_size; ntr_setup_inputs(c); ntr_setup_outputs(c); /* Emit RC instructions directly. */ ntr_emit_cf_list(c, &impl->body); /* Add constants referenced by the emitted RC instructions. */ ntr_add_constants(c); } static int type_size(const struct glsl_type *type, bool bindless) { return glsl_count_attribute_slots(type, false); } /* Allow vectorizing of ALU instructions. */ static uint8_t ntr_should_vectorize_instr(const nir_instr *instr, const void *data) { if (instr->type != nir_instr_type_alu) return 0; return 4; } struct ntr_lower_tex_state { nir_scalar channels[8]; unsigned i; }; static void nir_to_rc_lower_tex_instr_arg(nir_builder *b, nir_tex_instr *instr, nir_tex_src_type tex_src_type, struct ntr_lower_tex_state *s) { int tex_src = nir_tex_instr_src_index(instr, tex_src_type); if (tex_src < 0) return; nir_def *def = instr->src[tex_src].src.ssa; for (int i = 0; i < def->num_components; i++) { s->channels[s->i++] = nir_get_scalar(def, i); } nir_tex_instr_remove_src(instr, tex_src); } /** * Merges together a vec4 of tex coordinate/compare/bias/lod into a backend tex * src. This lets NIR handle the coalescing of the vec4 rather than trying to * manage it on our own, and may lead to more vectorization. */ static bool nir_to_rc_lower_tex_instr(nir_builder *b, nir_tex_instr *tex, void *data) { if (nir_tex_instr_src_index(tex, nir_tex_src_coord) < 0) return false; b->cursor = nir_before_instr(&tex->instr); struct ntr_lower_tex_state s = {0}; nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_coord, &s); /* We always have at least two slots for the coordinate, even on 1D. */ s.i = MAX2(s.i, 2); nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_comparator, &s); s.i = MAX2(s.i, 3); nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_bias, &s); /* XXX: LZ */ nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_lod, &s); nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_projector, &s); nir_to_rc_lower_tex_instr_arg(b, tex, nir_tex_src_ms_index, &s); /* No need to pack undefs in unused channels of the tex instr */ while (!s.channels[s.i - 1].def) s.i--; /* Instead of putting undefs in the unused slots of the vecs, just put in * another used channel. Otherwise, we'll get unnecessary moves into * registers. */ assert(s.channels[0].def != NULL); for (int i = 1; i < s.i; i++) { if (!s.channels[i].def) s.channels[i] = s.channels[0]; } nir_tex_instr_add_src(tex, nir_tex_src_backend1, nir_vec_scalars(b, s.channels, MIN2(s.i, 4))); if (s.i > 4) nir_tex_instr_add_src(tex, nir_tex_src_backend2, nir_vec_scalars(b, &s.channels[4], s.i - 4)); return true; } static bool nir_to_rc_lower_tex(nir_shader *s) { return nir_shader_tex_pass(s, nir_to_rc_lower_tex_instr, nir_metadata_control_flow, NULL); } /* Lowers texture projectors if we can't do them as RC_OPCODE_TXP. */ static void nir_to_rc_lower_txp(nir_shader *s) { nir_lower_tex_options lower_tex_options = { .lower_txp = 0, }; nir_foreach_block (block, nir_shader_get_entrypoint(s)) { nir_foreach_instr (instr, block) { if (instr->type != nir_instr_type_tex) continue; nir_tex_instr *tex = nir_instr_as_tex(instr); if (nir_tex_instr_src_index(tex, nir_tex_src_projector) < 0) continue; bool has_compare = nir_tex_instr_src_index(tex, nir_tex_src_comparator) >= 0; bool has_lod = nir_tex_instr_src_index(tex, nir_tex_src_lod) >= 0 || s->info.stage != MESA_SHADER_FRAGMENT; bool has_offset = nir_tex_instr_src_index(tex, nir_tex_src_offset) >= 0; /* We can do TXP for any tex (not txg) where we can fit all the * coordinates and comparator and projector in one vec4 without any * other modifiers to add on. * * nir_lower_tex() only handles the lowering on a sampler-dim basis, so * if we get any funny projectors then we just blow them all away. */ if (tex->op != nir_texop_tex || has_lod || has_offset || (tex->coord_components >= 3 && has_compare)) lower_tex_options.lower_txp |= 1 << tex->sampler_dim; } } /* nir_lower_tex must be run even if no options are set, because we need the * LOD to be set for query_levels and for non-fragment shaders. */ NIR_PASS(_, s, nir_lower_tex, &lower_tex_options); } /* There are some issues with the tgsi_texcoord = false support in the state * tracker, specifically we are still getting texcoords and pointcoord in some cases * and ocassionally (with fixed function shaders) there are some inconsistencies * like vs using generics and fs using texcoords. This function tries to fix it. * See https://gitlab.freedesktop.org/mesa/mesa/-/issues/12749 for more details. */ void ntr_fixup_varying_slots(nir_shader *s, nir_variable_mode mode) { if (s->info.name && !strcmp(s->info.name, "st/drawtex VS")) return; nir_foreach_variable_with_modes (var, s, mode) { if (var->data.location >= VARYING_SLOT_VAR0 && var->data.location < VARYING_SLOT_PATCH0) { var->data.location += 9; } else if (var->data.location == VARYING_SLOT_PNTC) { var->data.location = VARYING_SLOT_VAR8; } else if ((var->data.location >= VARYING_SLOT_TEX0) && (var->data.location <= VARYING_SLOT_TEX7)) { var->data.location += VARYING_SLOT_VAR0 - VARYING_SLOT_TEX0; } } } /** * Translates the NIR shader to RC instructions on the given compiler. * * This requires some lowering of the NIR shader to prepare it for * translation. We take ownership of the NIR shader passed; if you need to * keep the NIR, then pass us a clone. */ void nir_to_rc(struct nir_shader *s, struct pipe_screen *screen, struct r300_fragment_program_external_state state, union r300_shader_code rc, struct radeon_compiler *compiler) { struct ntr_compile *c; bool is_r500 = r300_screen(screen)->caps.is_r500; c = rzalloc(NULL, struct ntr_compile); c->compiler = compiler; c->lower_fabs = !is_r500 && s->info.stage == MESA_SHADER_VERTEX; c->addr_reg = ntr_writemask(ntr_dst_register(c, RC_FILE_ADDRESS, 0), RC_MASK_X); util_dynarray_init(&c->immediates, c); for (unsigned i = 0; i < ARRAY_SIZE(c->fs_output_color_index); i++) c->fs_output_color_index[i] = -1; c->fs_output_depth_index = -1; for (unsigned i = 0; i < ARRAY_SIZE(c->fs_output_index); i++) c->fs_output_index[i] = -1; if (s->info.stage == MESA_SHADER_FRAGMENT) { c->semantics = &rc.f->inputs; } else { c->semantics = &rc.v->outputs; } if (s->info.stage == MESA_SHADER_FRAGMENT) { static const nir_lower_sysvals_to_varyings_options sysval_options = { .frag_coord = true, .point_coord = true, .front_face = true, }; NIR_PASS(_, s, nir_lower_sysvals_to_varyings, &sysval_options); } ntr_fixup_varying_slots(s, s->info.stage == MESA_SHADER_FRAGMENT ? nir_var_shader_in : nir_var_shader_out); /* The fragment backend doesn't support relative addressing of input * sources, so lower array indexing on FS inputs before nir_lower_io turns * derefs into load_input offsets. */ if (s->info.stage == MESA_SHADER_FRAGMENT) { NIR_PASS(_, s, nir_lower_indirect_derefs_to_if_else_trees, nir_var_shader_in, UINT32_MAX); NIR_PASS(_, s, nir_remove_dead_variables, nir_var_shader_in, NULL); } NIR_PASS(_, s, nir_lower_io, nir_var_shader_in | nir_var_shader_out, type_size, nir_lower_io_use_interpolated_input_intrinsics); if (s->info.stage == MESA_SHADER_FRAGMENT) { /* Shadow lowering. */ int num_texture_states = state.sampler_state_count; if (num_texture_states > 0) { nir_lower_tex_shadow_swizzle tex_swizzle[PIPE_MAX_SHADER_SAMPLER_VIEWS]; enum compare_func tex_compare_func[PIPE_MAX_SHADER_SAMPLER_VIEWS]; for (unsigned i = 0; i < num_texture_states; i++) { tex_compare_func[i] = state.unit[i].texture_compare_func; tex_swizzle[i].swizzle_r = GET_SWZ(state.unit[i].texture_swizzle, 0); tex_swizzle[i].swizzle_g = GET_SWZ(state.unit[i].texture_swizzle, 1); tex_swizzle[i].swizzle_b = GET_SWZ(state.unit[i].texture_swizzle, 2); tex_swizzle[i].swizzle_a = GET_SWZ(state.unit[i].texture_swizzle, 3); } NIR_PASS(_, s, nir_lower_tex_shadow, num_texture_states, tex_compare_func, tex_swizzle, true); } nir_to_rc_lower_txp(s); NIR_PASS(_, s, nir_to_rc_lower_tex); } bool progress; do { progress = false; NIR_PASS(progress, s, nir_opt_algebraic); NIR_PASS(progress, s, nir_opt_constant_folding); } while (progress); do { progress = false; NIR_PASS(progress, s, nir_opt_algebraic_late); if (progress) { NIR_PASS(_, s, nir_opt_copy_prop); NIR_PASS(_, s, nir_opt_dce); NIR_PASS(_, s, nir_opt_cse); } } while (progress); if (s->info.stage == MESA_SHADER_FRAGMENT) { NIR_PASS(_, s, r300_nir_prepare_presubtract); } NIR_PASS(_, s, nir_lower_int_to_float); NIR_PASS(_, s, nir_opt_copy_prop); NIR_PASS(_, s, r300_nir_post_integer_lowering); NIR_PASS(_, s, nir_lower_bool_to_float, is_r500 || s->info.stage == MESA_SHADER_FRAGMENT); /* bool_to_float generates MOVs for b2f32 that we want to clean up. */ NIR_PASS(_, s, nir_opt_copy_prop); /* CSE cleanup after late ftrunc lowering. */ NIR_PASS(_, s, nir_opt_cse); /* At this point we need to clean; * a) fcsel_gt that come from the ftrunc lowering on R300, * b) all flavours of fcsels that read three different temp sources on R500. */ if (s->info.stage == MESA_SHADER_VERTEX) { if (is_r500) NIR_PASS(_, s, r300_nir_lower_fcsel_r500); else NIR_PASS(_, s, r300_nir_lower_fcsel_r300); NIR_PASS(_, s, r300_nir_lower_flrp); } else { NIR_PASS(_, s, r300_nir_lower_comparison_fs); } NIR_PASS(_, s, r300_nir_opt_algebraic_late); NIR_PASS(_, s, nir_opt_dce); NIR_PASS(_, s, nir_opt_shrink_vectors, false); NIR_PASS(_, s, nir_opt_dce); nir_move_options move_all = nir_move_const_undef | nir_move_load_ubo | nir_move_load_input | nir_move_load_frag_coord | nir_move_comparisons | nir_move_copies | nir_move_load_ssbo; NIR_PASS(_, s, nir_opt_move, move_all); NIR_PASS(_, s, nir_move_vec_src_uses_to_dest, true); /* Late vectorizing after nir_move_vec_src_uses_to_dest helps instructions but * increases register usage. Testing shows this is beneficial only in VS. */ if (s->info.stage == MESA_SHADER_VERTEX) NIR_PASS(_, s, nir_opt_vectorize, ntr_should_vectorize_instr, NULL); NIR_PASS(_, s, nir_convert_from_ssa, true, false); NIR_PASS(_, s, nir_lower_vec_to_regs, NULL, NULL); /* locals_to_reg_intrinsics will leave dead derefs that are good to clean up. */ NIR_PASS(_, s, nir_lower_locals_to_regs, 32); NIR_PASS(_, s, nir_opt_dce); /* See comment in ntr_get_alu_src for supported modifiers */ NIR_PASS(_, s, nir_legacy_trivialize, !c->lower_fabs); if (NIR_DEBUG(TGSI)) { fprintf(stderr, "NIR before translation to TGSI:\n"); nir_print_shader(s, stderr); } c->s = s; /* Emit the main function */ nir_function_impl *impl = nir_shader_get_entrypoint(c->s); ntr_emit_impl(c, impl); /* For FS, populate the FS-specific compiler outputs. */ if (s->info.stage == MESA_SHADER_FRAGMENT) { struct r300_fragment_program_compiler *fc = (struct r300_fragment_program_compiler *)compiler; rc.f->uses_discard = s->info.fs.uses_discard; fc->OutputDepth = c->fs_output_depth_index >= 0 ? c->fs_output_depth_index : c->num_outputs; for (unsigned i = 0; i < ARRAY_SIZE(c->fs_output_color_index); i++) { fc->OutputColor[i] = c->fs_output_color_index[i] >= 0 ? c->fs_output_color_index[i] : c->num_outputs; } } else if (s->info.stage == MESA_SHADER_VERTEX) { rc.v->num_inputs = s->num_inputs; } rc_calculate_inputs_outputs(compiler); ralloc_free(c); ralloc_free(s); }