aco/isel: move select_vs_prolog() into separate file

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/34977>
This commit is contained in:
Daniel Schürmann
2025-05-12 14:35:55 +02:00
committed by Marge Bot
parent c4ec430c26
commit c3ef927e31
3 changed files with 636 additions and 616 deletions
@@ -10579,622 +10579,6 @@ select_program(Program* program, unsigned shader_count, struct nir_shader* const
}
}
Operand
get_arg_fixed(const struct ac_shader_args* args, struct ac_arg arg)
{
enum ac_arg_regfile file = args->args[arg.arg_index].file;
unsigned size = args->args[arg.arg_index].size;
RegClass rc = RegClass(file == AC_ARG_SGPR ? RegType::sgpr : RegType::vgpr, size);
return Operand(get_arg_reg(args, arg), rc);
}
unsigned
load_vb_descs(Builder& bld, PhysReg dest, Operand base, unsigned start, unsigned max)
{
unsigned sgpr_limit = get_addr_regs_from_waves(bld.program, bld.program->min_waves).sgpr;
unsigned count = MIN2((sgpr_limit - dest.reg()) / 4u, max);
for (unsigned i = 0; i < count;) {
unsigned size = 1u << util_logbase2(MIN2(count - i, 4));
if (size == 4)
bld.smem(aco_opcode::s_load_dwordx16, Definition(dest, s16), base,
Operand::c32((start + i) * 16u));
else if (size == 2)
bld.smem(aco_opcode::s_load_dwordx8, Definition(dest, s8), base,
Operand::c32((start + i) * 16u));
else
bld.smem(aco_opcode::s_load_dwordx4, Definition(dest, s4), base,
Operand::c32((start + i) * 16u));
dest = dest.advance(size * 16u);
i += size;
}
return count;
}
void
wait_for_smem_loads(Builder& bld)
{
if (bld.program->gfx_level >= GFX12) {
bld.sopp(aco_opcode::s_wait_kmcnt, 0);
} else {
wait_imm lgkm_imm;
lgkm_imm.lgkm = 0;
bld.sopp(aco_opcode::s_waitcnt, lgkm_imm.pack(bld.program->gfx_level));
}
}
void
wait_for_vmem_loads(Builder& bld)
{
if (bld.program->gfx_level >= GFX12) {
bld.sopp(aco_opcode::s_wait_loadcnt, 0);
} else {
wait_imm vm_imm;
vm_imm.vm = 0;
bld.sopp(aco_opcode::s_waitcnt, vm_imm.pack(bld.program->gfx_level));
}
}
Operand
calc_nontrivial_instance_id(Builder& bld, const struct ac_shader_args* args,
const struct aco_vs_prolog_info* pinfo, unsigned index,
Operand instance_id, Operand start_instance, PhysReg tmp_sgpr,
PhysReg tmp_vgpr0, PhysReg tmp_vgpr1)
{
bld.smem(aco_opcode::s_load_dwordx2, Definition(tmp_sgpr, s2),
get_arg_fixed(args, pinfo->inputs), Operand::c32(8u + index * 8u));
wait_for_smem_loads(bld);
Definition fetch_index_def(tmp_vgpr0, v1);
Operand fetch_index(tmp_vgpr0, v1);
Operand div_info(tmp_sgpr, s1);
if (bld.program->gfx_level >= GFX8 && bld.program->gfx_level < GFX11) {
/* use SDWA */
if (bld.program->gfx_level < GFX9) {
bld.vop1(aco_opcode::v_mov_b32, Definition(tmp_vgpr1, v1), div_info);
div_info = Operand(tmp_vgpr1, v1);
}
bld.vop2(aco_opcode::v_lshrrev_b32, fetch_index_def, div_info, instance_id);
Instruction* instr;
if (bld.program->gfx_level >= GFX9)
instr = bld.vop2_sdwa(aco_opcode::v_add_u32, fetch_index_def, div_info, fetch_index).instr;
else
instr = bld.vop2_sdwa(aco_opcode::v_add_co_u32, fetch_index_def, Definition(vcc, bld.lm),
div_info, fetch_index)
.instr;
instr->sdwa().sel[0] = SubdwordSel::ubyte1;
bld.vop3(aco_opcode::v_mul_hi_u32, fetch_index_def, Operand(tmp_sgpr.advance(4), s1),
fetch_index);
instr =
bld.vop2_sdwa(aco_opcode::v_lshrrev_b32, fetch_index_def, div_info, fetch_index).instr;
instr->sdwa().sel[0] = SubdwordSel::ubyte2;
} else {
Operand tmp_op(tmp_vgpr1, v1);
Definition tmp_def(tmp_vgpr1, v1);
bld.vop2(aco_opcode::v_lshrrev_b32, fetch_index_def, div_info, instance_id);
bld.vop3(aco_opcode::v_bfe_u32, tmp_def, div_info, Operand::c32(8u), Operand::c32(8u));
bld.vadd32(fetch_index_def, tmp_op, fetch_index, false, Operand(s2), true);
bld.vop3(aco_opcode::v_mul_hi_u32, fetch_index_def, fetch_index,
Operand(tmp_sgpr.advance(4), s1));
bld.vop3(aco_opcode::v_bfe_u32, tmp_def, div_info, Operand::c32(16u), Operand::c32(8u));
bld.vop2(aco_opcode::v_lshrrev_b32, fetch_index_def, tmp_op, fetch_index);
}
bld.vadd32(fetch_index_def, start_instance, fetch_index, false, Operand(s2), true);
return fetch_index;
}
PhysReg
get_next_vgpr(unsigned size, unsigned* num, int *offset = NULL)
{
unsigned reg = *num + (offset ? *offset : 0);
if (reg + size >= *num) {
*num = reg + size;
if (offset)
*offset = 0;
} else if (offset) {
*offset += size;
}
return PhysReg(256 + reg);
}
struct UnalignedVsAttribLoad {
/* dst/scratch are PhysReg converted to unsigned */
unsigned dst;
unsigned scratch;
bool d16;
const struct ac_vtx_format_info* vtx_info;
};
struct UnalignedVsAttribLoadState {
unsigned max_vgprs;
unsigned initial_num_vgprs;
unsigned* num_vgprs;
unsigned overflow_num_vgprs;
aco::small_vec<UnalignedVsAttribLoad, 16> current_loads;
};
void
convert_unaligned_vs_attrib(Builder& bld, UnalignedVsAttribLoad load)
{
PhysReg dst(load.dst);
PhysReg scratch(load.scratch);
const struct ac_vtx_format_info* vtx_info = load.vtx_info;
unsigned dfmt = vtx_info->hw_format[0] & 0xf;
unsigned nfmt = vtx_info->hw_format[0] >> 4;
unsigned size = vtx_info->chan_byte_size ? vtx_info->chan_byte_size : vtx_info->element_size;
if (load.d16) {
bld.vop3(aco_opcode::v_lshl_or_b32, Definition(dst, v1), Operand(scratch, v1),
Operand::c32(8), Operand(dst, v1));
} else {
for (unsigned i = 1; i < size; i++) {
PhysReg byte_reg = scratch.advance(i * 4 - 4);
if (bld.program->gfx_level >= GFX9) {
bld.vop3(aco_opcode::v_lshl_or_b32, Definition(dst, v1), Operand(byte_reg, v1),
Operand::c32(i * 8), Operand(dst, v1));
} else {
bld.vop2(aco_opcode::v_lshlrev_b32, Definition(byte_reg, v1), Operand::c32(i * 8),
Operand(byte_reg, v1));
bld.vop2(aco_opcode::v_or_b32, Definition(dst, v1), Operand(dst, v1),
Operand(byte_reg, v1));
}
}
}
unsigned num_channels = vtx_info->chan_byte_size ? 1 : vtx_info->num_channels;
PhysReg chan[4] = {dst, dst.advance(4), dst.advance(8), dst.advance(12)};
if (dfmt == V_008F0C_BUF_DATA_FORMAT_10_11_11) {
bld.vop3(aco_opcode::v_bfe_u32, Definition(chan[2], v1), Operand(dst, v1), Operand::c32(22),
Operand::c32(10));
bld.vop3(aco_opcode::v_bfe_u32, Definition(chan[1], v1), Operand(dst, v1), Operand::c32(11),
Operand::c32(11));
bld.vop3(aco_opcode::v_bfe_u32, Definition(chan[0], v1), Operand(dst, v1), Operand::c32(0),
Operand::c32(11));
bld.vop2(aco_opcode::v_lshlrev_b32, Definition(chan[2], v1), Operand::c32(5),
Operand(chan[2], v1));
bld.vop2(aco_opcode::v_lshlrev_b32, Definition(chan[1], v1), Operand::c32(4),
Operand(chan[1], v1));
bld.vop2(aco_opcode::v_lshlrev_b32, Definition(chan[0], v1), Operand::c32(4),
Operand(chan[0], v1));
} else if (dfmt == V_008F0C_BUF_DATA_FORMAT_2_10_10_10) {
aco_opcode bfe = aco_opcode::v_bfe_u32;
switch (nfmt) {
case V_008F0C_BUF_NUM_FORMAT_SNORM:
case V_008F0C_BUF_NUM_FORMAT_SSCALED:
case V_008F0C_BUF_NUM_FORMAT_SINT: bfe = aco_opcode::v_bfe_i32; break;
default: break;
}
bool swapxz = G_008F0C_DST_SEL_X(vtx_info->dst_sel) != V_008F0C_SQ_SEL_X;
bld.vop3(bfe, Definition(chan[3], v1), Operand(dst, v1), Operand::c32(30), Operand::c32(2));
bld.vop3(bfe, Definition(chan[2], v1), Operand(dst, v1), Operand::c32(swapxz ? 0 : 20),
Operand::c32(10));
bld.vop3(bfe, Definition(chan[1], v1), Operand(dst, v1), Operand::c32(10), Operand::c32(10));
bld.vop3(bfe, Definition(chan[0], v1), Operand(dst, v1), Operand::c32(swapxz ? 20 : 0),
Operand::c32(10));
} else if (dfmt == V_008F0C_BUF_DATA_FORMAT_8 || dfmt == V_008F0C_BUF_DATA_FORMAT_16) {
unsigned bits = dfmt == V_008F0C_BUF_DATA_FORMAT_8 ? 8 : 16;
switch (nfmt) {
case V_008F0C_BUF_NUM_FORMAT_SNORM:
case V_008F0C_BUF_NUM_FORMAT_SSCALED:
case V_008F0C_BUF_NUM_FORMAT_SINT:
bld.vop3(aco_opcode::v_bfe_i32, Definition(dst, v1), Operand(dst, v1), Operand::c32(0),
Operand::c32(bits));
break;
default: break;
}
}
if (nfmt == V_008F0C_BUF_NUM_FORMAT_FLOAT &&
(dfmt == V_008F0C_BUF_DATA_FORMAT_16 || dfmt == V_008F0C_BUF_DATA_FORMAT_10_11_11)) {
for (unsigned i = 0; i < num_channels; i++)
bld.vop1(aco_opcode::v_cvt_f32_f16, Definition(chan[i], v1), Operand(chan[i], v1));
} else if (nfmt == V_008F0C_BUF_NUM_FORMAT_USCALED || nfmt == V_008F0C_BUF_NUM_FORMAT_UNORM) {
for (unsigned i = 0; i < num_channels; i++)
bld.vop1(aco_opcode::v_cvt_f32_u32, Definition(chan[i], v1), Operand(chan[i], v1));
} else if (nfmt == V_008F0C_BUF_NUM_FORMAT_SSCALED || nfmt == V_008F0C_BUF_NUM_FORMAT_SNORM) {
for (unsigned i = 0; i < num_channels; i++)
bld.vop1(aco_opcode::v_cvt_f32_i32, Definition(chan[i], v1), Operand(chan[i], v1));
}
std::array<unsigned, 4> chan_max;
switch (dfmt) {
case V_008F0C_BUF_DATA_FORMAT_2_10_10_10: chan_max = {1023, 1023, 1023, 3}; break;
case V_008F0C_BUF_DATA_FORMAT_8: chan_max = {255, 255, 255, 255}; break;
case V_008F0C_BUF_DATA_FORMAT_16: chan_max = {65535, 65535, 65535, 65535}; break;
}
if (nfmt == V_008F0C_BUF_NUM_FORMAT_UNORM) {
for (unsigned i = 0; i < num_channels; i++)
bld.vop2(aco_opcode::v_mul_f32, Definition(chan[i], v1),
Operand::c32(fui(1.0 / chan_max[i])), Operand(chan[i], v1));
} else if (nfmt == V_008F0C_BUF_NUM_FORMAT_SNORM) {
for (unsigned i = 0; i < num_channels; i++) {
bld.vop2(aco_opcode::v_mul_f32, Definition(chan[i], v1),
Operand::c32(fui(1.0 / (chan_max[i] >> 1))), Operand(chan[i], v1));
bld.vop2(aco_opcode::v_max_f32, Definition(chan[i], v1), Operand::c32(0xbf800000),
Operand(chan[i], v1));
}
}
}
void
convert_current_unaligned_vs_attribs(Builder& bld, UnalignedVsAttribLoadState* state)
{
if (state->current_loads.empty())
return;
wait_for_vmem_loads(bld);
for (UnalignedVsAttribLoad load : state->current_loads)
convert_unaligned_vs_attrib(bld, load);
state->current_loads.clear();
state->overflow_num_vgprs = state->initial_num_vgprs;
state->num_vgprs = &state->overflow_num_vgprs;
}
void
load_unaligned_vs_attrib(Builder& bld, PhysReg dst, Operand desc, Operand index, uint32_t offset,
const struct ac_vtx_format_info* vtx_info,
UnalignedVsAttribLoadState* state)
{
unsigned size = vtx_info->chan_byte_size ? vtx_info->chan_byte_size : vtx_info->element_size;
UnalignedVsAttribLoad load;
load.dst = dst;
load.vtx_info = vtx_info;
load.d16 = bld.program->gfx_level >= GFX9 && !bld.program->dev.sram_ecc_enabled && size == 4;
unsigned num_scratch_vgprs = load.d16 ? 1 : (size - 1);
if (!vtx_info->chan_byte_size) {
/* When chan_byte_size==0, we're loading the entire attribute, so we can use the last 3
* components of the destination.
*/
assert(num_scratch_vgprs <= 3);
load.scratch = dst.advance(4);
} else {
if (*state->num_vgprs + num_scratch_vgprs > state->max_vgprs)
convert_current_unaligned_vs_attribs(bld, state);
load.scratch = get_next_vgpr(num_scratch_vgprs, state->num_vgprs, NULL);
}
PhysReg scratch(load.scratch);
if (load.d16) {
bld.mubuf(aco_opcode::buffer_load_ubyte_d16, Definition(dst, v1), desc, index,
Operand::c32(0u), offset, false, true);
bld.mubuf(aco_opcode::buffer_load_ubyte_d16_hi, Definition(dst, v1), desc, index,
Operand::c32(0u), offset + 2, false, true);
bld.mubuf(aco_opcode::buffer_load_ubyte_d16, Definition(scratch, v1), desc, index,
Operand::c32(0u), offset + 1, false, true);
bld.mubuf(aco_opcode::buffer_load_ubyte_d16_hi, Definition(scratch, v1), desc, index,
Operand::c32(0u), offset + 3, false, true);
} else {
for (unsigned i = 0; i < size; i++) {
Definition def(i ? scratch.advance(i * 4 - 4) : dst, v1);
unsigned soffset = 0, const_offset = 0;
if (bld.program->gfx_level >= GFX12) {
const_offset = offset + i;
} else {
soffset = offset + i;
}
bld.mubuf(aco_opcode::buffer_load_ubyte, def, desc, index, Operand::c32(soffset),
const_offset, false, true);
}
}
state->current_loads.push_back(load);
}
void
select_vs_prolog(Program* program, const struct aco_vs_prolog_info* pinfo, ac_shader_config* config,
const struct aco_compiler_options* options, const struct aco_shader_info* info,
const struct ac_shader_args* args)
{
assert(pinfo->num_attributes > 0);
/* This should be enough for any shader/stage. */
unsigned max_user_sgprs = options->gfx_level >= GFX9 ? 32 : 16;
init_program(program, compute_cs, info, options->gfx_level, options->family, options->wgp_mode,
config);
program->dev.vgpr_limit = 256;
Block* block = program->create_and_insert_block();
block->kind = block_kind_top_level;
program->workgroup_size = 64;
calc_min_waves(program);
/* Addition on GFX6-8 requires a carry-out (we use VCC) */
program->needs_vcc = program->gfx_level <= GFX8;
Builder bld(program, block);
block->instructions.reserve(16 + pinfo->num_attributes * 4);
/* Besides performance, the purpose of this is also for the FeatureRequiredExportPriority GFX11.5
* issue. */
bld.sopp(aco_opcode::s_setprio, 3);
uint32_t attrib_mask = BITFIELD_MASK(pinfo->num_attributes);
bool has_nontrivial_divisors = pinfo->nontrivial_divisors;
/* choose sgprs */
PhysReg vertex_buffers(align(max_user_sgprs + 14, 2));
PhysReg prolog_input = vertex_buffers.advance(8);
PhysReg desc(
align((has_nontrivial_divisors ? prolog_input : vertex_buffers).advance(8).reg(), 4));
Operand start_instance = get_arg_fixed(args, args->start_instance);
Operand instance_id = get_arg_fixed(args, args->instance_id);
bool needs_instance_index =
pinfo->instance_rate_inputs &
~(pinfo->zero_divisors | pinfo->nontrivial_divisors); /* divisor is 1 */
bool needs_start_instance = pinfo->instance_rate_inputs & pinfo->zero_divisors;
bool needs_vertex_index = ~pinfo->instance_rate_inputs & attrib_mask;
bool needs_tmp_vgpr0 = has_nontrivial_divisors;
bool needs_tmp_vgpr1 = has_nontrivial_divisors &&
(program->gfx_level <= GFX8 || program->gfx_level >= GFX11);
int vgpr_offset = pinfo->misaligned_mask & (1u << (pinfo->num_attributes - 1)) ? 0 : -4;
unsigned num_vgprs = args->num_vgprs_used;
PhysReg attributes_start = get_next_vgpr(pinfo->num_attributes * 4, &num_vgprs);
PhysReg vertex_index, instance_index, start_instance_vgpr, nontrivial_tmp_vgpr0, nontrivial_tmp_vgpr1;
if (needs_vertex_index)
vertex_index = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
if (needs_instance_index)
instance_index = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
if (needs_start_instance)
start_instance_vgpr = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
if (needs_tmp_vgpr0)
nontrivial_tmp_vgpr0 = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
if (needs_tmp_vgpr1)
nontrivial_tmp_vgpr1 = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
bld.sop1(aco_opcode::s_mov_b32, Definition(vertex_buffers, s1),
get_arg_fixed(args, args->vertex_buffers));
if (options->address32_hi >= 0xffff8000 || options->address32_hi <= 0x7fff) {
bld.sopk(aco_opcode::s_movk_i32, Definition(vertex_buffers.advance(4), s1),
options->address32_hi & 0xFFFF);
} else {
bld.sop1(aco_opcode::s_mov_b32, Definition(vertex_buffers.advance(4), s1),
Operand::c32((unsigned)options->address32_hi));
}
const struct ac_vtx_format_info* vtx_info_table =
ac_get_vtx_format_info_table(GFX8, CHIP_POLARIS10);
UnalignedVsAttribLoadState unaligned_state;
unaligned_state.max_vgprs = MAX2(84, num_vgprs + 8);
unaligned_state.initial_num_vgprs = num_vgprs;
unaligned_state.num_vgprs = &num_vgprs;
unsigned num_sgprs = 0;
for (unsigned loc = 0; loc < pinfo->num_attributes;) {
unsigned num_descs =
load_vb_descs(bld, desc, Operand(vertex_buffers, s2), loc, pinfo->num_attributes - loc);
num_sgprs = MAX2(num_sgprs, desc.advance(num_descs * 16u).reg());
if (loc == 0) {
/* perform setup while we load the descriptors */
if (pinfo->is_ngg || pinfo->next_stage != MESA_SHADER_VERTEX) {
Operand count = get_arg_fixed(args, args->merged_wave_info);
bld.sop2(aco_opcode::s_bfm_b64, Definition(exec, s2), count, Operand::c32(0u));
if (program->wave_size == 64) {
bld.sopc(aco_opcode::s_bitcmp1_b32, Definition(scc, s1), count,
Operand::c32(6u /* log2(64) */));
bld.sop2(aco_opcode::s_cselect_b64, Definition(exec, s2), Operand::c64(UINT64_MAX),
Operand(exec, s2), Operand(scc, s1));
}
}
/* If there are no HS threads, SPI mistakenly loads the LS VGPRs starting at VGPR 0. */
if (info->hw_stage == AC_HW_HULL_SHADER && options->has_ls_vgpr_init_bug) {
/* We don't want load_vb_descs() to write vcc. */
assert(program->dev.sgpr_limit <= vcc.reg());
bld.sop2(aco_opcode::s_bfe_u32, Definition(vcc, s1), Definition(scc, s1),
get_arg_fixed(args, args->merged_wave_info), Operand::c32((8u << 16) | 8u));
bld.sop2(Builder::s_cselect, Definition(vcc, bld.lm), Operand::c32(-1), Operand::zero(),
Operand(scc, s1));
/* These copies are ordered so that vertex_id=tcs_patch_id doesn't overwrite vertex_id
* before instance_id=vertex_id. */
ac_arg src_args[] = {args->vertex_id, args->tcs_rel_ids, args->tcs_patch_id};
ac_arg dst_args[] = {args->instance_id, args->vs_rel_patch_id, args->vertex_id};
for (unsigned i = 0; i < 3; i++) {
bld.vop2(aco_opcode::v_cndmask_b32, Definition(get_arg_reg(args, dst_args[i]), v1),
get_arg_fixed(args, src_args[i]), get_arg_fixed(args, dst_args[i]),
Operand(vcc, bld.lm));
}
}
if (needs_vertex_index)
bld.vadd32(Definition(vertex_index, v1), get_arg_fixed(args, args->base_vertex),
get_arg_fixed(args, args->vertex_id), false, Operand(s2), true);
if (needs_instance_index)
bld.vadd32(Definition(instance_index, v1), start_instance, instance_id, false,
Operand(s2), true);
if (needs_start_instance)
bld.vop1(aco_opcode::v_mov_b32, Definition(start_instance_vgpr, v1), start_instance);
}
wait_for_smem_loads(bld);
for (unsigned i = 0; i < num_descs;) {
PhysReg dest(attributes_start.reg() + loc * 4u);
/* calculate index */
Operand fetch_index = Operand(vertex_index, v1);
if (pinfo->instance_rate_inputs & (1u << loc)) {
if (!(pinfo->zero_divisors & (1u << loc))) {
fetch_index = instance_id;
if (pinfo->nontrivial_divisors & (1u << loc)) {
unsigned index = util_bitcount(pinfo->nontrivial_divisors & BITFIELD_MASK(loc));
fetch_index = calc_nontrivial_instance_id(
bld, args, pinfo, index, instance_id, start_instance, prolog_input,
nontrivial_tmp_vgpr0, nontrivial_tmp_vgpr1);
} else {
fetch_index = Operand(instance_index, v1);
}
} else {
fetch_index = Operand(start_instance_vgpr, v1);
}
}
/* perform load */
PhysReg cur_desc = desc.advance(i * 16);
if ((pinfo->misaligned_mask & (1u << loc))) {
const struct ac_vtx_format_info* vtx_info = &vtx_info_table[pinfo->formats[loc]];
assert(vtx_info->has_hw_format & 0x1);
unsigned dfmt = vtx_info->hw_format[0] & 0xf;
unsigned nfmt = vtx_info->hw_format[0] >> 4;
for (unsigned j = 0; j < (vtx_info->chan_byte_size ? vtx_info->num_channels : 1); j++) {
bool post_shuffle = pinfo->post_shuffle & (1u << loc);
unsigned offset = vtx_info->chan_byte_size * (post_shuffle && j < 3 ? 2 - j : j);
unsigned soffset = 0, const_offset = 0;
/* We need to use soffset on GFX6-7 to avoid being considered
* out-of-bounds when offset>=stride. GFX12 doesn't support a
* non-zero constant soffset.
*/
if (program->gfx_level >= GFX12) {
const_offset = offset;
} else {
soffset = offset;
}
if ((pinfo->unaligned_mask & (1u << loc)) && vtx_info->chan_byte_size <= 4)
load_unaligned_vs_attrib(bld, dest.advance(j * 4u), Operand(cur_desc, s4),
fetch_index, offset, vtx_info, &unaligned_state);
else if (vtx_info->chan_byte_size == 8)
bld.mtbuf(aco_opcode::tbuffer_load_format_xy,
Definition(dest.advance(j * 8u), v2), Operand(cur_desc, s4),
fetch_index, Operand::c32(soffset), dfmt, nfmt, const_offset, false,
true);
else
bld.mtbuf(aco_opcode::tbuffer_load_format_x, Definition(dest.advance(j * 4u), v1),
Operand(cur_desc, s4), fetch_index, Operand::c32(soffset), dfmt, nfmt,
const_offset, false, true);
}
unsigned slots = vtx_info->chan_byte_size == 8 && vtx_info->num_channels > 2 ? 2 : 1;
loc += slots;
i += slots;
} else {
bld.mubuf(aco_opcode::buffer_load_format_xyzw, Definition(dest, v4),
Operand(cur_desc, s4), fetch_index, Operand::c32(0u), 0u, false, true);
loc++;
i++;
}
}
}
uint32_t constant_mask = pinfo->misaligned_mask;
while (constant_mask) {
unsigned loc = u_bit_scan(&constant_mask);
const struct ac_vtx_format_info* vtx_info = &vtx_info_table[pinfo->formats[loc]];
/* 22.1.1. Attribute Location and Component Assignment of Vulkan 1.3 specification:
* For 64-bit data types, no default attribute values are provided. Input variables must
* not use more components than provided by the attribute.
*/
if (vtx_info->chan_byte_size == 8) {
if (vtx_info->num_channels > 2)
u_bit_scan(&constant_mask);
continue;
}
assert(vtx_info->has_hw_format & 0x1);
unsigned nfmt = vtx_info->hw_format[0] >> 4;
uint32_t one = nfmt == V_008F0C_BUF_NUM_FORMAT_UINT || nfmt == V_008F0C_BUF_NUM_FORMAT_SINT
? 1u
: 0x3f800000u;
PhysReg dest(attributes_start.reg() + loc * 4u);
for (unsigned j = vtx_info->num_channels; j < 4; j++) {
bld.vop1(aco_opcode::v_mov_b32, Definition(dest.advance(j * 4u), v1),
Operand::c32(j == 3 ? one : 0u));
}
}
convert_current_unaligned_vs_attribs(bld, &unaligned_state);
if (pinfo->alpha_adjust_lo | pinfo->alpha_adjust_hi)
wait_for_vmem_loads(bld);
/* For 2_10_10_10 formats the alpha is handled as unsigned by pre-vega HW.
* so we may need to fix it up. */
u_foreach_bit (loc, (pinfo->alpha_adjust_lo | pinfo->alpha_adjust_hi)) {
PhysReg alpha(attributes_start.reg() + loc * 4u + 3);
unsigned alpha_adjust = (pinfo->alpha_adjust_lo >> loc) & 0x1;
alpha_adjust |= ((pinfo->alpha_adjust_hi >> loc) & 0x1) << 1;
if (alpha_adjust == AC_ALPHA_ADJUST_SSCALED)
bld.vop1(aco_opcode::v_cvt_u32_f32, Definition(alpha, v1), Operand(alpha, v1));
/* For the integer-like cases, do a natural sign extension.
*
* For the SNORM case, the values are 0.0, 0.333, 0.666, 1.0
* and happen to contain 0, 1, 2, 3 as the two LSBs of the
* exponent.
*/
unsigned offset = alpha_adjust == AC_ALPHA_ADJUST_SNORM ? 23u : 0u;
bld.vop3(aco_opcode::v_bfe_i32, Definition(alpha, v1), Operand(alpha, v1),
Operand::c32(offset), Operand::c32(2u));
/* Convert back to the right type. */
if (alpha_adjust == AC_ALPHA_ADJUST_SNORM) {
bld.vop1(aco_opcode::v_cvt_f32_i32, Definition(alpha, v1), Operand(alpha, v1));
bld.vop2(aco_opcode::v_max_f32, Definition(alpha, v1), Operand::c32(0xbf800000u),
Operand(alpha, v1));
} else if (alpha_adjust == AC_ALPHA_ADJUST_SSCALED) {
bld.vop1(aco_opcode::v_cvt_f32_i32, Definition(alpha, v1), Operand(alpha, v1));
}
}
block->kind |= block_kind_uniform;
/* continue on to the main shader */
Operand continue_pc = get_arg_fixed(args, pinfo->inputs);
if (has_nontrivial_divisors) {
bld.smem(aco_opcode::s_load_dwordx2, Definition(prolog_input, s2),
get_arg_fixed(args, pinfo->inputs), Operand::c32(0u));
wait_for_smem_loads(bld);
continue_pc = Operand(prolog_input, s2);
}
bld.sop1(aco_opcode::s_setpc_b64, continue_pc);
program->config->float_mode = program->blocks[0].fp_mode.val;
program->config->num_vgprs = std::min<uint16_t>(get_vgpr_alloc(program, num_vgprs), 256);
program->config->num_sgprs = get_sgpr_alloc(program, num_sgprs);
}
void
select_ps_epilog(Program* program, void* pinfo, ac_shader_config* config,
const struct aco_compiler_options* options, const struct aco_shader_info* info,
@@ -0,0 +1,635 @@
/*
* Copyright © 2021 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#include "aco_builder.h"
#include "aco_instruction_selection.h"
#include "aco_ir.h"
#include "amdgfxregs.h"
namespace aco {
namespace {
Operand
get_arg_fixed(const struct ac_shader_args* args, struct ac_arg arg)
{
enum ac_arg_regfile file = args->args[arg.arg_index].file;
unsigned size = args->args[arg.arg_index].size;
RegClass rc = RegClass(file == AC_ARG_SGPR ? RegType::sgpr : RegType::vgpr, size);
return Operand(get_arg_reg(args, arg), rc);
}
unsigned
load_vb_descs(Builder& bld, PhysReg dest, Operand base, unsigned start, unsigned max)
{
unsigned sgpr_limit = get_addr_regs_from_waves(bld.program, bld.program->min_waves).sgpr;
unsigned count = MIN2((sgpr_limit - dest.reg()) / 4u, max);
for (unsigned i = 0; i < count;) {
unsigned size = 1u << util_logbase2(MIN2(count - i, 4));
if (size == 4)
bld.smem(aco_opcode::s_load_dwordx16, Definition(dest, s16), base,
Operand::c32((start + i) * 16u));
else if (size == 2)
bld.smem(aco_opcode::s_load_dwordx8, Definition(dest, s8), base,
Operand::c32((start + i) * 16u));
else
bld.smem(aco_opcode::s_load_dwordx4, Definition(dest, s4), base,
Operand::c32((start + i) * 16u));
dest = dest.advance(size * 16u);
i += size;
}
return count;
}
void
wait_for_smem_loads(Builder& bld)
{
if (bld.program->gfx_level >= GFX12) {
bld.sopp(aco_opcode::s_wait_kmcnt, 0);
} else {
wait_imm lgkm_imm;
lgkm_imm.lgkm = 0;
bld.sopp(aco_opcode::s_waitcnt, lgkm_imm.pack(bld.program->gfx_level));
}
}
void
wait_for_vmem_loads(Builder& bld)
{
if (bld.program->gfx_level >= GFX12) {
bld.sopp(aco_opcode::s_wait_loadcnt, 0);
} else {
wait_imm vm_imm;
vm_imm.vm = 0;
bld.sopp(aco_opcode::s_waitcnt, vm_imm.pack(bld.program->gfx_level));
}
}
Operand
calc_nontrivial_instance_id(Builder& bld, const struct ac_shader_args* args,
const struct aco_vs_prolog_info* pinfo, unsigned index,
Operand instance_id, Operand start_instance, PhysReg tmp_sgpr,
PhysReg tmp_vgpr0, PhysReg tmp_vgpr1)
{
bld.smem(aco_opcode::s_load_dwordx2, Definition(tmp_sgpr, s2),
get_arg_fixed(args, pinfo->inputs), Operand::c32(8u + index * 8u));
wait_for_smem_loads(bld);
Definition fetch_index_def(tmp_vgpr0, v1);
Operand fetch_index(tmp_vgpr0, v1);
Operand div_info(tmp_sgpr, s1);
if (bld.program->gfx_level >= GFX8 && bld.program->gfx_level < GFX11) {
/* use SDWA */
if (bld.program->gfx_level < GFX9) {
bld.vop1(aco_opcode::v_mov_b32, Definition(tmp_vgpr1, v1), div_info);
div_info = Operand(tmp_vgpr1, v1);
}
bld.vop2(aco_opcode::v_lshrrev_b32, fetch_index_def, div_info, instance_id);
Instruction* instr;
if (bld.program->gfx_level >= GFX9)
instr = bld.vop2_sdwa(aco_opcode::v_add_u32, fetch_index_def, div_info, fetch_index).instr;
else
instr = bld.vop2_sdwa(aco_opcode::v_add_co_u32, fetch_index_def, Definition(vcc, bld.lm),
div_info, fetch_index)
.instr;
instr->sdwa().sel[0] = SubdwordSel::ubyte1;
bld.vop3(aco_opcode::v_mul_hi_u32, fetch_index_def, Operand(tmp_sgpr.advance(4), s1),
fetch_index);
instr =
bld.vop2_sdwa(aco_opcode::v_lshrrev_b32, fetch_index_def, div_info, fetch_index).instr;
instr->sdwa().sel[0] = SubdwordSel::ubyte2;
} else {
Operand tmp_op(tmp_vgpr1, v1);
Definition tmp_def(tmp_vgpr1, v1);
bld.vop2(aco_opcode::v_lshrrev_b32, fetch_index_def, div_info, instance_id);
bld.vop3(aco_opcode::v_bfe_u32, tmp_def, div_info, Operand::c32(8u), Operand::c32(8u));
bld.vadd32(fetch_index_def, tmp_op, fetch_index, false, Operand(s2), true);
bld.vop3(aco_opcode::v_mul_hi_u32, fetch_index_def, fetch_index,
Operand(tmp_sgpr.advance(4), s1));
bld.vop3(aco_opcode::v_bfe_u32, tmp_def, div_info, Operand::c32(16u), Operand::c32(8u));
bld.vop2(aco_opcode::v_lshrrev_b32, fetch_index_def, tmp_op, fetch_index);
}
bld.vadd32(fetch_index_def, start_instance, fetch_index, false, Operand(s2), true);
return fetch_index;
}
PhysReg
get_next_vgpr(unsigned size, unsigned* num, int* offset = NULL)
{
unsigned reg = *num + (offset ? *offset : 0);
if (reg + size >= *num) {
*num = reg + size;
if (offset)
*offset = 0;
} else if (offset) {
*offset += size;
}
return PhysReg(256 + reg);
}
struct UnalignedVsAttribLoad {
/* dst/scratch are PhysReg converted to unsigned */
unsigned dst;
unsigned scratch;
bool d16;
const struct ac_vtx_format_info* vtx_info;
};
struct UnalignedVsAttribLoadState {
unsigned max_vgprs;
unsigned initial_num_vgprs;
unsigned* num_vgprs;
unsigned overflow_num_vgprs;
aco::small_vec<UnalignedVsAttribLoad, 16> current_loads;
};
void
convert_unaligned_vs_attrib(Builder& bld, UnalignedVsAttribLoad load)
{
PhysReg dst(load.dst);
PhysReg scratch(load.scratch);
const struct ac_vtx_format_info* vtx_info = load.vtx_info;
unsigned dfmt = vtx_info->hw_format[0] & 0xf;
unsigned nfmt = vtx_info->hw_format[0] >> 4;
unsigned size = vtx_info->chan_byte_size ? vtx_info->chan_byte_size : vtx_info->element_size;
if (load.d16) {
bld.vop3(aco_opcode::v_lshl_or_b32, Definition(dst, v1), Operand(scratch, v1),
Operand::c32(8), Operand(dst, v1));
} else {
for (unsigned i = 1; i < size; i++) {
PhysReg byte_reg = scratch.advance(i * 4 - 4);
if (bld.program->gfx_level >= GFX9) {
bld.vop3(aco_opcode::v_lshl_or_b32, Definition(dst, v1), Operand(byte_reg, v1),
Operand::c32(i * 8), Operand(dst, v1));
} else {
bld.vop2(aco_opcode::v_lshlrev_b32, Definition(byte_reg, v1), Operand::c32(i * 8),
Operand(byte_reg, v1));
bld.vop2(aco_opcode::v_or_b32, Definition(dst, v1), Operand(dst, v1),
Operand(byte_reg, v1));
}
}
}
unsigned num_channels = vtx_info->chan_byte_size ? 1 : vtx_info->num_channels;
PhysReg chan[4] = {dst, dst.advance(4), dst.advance(8), dst.advance(12)};
if (dfmt == V_008F0C_BUF_DATA_FORMAT_10_11_11) {
bld.vop3(aco_opcode::v_bfe_u32, Definition(chan[2], v1), Operand(dst, v1), Operand::c32(22),
Operand::c32(10));
bld.vop3(aco_opcode::v_bfe_u32, Definition(chan[1], v1), Operand(dst, v1), Operand::c32(11),
Operand::c32(11));
bld.vop3(aco_opcode::v_bfe_u32, Definition(chan[0], v1), Operand(dst, v1), Operand::c32(0),
Operand::c32(11));
bld.vop2(aco_opcode::v_lshlrev_b32, Definition(chan[2], v1), Operand::c32(5),
Operand(chan[2], v1));
bld.vop2(aco_opcode::v_lshlrev_b32, Definition(chan[1], v1), Operand::c32(4),
Operand(chan[1], v1));
bld.vop2(aco_opcode::v_lshlrev_b32, Definition(chan[0], v1), Operand::c32(4),
Operand(chan[0], v1));
} else if (dfmt == V_008F0C_BUF_DATA_FORMAT_2_10_10_10) {
aco_opcode bfe = aco_opcode::v_bfe_u32;
switch (nfmt) {
case V_008F0C_BUF_NUM_FORMAT_SNORM:
case V_008F0C_BUF_NUM_FORMAT_SSCALED:
case V_008F0C_BUF_NUM_FORMAT_SINT: bfe = aco_opcode::v_bfe_i32; break;
default: break;
}
bool swapxz = G_008F0C_DST_SEL_X(vtx_info->dst_sel) != V_008F0C_SQ_SEL_X;
bld.vop3(bfe, Definition(chan[3], v1), Operand(dst, v1), Operand::c32(30), Operand::c32(2));
bld.vop3(bfe, Definition(chan[2], v1), Operand(dst, v1), Operand::c32(swapxz ? 0 : 20),
Operand::c32(10));
bld.vop3(bfe, Definition(chan[1], v1), Operand(dst, v1), Operand::c32(10), Operand::c32(10));
bld.vop3(bfe, Definition(chan[0], v1), Operand(dst, v1), Operand::c32(swapxz ? 20 : 0),
Operand::c32(10));
} else if (dfmt == V_008F0C_BUF_DATA_FORMAT_8 || dfmt == V_008F0C_BUF_DATA_FORMAT_16) {
unsigned bits = dfmt == V_008F0C_BUF_DATA_FORMAT_8 ? 8 : 16;
switch (nfmt) {
case V_008F0C_BUF_NUM_FORMAT_SNORM:
case V_008F0C_BUF_NUM_FORMAT_SSCALED:
case V_008F0C_BUF_NUM_FORMAT_SINT:
bld.vop3(aco_opcode::v_bfe_i32, Definition(dst, v1), Operand(dst, v1), Operand::c32(0),
Operand::c32(bits));
break;
default: break;
}
}
if (nfmt == V_008F0C_BUF_NUM_FORMAT_FLOAT &&
(dfmt == V_008F0C_BUF_DATA_FORMAT_16 || dfmt == V_008F0C_BUF_DATA_FORMAT_10_11_11)) {
for (unsigned i = 0; i < num_channels; i++)
bld.vop1(aco_opcode::v_cvt_f32_f16, Definition(chan[i], v1), Operand(chan[i], v1));
} else if (nfmt == V_008F0C_BUF_NUM_FORMAT_USCALED || nfmt == V_008F0C_BUF_NUM_FORMAT_UNORM) {
for (unsigned i = 0; i < num_channels; i++)
bld.vop1(aco_opcode::v_cvt_f32_u32, Definition(chan[i], v1), Operand(chan[i], v1));
} else if (nfmt == V_008F0C_BUF_NUM_FORMAT_SSCALED || nfmt == V_008F0C_BUF_NUM_FORMAT_SNORM) {
for (unsigned i = 0; i < num_channels; i++)
bld.vop1(aco_opcode::v_cvt_f32_i32, Definition(chan[i], v1), Operand(chan[i], v1));
}
std::array<unsigned, 4> chan_max;
switch (dfmt) {
case V_008F0C_BUF_DATA_FORMAT_2_10_10_10: chan_max = {1023, 1023, 1023, 3}; break;
case V_008F0C_BUF_DATA_FORMAT_8: chan_max = {255, 255, 255, 255}; break;
case V_008F0C_BUF_DATA_FORMAT_16: chan_max = {65535, 65535, 65535, 65535}; break;
}
if (nfmt == V_008F0C_BUF_NUM_FORMAT_UNORM) {
for (unsigned i = 0; i < num_channels; i++)
bld.vop2(aco_opcode::v_mul_f32, Definition(chan[i], v1),
Operand::c32(fui(1.0 / chan_max[i])), Operand(chan[i], v1));
} else if (nfmt == V_008F0C_BUF_NUM_FORMAT_SNORM) {
for (unsigned i = 0; i < num_channels; i++) {
bld.vop2(aco_opcode::v_mul_f32, Definition(chan[i], v1),
Operand::c32(fui(1.0 / (chan_max[i] >> 1))), Operand(chan[i], v1));
bld.vop2(aco_opcode::v_max_f32, Definition(chan[i], v1), Operand::c32(0xbf800000),
Operand(chan[i], v1));
}
}
}
void
convert_current_unaligned_vs_attribs(Builder& bld, UnalignedVsAttribLoadState* state)
{
if (state->current_loads.empty())
return;
wait_for_vmem_loads(bld);
for (UnalignedVsAttribLoad load : state->current_loads)
convert_unaligned_vs_attrib(bld, load);
state->current_loads.clear();
state->overflow_num_vgprs = state->initial_num_vgprs;
state->num_vgprs = &state->overflow_num_vgprs;
}
void
load_unaligned_vs_attrib(Builder& bld, PhysReg dst, Operand desc, Operand index, uint32_t offset,
const struct ac_vtx_format_info* vtx_info,
UnalignedVsAttribLoadState* state)
{
unsigned size = vtx_info->chan_byte_size ? vtx_info->chan_byte_size : vtx_info->element_size;
UnalignedVsAttribLoad load;
load.dst = dst;
load.vtx_info = vtx_info;
load.d16 = bld.program->gfx_level >= GFX9 && !bld.program->dev.sram_ecc_enabled && size == 4;
unsigned num_scratch_vgprs = load.d16 ? 1 : (size - 1);
if (!vtx_info->chan_byte_size) {
/* When chan_byte_size==0, we're loading the entire attribute, so we can use the last 3
* components of the destination.
*/
assert(num_scratch_vgprs <= 3);
load.scratch = dst.advance(4);
} else {
if (*state->num_vgprs + num_scratch_vgprs > state->max_vgprs)
convert_current_unaligned_vs_attribs(bld, state);
load.scratch = get_next_vgpr(num_scratch_vgprs, state->num_vgprs, NULL);
}
PhysReg scratch(load.scratch);
if (load.d16) {
bld.mubuf(aco_opcode::buffer_load_ubyte_d16, Definition(dst, v1), desc, index,
Operand::c32(0u), offset, false, true);
bld.mubuf(aco_opcode::buffer_load_ubyte_d16_hi, Definition(dst, v1), desc, index,
Operand::c32(0u), offset + 2, false, true);
bld.mubuf(aco_opcode::buffer_load_ubyte_d16, Definition(scratch, v1), desc, index,
Operand::c32(0u), offset + 1, false, true);
bld.mubuf(aco_opcode::buffer_load_ubyte_d16_hi, Definition(scratch, v1), desc, index,
Operand::c32(0u), offset + 3, false, true);
} else {
for (unsigned i = 0; i < size; i++) {
Definition def(i ? scratch.advance(i * 4 - 4) : dst, v1);
unsigned soffset = 0, const_offset = 0;
if (bld.program->gfx_level >= GFX12) {
const_offset = offset + i;
} else {
soffset = offset + i;
}
bld.mubuf(aco_opcode::buffer_load_ubyte, def, desc, index, Operand::c32(soffset),
const_offset, false, true);
}
}
state->current_loads.push_back(load);
}
} // namespace
void
select_vs_prolog(Program* program, const struct aco_vs_prolog_info* pinfo, ac_shader_config* config,
const struct aco_compiler_options* options, const struct aco_shader_info* info,
const struct ac_shader_args* args)
{
assert(pinfo->num_attributes > 0);
/* This should be enough for any shader/stage. */
unsigned max_user_sgprs = options->gfx_level >= GFX9 ? 32 : 16;
init_program(program, compute_cs, info, options->gfx_level, options->family, options->wgp_mode,
config);
program->dev.vgpr_limit = 256;
Block* block = program->create_and_insert_block();
block->kind = block_kind_top_level;
program->workgroup_size = 64;
calc_min_waves(program);
/* Addition on GFX6-8 requires a carry-out (we use VCC) */
program->needs_vcc = program->gfx_level <= GFX8;
Builder bld(program, block);
block->instructions.reserve(16 + pinfo->num_attributes * 4);
/* Besides performance, the purpose of this is also for the FeatureRequiredExportPriority GFX11.5
* issue. */
bld.sopp(aco_opcode::s_setprio, 3);
uint32_t attrib_mask = BITFIELD_MASK(pinfo->num_attributes);
bool has_nontrivial_divisors = pinfo->nontrivial_divisors;
/* choose sgprs */
PhysReg vertex_buffers(align(max_user_sgprs + 14, 2));
PhysReg prolog_input = vertex_buffers.advance(8);
PhysReg desc(
align((has_nontrivial_divisors ? prolog_input : vertex_buffers).advance(8).reg(), 4));
Operand start_instance = get_arg_fixed(args, args->start_instance);
Operand instance_id = get_arg_fixed(args, args->instance_id);
bool needs_instance_index =
pinfo->instance_rate_inputs &
~(pinfo->zero_divisors | pinfo->nontrivial_divisors); /* divisor is 1 */
bool needs_start_instance = pinfo->instance_rate_inputs & pinfo->zero_divisors;
bool needs_vertex_index = ~pinfo->instance_rate_inputs & attrib_mask;
bool needs_tmp_vgpr0 = has_nontrivial_divisors;
bool needs_tmp_vgpr1 =
has_nontrivial_divisors && (program->gfx_level <= GFX8 || program->gfx_level >= GFX11);
int vgpr_offset = pinfo->misaligned_mask & (1u << (pinfo->num_attributes - 1)) ? 0 : -4;
unsigned num_vgprs = args->num_vgprs_used;
PhysReg attributes_start = get_next_vgpr(pinfo->num_attributes * 4, &num_vgprs);
PhysReg vertex_index, instance_index, start_instance_vgpr, nontrivial_tmp_vgpr0,
nontrivial_tmp_vgpr1;
if (needs_vertex_index)
vertex_index = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
if (needs_instance_index)
instance_index = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
if (needs_start_instance)
start_instance_vgpr = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
if (needs_tmp_vgpr0)
nontrivial_tmp_vgpr0 = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
if (needs_tmp_vgpr1)
nontrivial_tmp_vgpr1 = get_next_vgpr(1, &num_vgprs, &vgpr_offset);
bld.sop1(aco_opcode::s_mov_b32, Definition(vertex_buffers, s1),
get_arg_fixed(args, args->vertex_buffers));
if (options->address32_hi >= 0xffff8000 || options->address32_hi <= 0x7fff) {
bld.sopk(aco_opcode::s_movk_i32, Definition(vertex_buffers.advance(4), s1),
options->address32_hi & 0xFFFF);
} else {
bld.sop1(aco_opcode::s_mov_b32, Definition(vertex_buffers.advance(4), s1),
Operand::c32((unsigned)options->address32_hi));
}
const struct ac_vtx_format_info* vtx_info_table =
ac_get_vtx_format_info_table(GFX8, CHIP_POLARIS10);
UnalignedVsAttribLoadState unaligned_state;
unaligned_state.max_vgprs = MAX2(84, num_vgprs + 8);
unaligned_state.initial_num_vgprs = num_vgprs;
unaligned_state.num_vgprs = &num_vgprs;
unsigned num_sgprs = 0;
for (unsigned loc = 0; loc < pinfo->num_attributes;) {
unsigned num_descs =
load_vb_descs(bld, desc, Operand(vertex_buffers, s2), loc, pinfo->num_attributes - loc);
num_sgprs = MAX2(num_sgprs, desc.advance(num_descs * 16u).reg());
if (loc == 0) {
/* perform setup while we load the descriptors */
if (pinfo->is_ngg || pinfo->next_stage != MESA_SHADER_VERTEX) {
Operand count = get_arg_fixed(args, args->merged_wave_info);
bld.sop2(aco_opcode::s_bfm_b64, Definition(exec, s2), count, Operand::c32(0u));
if (program->wave_size == 64) {
bld.sopc(aco_opcode::s_bitcmp1_b32, Definition(scc, s1), count,
Operand::c32(6u /* log2(64) */));
bld.sop2(aco_opcode::s_cselect_b64, Definition(exec, s2), Operand::c64(UINT64_MAX),
Operand(exec, s2), Operand(scc, s1));
}
}
/* If there are no HS threads, SPI mistakenly loads the LS VGPRs starting at VGPR 0. */
if (info->hw_stage == AC_HW_HULL_SHADER && options->has_ls_vgpr_init_bug) {
/* We don't want load_vb_descs() to write vcc. */
assert(program->dev.sgpr_limit <= vcc.reg());
bld.sop2(aco_opcode::s_bfe_u32, Definition(vcc, s1), Definition(scc, s1),
get_arg_fixed(args, args->merged_wave_info), Operand::c32((8u << 16) | 8u));
bld.sop2(Builder::s_cselect, Definition(vcc, bld.lm), Operand::c32(-1), Operand::zero(),
Operand(scc, s1));
/* These copies are ordered so that vertex_id=tcs_patch_id doesn't overwrite vertex_id
* before instance_id=vertex_id. */
ac_arg src_args[] = {args->vertex_id, args->tcs_rel_ids, args->tcs_patch_id};
ac_arg dst_args[] = {args->instance_id, args->vs_rel_patch_id, args->vertex_id};
for (unsigned i = 0; i < 3; i++) {
bld.vop2(aco_opcode::v_cndmask_b32, Definition(get_arg_reg(args, dst_args[i]), v1),
get_arg_fixed(args, src_args[i]), get_arg_fixed(args, dst_args[i]),
Operand(vcc, bld.lm));
}
}
if (needs_vertex_index)
bld.vadd32(Definition(vertex_index, v1), get_arg_fixed(args, args->base_vertex),
get_arg_fixed(args, args->vertex_id), false, Operand(s2), true);
if (needs_instance_index)
bld.vadd32(Definition(instance_index, v1), start_instance, instance_id, false,
Operand(s2), true);
if (needs_start_instance)
bld.vop1(aco_opcode::v_mov_b32, Definition(start_instance_vgpr, v1), start_instance);
}
wait_for_smem_loads(bld);
for (unsigned i = 0; i < num_descs;) {
PhysReg dest(attributes_start.reg() + loc * 4u);
/* calculate index */
Operand fetch_index = Operand(vertex_index, v1);
if (pinfo->instance_rate_inputs & (1u << loc)) {
if (!(pinfo->zero_divisors & (1u << loc))) {
fetch_index = instance_id;
if (pinfo->nontrivial_divisors & (1u << loc)) {
unsigned index = util_bitcount(pinfo->nontrivial_divisors & BITFIELD_MASK(loc));
fetch_index = calc_nontrivial_instance_id(
bld, args, pinfo, index, instance_id, start_instance, prolog_input,
nontrivial_tmp_vgpr0, nontrivial_tmp_vgpr1);
} else {
fetch_index = Operand(instance_index, v1);
}
} else {
fetch_index = Operand(start_instance_vgpr, v1);
}
}
/* perform load */
PhysReg cur_desc = desc.advance(i * 16);
if ((pinfo->misaligned_mask & (1u << loc))) {
const struct ac_vtx_format_info* vtx_info = &vtx_info_table[pinfo->formats[loc]];
assert(vtx_info->has_hw_format & 0x1);
unsigned dfmt = vtx_info->hw_format[0] & 0xf;
unsigned nfmt = vtx_info->hw_format[0] >> 4;
for (unsigned j = 0; j < (vtx_info->chan_byte_size ? vtx_info->num_channels : 1); j++) {
bool post_shuffle = pinfo->post_shuffle & (1u << loc);
unsigned offset = vtx_info->chan_byte_size * (post_shuffle && j < 3 ? 2 - j : j);
unsigned soffset = 0, const_offset = 0;
/* We need to use soffset on GFX6-7 to avoid being considered
* out-of-bounds when offset>=stride. GFX12 doesn't support a
* non-zero constant soffset.
*/
if (program->gfx_level >= GFX12) {
const_offset = offset;
} else {
soffset = offset;
}
if ((pinfo->unaligned_mask & (1u << loc)) && vtx_info->chan_byte_size <= 4)
load_unaligned_vs_attrib(bld, dest.advance(j * 4u), Operand(cur_desc, s4),
fetch_index, offset, vtx_info, &unaligned_state);
else if (vtx_info->chan_byte_size == 8)
bld.mtbuf(aco_opcode::tbuffer_load_format_xy,
Definition(dest.advance(j * 8u), v2), Operand(cur_desc, s4),
fetch_index, Operand::c32(soffset), dfmt, nfmt, const_offset, false,
true);
else
bld.mtbuf(aco_opcode::tbuffer_load_format_x, Definition(dest.advance(j * 4u), v1),
Operand(cur_desc, s4), fetch_index, Operand::c32(soffset), dfmt, nfmt,
const_offset, false, true);
}
unsigned slots = vtx_info->chan_byte_size == 8 && vtx_info->num_channels > 2 ? 2 : 1;
loc += slots;
i += slots;
} else {
bld.mubuf(aco_opcode::buffer_load_format_xyzw, Definition(dest, v4),
Operand(cur_desc, s4), fetch_index, Operand::c32(0u), 0u, false, true);
loc++;
i++;
}
}
}
uint32_t constant_mask = pinfo->misaligned_mask;
while (constant_mask) {
unsigned loc = u_bit_scan(&constant_mask);
const struct ac_vtx_format_info* vtx_info = &vtx_info_table[pinfo->formats[loc]];
/* 22.1.1. Attribute Location and Component Assignment of Vulkan 1.3 specification:
* For 64-bit data types, no default attribute values are provided. Input variables must
* not use more components than provided by the attribute.
*/
if (vtx_info->chan_byte_size == 8) {
if (vtx_info->num_channels > 2)
u_bit_scan(&constant_mask);
continue;
}
assert(vtx_info->has_hw_format & 0x1);
unsigned nfmt = vtx_info->hw_format[0] >> 4;
uint32_t one = nfmt == V_008F0C_BUF_NUM_FORMAT_UINT || nfmt == V_008F0C_BUF_NUM_FORMAT_SINT
? 1u
: 0x3f800000u;
PhysReg dest(attributes_start.reg() + loc * 4u);
for (unsigned j = vtx_info->num_channels; j < 4; j++) {
bld.vop1(aco_opcode::v_mov_b32, Definition(dest.advance(j * 4u), v1),
Operand::c32(j == 3 ? one : 0u));
}
}
convert_current_unaligned_vs_attribs(bld, &unaligned_state);
if (pinfo->alpha_adjust_lo | pinfo->alpha_adjust_hi)
wait_for_vmem_loads(bld);
/* For 2_10_10_10 formats the alpha is handled as unsigned by pre-vega HW.
* so we may need to fix it up. */
u_foreach_bit (loc, (pinfo->alpha_adjust_lo | pinfo->alpha_adjust_hi)) {
PhysReg alpha(attributes_start.reg() + loc * 4u + 3);
unsigned alpha_adjust = (pinfo->alpha_adjust_lo >> loc) & 0x1;
alpha_adjust |= ((pinfo->alpha_adjust_hi >> loc) & 0x1) << 1;
if (alpha_adjust == AC_ALPHA_ADJUST_SSCALED)
bld.vop1(aco_opcode::v_cvt_u32_f32, Definition(alpha, v1), Operand(alpha, v1));
/* For the integer-like cases, do a natural sign extension.
*
* For the SNORM case, the values are 0.0, 0.333, 0.666, 1.0
* and happen to contain 0, 1, 2, 3 as the two LSBs of the
* exponent.
*/
unsigned offset = alpha_adjust == AC_ALPHA_ADJUST_SNORM ? 23u : 0u;
bld.vop3(aco_opcode::v_bfe_i32, Definition(alpha, v1), Operand(alpha, v1),
Operand::c32(offset), Operand::c32(2u));
/* Convert back to the right type. */
if (alpha_adjust == AC_ALPHA_ADJUST_SNORM) {
bld.vop1(aco_opcode::v_cvt_f32_i32, Definition(alpha, v1), Operand(alpha, v1));
bld.vop2(aco_opcode::v_max_f32, Definition(alpha, v1), Operand::c32(0xbf800000u),
Operand(alpha, v1));
} else if (alpha_adjust == AC_ALPHA_ADJUST_SSCALED) {
bld.vop1(aco_opcode::v_cvt_f32_i32, Definition(alpha, v1), Operand(alpha, v1));
}
}
block->kind |= block_kind_uniform;
/* continue on to the main shader */
Operand continue_pc = get_arg_fixed(args, pinfo->inputs);
if (has_nontrivial_divisors) {
bld.smem(aco_opcode::s_load_dwordx2, Definition(prolog_input, s2),
get_arg_fixed(args, pinfo->inputs), Operand::c32(0u));
wait_for_smem_loads(bld);
continue_pc = Operand(prolog_input, s2);
}
bld.sop1(aco_opcode::s_setpc_b64, continue_pc);
program->config->float_mode = program->blocks[0].fp_mode.val;
program->config->num_vgprs = std::min<uint16_t>(get_vgpr_alloc(program, num_vgprs), 256);
program->config->num_sgprs = get_sgpr_alloc(program, num_sgprs);
}
} // namespace aco
+1
View File
@@ -38,6 +38,7 @@ libaco_files = files(
'instruction_selection/aco_isel_setup.cpp',
'instruction_selection/aco_select_rt_prolog.cpp',
'instruction_selection/aco_select_trap_handler.cpp',
'instruction_selection/aco_select_vs_prolog.cpp',
'aco_dead_code_analysis.cpp',
'aco_dominance.cpp',
'aco_interface.cpp',