Path: blob/21.2-virgl/src/compiler/glsl/gl_nir_lower_buffers.c
4545 views
/*1* Copyright © 2019 Intel Corporation2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER20* DEALINGS IN THE SOFTWARE.21*/2223#include "compiler/nir/nir.h"24#include "compiler/nir/nir_builder.h"25#include "gl_nir.h"26#include "ir_uniform.h"2728#include "util/compiler.h"29#include "main/mtypes.h"3031static nir_ssa_def *32get_block_array_index(nir_builder *b, nir_deref_instr *deref,33const struct gl_shader_program *shader_program)34{35unsigned array_elements = 1;3637/* Build a block name such as "block[2][0]" for finding in the list of38* blocks later on as well as an optional dynamic index which gets added39* to the block index later.40*/41int binding = 0;42const char *block_name = "";43nir_ssa_def *nonconst_index = NULL;44while (deref->deref_type == nir_deref_type_array) {45nir_deref_instr *parent = nir_deref_instr_parent(deref);46assert(parent && glsl_type_is_array(parent->type));47unsigned arr_size = glsl_get_length(parent->type);4849if (nir_src_is_const(deref->arr.index)) {50unsigned arr_index = nir_src_as_uint(deref->arr.index);5152/* We're walking the deref from the tail so prepend the array index */53block_name = ralloc_asprintf(b->shader, "[%u]%s", arr_index,54block_name);5556binding += arr_index * array_elements;57} else {58nir_ssa_def *arr_index = nir_ssa_for_src(b, deref->arr.index, 1);59arr_index = nir_umin(b, arr_index, nir_imm_int(b, arr_size - 1));60nir_ssa_def *arr_offset = nir_amul_imm(b, arr_index, array_elements);61if (nonconst_index)62nonconst_index = nir_iadd(b, nonconst_index, arr_offset);63else64nonconst_index = arr_offset;6566/* We're walking the deref from the tail so prepend the array index */67block_name = ralloc_asprintf(b->shader, "[0]%s", block_name);68}6970array_elements *= arr_size;71deref = parent;72}7374assert(deref->deref_type == nir_deref_type_var);75binding += deref->var->data.binding;76block_name = ralloc_asprintf(b->shader, "%s%s",77glsl_get_type_name(deref->var->interface_type),78block_name);7980struct gl_linked_shader *linked_shader =81shader_program->_LinkedShaders[b->shader->info.stage];8283unsigned num_blocks;84struct gl_uniform_block **blocks;85if (nir_deref_mode_is(deref, nir_var_mem_ubo)) {86num_blocks = linked_shader->Program->info.num_ubos;87blocks = linked_shader->Program->sh.UniformBlocks;88} else {89assert(nir_deref_mode_is(deref, nir_var_mem_ssbo));90num_blocks = linked_shader->Program->info.num_ssbos;91blocks = linked_shader->Program->sh.ShaderStorageBlocks;92}9394/* Block names are optional with ARB_gl_spirv so use the binding instead. */95bool use_bindings = shader_program->data->spirv;9697for (unsigned i = 0; i < num_blocks; i++) {98if (( use_bindings && binding == blocks[i]->Binding) ||99(!use_bindings && strcmp(block_name, blocks[i]->Name) == 0)) {100if (nonconst_index)101return nir_iadd_imm(b, nonconst_index, i);102else103return nir_imm_int(b, i);104}105}106107/* TODO: Investigate if we could change the code to assign Bindings to the108* blocks that were not explicitly assigned, so we can always compare109* bindings.110*/111112if (use_bindings)113unreachable("Failed to find the block by binding");114else115unreachable("Failed to find the block by name");116}117118static void119get_block_index_offset(nir_variable *var,120const struct gl_shader_program *shader_program,121gl_shader_stage stage,122unsigned *index, unsigned *offset)123{124125struct gl_linked_shader *linked_shader =126shader_program->_LinkedShaders[stage];127128unsigned num_blocks;129struct gl_uniform_block **blocks;130if (var->data.mode == nir_var_mem_ubo) {131num_blocks = linked_shader->Program->info.num_ubos;132blocks = linked_shader->Program->sh.UniformBlocks;133} else {134assert(var->data.mode == nir_var_mem_ssbo);135num_blocks = linked_shader->Program->info.num_ssbos;136blocks = linked_shader->Program->sh.ShaderStorageBlocks;137}138139/* Block names are optional with ARB_gl_spirv so use the binding instead. */140bool use_bindings = shader_program->data->spirv;141142for (unsigned i = 0; i < num_blocks; i++) {143const char *block_name = glsl_get_type_name(var->interface_type);144if (( use_bindings && blocks[i]->Binding == var->data.binding) ||145(!use_bindings && strcmp(block_name, blocks[i]->Name) == 0)) {146*index = i;147*offset = blocks[i]->Uniforms[var->data.location].Offset;148return;149}150}151152if (use_bindings)153unreachable("Failed to find the block by binding");154else155unreachable("Failed to find the block by name");156}157158static bool159lower_buffer_interface_derefs_impl(nir_function_impl *impl,160const struct gl_shader_program *shader_program)161{162bool progress = false;163164nir_builder b;165nir_builder_init(&b, impl);166167/* this must be a separate loop before the main pass in order to ensure that168* access info is fully propagated prior to the info being lost during rewrites169*/170nir_foreach_block(block, impl) {171nir_foreach_instr(instr, block) {172if (instr->type != nir_instr_type_intrinsic)173continue;174175nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);176if (intrin->intrinsic == nir_intrinsic_load_deref ||177intrin->intrinsic == nir_intrinsic_store_deref) {178nir_variable *var = nir_intrinsic_get_var(intrin, 0);179assert(var);180nir_intrinsic_set_access(intrin, nir_intrinsic_access(intrin) | var->data.access);181}182}183}184185nir_foreach_block(block, impl) {186nir_foreach_instr_safe(instr, block) {187switch (instr->type) {188case nir_instr_type_deref: {189nir_deref_instr *deref = nir_instr_as_deref(instr);190if (!nir_deref_mode_is_one_of(deref, nir_var_mem_ubo |191nir_var_mem_ssbo))192break;193194/* We use nir_address_format_32bit_index_offset */195assert(deref->dest.is_ssa);196assert(deref->dest.ssa.bit_size == 32);197deref->dest.ssa.num_components = 2;198199progress = true;200201b.cursor = nir_before_instr(&deref->instr);202203unsigned offset = 0;204nir_ssa_def *ptr;205if (deref->deref_type == nir_deref_type_var &&206!glsl_type_is_interface(glsl_without_array(deref->var->type))) {207/* This variable is contained in an interface block rather than208* containing one. We need the block index and its offset209* inside that block210*/211unsigned index;212get_block_index_offset(deref->var, shader_program,213b.shader->info.stage,214&index, &offset);215ptr = nir_imm_ivec2(&b, index, offset);216} else if (glsl_type_is_interface(deref->type)) {217/* This is the last deref before the block boundary.218* Everything after this point is a byte offset and will be219* handled by nir_lower_explicit_io().220*/221nir_ssa_def *index = get_block_array_index(&b, deref,222shader_program);223ptr = nir_vec2(&b, index, nir_imm_int(&b, offset));224} else {225/* This will get handled by nir_lower_explicit_io(). */226break;227}228229nir_deref_instr *cast = nir_build_deref_cast(&b, ptr, deref->modes,230deref->type, 0);231/* Set the alignment on the cast so that we get good alignment out232* of nir_lower_explicit_io. Our offset to the start of the UBO233* variable is always a constant, so we can use the maximum234* align_mul.235*/236cast->cast.align_mul = NIR_ALIGN_MUL_MAX;237cast->cast.align_offset = offset % NIR_ALIGN_MUL_MAX;238239nir_ssa_def_rewrite_uses(&deref->dest.ssa,240&cast->dest.ssa);241nir_deref_instr_remove_if_unused(deref);242break;243}244245case nir_instr_type_intrinsic: {246nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);247switch (intrin->intrinsic) {248case nir_intrinsic_load_deref: {249nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);250if (!nir_deref_mode_is_one_of(deref, nir_var_mem_ubo |251nir_var_mem_ssbo))252break;253254/* UBO and SSBO Booleans are 32-bit integers where any non-zero255* value is considered true. NIR Booleans, on the other hand256* are 1-bit values until you get to a very late stage of the257* compilation process. We need to turn those 1-bit loads into258* a 32-bit load wrapped in an i2b to get a proper NIR boolean259* from the SSBO.260*/261if (glsl_type_is_boolean(deref->type)) {262assert(intrin->dest.is_ssa);263b.cursor = nir_after_instr(&intrin->instr);264intrin->dest.ssa.bit_size = 32;265nir_ssa_def *bval = nir_i2b(&b, &intrin->dest.ssa);266nir_ssa_def_rewrite_uses_after(&intrin->dest.ssa,267bval,268bval->parent_instr);269progress = true;270}271break;272}273274case nir_intrinsic_store_deref: {275nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);276if (!nir_deref_mode_is_one_of(deref, nir_var_mem_ubo |277nir_var_mem_ssbo))278break;279280/* SSBO Booleans are 32-bit integers where any non-zero value281* is considered true. NIR Booleans, on the other hand are282* 1-bit values until you get to a very late stage of the283* compilation process. We need to turn those 1-bit stores284* into a b2i32 followed by a 32-bit store. Technically the285* value we write doesn't have to be 0/1 so once Booleans are286* lowered to 32-bit values, we have an unneeded sanitation287* step but in practice it doesn't cost much.288*/289if (glsl_type_is_boolean(deref->type)) {290assert(intrin->src[1].is_ssa);291b.cursor = nir_before_instr(&intrin->instr);292nir_ssa_def *ival = nir_b2i32(&b, intrin->src[1].ssa);293nir_instr_rewrite_src(&intrin->instr, &intrin->src[1],294nir_src_for_ssa(ival));295progress = true;296}297break;298}299300case nir_intrinsic_copy_deref:301unreachable("copy_deref should be lowered by now");302break;303304default:305/* Nothing to do */306break;307}308break;309}310311default:312break; /* Nothing to do */313}314}315}316317if (progress) {318nir_metadata_preserve(impl, nir_metadata_block_index |319nir_metadata_dominance);320}321322return progress;323}324325bool326gl_nir_lower_buffers(nir_shader *shader,327const struct gl_shader_program *shader_program)328{329bool progress = false;330331/* First, we lower the derefs to turn block variable and array derefs into332* a nir_address_format_32bit_index_offset pointer. From there forward,333* we leave the derefs in place and let nir_lower_explicit_io handle them.334*/335nir_foreach_function(function, shader) {336if (function->impl &&337lower_buffer_interface_derefs_impl(function->impl, shader_program))338progress = true;339}340341/* If that did something, we validate and then call nir_lower_explicit_io342* to finish the process.343*/344if (progress) {345nir_validate_shader(shader, "Lowering buffer interface derefs");346nir_lower_explicit_io(shader, nir_var_mem_ubo | nir_var_mem_ssbo,347nir_address_format_32bit_index_offset);348}349350return progress;351}352353354