Path: blob/21.2-virgl/src/gallium/auxiliary/tgsi/tgsi_dynamic_indexing.c
4565 views
/*1* Copyright 2018 VMware, Inc.2* All Rights Reserved.3*4* Permission is hereby granted, free of charge, to any person obtaining a5* copy of this software and associated documentation files (the6* "Software"), to deal in the Software without restriction, including7* without limitation the rights to use, copy, modify, merge, publish,8* distribute, sub license, and/or sell copies of the Software, and to9* permit persons to whom the Software is furnished to do so, subject to10* the following conditions:11*12* The above copyright notice and this permission notice (including the13* next paragraph) shall be included in all copies or substantial portions14* of the Software.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS17* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.19* IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR20* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,21* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE22* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.23*/242526/**27* This utility transforms the shader to support dynamic array indexing28* for samplers and constant buffers.29* It calculates dynamic array index first and then compare it with each30* index and operation will be performed with matching index31*/3233#include "util/u_debug.h"34#include "util/u_math.h"35#include "tgsi_info.h"36#include "tgsi_dynamic_indexing.h"37#include "tgsi_transform.h"38#include "tgsi_dump.h"39#include "pipe/p_state.h"404142struct dIndexing_transform_context43{44struct tgsi_transform_context base;45unsigned orig_num_tmp;46unsigned orig_num_imm;47unsigned num_const_bufs;48unsigned num_samplers;49unsigned num_iterations;50unsigned const_buf_range[PIPE_MAX_CONSTANT_BUFFERS];51};525354static inline struct dIndexing_transform_context *55dIndexing_transform_context(struct tgsi_transform_context *ctx)56{57return (struct dIndexing_transform_context *) ctx;58}596061/**62* TGSI declaration transform callback.63*/64static void65dIndexing_decl(struct tgsi_transform_context *ctx,66struct tgsi_full_declaration *decl)67{68struct dIndexing_transform_context *dc = dIndexing_transform_context(ctx);6970if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {71/**72* Emit some extra temporary register to use in keeping track of73* dynamic index.74*/75dc->orig_num_tmp = decl->Range.Last;76decl->Range.Last = decl->Range.Last + 3;77}78else if (decl->Declaration.File == TGSI_FILE_CONSTANT) {79/* Keep track of number of constants in each buffer */80dc->const_buf_range[decl->Dim.Index2D] = decl->Range.Last;81}82ctx->emit_declaration(ctx, decl);83}848586/**87* TGSI transform prolog callback.88*/89static void90dIndexing_prolog(struct tgsi_transform_context *ctx)91{92tgsi_transform_immediate_int_decl(ctx, 0, 1, 2, 3);93tgsi_transform_immediate_int_decl(ctx, 4, 5, 6, 7);94}959697/**98* This function emits some extra instruction to remove dynamic array99* indexing of constant buffers / samplers from the shader.100* It calculates dynamic array index first and compare it with each index for101* declared constants/samplers.102*/103static void104remove_dynamic_indexes(struct tgsi_transform_context *ctx,105struct tgsi_full_instruction *orig_inst,106const struct tgsi_full_src_register *reg)107{108struct dIndexing_transform_context *dc = dIndexing_transform_context(ctx);109int i, j;110int tmp_loopIdx = dc->orig_num_tmp + 1;111int tmp_cond = dc->orig_num_tmp + 2;112int tmp_arrayIdx = dc->orig_num_tmp + 3;113int imm_index = dc->orig_num_imm;114struct tgsi_full_instruction inst;115unsigned INVALID_INDEX = 99999;116unsigned file = TGSI_FILE_NULL, index = INVALID_INDEX;117unsigned imm_swz_index = INVALID_INDEX;118119/* calculate dynamic array index store it in tmp_arrayIdx.x */120inst = tgsi_default_full_instruction();121inst.Instruction.Opcode = TGSI_OPCODE_UADD;122inst.Instruction.NumDstRegs = 1;123tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,124tmp_arrayIdx, TGSI_WRITEMASK_X);125inst.Instruction.NumSrcRegs = 2;126if (reg->Register.File == TGSI_FILE_CONSTANT) {127file = reg->DimIndirect.File;128index = reg->DimIndirect.Index;129imm_swz_index = reg->Dimension.Index;130}131else if (reg->Register.File == TGSI_FILE_SAMPLER) {132file = reg->Indirect.File;133index = reg->Indirect.Index;134imm_swz_index = reg->Register.Index;135}136tgsi_transform_src_reg(&inst.Src[0], file,137index, TGSI_SWIZZLE_X,138TGSI_SWIZZLE_X, TGSI_SWIZZLE_X, TGSI_SWIZZLE_X);139tgsi_transform_src_reg(&inst.Src[1], TGSI_FILE_IMMEDIATE,140imm_index + (imm_swz_index / 4),141imm_swz_index % 4,142imm_swz_index % 4,143imm_swz_index % 4,144imm_swz_index % 4);145ctx->emit_instruction(ctx, &inst);146147/* initialize counter to zero: tmp_loopIdx = 0 */148inst = tgsi_default_full_instruction();149inst.Instruction.Opcode = TGSI_OPCODE_MOV;150inst.Instruction.NumDstRegs = 1;151tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,152tmp_loopIdx, TGSI_WRITEMASK_X);153inst.Instruction.NumSrcRegs = 1;154tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_IMMEDIATE,155imm_index, TGSI_SWIZZLE_X,156TGSI_SWIZZLE_X, TGSI_SWIZZLE_X,157TGSI_SWIZZLE_X);158ctx->emit_instruction(ctx, &inst);159160for (i = 0; i < dc->num_iterations; i++) {161boolean out_of_bound_index = FALSE;162/**163* Make sure we are not exceeding index limit of constant buffer164*165* For example, In declaration, We have166*167* DCL CONST[0][0..1]168* DCL CONST[1][0..2]169* DCL CONST[2][0]170*171* and our dynamic index instruction is172* MOV TEMP[0], CONST[ADDR[0].x][1]173*174* We have to make sure to skip unrolling for CONST[2] because175* it has only one constant in the buffer176*/177if ((reg->Register.File == TGSI_FILE_CONSTANT) &&178(!reg->Register.Indirect &&179(reg->Register.Index > dc->const_buf_range[i]))) {180out_of_bound_index = TRUE;181}182183if (!out_of_bound_index) {184/**185* If we have an instruction of the format:186* OPCODE dst, src..., CONST[K][foo], src...187* where K is dynamic and tmp_loopIdx = i (loopcount),188* replace it with:189*190* if (K == tmp_loopIdx)191* OPCODE dst, src... where src is CONST[i][foo] and i is constant192* }193*194* Similarly, If instruction uses dynamic array index for samplers195* e.g. OPCODE dst, src, SAMPL[k] ..196* replace it with:197* if (K == tmp_loopIdx)198* OPCODE dst, src, SAMPL[i][foo]... where i is constant.199* }200*/201inst = tgsi_default_full_instruction();202inst.Instruction.Opcode = TGSI_OPCODE_USEQ;203inst.Instruction.NumDstRegs = 1;204tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,205tmp_cond, TGSI_WRITEMASK_X);206inst.Instruction.NumSrcRegs = 2;207tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_TEMPORARY,208tmp_arrayIdx, TGSI_SWIZZLE_X,209TGSI_SWIZZLE_X, TGSI_SWIZZLE_X,210TGSI_SWIZZLE_X);211tgsi_transform_src_reg(&inst.Src[1], TGSI_FILE_TEMPORARY,212tmp_loopIdx, TGSI_SWIZZLE_X,213TGSI_SWIZZLE_X, TGSI_SWIZZLE_X,214TGSI_SWIZZLE_X);215ctx->emit_instruction(ctx, &inst);216217inst = tgsi_default_full_instruction();218inst.Instruction.Opcode = TGSI_OPCODE_UIF;219inst.Instruction.NumDstRegs = 0;220inst.Instruction.NumSrcRegs = 1;221tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_TEMPORARY,222tmp_cond, TGSI_SWIZZLE_X,223TGSI_SWIZZLE_X, TGSI_SWIZZLE_X,224TGSI_SWIZZLE_X);225ctx->emit_instruction(ctx, &inst);226227/* emit instruction with new, non-dynamic source registers */228inst = *orig_inst;229for (j = 0; j < inst.Instruction.NumSrcRegs; j++) {230if (inst.Src[j].Dimension.Indirect &&231inst.Src[j].Register.File == TGSI_FILE_CONSTANT) {232inst.Src[j].Register.Dimension = 1;233inst.Src[j].Dimension.Index = i;234inst.Src[j].Dimension.Indirect = 0;235}236else if (inst.Src[j].Register.Indirect &&237inst.Src[j].Register.File == TGSI_FILE_SAMPLER) {238inst.Src[j].Register.Indirect = 0;239inst.Src[j].Register.Index = i;240}241}242ctx->emit_instruction(ctx, &inst);243244inst = tgsi_default_full_instruction();245inst.Instruction.Opcode = TGSI_OPCODE_ENDIF;246inst.Instruction.NumDstRegs = 0;247inst.Instruction.NumSrcRegs = 0;248ctx->emit_instruction(ctx, &inst);249}250251/**252* Increment counter253* UADD tmp_loopIdx.x tmp_loopIdx.x imm(1)254*/255inst = tgsi_default_full_instruction();256inst.Instruction.Opcode = TGSI_OPCODE_UADD;257inst.Instruction.NumDstRegs = 1;258tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,259tmp_loopIdx, TGSI_WRITEMASK_X);260inst.Instruction.NumSrcRegs = 2;261tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_TEMPORARY,262tmp_loopIdx, TGSI_SWIZZLE_X,263TGSI_SWIZZLE_X, TGSI_SWIZZLE_X, TGSI_SWIZZLE_X);264tgsi_transform_src_reg(&inst.Src[1], TGSI_FILE_IMMEDIATE, imm_index,265TGSI_SWIZZLE_Y, TGSI_SWIZZLE_Y,266TGSI_SWIZZLE_Y, TGSI_SWIZZLE_Y);267268ctx->emit_instruction(ctx, &inst);269}270}271272273/**274* TGSI instruction transform callback.275*/276static void277dIndexing_inst(struct tgsi_transform_context *ctx,278struct tgsi_full_instruction *inst)279{280int i;281boolean indexing = FALSE;282struct dIndexing_transform_context *dc = dIndexing_transform_context(ctx);283284for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {285struct tgsi_full_src_register *src;286src = &inst->Src[i];287/* check if constant buffer/sampler is using dynamic index */288if ((src->Dimension.Indirect &&289src->Register.File == TGSI_FILE_CONSTANT) ||290(src->Register.Indirect &&291src->Register.File == TGSI_FILE_SAMPLER)) {292293if (indexing)294assert("More than one src has dynamic indexing");295296if (src->Register.File == TGSI_FILE_CONSTANT)297dc->num_iterations = dc->num_const_bufs;298else299dc->num_iterations = dc->num_samplers;300301remove_dynamic_indexes(ctx, inst, src);302indexing = TRUE;303}304}305306if (!indexing) {307ctx->emit_instruction(ctx, inst);308}309}310311/**312* TGSI utility to remove dynamic array indexing for constant buffers and313* samplers.314*315* This utility accepts bitmask of declared constant buffers and samplers,316* number of immediates used in shader.317*318* If dynamic array index is used for constant buffers and samplers, this319* utility removes those dynamic indexes from shader. It also makes sure320* that it has same output as per original shader.321* This is achieved by calculating dynamic array index first and then compare322* it with each constant buffer/ sampler index and replace that dynamic index323* with static index.324*/325struct tgsi_token *326tgsi_remove_dynamic_indexing(const struct tgsi_token *tokens_in,327unsigned const_buffers_declared_bitmask,328unsigned samplers_declared_bitmask,329unsigned imm_count)330{331struct dIndexing_transform_context transform;332const uint num_new_tokens = 1000; /* should be enough */333const uint new_len = tgsi_num_tokens(tokens_in) + num_new_tokens;334struct tgsi_token *new_tokens;335336/* setup transformation context */337memset(&transform, 0, sizeof(transform));338transform.base.transform_declaration = dIndexing_decl;339transform.base.transform_instruction = dIndexing_inst;340transform.base.prolog = dIndexing_prolog;341342transform.orig_num_tmp = 0;343transform.orig_num_imm = imm_count;344/* get count of declared const buffers and sampler from their bitmasks*/345transform.num_const_bufs = log2(const_buffers_declared_bitmask + 1);346transform.num_samplers = log2(samplers_declared_bitmask + 1);347transform.num_iterations = 0;348349/* allocate new tokens buffer */350new_tokens = tgsi_alloc_tokens(new_len);351if (!new_tokens)352return NULL;353354/* transform the shader */355tgsi_transform_shader(tokens_in, new_tokens, new_len, &transform.base);356357return new_tokens;358}359360361362363