mesa/src/panfrost/vulkan/panvk_shader.c

467 lines
16 KiB
C

/*
* Copyright © 2021 Collabora Ltd.
*
* Derived from tu_shader.c which is:
* Copyright © 2019 Google LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "panvk_private.h"
#include "nir_builder.h"
#include "nir_lower_blend.h"
#include "spirv/nir_spirv.h"
#include "util/mesa-sha1.h"
#include "panfrost-quirks.h"
#include "pan_shader.h"
static nir_shader *
panvk_spirv_to_nir(const void *code,
size_t codesize,
gl_shader_stage stage,
const char *entry_point_name,
const VkSpecializationInfo *spec_info,
const nir_shader_compiler_options *nir_options)
{
/* TODO these are made-up */
const struct spirv_to_nir_options spirv_options = {
.caps = { false },
.ubo_addr_format = nir_address_format_32bit_index_offset,
.ssbo_addr_format = nir_address_format_32bit_index_offset,
};
/* convert VkSpecializationInfo */
struct nir_spirv_specialization *spec = NULL;
uint32_t num_spec = 0;
if (spec_info && spec_info->mapEntryCount) {
spec = malloc(sizeof(*spec) * spec_info->mapEntryCount);
if (!spec)
return NULL;
for (uint32_t i = 0; i < spec_info->mapEntryCount; i++) {
const VkSpecializationMapEntry *entry = &spec_info->pMapEntries[i];
const void *data = spec_info->pData + entry->offset;
assert(data + entry->size <= spec_info->pData + spec_info->dataSize);
spec[i].id = entry->constantID;
switch (entry->size) {
case 8:
spec[i].value.u64 = *(const uint64_t *)data;
break;
case 4:
spec[i].value.u32 = *(const uint32_t *)data;
break;
case 2:
spec[i].value.u16 = *(const uint16_t *)data;
break;
case 1:
spec[i].value.u8 = *(const uint8_t *)data;
break;
default:
assert(!"Invalid spec constant size");
break;
}
spec[i].defined_on_module = false;
}
num_spec = spec_info->mapEntryCount;
}
nir_shader *nir = spirv_to_nir(code, codesize / sizeof(uint32_t), spec,
num_spec, stage, entry_point_name,
&spirv_options, nir_options);
free(spec);
assert(nir->info.stage == stage);
nir_validate_shader(nir, "after spirv_to_nir");
return nir;
}
struct panvk_lower_misc_ctx {
struct panvk_shader *shader;
const struct panvk_pipeline_layout *layout;
};
static unsigned
get_fixed_sampler_index(nir_deref_instr *deref,
const struct panvk_lower_misc_ctx *ctx)
{
nir_variable *var = nir_deref_instr_get_variable(deref);
unsigned set = var->data.descriptor_set;
unsigned binding = var->data.binding;
const struct panvk_descriptor_set_binding_layout *bind_layout =
&ctx->layout->sets[set].layout->bindings[binding];
return bind_layout->sampler_idx + ctx->layout->sets[set].sampler_offset;
}
static unsigned
get_fixed_texture_index(nir_deref_instr *deref,
const struct panvk_lower_misc_ctx *ctx)
{
nir_variable *var = nir_deref_instr_get_variable(deref);
unsigned set = var->data.descriptor_set;
unsigned binding = var->data.binding;
const struct panvk_descriptor_set_binding_layout *bind_layout =
&ctx->layout->sets[set].layout->bindings[binding];
return bind_layout->tex_idx + ctx->layout->sets[set].tex_offset;
}
static bool
lower_tex(nir_builder *b, nir_tex_instr *tex,
const struct panvk_lower_misc_ctx *ctx)
{
bool progress = false;
int sampler_src_idx = nir_tex_instr_src_index(tex, nir_tex_src_sampler_deref);
b->cursor = nir_before_instr(&tex->instr);
if (sampler_src_idx >= 0) {
nir_deref_instr *deref = nir_src_as_deref(tex->src[sampler_src_idx].src);
tex->sampler_index = get_fixed_sampler_index(deref, ctx);
nir_tex_instr_remove_src(tex, sampler_src_idx);
progress = true;
}
int tex_src_idx = nir_tex_instr_src_index(tex, nir_tex_src_texture_deref);
if (tex_src_idx >= 0) {
nir_deref_instr *deref = nir_src_as_deref(tex->src[tex_src_idx].src);
tex->texture_index = get_fixed_texture_index(deref, ctx);
nir_tex_instr_remove_src(tex, tex_src_idx);
progress = true;
}
return progress;
}
static void
lower_vulkan_resource_index(nir_builder *b, nir_intrinsic_instr *intr,
const struct panvk_lower_misc_ctx *ctx)
{
nir_ssa_def *vulkan_idx = intr->src[0].ssa;
unsigned set = nir_intrinsic_desc_set(intr);
unsigned binding = nir_intrinsic_binding(intr);
struct panvk_descriptor_set_layout *set_layout = ctx->layout->sets[set].layout;
struct panvk_descriptor_set_binding_layout *binding_layout =
&set_layout->bindings[binding];
unsigned base;
switch (binding_layout->type) {
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
base = binding_layout->ubo_idx + ctx->layout->sets[set].ubo_offset;
break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
base = binding_layout->ssbo_idx + ctx->layout->sets[set].ssbo_offset;
break;
default:
unreachable("Invalid descriptor type");
break;
}
b->cursor = nir_before_instr(&intr->instr);
nir_ssa_def *idx = nir_iadd(b, nir_imm_int(b, base), vulkan_idx);
nir_ssa_def_rewrite_uses(&intr->dest.ssa, idx);
nir_instr_remove(&intr->instr);
}
static void
lower_load_vulkan_descriptor(nir_builder *b, nir_intrinsic_instr *intrin)
{
/* Loading the descriptor happens as part of the load/store instruction so
* this is a no-op.
*/
b->cursor = nir_before_instr(&intrin->instr);
nir_ssa_def *val = nir_vec2(b, intrin->src[0].ssa, nir_imm_int(b, 0));
nir_ssa_def_rewrite_uses(&intrin->dest.ssa, val);
nir_instr_remove(&intrin->instr);
}
static bool
lower_intrinsic(nir_builder *b, nir_intrinsic_instr *intr,
const struct panvk_lower_misc_ctx *ctx)
{
switch (intr->intrinsic) {
case nir_intrinsic_vulkan_resource_index:
lower_vulkan_resource_index(b, intr, ctx);
return true;
case nir_intrinsic_load_vulkan_descriptor:
lower_load_vulkan_descriptor(b, intr);
return true;
default:
return false;
}
}
static bool
panvk_lower_misc_instr(nir_builder *b,
nir_instr *instr,
void *data)
{
const struct panvk_lower_misc_ctx *ctx = data;
switch (instr->type) {
case nir_instr_type_tex:
return lower_tex(b, nir_instr_as_tex(instr), ctx);
case nir_instr_type_intrinsic:
return lower_intrinsic(b, nir_instr_as_intrinsic(instr), ctx);
default:
return false;
}
}
static bool
panvk_lower_misc(nir_shader *nir, const struct panvk_lower_misc_ctx *ctx)
{
return nir_shader_instructions_pass(nir, panvk_lower_misc_instr,
nir_metadata_block_index |
nir_metadata_dominance,
(void *)ctx);
}
static void
panvk_lower_blend(struct panfrost_device *pdev,
nir_shader *nir,
struct pan_blend_state *blend_state,
bool static_blend_constants)
{
nir_lower_blend_options options = {
.logicop_enable = blend_state->logicop_enable,
.logicop_func = blend_state->logicop_func,
};
bool lower_blend = false;
for (unsigned rt = 0; rt < blend_state->rt_count; rt++) {
if (!panvk_blend_needs_lowering(pdev, blend_state, rt))
continue;
const struct pan_blend_rt_state *rt_state = &blend_state->rts[rt];
options.rt[rt].colormask = rt_state->equation.color_mask;
options.format[rt] = rt_state->format;
if (!rt_state->equation.blend_enable) {
static const nir_lower_blend_channel replace = {
.func = BLEND_FUNC_ADD,
.src_factor = BLEND_FACTOR_ZERO,
.invert_src_factor = true,
.dst_factor = BLEND_FACTOR_ZERO,
.invert_dst_factor = false,
};
options.rt[rt].rgb = replace;
options.rt[rt].alpha = replace;
} else {
options.rt[rt].rgb.func = rt_state->equation.rgb_func;
options.rt[rt].rgb.src_factor = rt_state->equation.rgb_src_factor;
options.rt[rt].rgb.invert_src_factor = rt_state->equation.rgb_invert_src_factor;
options.rt[rt].rgb.dst_factor = rt_state->equation.rgb_dst_factor;
options.rt[rt].rgb.invert_dst_factor = rt_state->equation.rgb_invert_dst_factor;
options.rt[rt].alpha.func = rt_state->equation.alpha_func;
options.rt[rt].alpha.src_factor = rt_state->equation.alpha_src_factor;
options.rt[rt].alpha.invert_src_factor = rt_state->equation.alpha_invert_src_factor;
options.rt[rt].alpha.dst_factor = rt_state->equation.alpha_dst_factor;
options.rt[rt].alpha.invert_dst_factor = rt_state->equation.alpha_invert_dst_factor;
}
lower_blend = true;
}
/* FIXME: currently untested */
assert(!lower_blend);
if (lower_blend)
NIR_PASS_V(nir, nir_lower_blend, options);
}
struct panvk_shader *
panvk_shader_create(struct panvk_device *dev,
gl_shader_stage stage,
const VkPipelineShaderStageCreateInfo *stage_info,
const struct panvk_pipeline_layout *layout,
unsigned sysval_ubo,
struct pan_blend_state *blend_state,
bool static_blend_constants,
const VkAllocationCallbacks *alloc)
{
const struct panvk_shader_module *module = panvk_shader_module_from_handle(stage_info->module);
struct panfrost_device *pdev = &dev->physical_device->pdev;
struct panvk_shader *shader;
shader = vk_zalloc2(&dev->vk.alloc, alloc, sizeof(*shader), 8,
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (!shader)
return NULL;
util_dynarray_init(&shader->binary, NULL);
/* translate SPIR-V to NIR */
assert(module->code_size % 4 == 0);
nir_shader *nir = panvk_spirv_to_nir(module->code,
module->code_size,
stage, stage_info->pName,
stage_info->pSpecializationInfo,
pan_shader_get_compiler_options(pdev));
if (!nir) {
vk_free2(&dev->vk.alloc, alloc, shader);
return NULL;
}
if (stage == MESA_SHADER_FRAGMENT)
panvk_lower_blend(pdev, nir, blend_state, static_blend_constants);
/* multi step inlining procedure */
NIR_PASS_V(nir, nir_lower_variable_initializers, nir_var_function_temp);
NIR_PASS_V(nir, nir_lower_returns);
NIR_PASS_V(nir, nir_inline_functions);
NIR_PASS_V(nir, nir_copy_prop);
NIR_PASS_V(nir, nir_opt_deref);
foreach_list_typed_safe(nir_function, func, node, &nir->functions) {
if (!func->is_entrypoint)
exec_node_remove(&func->node);
}
assert(exec_list_length(&nir->functions) == 1);
NIR_PASS_V(nir, nir_lower_variable_initializers, ~nir_var_function_temp);
/* Split member structs. We do this before lower_io_to_temporaries so that
* it doesn't lower system values to temporaries by accident.
*/
NIR_PASS_V(nir, nir_split_var_copies);
NIR_PASS_V(nir, nir_split_per_member_structs);
NIR_PASS_V(nir, nir_remove_dead_variables,
nir_var_shader_in | nir_var_shader_out |
nir_var_system_value | nir_var_mem_shared,
NULL);
NIR_PASS_V(nir, nir_lower_io_to_temporaries,
nir_shader_get_entrypoint(nir), true, true);
NIR_PASS_V(nir, nir_lower_indirect_derefs,
nir_var_shader_in | nir_var_shader_out,
UINT32_MAX);
NIR_PASS_V(nir, nir_opt_copy_prop_vars);
NIR_PASS_V(nir, nir_opt_combine_stores, nir_var_all);
NIR_PASS_V(nir, nir_lower_uniforms_to_ubo, true, false);
NIR_PASS_V(nir, nir_lower_explicit_io,
nir_var_mem_ubo | nir_var_mem_ssbo,
nir_address_format_32bit_index_offset);
nir_assign_io_var_locations(nir, nir_var_shader_in, &nir->num_inputs, stage);
nir_assign_io_var_locations(nir, nir_var_shader_out, &nir->num_outputs, stage);
NIR_PASS_V(nir, nir_lower_system_values);
NIR_PASS_V(nir, nir_lower_compute_system_values, NULL);
NIR_PASS_V(nir, nir_lower_var_copies);
struct panvk_lower_misc_ctx ctx = {
.shader = shader,
.layout = layout,
};
NIR_PASS_V(nir, panvk_lower_misc, &ctx);
nir_shader_gather_info(nir, nir_shader_get_entrypoint(nir));
if (unlikely(dev->physical_device->instance->debug_flags & PANVK_DEBUG_NIR)) {
fprintf(stderr, "translated nir:\n");
nir_print_shader(nir, stderr);
}
struct panfrost_compile_inputs inputs = {
.gpu_id = pdev->gpu_id,
.no_ubo_to_push = true,
.sysval_ubo = sysval_ubo,
};
pan_shader_compile(pdev, nir, &inputs, &shader->binary, &shader->info);
/* Patch the descriptor count */
shader->info.ubo_count =
shader->info.sysvals.sysval_count ? sysval_ubo + 1 : layout->num_ubos;
shader->info.sampler_count = layout->num_samplers;
shader->info.texture_count = layout->num_textures;
shader->sysval_ubo = sysval_ubo;
ralloc_free(nir);
return shader;
}
void
panvk_shader_destroy(struct panvk_device *dev,
struct panvk_shader *shader,
const VkAllocationCallbacks *alloc)
{
util_dynarray_fini(&shader->binary);
vk_free2(&dev->vk.alloc, alloc, shader);
}
VkResult
panvk_CreateShaderModule(VkDevice _device,
const VkShaderModuleCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkShaderModule *pShaderModule)
{
VK_FROM_HANDLE(panvk_device, device, _device);
struct panvk_shader_module *module;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO);
assert(pCreateInfo->flags == 0);
assert(pCreateInfo->codeSize % 4 == 0);
module = vk_object_zalloc(&device->vk, pAllocator,
sizeof(*module) + pCreateInfo->codeSize,
VK_OBJECT_TYPE_SHADER_MODULE);
if (module == NULL)
return vk_error(device->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
module->code_size = pCreateInfo->codeSize;
memcpy(module->code, pCreateInfo->pCode, pCreateInfo->codeSize);
_mesa_sha1_compute(module->code, module->code_size, module->sha1);
*pShaderModule = panvk_shader_module_to_handle(module);
return VK_SUCCESS;
}
void
panvk_DestroyShaderModule(VkDevice _device,
VkShaderModule _module,
const VkAllocationCallbacks *pAllocator)
{
VK_FROM_HANDLE(panvk_device, device, _device);
VK_FROM_HANDLE(panvk_shader_module, module, _module);
if (!module)
return;
vk_object_free(&device->vk, pAllocator, module);
}